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
|
# all gen targets should be added here and to gen/mark-fresh
|
||||||
gen: \
|
gen: \
|
||||||
coderd/database/dump.sql \
|
|
||||||
$(DB_GEN_FILES) \
|
|
||||||
provisionersdk/proto/provisioner.pb.go \
|
provisionersdk/proto/provisioner.pb.go \
|
||||||
provisionerd/proto/provisionerd.pb.go \
|
provisionerd/proto/provisionerd.pb.go \
|
||||||
|
coderd/database/dump.sql \
|
||||||
|
$(DB_GEN_FILES) \
|
||||||
site/src/api/typesGenerated.ts \
|
site/src/api/typesGenerated.ts \
|
||||||
coderd/rbac/object_gen.go \
|
coderd/rbac/object_gen.go \
|
||||||
docs/admin/prometheus.md \
|
docs/admin/prometheus.md \
|
||||||
|
@ -478,10 +478,10 @@ gen: \
|
||||||
# used during releases so we don't run generation scripts.
|
# used during releases so we don't run generation scripts.
|
||||||
gen/mark-fresh:
|
gen/mark-fresh:
|
||||||
files="\
|
files="\
|
||||||
coderd/database/dump.sql \
|
|
||||||
$(DB_GEN_FILES) \
|
|
||||||
provisionersdk/proto/provisioner.pb.go \
|
provisionersdk/proto/provisioner.pb.go \
|
||||||
provisionerd/proto/provisionerd.pb.go \
|
provisionerd/proto/provisionerd.pb.go \
|
||||||
|
coderd/database/dump.sql \
|
||||||
|
$(DB_GEN_FILES) \
|
||||||
site/src/api/typesGenerated.ts \
|
site/src/api/typesGenerated.ts \
|
||||||
coderd/rbac/object_gen.go \
|
coderd/rbac/object_gen.go \
|
||||||
docs/admin/prometheus.md \
|
docs/admin/prometheus.md \
|
||||||
|
|
|
@ -75,9 +75,9 @@ func TestWorkspaceAgent(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "somename",
|
Name: "somename",
|
||||||
Type: "someinstance",
|
Type: "someinstance",
|
||||||
|
@ -127,9 +127,9 @@ func TestWorkspaceAgent(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "somename",
|
Name: "somename",
|
||||||
Type: "someinstance",
|
Type: "someinstance",
|
||||||
|
@ -179,9 +179,9 @@ func TestWorkspaceAgent(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "somename",
|
Name: "somename",
|
||||||
Type: "someinstance",
|
Type: "someinstance",
|
||||||
|
|
|
@ -82,9 +82,9 @@ func TestConfigSSH(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: []*proto.Provision_Response{{
|
ProvisionPlan: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Plan{
|
||||||
Complete: &proto.Provision_Complete{
|
Plan: &proto.PlanComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
@ -720,22 +720,11 @@ func TestConfigSSH_Hostnames(t *testing.T) {
|
||||||
resources = append(resources, resource)
|
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})
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
// authToken := uuid.NewString()
|
// authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID,
|
||||||
Parse: echo.ParseComplete,
|
echo.WithResources(resources))
|
||||||
ProvisionPlan: provisionResponse,
|
|
||||||
ProvisionApply: provisionResponse,
|
|
||||||
})
|
|
||||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.ID)
|
||||||
|
|
|
@ -29,11 +29,7 @@ func TestCreate(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, completeWithAgent())
|
||||||
Parse: echo.ParseComplete,
|
|
||||||
ProvisionApply: provisionCompleteWithAgent,
|
|
||||||
ProvisionPlan: provisionCompleteWithAgent,
|
|
||||||
})
|
|
||||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
args := []string{
|
args := []string{
|
||||||
|
@ -84,11 +80,7 @@ func TestCreate(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||||
owner := coderdtest.CreateFirstUser(t, client)
|
owner := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, completeWithAgent())
|
||||||
Parse: echo.ParseComplete,
|
|
||||||
ProvisionApply: provisionCompleteWithAgent,
|
|
||||||
ProvisionPlan: provisionCompleteWithAgent,
|
|
||||||
})
|
|
||||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||||
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, version.ID)
|
||||||
_, user := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
|
_, user := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID)
|
||||||
|
@ -141,11 +133,7 @@ func TestCreate(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, completeWithAgent())
|
||||||
Parse: echo.ParseComplete,
|
|
||||||
ProvisionApply: provisionCompleteWithAgent,
|
|
||||||
ProvisionPlan: provisionCompleteWithAgent,
|
|
||||||
})
|
|
||||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||||
var defaultTTLMillis int64 = 2 * 60 * 60 * 1000 // 2 hours
|
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) {
|
func TestCreateWithRichParameters(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
@ -258,27 +262,12 @@ func TestCreateWithRichParameters(t *testing.T) {
|
||||||
immutableParameterValue = "4"
|
immutableParameterValue = "4"
|
||||||
)
|
)
|
||||||
|
|
||||||
echoResponses := &echo.Responses{
|
echoResponses := prepareEchoResponses([]*proto.RichParameter{
|
||||||
Parse: echo.ParseComplete,
|
{Name: firstParameterName, Description: firstParameterDescription, Mutable: true},
|
||||||
ProvisionPlan: []*proto.Provision_Response{
|
{Name: secondParameterName, DisplayName: secondParameterDisplayName, Description: secondParameterDescription, Mutable: true},
|
||||||
{
|
{Name: immutableParameterName, Description: immutableParameterDescription, Mutable: false},
|
||||||
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{},
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("InputParameters", func(t *testing.T) {
|
t.Run("InputParameters", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
@ -427,28 +416,6 @@ func TestCreateValidateRichParameters(t *testing.T) {
|
||||||
{Name: boolParameterName, Type: "bool", Mutable: true},
|
{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.Run("ValidateString", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
@ -626,20 +593,16 @@ func TestCreateWithGitAuth(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
echoResponses := &echo.Responses{
|
echoResponses := &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: []*proto.Provision_Response{
|
ProvisionPlan: []*proto.Response{
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Plan{
|
||||||
Complete: &proto.Provision_Complete{
|
Plan: &proto.PlanComplete{
|
||||||
GitAuthProviders: []string{"github"},
|
GitAuthProviders: []string{"github"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: echo.ApplyComplete,
|
||||||
Type: &proto.Provision_Response_Complete{
|
|
||||||
Complete: &proto.Provision_Complete{},
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client := coderdtest.New(t, &coderdtest.Options{
|
client := coderdtest.New(t, &coderdtest.Options{
|
||||||
|
|
|
@ -48,7 +48,7 @@ func prepareTestGitSSH(ctx context.Context, t *testing.T) (*codersdk.Client, str
|
||||||
agentToken := uuid.NewString()
|
agentToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(agentToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(agentToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
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()
|
agentToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, orgID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, orgID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(agentToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(agentToken),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -20,30 +20,14 @@ import (
|
||||||
func TestRestart(t *testing.T) {
|
func TestRestart(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
echoResponses := &echo.Responses{
|
echoResponses := prepareEchoResponses([]*proto.RichParameter{
|
||||||
Parse: echo.ParseComplete,
|
{
|
||||||
ProvisionPlan: []*proto.Provision_Response{
|
Name: ephemeralParameterName,
|
||||||
{
|
Description: ephemeralParameterDescription,
|
||||||
Type: &proto.Provision_Response_Complete{
|
Mutable: true,
|
||||||
Complete: &proto.Provision_Complete{
|
Ephemeral: true,
|
||||||
Parameters: []*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.Run("OK", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
@ -187,10 +171,10 @@ func TestRestartWithParameters(t *testing.T) {
|
||||||
|
|
||||||
echoResponses := &echo.Responses{
|
echoResponses := &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: []*proto.Provision_Response{
|
ProvisionPlan: []*proto.Response{
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Plan{
|
||||||
Complete: &proto.Provision_Complete{
|
Plan: &proto.PlanComplete{
|
||||||
Parameters: []*proto.RichParameter{
|
Parameters: []*proto.RichParameter{
|
||||||
{
|
{
|
||||||
Name: immutableParameterName,
|
Name: immutableParameterName,
|
||||||
|
@ -202,11 +186,7 @@ func TestRestartWithParameters(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: echo.ApplyComplete,
|
||||||
Type: &proto.Provision_Response_Complete{
|
|
||||||
Complete: &proto.Provision_Complete{},
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("DoNotAskForImmutables", func(t *testing.T) {
|
t.Run("DoNotAskForImmutables", func(t *testing.T) {
|
||||||
|
|
|
@ -41,7 +41,6 @@ import (
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/collectors"
|
"github.com/prometheus/client_golang/prometheus/collectors"
|
||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
"github.com/spf13/afero"
|
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
"golang.org/x/mod/semver"
|
"golang.org/x/mod/semver"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
@ -1304,7 +1303,11 @@ func newProvisionerDaemon(
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
defer cancel()
|
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 {
|
if err != nil {
|
||||||
select {
|
select {
|
||||||
case errCh <- err:
|
case errCh <- err:
|
||||||
|
@ -1336,10 +1339,11 @@ func newProvisionerDaemon(
|
||||||
|
|
||||||
err := terraform.Serve(ctx, &terraform.ServeOptions{
|
err := terraform.Serve(ctx, &terraform.ServeOptions{
|
||||||
ServeOptions: &provisionersdk.ServeOptions{
|
ServeOptions: &provisionersdk.ServeOptions{
|
||||||
Listener: terraformServer,
|
Listener: terraformServer,
|
||||||
|
Logger: logger.Named("terraform"),
|
||||||
|
WorkDirectory: workDir,
|
||||||
},
|
},
|
||||||
CachePath: tfDir,
|
CachePath: tfDir,
|
||||||
Logger: logger.Named("terraform"),
|
|
||||||
Tracer: tracer,
|
Tracer: tracer,
|
||||||
})
|
})
|
||||||
if err != nil && !xerrors.Is(err, context.Canceled) {
|
if err != nil && !xerrors.Is(err, context.Canceled) {
|
||||||
|
@ -1366,7 +1370,6 @@ func newProvisionerDaemon(
|
||||||
UpdateInterval: time.Second,
|
UpdateInterval: time.Second,
|
||||||
ForceCancelInterval: cfg.Provisioner.ForceCancelInterval.Value(),
|
ForceCancelInterval: cfg.Provisioner.ForceCancelInterval.Value(),
|
||||||
Provisioners: provisioners,
|
Provisioners: provisioners,
|
||||||
WorkDirectory: workDir,
|
|
||||||
TracerProvider: coderAPI.TracerProvider,
|
TracerProvider: coderAPI.TracerProvider,
|
||||||
Metrics: &metrics,
|
Metrics: &metrics,
|
||||||
}), nil
|
}), nil
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
|
|
||||||
"github.com/coder/coder/v2/cli/clitest"
|
"github.com/coder/coder/v2/cli/clitest"
|
||||||
"github.com/coder/coder/v2/coderd/coderdtest"
|
"github.com/coder/coder/v2/coderd/coderdtest"
|
||||||
"github.com/coder/coder/v2/provisioner/echo"
|
|
||||||
"github.com/coder/coder/v2/pty/ptytest"
|
"github.com/coder/coder/v2/pty/ptytest"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,11 +16,7 @@ func TestShow(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, completeWithAgent())
|
||||||
Parse: echo.ParseComplete,
|
|
||||||
ProvisionApply: provisionCompleteWithAgent,
|
|
||||||
ProvisionPlan: provisionCompleteWithAgent,
|
|
||||||
})
|
|
||||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
workspace := coderdtest.CreateWorkspace(t, client, user.OrganizationID, template.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()
|
agentToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "dev",
|
Name: "dev",
|
||||||
Type: "google_compute_instance",
|
Type: "google_compute_instance",
|
||||||
|
|
|
@ -33,10 +33,10 @@ func TestStart(t *testing.T) {
|
||||||
|
|
||||||
echoResponses := &echo.Responses{
|
echoResponses := &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: []*proto.Provision_Response{
|
ProvisionPlan: []*proto.Response{
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Plan{
|
||||||
Complete: &proto.Provision_Complete{
|
Plan: &proto.PlanComplete{
|
||||||
Parameters: []*proto.RichParameter{
|
Parameters: []*proto.RichParameter{
|
||||||
{
|
{
|
||||||
Name: ephemeralParameterName,
|
Name: ephemeralParameterName,
|
||||||
|
@ -49,11 +49,7 @@ func TestStart(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: echo.ApplyComplete,
|
||||||
Type: &proto.Provision_Response_Complete{
|
|
||||||
Complete: &proto.Provision_Complete{},
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("BuildOptions", func(t *testing.T) {
|
t.Run("BuildOptions", func(t *testing.T) {
|
||||||
|
@ -151,10 +147,10 @@ func TestStartWithParameters(t *testing.T) {
|
||||||
|
|
||||||
echoResponses := &echo.Responses{
|
echoResponses := &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: []*proto.Provision_Response{
|
ProvisionPlan: []*proto.Response{
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Plan{
|
||||||
Complete: &proto.Provision_Complete{
|
Plan: &proto.PlanComplete{
|
||||||
Parameters: []*proto.RichParameter{
|
Parameters: []*proto.RichParameter{
|
||||||
{
|
{
|
||||||
Name: immutableParameterName,
|
Name: immutableParameterName,
|
||||||
|
@ -166,11 +162,7 @@ func TestStartWithParameters(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: echo.ApplyComplete,
|
||||||
Type: &proto.Provision_Response_Complete{
|
|
||||||
Complete: &proto.Provision_Complete{},
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("DoNotAskForImmutables", func(t *testing.T) {
|
t.Run("DoNotAskForImmutables", func(t *testing.T) {
|
||||||
|
|
|
@ -25,9 +25,9 @@ func TestStatePull(t *testing.T) {
|
||||||
wantState := []byte("some state")
|
wantState := []byte("some state")
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
State: wantState,
|
State: wantState,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -53,9 +53,9 @@ func TestStatePull(t *testing.T) {
|
||||||
wantState := []byte("some state")
|
wantState := []byte("some state")
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
State: wantState,
|
State: wantState,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -83,7 +83,7 @@ func TestStatePush(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: echo.ProvisionComplete,
|
ProvisionApply: echo.ApplyComplete,
|
||||||
})
|
})
|
||||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, 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)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: echo.ProvisionComplete,
|
ProvisionApply: echo.ApplyComplete,
|
||||||
})
|
})
|
||||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
|
|
@ -19,26 +19,52 @@ import (
|
||||||
"github.com/coder/coder/v2/testutil"
|
"github.com/coder/coder/v2/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var provisionCompleteWithAgent = []*proto.Provision_Response{
|
func completeWithAgent() *echo.Responses {
|
||||||
{
|
return &echo.Responses{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Parse: echo.ParseComplete,
|
||||||
Complete: &proto.Provision_Complete{
|
ProvisionPlan: []*proto.Response{
|
||||||
Resources: []*proto.Resource{
|
{
|
||||||
{
|
Type: &proto.Response_Plan{
|
||||||
Type: "compute",
|
Plan: &proto.PlanComplete{
|
||||||
Name: "main",
|
Resources: []*proto.Resource{
|
||||||
Agents: []*proto.Agent{
|
|
||||||
{
|
{
|
||||||
Name: "smith",
|
Type: "compute",
|
||||||
OperatingSystem: "linux",
|
Name: "main",
|
||||||
Architecture: "i386",
|
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) {
|
func TestTemplateCreate(t *testing.T) {
|
||||||
|
@ -47,10 +73,7 @@ func TestTemplateCreate(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||||
coderdtest.CreateFirstUser(t, client)
|
coderdtest.CreateFirstUser(t, client)
|
||||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
source := clitest.CreateTemplateVersionSource(t, completeWithAgent())
|
||||||
Parse: echo.ParseComplete,
|
|
||||||
ProvisionApply: provisionCompleteWithAgent,
|
|
||||||
})
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"templates",
|
"templates",
|
||||||
"create",
|
"create",
|
||||||
|
@ -85,10 +108,7 @@ func TestTemplateCreate(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||||
coderdtest.CreateFirstUser(t, client)
|
coderdtest.CreateFirstUser(t, client)
|
||||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
source := clitest.CreateTemplateVersionSource(t, completeWithAgent())
|
||||||
Parse: echo.ParseComplete,
|
|
||||||
ProvisionApply: provisionCompleteWithAgent,
|
|
||||||
})
|
|
||||||
require.NoError(t, os.Remove(filepath.Join(source, ".terraform.lock.hcl")))
|
require.NoError(t, os.Remove(filepath.Join(source, ".terraform.lock.hcl")))
|
||||||
args := []string{
|
args := []string{
|
||||||
"templates",
|
"templates",
|
||||||
|
@ -128,10 +148,7 @@ func TestTemplateCreate(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||||
coderdtest.CreateFirstUser(t, client)
|
coderdtest.CreateFirstUser(t, client)
|
||||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
source := clitest.CreateTemplateVersionSource(t, completeWithAgent())
|
||||||
Parse: echo.ParseComplete,
|
|
||||||
ProvisionApply: provisionCompleteWithAgent,
|
|
||||||
})
|
|
||||||
require.NoError(t, os.Remove(filepath.Join(source, ".terraform.lock.hcl")))
|
require.NoError(t, os.Remove(filepath.Join(source, ".terraform.lock.hcl")))
|
||||||
args := []string{
|
args := []string{
|
||||||
"templates",
|
"templates",
|
||||||
|
@ -167,10 +184,7 @@ func TestTemplateCreate(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||||
coderdtest.CreateFirstUser(t, client)
|
coderdtest.CreateFirstUser(t, client)
|
||||||
source, err := echo.Tar(&echo.Responses{
|
source, err := echo.Tar(completeWithAgent())
|
||||||
Parse: echo.ParseComplete,
|
|
||||||
ProvisionApply: provisionCompleteWithAgent,
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
args := []string{
|
args := []string{
|
||||||
|
@ -196,10 +210,7 @@ func TestTemplateCreate(t *testing.T) {
|
||||||
coderdtest.CreateFirstUser(t, client)
|
coderdtest.CreateFirstUser(t, client)
|
||||||
|
|
||||||
create := func() error {
|
create := func() error {
|
||||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
source := clitest.CreateTemplateVersionSource(t, completeWithAgent())
|
||||||
Parse: echo.ParseComplete,
|
|
||||||
ProvisionApply: provisionCompleteWithAgent,
|
|
||||||
})
|
|
||||||
args := []string{
|
args := []string{
|
||||||
"templates",
|
"templates",
|
||||||
"create",
|
"create",
|
||||||
|
|
|
@ -205,9 +205,9 @@ func TestTemplatePull(t *testing.T) {
|
||||||
// a template version source.
|
// a template version source.
|
||||||
func genTemplateVersionSource() *echo.Responses {
|
func genTemplateVersionSource() *echo.Responses {
|
||||||
return &echo.Responses{
|
return &echo.Responses{
|
||||||
Parse: []*proto.Parse_Response{
|
Parse: []*proto.Response{
|
||||||
{
|
{
|
||||||
Type: &proto.Parse_Response_Log{
|
Type: &proto.Response_Log{
|
||||||
Log: &proto.Log{
|
Log: &proto.Log{
|
||||||
Output: uuid.NewString(),
|
Output: uuid.NewString(),
|
||||||
},
|
},
|
||||||
|
@ -215,11 +215,11 @@ func genTemplateVersionSource() *echo.Responses {
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
Type: &proto.Parse_Response_Complete{
|
Type: &proto.Response_Parse{
|
||||||
Complete: &proto.Parse_Complete{},
|
Parse: &proto.ParseComplete{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ProvisionApply: echo.ProvisionComplete,
|
ProvisionApply: echo.ApplyComplete,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ func TestTemplatePush(t *testing.T) {
|
||||||
// Test the cli command.
|
// Test the cli command.
|
||||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
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")
|
inv, root := clitest.New(t, "templates", "push", template.Name, "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho), "--name", "example")
|
||||||
clitest.SetupConfig(t, client, root)
|
clitest.SetupConfig(t, client, root)
|
||||||
|
@ -82,7 +82,7 @@ func TestTemplatePush(t *testing.T) {
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: echo.ProvisionComplete,
|
ProvisionApply: echo.ApplyComplete,
|
||||||
})
|
})
|
||||||
|
|
||||||
wantMessage := strings.Repeat("a", 72)
|
wantMessage := strings.Repeat("a", 72)
|
||||||
|
@ -121,7 +121,7 @@ func TestTemplatePush(t *testing.T) {
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: echo.ProvisionComplete,
|
ProvisionApply: echo.ApplyComplete,
|
||||||
})
|
})
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
ctx, cancel := context.WithTimeout(context.Background(), testutil.WaitLong)
|
||||||
|
@ -168,7 +168,7 @@ func TestTemplatePush(t *testing.T) {
|
||||||
// Test the cli command.
|
// Test the cli command.
|
||||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: echo.ProvisionComplete,
|
ProvisionApply: echo.ApplyComplete,
|
||||||
})
|
})
|
||||||
require.NoError(t, os.Remove(filepath.Join(source, ".terraform.lock.hcl")))
|
require.NoError(t, os.Remove(filepath.Join(source, ".terraform.lock.hcl")))
|
||||||
|
|
||||||
|
@ -211,7 +211,7 @@ func TestTemplatePush(t *testing.T) {
|
||||||
// Test the cli command.
|
// Test the cli command.
|
||||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: echo.ProvisionComplete,
|
ProvisionApply: echo.ApplyComplete,
|
||||||
})
|
})
|
||||||
require.NoError(t, os.Remove(filepath.Join(source, ".terraform.lock.hcl")))
|
require.NoError(t, os.Remove(filepath.Join(source, ".terraform.lock.hcl")))
|
||||||
|
|
||||||
|
@ -248,7 +248,7 @@ func TestTemplatePush(t *testing.T) {
|
||||||
// Test the cli command.
|
// Test the cli command.
|
||||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
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")
|
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)
|
clitest.SetupConfig(t, client, root)
|
||||||
|
@ -293,7 +293,7 @@ func TestTemplatePush(t *testing.T) {
|
||||||
// Test the cli command.
|
// Test the cli command.
|
||||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: echo.ProvisionComplete,
|
ProvisionApply: echo.ApplyComplete,
|
||||||
})
|
})
|
||||||
|
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID,
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID,
|
||||||
|
@ -340,7 +340,7 @@ func TestTemplatePush(t *testing.T) {
|
||||||
|
|
||||||
source, err := echo.Tar(&echo.Responses{
|
source, err := echo.Tar(&echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: echo.ProvisionComplete,
|
ProvisionApply: echo.ApplyComplete,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -619,10 +619,7 @@ func TestTemplatePush(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
|
source := clitest.CreateTemplateVersionSource(t, completeWithAgent())
|
||||||
Parse: echo.ParseComplete,
|
|
||||||
ProvisionApply: provisionCompleteWithAgent,
|
|
||||||
})
|
|
||||||
|
|
||||||
const templateName = "my-template"
|
const templateName = "my-template"
|
||||||
args := []string{
|
args := []string{
|
||||||
|
@ -665,16 +662,16 @@ func TestTemplatePush(t *testing.T) {
|
||||||
|
|
||||||
func createEchoResponsesWithTemplateVariables(templateVariables []*proto.TemplateVariable) *echo.Responses {
|
func createEchoResponsesWithTemplateVariables(templateVariables []*proto.TemplateVariable) *echo.Responses {
|
||||||
return &echo.Responses{
|
return &echo.Responses{
|
||||||
Parse: []*proto.Parse_Response{
|
Parse: []*proto.Response{
|
||||||
{
|
{
|
||||||
Type: &proto.Parse_Response_Complete{
|
Type: &proto.Response_Parse{
|
||||||
Complete: &proto.Parse_Complete{
|
Parse: &proto.ParseComplete{
|
||||||
TemplateVariables: templateVariables,
|
TemplateVariables: templateVariables,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionComplete,
|
ProvisionApply: echo.ApplyComplete,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,8 +57,8 @@ func TestUpdate(t *testing.T) {
|
||||||
|
|
||||||
version2 := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version2 := coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: echo.ProvisionComplete,
|
ProvisionApply: echo.ApplyComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
}, template.ID)
|
}, template.ID)
|
||||||
_ = coderdtest.AwaitTemplateVersionJob(t, client, version2.ID)
|
_ = coderdtest.AwaitTemplateVersionJob(t, client, version2.ID)
|
||||||
|
|
||||||
|
@ -100,28 +100,13 @@ func TestUpdateWithRichParameters(t *testing.T) {
|
||||||
immutableParameterValue = "4"
|
immutableParameterValue = "4"
|
||||||
)
|
)
|
||||||
|
|
||||||
echoResponses := &echo.Responses{
|
echoResponses := prepareEchoResponses([]*proto.RichParameter{
|
||||||
Parse: echo.ParseComplete,
|
{Name: firstParameterName, Description: firstParameterDescription, Mutable: true},
|
||||||
ProvisionPlan: []*proto.Provision_Response{
|
{Name: immutableParameterName, Description: immutableParameterDescription, Mutable: false},
|
||||||
{
|
{Name: secondParameterName, Description: secondParameterDescription, Mutable: true},
|
||||||
Type: &proto.Provision_Response_Complete{
|
{Name: ephemeralParameterName, Description: ephemeralParameterDescription, Mutable: true, Ephemeral: true},
|
||||||
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{},
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("ImmutableCannotBeCustomized", func(t *testing.T) {
|
t.Run("ImmutableCannotBeCustomized", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
@ -313,28 +298,6 @@ func TestUpdateValidateRichParameters(t *testing.T) {
|
||||||
{Name: boolParameterName, Type: "bool", Mutable: true},
|
{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.Run("ValidateString", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ import (
|
||||||
"github.com/coder/coder/v2/codersdk"
|
"github.com/coder/coder/v2/codersdk"
|
||||||
"github.com/coder/coder/v2/codersdk/agentsdk"
|
"github.com/coder/coder/v2/codersdk/agentsdk"
|
||||||
"github.com/coder/coder/v2/provisioner/echo"
|
"github.com/coder/coder/v2/provisioner/echo"
|
||||||
"github.com/coder/coder/v2/provisionersdk/proto"
|
|
||||||
"github.com/coder/coder/v2/testutil"
|
"github.com/coder/coder/v2/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -60,25 +59,9 @@ func TestWorkspaceActivityBump(t *testing.T) {
|
||||||
ttlMillis := int64(ttl / time.Millisecond)
|
ttlMillis := int64(ttl / time.Millisecond)
|
||||||
agentToken := uuid.NewString()
|
agentToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: echo.ProvisionApplyWithAgent(agentToken),
|
||||||
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,
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||||
|
|
|
@ -683,8 +683,8 @@ func TestExecutorFailedWorkspace(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionFailed,
|
ProvisionApply: echo.ApplyFailed,
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||||
ctr.FailureTTLMillis = ptr.Ref[int64](failureTTL.Milliseconds())
|
ctr.FailureTTLMillis = ptr.Ref[int64](failureTTL.Milliseconds())
|
||||||
|
@ -733,8 +733,8 @@ func TestExecutorInactiveWorkspace(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionComplete,
|
ProvisionApply: echo.ApplyComplete,
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||||
ctr.TimeTilDormantMillis = ptr.Ref[int64](inactiveTTL.Milliseconds())
|
ctr.TimeTilDormantMillis = ptr.Ref[int64](inactiveTTL.Milliseconds())
|
||||||
|
@ -766,22 +766,16 @@ func mustProvisionWorkspaceWithParameters(t *testing.T, client *codersdk.Client,
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: []*proto.Provision_Response{
|
ProvisionPlan: []*proto.Response{
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Plan{
|
||||||
Complete: &proto.Provision_Complete{
|
Plan: &proto.PlanComplete{
|
||||||
Parameters: richParameters,
|
Parameters: richParameters,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ProvisionApply: []*proto.Provision_Response{
|
ProvisionApply: echo.ApplyComplete,
|
||||||
{
|
|
||||||
Type: &proto.Provision_Response_Complete{
|
|
||||||
Complete: &proto.Provision_Complete{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||||
|
|
|
@ -181,7 +181,7 @@ func TestDERPForceWebSockets(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
|
|
@ -37,7 +37,6 @@ import (
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/moby/moby/pkg/namesgenerator"
|
"github.com/moby/moby/pkg/namesgenerator"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/spf13/afero"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
@ -469,10 +468,13 @@ func NewProvisionerDaemon(t testing.TB, coderAPI *coderd.API) io.Closer {
|
||||||
_ = echoServer.Close()
|
_ = echoServer.Close()
|
||||||
cancelFunc()
|
cancelFunc()
|
||||||
})
|
})
|
||||||
fs := afero.NewMemMapFs()
|
// seems t.TempDir() is not safe to call from a different goroutine
|
||||||
|
workDir := t.TempDir()
|
||||||
go func() {
|
go func() {
|
||||||
err := echo.Serve(ctx, fs, &provisionersdk.ServeOptions{
|
err := echo.Serve(ctx, &provisionersdk.ServeOptions{
|
||||||
Listener: echoServer,
|
Listener: echoServer,
|
||||||
|
WorkDirectory: workDir,
|
||||||
|
Logger: coderAPI.Logger.Named("echo").Leveled(slog.LevelDebug),
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
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) {
|
closer := provisionerd.New(func(ctx context.Context) (provisionerdproto.DRPCProvisionerDaemonClient, error) {
|
||||||
return coderAPI.CreateInMemoryProvisionerDaemon(ctx, 0)
|
return coderAPI.CreateInMemoryProvisionerDaemon(ctx, 0)
|
||||||
}, &provisionerd.Options{
|
}, &provisionerd.Options{
|
||||||
Filesystem: fs,
|
|
||||||
Logger: coderAPI.Logger.Named("provisionerd").Leveled(slog.LevelDebug),
|
Logger: coderAPI.Logger.Named("provisionerd").Leveled(slog.LevelDebug),
|
||||||
JobPollInterval: 50 * time.Millisecond,
|
JobPollInterval: 50 * time.Millisecond,
|
||||||
UpdateInterval: 250 * time.Millisecond,
|
UpdateInterval: 250 * time.Millisecond,
|
||||||
|
@ -488,7 +489,6 @@ func NewProvisionerDaemon(t testing.TB, coderAPI *coderd.API) io.Closer {
|
||||||
Provisioners: provisionerd.Provisioners{
|
Provisioners: provisionerd.Provisioners{
|
||||||
string(database.ProvisionerTypeEcho): sdkproto.NewDRPCProvisionerClient(echoClient),
|
string(database.ProvisionerTypeEcho): sdkproto.NewDRPCProvisionerClient(echoClient),
|
||||||
},
|
},
|
||||||
WorkDirectory: t.TempDir(),
|
|
||||||
})
|
})
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
_ = closer.Close()
|
_ = closer.Close()
|
||||||
|
@ -506,11 +506,11 @@ func NewExternalProvisionerDaemon(t *testing.T, client *codersdk.Client, org uui
|
||||||
cancelFunc()
|
cancelFunc()
|
||||||
<-serveDone
|
<-serveDone
|
||||||
})
|
})
|
||||||
fs := afero.NewMemMapFs()
|
|
||||||
go func() {
|
go func() {
|
||||||
defer close(serveDone)
|
defer close(serveDone)
|
||||||
err := echo.Serve(ctx, fs, &provisionersdk.ServeOptions{
|
err := echo.Serve(ctx, &provisionersdk.ServeOptions{
|
||||||
Listener: echoServer,
|
Listener: echoServer,
|
||||||
|
WorkDirectory: t.TempDir(),
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}()
|
}()
|
||||||
|
@ -522,7 +522,6 @@ func NewExternalProvisionerDaemon(t *testing.T, client *codersdk.Client, org uui
|
||||||
Tags: tags,
|
Tags: tags,
|
||||||
})
|
})
|
||||||
}, &provisionerd.Options{
|
}, &provisionerd.Options{
|
||||||
Filesystem: fs,
|
|
||||||
Logger: slogtest.Make(t, nil).Named("provisionerd").Leveled(slog.LevelDebug),
|
Logger: slogtest.Make(t, nil).Named("provisionerd").Leveled(slog.LevelDebug),
|
||||||
JobPollInterval: 50 * time.Millisecond,
|
JobPollInterval: 50 * time.Millisecond,
|
||||||
UpdateInterval: 250 * time.Millisecond,
|
UpdateInterval: 250 * time.Millisecond,
|
||||||
|
@ -530,7 +529,6 @@ func NewExternalProvisionerDaemon(t *testing.T, client *codersdk.Client, org uui
|
||||||
Provisioners: provisionerd.Provisioners{
|
Provisioners: provisionerd.Provisioners{
|
||||||
string(database.ProvisionerTypeEcho): sdkproto.NewDRPCProvisionerClient(echoClient),
|
string(database.ProvisionerTypeEcho): sdkproto.NewDRPCProvisionerClient(echoClient),
|
||||||
},
|
},
|
||||||
WorkDirectory: t.TempDir(),
|
|
||||||
})
|
})
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
_ = closer.Close()
|
_ = closer.Close()
|
||||||
|
|
|
@ -23,7 +23,6 @@ import (
|
||||||
"github.com/coder/coder/v2/codersdk"
|
"github.com/coder/coder/v2/codersdk"
|
||||||
"github.com/coder/coder/v2/codersdk/agentsdk"
|
"github.com/coder/coder/v2/codersdk/agentsdk"
|
||||||
"github.com/coder/coder/v2/provisioner/echo"
|
"github.com/coder/coder/v2/provisioner/echo"
|
||||||
"github.com/coder/coder/v2/provisionersdk/proto"
|
|
||||||
"github.com/coder/coder/v2/testutil"
|
"github.com/coder/coder/v2/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -227,7 +226,7 @@ func TestGitAuthCallback(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
@ -256,24 +255,9 @@ func TestGitAuthCallback(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
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,
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||||
|
@ -342,7 +326,7 @@ func TestGitAuthCallback(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
@ -400,7 +384,7 @@ func TestGitAuthCallback(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
@ -443,7 +427,7 @@ func TestGitAuthCallback(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
|
|
@ -108,7 +108,7 @@ func TestAgentGitSSHKey(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
project := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
project := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
|
|
@ -48,7 +48,7 @@ func TestDeploymentInsights(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
@ -134,7 +134,7 @@ func TestUserLatencyInsights(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
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.
|
// Create the template version and template.
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, firstUser.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, firstUser.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: []*proto.Provision_Response{
|
ProvisionPlan: []*proto.Response{
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Plan{
|
||||||
Complete: &proto.Provision_Complete{
|
Plan: &proto.PlanComplete{
|
||||||
Parameters: parameters,
|
Parameters: parameters,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: resources,
|
Resources: resources,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -268,10 +268,10 @@ func TestAgents(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
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{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
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),
|
RichParameterValues: convertRichParameterValues(workspaceBuildParameters),
|
||||||
VariableValues: asVariableValues(templateVariables),
|
VariableValues: asVariableValues(templateVariables),
|
||||||
GitAuthProviders: gitAuthProviders,
|
GitAuthProviders: gitAuthProviders,
|
||||||
Metadata: &sdkproto.Provision_Metadata{
|
Metadata: &sdkproto.Metadata{
|
||||||
CoderUrl: server.AccessURL.String(),
|
CoderUrl: server.AccessURL.String(),
|
||||||
WorkspaceTransition: transition,
|
WorkspaceTransition: transition,
|
||||||
WorkspaceName: workspace.Name,
|
WorkspaceName: workspace.Name,
|
||||||
|
@ -316,7 +316,7 @@ func (server *Server) AcquireJob(ctx context.Context, _ *proto.Empty) (*proto.Ac
|
||||||
TemplateDryRun: &proto.AcquiredJob_TemplateDryRun{
|
TemplateDryRun: &proto.AcquiredJob_TemplateDryRun{
|
||||||
RichParameterValues: convertRichParameterValues(input.RichParameterValues),
|
RichParameterValues: convertRichParameterValues(input.RichParameterValues),
|
||||||
VariableValues: asVariableValues(templateVariables),
|
VariableValues: asVariableValues(templateVariables),
|
||||||
Metadata: &sdkproto.Provision_Metadata{
|
Metadata: &sdkproto.Metadata{
|
||||||
CoderUrl: server.AccessURL.String(),
|
CoderUrl: server.AccessURL.String(),
|
||||||
WorkspaceName: input.WorkspaceName,
|
WorkspaceName: input.WorkspaceName,
|
||||||
},
|
},
|
||||||
|
@ -337,7 +337,7 @@ func (server *Server) AcquireJob(ctx context.Context, _ *proto.Empty) (*proto.Ac
|
||||||
protoJob.Type = &proto.AcquiredJob_TemplateImport_{
|
protoJob.Type = &proto.AcquiredJob_TemplateImport_{
|
||||||
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
||||||
UserVariableValues: convertVariableValues(userVariableValues),
|
UserVariableValues: convertVariableValues(userVariableValues),
|
||||||
Metadata: &sdkproto.Provision_Metadata{
|
Metadata: &sdkproto.Metadata{
|
||||||
CoderUrl: server.AccessURL.String(),
|
CoderUrl: server.AccessURL.String(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -267,7 +267,7 @@ func TestAcquireJob(t *testing.T) {
|
||||||
Id: gitAuthProvider,
|
Id: gitAuthProvider,
|
||||||
AccessToken: "access_token",
|
AccessToken: "access_token",
|
||||||
}},
|
}},
|
||||||
Metadata: &sdkproto.Provision_Metadata{
|
Metadata: &sdkproto.Metadata{
|
||||||
CoderUrl: srv.AccessURL.String(),
|
CoderUrl: srv.AccessURL.String(),
|
||||||
WorkspaceTransition: sdkproto.WorkspaceTransition_START,
|
WorkspaceTransition: sdkproto.WorkspaceTransition_START,
|
||||||
WorkspaceName: workspace.Name,
|
WorkspaceName: workspace.Name,
|
||||||
|
@ -359,7 +359,7 @@ func TestAcquireJob(t *testing.T) {
|
||||||
|
|
||||||
want, err := json.Marshal(&proto.AcquiredJob_TemplateDryRun_{
|
want, err := json.Marshal(&proto.AcquiredJob_TemplateDryRun_{
|
||||||
TemplateDryRun: &proto.AcquiredJob_TemplateDryRun{
|
TemplateDryRun: &proto.AcquiredJob_TemplateDryRun{
|
||||||
Metadata: &sdkproto.Provision_Metadata{
|
Metadata: &sdkproto.Metadata{
|
||||||
CoderUrl: srv.AccessURL.String(),
|
CoderUrl: srv.AccessURL.String(),
|
||||||
WorkspaceName: "testing",
|
WorkspaceName: "testing",
|
||||||
},
|
},
|
||||||
|
@ -391,7 +391,7 @@ func TestAcquireJob(t *testing.T) {
|
||||||
|
|
||||||
want, err := json.Marshal(&proto.AcquiredJob_TemplateImport_{
|
want, err := json.Marshal(&proto.AcquiredJob_TemplateImport_{
|
||||||
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
||||||
Metadata: &sdkproto.Provision_Metadata{
|
Metadata: &sdkproto.Metadata{
|
||||||
CoderUrl: srv.AccessURL.String(),
|
CoderUrl: srv.AccessURL.String(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -434,7 +434,7 @@ func TestAcquireJob(t *testing.T) {
|
||||||
UserVariableValues: []*sdkproto.VariableValue{
|
UserVariableValues: []*sdkproto.VariableValue{
|
||||||
{Name: "first", Sensitive: true, Value: "first_value"},
|
{Name: "first", Sensitive: true, Value: "first_value"},
|
||||||
},
|
},
|
||||||
Metadata: &sdkproto.Provision_Metadata{
|
Metadata: &sdkproto.Metadata{
|
||||||
CoderUrl: srv.AccessURL.String(),
|
CoderUrl: srv.AccessURL.String(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -20,16 +20,16 @@ func TestProvisionerJobLogs(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Log{
|
Type: &proto.Response_Log{
|
||||||
Log: &proto.Log{
|
Log: &proto.Log{
|
||||||
Level: proto.LogLevel_INFO,
|
Level: proto.LogLevel_INFO,
|
||||||
Output: "log-output",
|
Output: "log-output",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{},
|
Apply: &proto.ApplyComplete{},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
|
@ -59,16 +59,16 @@ func TestProvisionerJobLogs(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Log{
|
Type: &proto.Response_Log{
|
||||||
Log: &proto.Log{
|
Log: &proto.Log{
|
||||||
Level: proto.LogLevel_INFO,
|
Level: proto.LogLevel_INFO,
|
||||||
Output: "log-output",
|
Output: "log-output",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{},
|
Apply: &proto.ApplyComplete{},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
|
|
|
@ -1203,7 +1203,7 @@ func TestTemplateMetrics(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
|
|
@ -136,8 +136,8 @@ func TestPostTemplateVersionsByOrganization(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
data, err := echo.Tar(&echo.Responses{
|
data, err := echo.Tar(&echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: echo.ProvisionComplete,
|
ProvisionApply: echo.ApplyComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
@ -245,8 +245,8 @@ func TestPatchCancelTemplateVersion(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Log{
|
Type: &proto.Response_Log{
|
||||||
Log: &proto.Log{},
|
Log: &proto.Log{},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
|
@ -284,8 +284,8 @@ func TestPatchCancelTemplateVersion(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Log{
|
Type: &proto.Response_Log{
|
||||||
Log: &proto.Log{},
|
Log: &proto.Log{},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
|
@ -346,9 +346,9 @@ func TestTemplateVersionsGitAuth(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: []*proto.Provision_Response{{
|
ProvisionPlan: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Plan{
|
||||||
Complete: &proto.Provision_Complete{
|
Plan: &proto.PlanComplete{
|
||||||
GitAuthProviders: []string{"github"},
|
GitAuthProviders: []string{"github"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -400,9 +400,9 @@ func TestTemplateVersionResources(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "some",
|
Name: "some",
|
||||||
Type: "example",
|
Type: "example",
|
||||||
|
@ -439,17 +439,17 @@ func TestTemplateVersionLogs(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Log{
|
Type: &proto.Response_Log{
|
||||||
Log: &proto.Log{
|
Log: &proto.Log{
|
||||||
Level: proto.LogLevel_INFO,
|
Level: proto.LogLevel_INFO,
|
||||||
Output: "example",
|
Output: "example",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "some",
|
Name: "some",
|
||||||
Type: "example",
|
Type: "example",
|
||||||
|
@ -610,15 +610,15 @@ func TestTemplateVersionDryRun(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{
|
ProvisionApply: []*proto.Response{
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Log{
|
Type: &proto.Response_Log{
|
||||||
Log: &proto.Log{},
|
Log: &proto.Log{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{resource},
|
Resources: []*proto.Resource{resource},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -677,8 +677,8 @@ func TestTemplateVersionDryRun(t *testing.T) {
|
||||||
// This import job will never finish
|
// This import job will never finish
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Log{
|
Type: &proto.Response_Log{
|
||||||
Log: &proto.Log{},
|
Log: &proto.Log{},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
|
@ -705,15 +705,15 @@ func TestTemplateVersionDryRun(t *testing.T) {
|
||||||
|
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{
|
ProvisionApply: []*proto.Response{
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Log{
|
Type: &proto.Response_Log{
|
||||||
Log: &proto.Log{},
|
Log: &proto.Log{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{},
|
Apply: &proto.ApplyComplete{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -776,15 +776,15 @@ func TestTemplateVersionDryRun(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{
|
ProvisionApply: []*proto.Response{
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Log{
|
Type: &proto.Response_Log{
|
||||||
Log: &proto.Log{},
|
Log: &proto.Log{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{},
|
Apply: &proto.ApplyComplete{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -1040,21 +1040,17 @@ func TestTemplateVersionVariables(t *testing.T) {
|
||||||
|
|
||||||
createEchoResponses := func(templateVariables []*proto.TemplateVariable) *echo.Responses {
|
createEchoResponses := func(templateVariables []*proto.TemplateVariable) *echo.Responses {
|
||||||
return &echo.Responses{
|
return &echo.Responses{
|
||||||
Parse: []*proto.Parse_Response{
|
Parse: []*proto.Response{
|
||||||
{
|
{
|
||||||
Type: &proto.Parse_Response_Complete{
|
Type: &proto.Response_Parse{
|
||||||
Complete: &proto.Parse_Complete{
|
Parse: &proto.ParseComplete{
|
||||||
TemplateVariables: templateVariables,
|
TemplateVariables: templateVariables,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: echo.ApplyComplete,
|
||||||
Type: &proto.Provision_Response_Complete{
|
|
||||||
Complete: &proto.Provision_Complete{},
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1418,10 +1414,10 @@ func TestTemplateVersionParameters_Order(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: []*proto.Provision_Response{
|
ProvisionPlan: []*proto.Response{
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Plan{
|
||||||
Complete: &proto.Provision_Complete{
|
Plan: &proto.PlanComplete{
|
||||||
Parameters: []*proto.RichParameter{
|
Parameters: []*proto.RichParameter{
|
||||||
{
|
{
|
||||||
Name: firstParameterName,
|
Name: firstParameterName,
|
||||||
|
@ -1453,11 +1449,7 @@ func TestTemplateVersionParameters_Order(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: echo.ApplyComplete,
|
||||||
Type: &proto.Provision_Response_Complete{
|
|
||||||
Complete: &proto.Provision_Complete{},
|
|
||||||
},
|
|
||||||
}},
|
|
||||||
})
|
})
|
||||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||||
|
|
||||||
|
|
|
@ -43,10 +43,10 @@ func TestWorkspaceAgent(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
@ -87,10 +87,10 @@ func TestWorkspaceAgent(t *testing.T) {
|
||||||
tmpDir := t.TempDir()
|
tmpDir := t.TempDir()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
@ -132,10 +132,10 @@ func TestWorkspaceAgent(t *testing.T) {
|
||||||
|
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
@ -188,10 +188,10 @@ func TestWorkspaceAgentStartupLogs(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
@ -252,10 +252,10 @@ func TestWorkspaceAgentStartupLogs(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
@ -319,7 +319,7 @@ func TestWorkspaceAgentListen(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
@ -360,7 +360,7 @@ func TestWorkspaceAgentListen(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -371,10 +371,10 @@ func TestWorkspaceAgentListen(t *testing.T) {
|
||||||
|
|
||||||
version = coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version = coderdtest.UpdateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
@ -419,7 +419,7 @@ func TestWorkspaceAgentTailnet(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
@ -471,7 +471,7 @@ func TestWorkspaceAgentTailnetDirectDisabled(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
@ -548,10 +548,10 @@ func TestWorkspaceAgentListeningPorts(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
@ -806,9 +806,9 @@ func TestWorkspaceAgentAppHealth(t *testing.T) {
|
||||||
}
|
}
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
@ -893,7 +893,7 @@ func TestWorkspaceAgentReportStats(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
@ -942,7 +942,7 @@ func TestWorkspaceAgent_LifecycleState(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
@ -1014,10 +1014,10 @@ func TestWorkspaceAgent_Metadata(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
@ -1184,7 +1184,7 @@ func TestWorkspaceAgent_Startup(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
@ -1238,7 +1238,7 @@ func TestWorkspaceAgent_Startup(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
@ -1293,7 +1293,7 @@ func TestWorkspaceAgent_UpdatedDERP(t *testing.T) {
|
||||||
agentToken := uuid.NewString()
|
agentToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(agentToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(agentToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
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)
|
appURL := fmt.Sprintf("%s://127.0.0.1:%d?%s", scheme, port, proxyTestAppQuery)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, orgID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, orgID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
|
|
@ -94,10 +94,10 @@ func Test_ResolveRequest(t *testing.T) {
|
||||||
agentAuthToken := uuid.NewString()
|
agentAuthToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, firstUser.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, firstUser.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
|
|
@ -376,12 +376,12 @@ func TestPatchCancelWorkspaceBuild(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Log{
|
Type: &proto.Response_Log{
|
||||||
Log: &proto.Log{},
|
Log: &proto.Log{},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
})
|
})
|
||||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, 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 {
|
require.Eventually(t, func() bool {
|
||||||
var err error
|
var err error
|
||||||
build, err = client.WorkspaceBuild(ctx, build.ID)
|
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) &&
|
return assert.NoError(t, err) &&
|
||||||
// The job will never actually cancel successfully because it will never send a
|
build.Job.Error == "canceled" &&
|
||||||
// provision complete response.
|
build.Job.Status == codersdk.ProvisionerJobFailed
|
||||||
assert.Empty(t, build.Job.Error) &&
|
|
||||||
build.Job.Status == codersdk.ProvisionerJobCanceling
|
|
||||||
}, testutil.WaitShort, testutil.IntervalFast)
|
}, testutil.WaitShort, testutil.IntervalFast)
|
||||||
})
|
})
|
||||||
t.Run("User is not allowed to cancel", func(t *testing.T) {
|
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)
|
owner := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Log{
|
Type: &proto.Response_Log{
|
||||||
Log: &proto.Log{},
|
Log: &proto.Log{},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
})
|
})
|
||||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||||
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, 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)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "some",
|
Name: "some",
|
||||||
Type: "example",
|
Type: "example",
|
||||||
|
@ -494,16 +495,16 @@ func TestWorkspaceBuildLogs(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Log{
|
Type: &proto.Response_Log{
|
||||||
Log: &proto.Log{
|
Log: &proto.Log{
|
||||||
Level: proto.LogLevel_INFO,
|
Level: proto.LogLevel_INFO,
|
||||||
Output: "example",
|
Output: "example",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "some",
|
Name: "some",
|
||||||
Type: "example",
|
Type: "example",
|
||||||
|
@ -548,10 +549,10 @@ func TestWorkspaceBuildState(t *testing.T) {
|
||||||
wantState := []byte("some kinda state")
|
wantState := []byte("some kinda state")
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
State: wantState,
|
State: wantState,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -764,31 +765,31 @@ func TestWorkspaceBuildDebugMode(t *testing.T) {
|
||||||
// Interact as template admin
|
// Interact as template admin
|
||||||
echoResponses := &echo.Responses{
|
echoResponses := &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Log{
|
Type: &proto.Response_Log{
|
||||||
Log: &proto.Log{
|
Log: &proto.Log{
|
||||||
Level: proto.LogLevel_DEBUG,
|
Level: proto.LogLevel_DEBUG,
|
||||||
Output: "want-it",
|
Output: "want-it",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Type: &proto.Provision_Response_Log{
|
Type: &proto.Response_Log{
|
||||||
Log: &proto.Log{
|
Log: &proto.Log{
|
||||||
Level: proto.LogLevel_TRACE,
|
Level: proto.LogLevel_TRACE,
|
||||||
Output: "dont-want-it",
|
Output: "dont-want-it",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Type: &proto.Provision_Response_Log{
|
Type: &proto.Response_Log{
|
||||||
Log: &proto.Log{
|
Log: &proto.Log{
|
||||||
Level: proto.LogLevel_DEBUG,
|
Level: proto.LogLevel_DEBUG,
|
||||||
Output: "done",
|
Output: "done",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{},
|
Apply: &proto.ApplyComplete{},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
}
|
}
|
||||||
|
@ -831,7 +832,10 @@ func TestWorkspaceBuildDebugMode(t *testing.T) {
|
||||||
if !ok {
|
if !ok {
|
||||||
break processingLogs
|
break processingLogs
|
||||||
}
|
}
|
||||||
|
t.Logf("got log: %s -- %s | %s | %s", log.Level, log.Stage, log.Source, log.Output)
|
||||||
|
if log.Source != "provisioner" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
logsProcessed++
|
logsProcessed++
|
||||||
|
|
||||||
require.NotEqual(t, "dont-want-it", log.Output, "unexpected log message", "%s log message shouldn't be logged: %s")
|
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.Equal(t, 2, logsProcessed)
|
||||||
require.Len(t, echoResponses.ProvisionApply, logsProcessed)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,9 +26,9 @@ func TestPostWorkspaceAuthAzureInstanceIdentity(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "somename",
|
Name: "somename",
|
||||||
Type: "someinstance",
|
Type: "someinstance",
|
||||||
|
@ -71,9 +71,9 @@ func TestPostWorkspaceAuthAWSInstanceIdentity(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "somename",
|
Name: "somename",
|
||||||
Type: "someinstance",
|
Type: "someinstance",
|
||||||
|
@ -157,9 +157,9 @@ func TestPostWorkspaceAuthGoogleInstanceIdentity(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "somename",
|
Name: "somename",
|
||||||
Type: "someinstance",
|
Type: "someinstance",
|
||||||
|
|
|
@ -174,9 +174,9 @@ func TestWorkspace(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "some",
|
Name: "some",
|
||||||
Type: "example",
|
Type: "example",
|
||||||
|
@ -214,9 +214,9 @@ func TestWorkspace(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "some",
|
Name: "some",
|
||||||
Type: "example",
|
Type: "example",
|
||||||
|
@ -258,9 +258,9 @@ func TestWorkspace(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "some",
|
Name: "some",
|
||||||
Type: "example",
|
Type: "example",
|
||||||
|
@ -1248,7 +1248,7 @@ func TestWorkspaceFilterManual(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
@ -1276,7 +1276,7 @@ func TestWorkspaceFilterManual(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
@ -1316,10 +1316,10 @@ func TestWorkspaceFilterManual(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
@ -1374,7 +1374,7 @@ func TestWorkspaceFilterManual(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
@ -1418,7 +1418,7 @@ func TestWorkspaceFilterManual(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
@ -1457,7 +1457,7 @@ func TestWorkspaceFilterManual(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
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})
|
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
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)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||||
|
@ -2138,10 +2138,10 @@ func TestWorkspaceWatcher(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
@ -2231,10 +2231,10 @@ func TestWorkspaceWatcher(t *testing.T) {
|
||||||
// Add a new version that will fail.
|
// Add a new version that will fail.
|
||||||
badVersion := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
badVersion := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Error: "test error",
|
Error: "test error",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -2299,9 +2299,9 @@ func TestWorkspaceResource(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "beta",
|
Name: "beta",
|
||||||
Type: "example",
|
Type: "example",
|
||||||
|
@ -2367,9 +2367,9 @@ func TestWorkspaceResource(t *testing.T) {
|
||||||
}
|
}
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "some",
|
Name: "some",
|
||||||
Type: "example",
|
Type: "example",
|
||||||
|
@ -2424,9 +2424,9 @@ func TestWorkspaceResource(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "some",
|
Name: "some",
|
||||||
Type: "example",
|
Type: "example",
|
||||||
|
@ -2497,10 +2497,10 @@ func TestWorkspaceWithRichParameters(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: []*proto.Provision_Response{
|
ProvisionPlan: []*proto.Response{
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Plan{
|
||||||
Complete: &proto.Provision_Complete{
|
Plan: &proto.PlanComplete{
|
||||||
Parameters: []*proto.RichParameter{
|
Parameters: []*proto.RichParameter{
|
||||||
{
|
{
|
||||||
Name: firstParameterName,
|
Name: firstParameterName,
|
||||||
|
@ -2521,9 +2521,9 @@ func TestWorkspaceWithRichParameters(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{},
|
Apply: &proto.ApplyComplete{},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
|
@ -2590,10 +2590,10 @@ func TestWorkspaceWithOptionalRichParameters(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: []*proto.Provision_Response{
|
ProvisionPlan: []*proto.Response{
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Plan{
|
||||||
Complete: &proto.Provision_Complete{
|
Plan: &proto.PlanComplete{
|
||||||
Parameters: []*proto.RichParameter{
|
Parameters: []*proto.RichParameter{
|
||||||
{
|
{
|
||||||
Name: firstParameterName,
|
Name: firstParameterName,
|
||||||
|
@ -2612,9 +2612,9 @@ func TestWorkspaceWithOptionalRichParameters(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{},
|
Apply: &proto.ApplyComplete{},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
|
@ -2681,10 +2681,10 @@ func TestWorkspaceWithEphemeralRichParameters(t *testing.T) {
|
||||||
user := coderdtest.CreateFirstUser(t, client)
|
user := coderdtest.CreateFirstUser(t, client)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: []*proto.Provision_Response{
|
ProvisionPlan: []*proto.Response{
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Plan{
|
||||||
Complete: &proto.Provision_Complete{
|
Plan: &proto.PlanComplete{
|
||||||
Parameters: []*proto.RichParameter{
|
Parameters: []*proto.RichParameter{
|
||||||
{
|
{
|
||||||
Name: firstParameterName,
|
Name: firstParameterName,
|
||||||
|
@ -2706,9 +2706,9 @@ func TestWorkspaceWithEphemeralRichParameters(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{},
|
Apply: &proto.ApplyComplete{},
|
||||||
},
|
},
|
||||||
}},
|
}},
|
||||||
})
|
})
|
||||||
|
|
|
@ -70,6 +70,11 @@ func (r *RootCmd) provisionerDaemonStart() *clibase.Cmd {
|
||||||
return xerrors.Errorf("mkdir %q: %w", cacheDir, err)
|
return xerrors.Errorf("mkdir %q: %w", cacheDir, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tempDir, err := os.MkdirTemp("", "provisionerd")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
terraformClient, terraformServer := provisionersdk.MemTransportPipe()
|
terraformClient, terraformServer := provisionersdk.MemTransportPipe()
|
||||||
go func() {
|
go func() {
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
|
@ -84,10 +89,11 @@ func (r *RootCmd) provisionerDaemonStart() *clibase.Cmd {
|
||||||
|
|
||||||
err := terraform.Serve(ctx, &terraform.ServeOptions{
|
err := terraform.Serve(ctx, &terraform.ServeOptions{
|
||||||
ServeOptions: &provisionersdk.ServeOptions{
|
ServeOptions: &provisionersdk.ServeOptions{
|
||||||
Listener: terraformServer,
|
Listener: terraformServer,
|
||||||
|
Logger: logger.Named("terraform"),
|
||||||
|
WorkDirectory: tempDir,
|
||||||
},
|
},
|
||||||
CachePath: cacheDir,
|
CachePath: cacheDir,
|
||||||
Logger: logger.Named("terraform"),
|
|
||||||
})
|
})
|
||||||
if err != nil && !xerrors.Is(err, context.Canceled) {
|
if err != nil && !xerrors.Is(err, context.Canceled) {
|
||||||
select {
|
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))
|
logger.Info(ctx, "starting provisioner daemon", slog.F("tags", tags))
|
||||||
|
|
||||||
provisioners := provisionerd.Provisioners{
|
provisioners := provisionerd.Provisioners{
|
||||||
|
@ -121,7 +122,6 @@ func (r *RootCmd) provisionerDaemonStart() *clibase.Cmd {
|
||||||
JobPollJitter: pollJitter,
|
JobPollJitter: pollJitter,
|
||||||
UpdateInterval: 500 * time.Millisecond,
|
UpdateInterval: 500 * time.Millisecond,
|
||||||
Provisioners: provisioners,
|
Provisioners: provisioners,
|
||||||
WorkDirectory: tempDir,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
var exitErr error
|
var exitErr error
|
||||||
|
|
|
@ -111,7 +111,7 @@ func TestServiceBanners(t *testing.T) {
|
||||||
agentClient.SetSessionToken(authToken)
|
agentClient.SetSessionToken(authToken)
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
ProvisionApply: echo.ProvisionApplyWithAgent(authToken),
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
||||||
|
|
|
@ -139,9 +139,9 @@ func TestProvisionerDaemonServe(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
data, err := echo.Tar(&echo.Responses{
|
data, err := echo.Tar(&echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: []*proto.Provision_Response{{
|
ProvisionPlan: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Plan{
|
||||||
Complete: &proto.Provision_Complete{
|
Plan: &proto.PlanComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
|
|
@ -73,9 +73,9 @@ func setupWorkspaceAgent(t *testing.T, client *codersdk.Client, user codersdk.Cr
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
|
|
@ -89,9 +89,9 @@ func TestWorkspaceQuota(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
@ -183,39 +183,15 @@ func TestWorkspaceQuota(t *testing.T) {
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
verifyQuota(ctx, t, client, 0, 4)
|
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{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlanMap: map[proto.WorkspaceTransition][]*proto.Provision_Response{
|
ProvisionPlanMap: map[proto.WorkspaceTransition][]*proto.Response{
|
||||||
proto.WorkspaceTransition_START: startResp,
|
proto.WorkspaceTransition_START: planWithCost(2),
|
||||||
proto.WorkspaceTransition_STOP: stopResp,
|
proto.WorkspaceTransition_STOP: planWithCost(1),
|
||||||
},
|
},
|
||||||
ProvisionApplyMap: map[proto.WorkspaceTransition][]*proto.Provision_Response{
|
ProvisionApplyMap: map[proto.WorkspaceTransition][]*proto.Response{
|
||||||
proto.WorkspaceTransition_START: startResp,
|
proto.WorkspaceTransition_START: applyWithCost(2),
|
||||||
proto.WorkspaceTransition_STOP: stopResp,
|
proto.WorkspaceTransition_STOP: applyWithCost(1),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -258,3 +234,31 @@ func TestWorkspaceQuota(t *testing.T) {
|
||||||
require.Equal(t, codersdk.WorkspaceStatusRunning, build.Status)
|
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{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionFailed,
|
ProvisionApply: echo.ApplyFailed,
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||||
ctr.FailureTTLMillis = ptr.Ref[int64](failureTTL.Milliseconds())
|
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{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionFailed,
|
ProvisionApply: echo.ApplyFailed,
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||||
ctr.FailureTTLMillis = ptr.Ref[int64](failureTTL.Milliseconds())
|
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{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionComplete,
|
ProvisionApply: echo.ApplyComplete,
|
||||||
})
|
})
|
||||||
// Create a template without setting a failure_ttl.
|
// Create a template without setting a failure_ttl.
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
|
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{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionComplete,
|
ProvisionApply: echo.ApplyComplete,
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||||
ctr.TimeTilDormantMillis = ptr.Ref[int64](inactiveTTL.Milliseconds())
|
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{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionComplete,
|
ProvisionApply: echo.ApplyComplete,
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||||
ctr.TimeTilDormantMillis = ptr.Ref[int64](inactiveTTL.Milliseconds())
|
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{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionComplete,
|
ProvisionApply: echo.ApplyComplete,
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||||
ctr.TimeTilDormantAutoDeleteMillis = ptr.Ref[int64](autoDeleteTTL.Milliseconds())
|
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{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionComplete,
|
ProvisionApply: echo.ApplyComplete,
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||||
ctr.TimeTilDormantMillis = ptr.Ref[int64](inactiveTTL.Milliseconds())
|
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{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionComplete,
|
ProvisionApply: echo.ApplyComplete,
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||||
ctr.TimeTilDormantMillis = ptr.Ref[int64](transitionTTL.Milliseconds())
|
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{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionComplete,
|
ProvisionApply: echo.ApplyComplete,
|
||||||
})
|
})
|
||||||
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID, func(ctr *codersdk.CreateTemplateRequest) {
|
||||||
ctr.TimeTilDormantAutoDeleteMillis = ptr.Ref[int64](dormantTTL.Milliseconds())
|
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{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: echo.ProvisionComplete,
|
ProvisionApply: echo.ApplyComplete,
|
||||||
})
|
})
|
||||||
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
coderdtest.AwaitTemplateVersionJob(t, client, version.ID)
|
||||||
|
|
||||||
|
|
|
@ -5,25 +5,24 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
protobuf "google.golang.org/protobuf/proto"
|
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"
|
||||||
"github.com/coder/coder/v2/provisionersdk/proto"
|
"github.com/coder/coder/v2/provisionersdk/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ProvisionApplyWithAgent returns provision responses that will mock a fake
|
// ProvisionApplyWithAgent returns provision responses that will mock a fake
|
||||||
// "aws_instance" resource with an agent that has the given auth token.
|
// "aws_instance" resource with an agent that has the given auth token.
|
||||||
func ProvisionApplyWithAgent(authToken string) []*proto.Provision_Response {
|
func ProvisionApplyWithAgent(authToken string) []*proto.Response {
|
||||||
return []*proto.Provision_Response{{
|
return []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
@ -42,23 +41,36 @@ func ProvisionApplyWithAgent(authToken string) []*proto.Provision_Response {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ParseComplete is a helper to indicate an empty parse completion.
|
// ParseComplete is a helper to indicate an empty parse completion.
|
||||||
ParseComplete = []*proto.Parse_Response{{
|
ParseComplete = []*proto.Response{{
|
||||||
Type: &proto.Parse_Response_Complete{
|
Type: &proto.Response_Parse{
|
||||||
Complete: &proto.Parse_Complete{},
|
Parse: &proto.ParseComplete{},
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
// ProvisionComplete is a helper to indicate an empty provision completion.
|
// PlanComplete is a helper to indicate an empty provision completion.
|
||||||
ProvisionComplete = []*proto.Provision_Response{{
|
PlanComplete = []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Plan{
|
||||||
Complete: &proto.Provision_Complete{},
|
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
|
// PlanFailed is a helper to convey a failed plan operation
|
||||||
// operation.
|
PlanFailed = []*proto.Response{{
|
||||||
ProvisionFailed = []*proto.Provision_Response{{
|
Type: &proto.Response_Plan{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Plan: &proto.PlanComplete{
|
||||||
Complete: &proto.Provision_Complete{
|
Error: "failed!",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
// ApplyFailed is a helper to convey a failed apply operation
|
||||||
|
ApplyFailed = []*proto.Response{{
|
||||||
|
Type: &proto.Response_Apply{
|
||||||
|
Apply: &proto.ApplyComplete{
|
||||||
Error: "failed!",
|
Error: "failed!",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -66,120 +78,116 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Serve starts the echo provisioner.
|
// Serve starts the echo provisioner.
|
||||||
func Serve(ctx context.Context, filesystem afero.Fs, options *provisionersdk.ServeOptions) error {
|
func Serve(ctx context.Context, options *provisionersdk.ServeOptions) error {
|
||||||
return provisionersdk.Serve(ctx, &echo{
|
return provisionersdk.Serve(ctx, &echo{}, options)
|
||||||
filesystem: filesystem,
|
|
||||||
}, options)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The echo provisioner serves as a dummy provisioner primarily
|
// The echo provisioner serves as a dummy provisioner primarily
|
||||||
// used for testing. It echos responses from JSON files in the
|
// used for testing. It echos responses from JSON files in the
|
||||||
// format %d.protobuf. It's used for testing.
|
// format %d.protobuf. It's used for testing.
|
||||||
type echo struct {
|
type echo struct{}
|
||||||
filesystem afero.Fs
|
|
||||||
|
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.
|
// Parse reads requests from the provided directory to stream responses.
|
||||||
func (e *echo) Parse(request *proto.Parse_Request, stream proto.DRPCProvisioner_ParseStream) error {
|
func (*echo) Parse(sess *provisionersdk.Session, _ *proto.ParseRequest, _ <-chan struct{}) *proto.ParseComplete {
|
||||||
for index := 0; ; index++ {
|
responses, err := readResponses(sess, "unspecified", "parse.protobuf")
|
||||||
path := filepath.Join(request.Directory, fmt.Sprintf("%d.parse.protobuf", index))
|
if err != nil {
|
||||||
_, err := e.filesystem.Stat(path)
|
return &proto.ParseComplete{Error: err.Error()}
|
||||||
if err != nil {
|
}
|
||||||
if index == 0 {
|
for _, response := range responses {
|
||||||
// Error if nothing is around to enable failed states.
|
if log := response.GetLog(); log != nil {
|
||||||
return xerrors.Errorf("no state: %w", err)
|
sess.ProvisionLog(log.Level, log.Output)
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
data, err := afero.ReadFile(e.filesystem, path)
|
if complete := response.GetParse(); complete != nil {
|
||||||
if err != nil {
|
return complete
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
<-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.
|
// Plan reads requests from the provided directory to stream responses.
|
||||||
func (e *echo) Provision(stream proto.DRPCProvisioner_ProvisionStream) error {
|
func (*echo) Plan(sess *provisionersdk.Session, req *proto.PlanRequest, canceledOrComplete <-chan struct{}) *proto.PlanComplete {
|
||||||
msg, err := stream.Recv()
|
responses, err := readResponses(
|
||||||
|
sess,
|
||||||
|
strings.ToLower(req.GetMetadata().GetWorkspaceTransition().String()),
|
||||||
|
"plan.protobuf")
|
||||||
if err != nil {
|
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
|
// some tests use Echo without a complete response to test cancel
|
||||||
switch {
|
<-canceledOrComplete
|
||||||
case msg.GetPlan() != nil:
|
return provisionersdk.PlanErrorf("canceled")
|
||||||
config = msg.GetPlan().GetConfig()
|
}
|
||||||
case msg.GetApply() != nil:
|
|
||||||
config = msg.GetApply().GetConfig()
|
// Apply reads requests from the provided directory to stream responses.
|
||||||
default:
|
func (*echo) Apply(sess *provisionersdk.Session, req *proto.ApplyRequest, canceledOrComplete <-chan struct{}) *proto.ApplyComplete {
|
||||||
// Probably a cancel
|
responses, err := readResponses(
|
||||||
return nil
|
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:
|
// some tests use Echo without a complete response to test cancel
|
||||||
for i := 0; ; i++ {
|
<-canceledOrComplete
|
||||||
var extension string
|
return provisionersdk.ApplyErrorf("canceled")
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*echo) Shutdown(_ context.Context, _ *proto.Empty) (*proto.Empty, error) {
|
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.
|
// Responses is a collection of mocked responses to Provision operations.
|
||||||
type Responses struct {
|
type Responses struct {
|
||||||
Parse []*proto.Parse_Response
|
Parse []*proto.Response
|
||||||
|
|
||||||
// ProvisionApply and ProvisionPlan are used to mock ALL responses of
|
// ProvisionApply and ProvisionPlan are used to mock ALL responses of
|
||||||
// Apply and Plan, regardless of transition.
|
// Apply and Plan, regardless of transition.
|
||||||
ProvisionApply []*proto.Provision_Response
|
ProvisionApply []*proto.Response
|
||||||
ProvisionPlan []*proto.Provision_Response
|
ProvisionPlan []*proto.Response
|
||||||
|
|
||||||
// ProvisionApplyMap and ProvisionPlanMap are used to mock specific
|
// ProvisionApplyMap and ProvisionPlanMap are used to mock specific
|
||||||
// transition responses. They are prioritized over the generic responses.
|
// transition responses. They are prioritized over the generic responses.
|
||||||
ProvisionApplyMap map[proto.WorkspaceTransition][]*proto.Provision_Response
|
ProvisionApplyMap map[proto.WorkspaceTransition][]*proto.Response
|
||||||
ProvisionPlanMap map[proto.WorkspaceTransition][]*proto.Provision_Response
|
ProvisionPlanMap map[proto.WorkspaceTransition][]*proto.Response
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tar returns a tar archive of responses to provisioner operations.
|
// Tar returns a tar archive of responses to provisioner operations.
|
||||||
func Tar(responses *Responses) ([]byte, error) {
|
func Tar(responses *Responses) ([]byte, error) {
|
||||||
if responses == nil {
|
if responses == nil {
|
||||||
responses = &Responses{
|
responses = &Responses{
|
||||||
ParseComplete, ProvisionComplete, ProvisionComplete,
|
ParseComplete, ApplyComplete, PlanComplete,
|
||||||
nil, nil,
|
nil, nil,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if responses.ProvisionPlan == 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
|
var buffer bytes.Buffer
|
||||||
|
@ -245,20 +266,20 @@ func Tar(responses *Responses) ([]byte, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for index, response := range responses.ProvisionApply {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for index, response := range responses.ProvisionPlan {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for trans, m := range responses.ProvisionApplyMap {
|
for trans, m := range responses.ProvisionApplyMap {
|
||||||
for i, rs := range m {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -266,7 +287,7 @@ func Tar(responses *Responses) ([]byte, error) {
|
||||||
}
|
}
|
||||||
for trans, m := range responses.ProvisionPlanMap {
|
for trans, m := range responses.ProvisionPlanMap {
|
||||||
for i, rs := range m {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -279,22 +300,14 @@ func Tar(responses *Responses) ([]byte, error) {
|
||||||
return buffer.Bytes(), nil
|
return buffer.Bytes(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func filterLogResponses(config *proto.Provision_Config, response *proto.Provision_Response) (*proto.Provision_Response, bool) {
|
func WithResources(resources []*proto.Resource) *Responses {
|
||||||
responseLog, ok := response.Type.(*proto.Provision_Response_Log)
|
return &Responses{
|
||||||
if !ok {
|
Parse: ParseComplete,
|
||||||
// Pass all non-log responses
|
ProvisionApply: []*proto.Response{{Type: &proto.Response_Apply{Apply: &proto.ApplyComplete{
|
||||||
return response, true
|
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
|
package echo_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"archive/tar"
|
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/spf13/afero"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/coder/coder/v2/provisioner/echo"
|
"github.com/coder/coder/v2/provisioner/echo"
|
||||||
"github.com/coder/coder/v2/provisionersdk"
|
"github.com/coder/coder/v2/provisionersdk"
|
||||||
"github.com/coder/coder/v2/provisionersdk/proto"
|
"github.com/coder/coder/v2/provisionersdk/proto"
|
||||||
|
"github.com/coder/coder/v2/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestEcho(t *testing.T) {
|
func TestEcho(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
fs := afero.NewMemMapFs()
|
workdir := t.TempDir()
|
||||||
|
|
||||||
// Create an in-memory provisioner to communicate with.
|
// Create an in-memory provisioner to communicate with.
|
||||||
client, server := provisionersdk.MemTransportPipe()
|
client, server := provisionersdk.MemTransportPipe()
|
||||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||||
|
@ -31,8 +27,9 @@ func TestEcho(t *testing.T) {
|
||||||
cancelFunc()
|
cancelFunc()
|
||||||
})
|
})
|
||||||
go func() {
|
go func() {
|
||||||
err := echo.Serve(ctx, fs, &provisionersdk.ServeOptions{
|
err := echo.Serve(ctx, &provisionersdk.ServeOptions{
|
||||||
Listener: server,
|
Listener: server,
|
||||||
|
WorkDirectory: workdir,
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}()
|
}()
|
||||||
|
@ -40,25 +37,39 @@ func TestEcho(t *testing.T) {
|
||||||
|
|
||||||
t.Run("Parse", func(t *testing.T) {
|
t.Run("Parse", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, testutil.WaitShort)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
responses := []*proto.Parse_Response{{
|
responses := []*proto.Response{
|
||||||
Type: &proto.Parse_Response_Log{
|
{
|
||||||
Log: &proto.Log{
|
Type: &proto.Response_Log{
|
||||||
Output: "log-output",
|
Log: &proto.Log{
|
||||||
|
Output: "log-output",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
{
|
||||||
Type: &proto.Parse_Response_Complete{
|
Type: &proto.Response_Parse{
|
||||||
Complete: &proto.Parse_Complete{},
|
Parse: &proto.ParseComplete{},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}}
|
}
|
||||||
data, err := echo.Tar(&echo.Responses{
|
data, err := echo.Tar(&echo.Responses{
|
||||||
Parse: responses,
|
Parse: responses,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
client, err := api.Parse(ctx, &proto.Parse_Request{
|
client, err := api.Session(ctx)
|
||||||
Directory: unpackTar(t, fs, data),
|
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)
|
require.NoError(t, err)
|
||||||
log, err := client.Recv()
|
log, err := client.Recv()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -70,95 +81,117 @@ func TestEcho(t *testing.T) {
|
||||||
|
|
||||||
t.Run("Provision", func(t *testing.T) {
|
t.Run("Provision", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, testutil.WaitShort)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
responses := []*proto.Provision_Response{{
|
planResponses := []*proto.Response{
|
||||||
Type: &proto.Provision_Response_Log{
|
{
|
||||||
Log: &proto.Log{
|
Type: &proto.Response_Log{
|
||||||
Level: proto.LogLevel_INFO,
|
Log: &proto.Log{
|
||||||
Output: "log-output",
|
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),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
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)
|
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()
|
log, err := client.Recv()
|
||||||
require.NoError(t, err)
|
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()
|
complete, err := client.Recv()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, responses[1].GetComplete().Resources[0].Name,
|
require.Equal(t, planResponses[1].GetPlan().Resources[0].Name,
|
||||||
complete.GetComplete().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.Run("ProvisionStop", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Stop responses should be returned when the workspace is being stopped.
|
// 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{
|
data, err := echo.Tar(&echo.Responses{
|
||||||
ProvisionApply: defaultResponses,
|
ProvisionApply: applyCompleteResource("DEFAULT"),
|
||||||
ProvisionPlan: defaultResponses,
|
ProvisionPlan: planCompleteResource("DEFAULT"),
|
||||||
ProvisionPlanMap: map[proto.WorkspaceTransition][]*proto.Provision_Response{
|
ProvisionPlanMap: map[proto.WorkspaceTransition][]*proto.Response{
|
||||||
proto.WorkspaceTransition_STOP: stopResponses,
|
proto.WorkspaceTransition_STOP: planCompleteResource("STOP"),
|
||||||
},
|
},
|
||||||
ProvisionApplyMap: map[proto.WorkspaceTransition][]*proto.Provision_Response{
|
ProvisionApplyMap: map[proto.WorkspaceTransition][]*proto.Response{
|
||||||
proto.WorkspaceTransition_STOP: stopResponses,
|
proto.WorkspaceTransition_STOP: applyCompleteResource("STOP"),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
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)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Do stop.
|
// Do stop.
|
||||||
err = client.Send(&proto.Provision_Request{
|
err = client.Send(&proto.Request{
|
||||||
Type: &proto.Provision_Request_Plan{
|
Type: &proto.Request_Plan{
|
||||||
Plan: &proto.Provision_Plan{
|
Plan: &proto.PlanRequest{
|
||||||
Config: &proto.Provision_Config{
|
Metadata: &proto.Metadata{
|
||||||
Directory: unpackTar(t, fs, data),
|
WorkspaceTransition: proto.WorkspaceTransition_STOP,
|
||||||
Metadata: &proto.Provision_Metadata{
|
|
||||||
WorkspaceTransition: proto.WorkspaceTransition_STOP,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -168,22 +201,16 @@ func TestEcho(t *testing.T) {
|
||||||
complete, err := client.Recv()
|
complete, err := client.Recv()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t,
|
require.Equal(t,
|
||||||
stopResponses[0].GetComplete().Resources[0].Name,
|
"STOP",
|
||||||
complete.GetComplete().Resources[0].Name,
|
complete.GetPlan().Resources[0].Name,
|
||||||
)
|
)
|
||||||
|
|
||||||
// Do start.
|
// Do start.
|
||||||
client, err = api.Provision(ctx)
|
err = client.Send(&proto.Request{
|
||||||
require.NoError(t, err)
|
Type: &proto.Request_Plan{
|
||||||
|
Plan: &proto.PlanRequest{
|
||||||
err = client.Send(&proto.Provision_Request{
|
Metadata: &proto.Metadata{
|
||||||
Type: &proto.Provision_Request_Plan{
|
WorkspaceTransition: proto.WorkspaceTransition_START,
|
||||||
Plan: &proto.Provision_Plan{
|
|
||||||
Config: &proto.Provision_Config{
|
|
||||||
Directory: unpackTar(t, fs, data),
|
|
||||||
Metadata: &proto.Provision_Metadata{
|
|
||||||
WorkspaceTransition: proto.WorkspaceTransition_START,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -193,31 +220,33 @@ func TestEcho(t *testing.T) {
|
||||||
complete, err = client.Recv()
|
complete, err = client.Recv()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t,
|
require.Equal(t,
|
||||||
defaultResponses[0].GetComplete().Resources[0].Name,
|
"DEFAULT",
|
||||||
complete.GetComplete().Resources[0].Name,
|
complete.GetPlan().Resources[0].Name,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("ProvisionWithLogLevel", func(t *testing.T) {
|
t.Run("ProvisionWithLogLevel", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, testutil.WaitShort)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
responses := []*proto.Provision_Response{{
|
responses := []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Log{
|
Type: &proto.Response_Log{
|
||||||
Log: &proto.Log{
|
Log: &proto.Log{
|
||||||
Level: proto.LogLevel_TRACE,
|
Level: proto.LogLevel_TRACE,
|
||||||
Output: "log-output-trace",
|
Output: "log-output-trace",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Type: &proto.Provision_Response_Log{
|
Type: &proto.Response_Log{
|
||||||
Log: &proto.Log{
|
Log: &proto.Log{
|
||||||
Level: proto.LogLevel_INFO,
|
Level: proto.LogLevel_INFO,
|
||||||
Output: "log-output-info",
|
Output: "log-output-info",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, {
|
}, {
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "resource",
|
Name: "resource",
|
||||||
}},
|
}},
|
||||||
|
@ -225,49 +254,62 @@ func TestEcho(t *testing.T) {
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
data, err := echo.Tar(&echo.Responses{
|
data, err := echo.Tar(&echo.Responses{
|
||||||
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: responses,
|
ProvisionApply: responses,
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
client, err := api.Provision(ctx)
|
client, err := api.Session(ctx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
err = client.Send(&proto.Provision_Request{
|
defer func() {
|
||||||
Type: &proto.Provision_Request_Plan{
|
err := client.Close()
|
||||||
Plan: &proto.Provision_Plan{
|
require.NoError(t, err)
|
||||||
Config: &proto.Provision_Config{
|
}()
|
||||||
Directory: unpackTar(t, fs, data),
|
err = client.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{
|
||||||
ProvisionerLogLevel: "debug",
|
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)
|
require.NoError(t, err)
|
||||||
log, err := client.Recv()
|
log, err := client.Recv()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
// Skip responses[0] as it's trace level
|
// Skip responses[0] as it's trace level
|
||||||
require.Equal(t, responses[1].GetLog().Output, log.GetLog().Output)
|
require.Equal(t, responses[1].GetLog().Output, log.GetLog().Output)
|
||||||
complete, err := client.Recv()
|
complete, err = client.Recv()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, responses[2].GetComplete().Resources[0].Name,
|
require.Equal(t, responses[2].GetApply().Resources[0].Name,
|
||||||
complete.GetComplete().Resources[0].Name)
|
complete.GetApply().Resources[0].Name)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func unpackTar(t *testing.T, fs afero.Fs, data []byte) string {
|
func planCompleteResource(name string) []*proto.Response {
|
||||||
directory := t.TempDir()
|
return []*proto.Response{{
|
||||||
reader := tar.NewReader(bytes.NewReader(data))
|
Type: &proto.Response_Plan{
|
||||||
for {
|
Plan: &proto.PlanComplete{
|
||||||
header, err := reader.Next()
|
Resources: []*proto.Resource{{
|
||||||
if err != nil {
|
Name: name,
|
||||||
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)
|
func applyCompleteResource(name string) []*proto.Response {
|
||||||
require.ErrorIs(t, err, io.EOF)
|
return []*proto.Response{{
|
||||||
err = file.Close()
|
Type: &proto.Response_Apply{
|
||||||
require.NoError(t, err)
|
Apply: &proto.ApplyComplete{
|
||||||
}
|
Resources: []*proto.Resource{{
|
||||||
return directory
|
Name: name,
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type executor struct {
|
type executor struct {
|
||||||
|
logger slog.Logger
|
||||||
server *server
|
server *server
|
||||||
mut *sync.Mutex
|
mut *sync.Mutex
|
||||||
binaryPath string
|
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]))
|
ctx, span := e.server.startTrace(ctx, fmt.Sprintf("exec - terraform %s", args[0]))
|
||||||
defer span.End()
|
defer span.End()
|
||||||
span.SetAttributes(attribute.StringSlice("args", args))
|
span.SetAttributes(attribute.StringSlice("args", args))
|
||||||
|
e.logger.Debug(ctx, "starting command", slog.F("args", args))
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
|
e.logger.Debug(ctx, "closing writers", slog.Error(err))
|
||||||
closeErr := stdOutWriter.Close()
|
closeErr := stdOutWriter.Close()
|
||||||
if err == nil && closeErr != nil {
|
if err == nil && closeErr != nil {
|
||||||
err = closeErr
|
err = closeErr
|
||||||
|
@ -62,6 +65,7 @@ func (e *executor) execWriteOutput(ctx, killCtx context.Context, args, env []str
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
if ctx.Err() != nil {
|
if ctx.Err() != nil {
|
||||||
|
e.logger.Debug(ctx, "context canceled before command started", slog.F("args", args))
|
||||||
return ctx.Err()
|
return ctx.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,11 +94,14 @@ func (e *executor) execWriteOutput(ctx, killCtx context.Context, args, env []str
|
||||||
)
|
)
|
||||||
err = cmd.Start()
|
err = cmd.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
e.logger.Debug(ctx, "failed to start command", slog.F("args", args))
|
||||||
return err
|
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.
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
interruptCommandOnCancel(ctx, killCtx, cmd)
|
interruptCommandOnCancel(ctx, killCtx, e.logger, cmd)
|
||||||
|
|
||||||
err = cmd.Wait()
|
err = cmd.Wait()
|
||||||
if err != nil {
|
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)
|
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
|
// 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())
|
ctx, span := e.server.startTrace(ctx, tracing.FuncName())
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
e.mut.Lock()
|
e.mut.Lock()
|
||||||
defer e.mut.Unlock()
|
defer e.mut.Unlock()
|
||||||
|
|
||||||
planfilePath := filepath.Join(e.workdir, "terraform.tfplan")
|
planfilePath := getPlanFilePath(e.workdir)
|
||||||
args := []string{
|
args := []string{
|
||||||
"plan",
|
"plan",
|
||||||
"-no-color",
|
"-no-color",
|
||||||
|
@ -248,19 +263,10 @@ func (e *executor) plan(ctx, killCtx context.Context, env, vars []string, logr l
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
planFileByt, err := os.ReadFile(planfilePath)
|
return &proto.PlanComplete{
|
||||||
if err != nil {
|
Parameters: state.Parameters,
|
||||||
return nil, err
|
Resources: state.Resources,
|
||||||
}
|
GitAuthProviders: state.GitAuthProviders,
|
||||||
return &proto.Provision_Response{
|
|
||||||
Type: &proto.Provision_Response_Complete{
|
|
||||||
Complete: &proto.Provision_Complete{
|
|
||||||
Parameters: state.Parameters,
|
|
||||||
Resources: state.Resources,
|
|
||||||
GitAuthProviders: state.GitAuthProviders,
|
|
||||||
Plan: planFileByt,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -346,7 +352,7 @@ func (e *executor) graph(ctx, killCtx context.Context) (string, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
interruptCommandOnCancel(ctx, killCtx, cmd)
|
interruptCommandOnCancel(ctx, killCtx, e.logger, cmd)
|
||||||
|
|
||||||
err = cmd.Wait()
|
err = cmd.Wait()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -357,33 +363,22 @@ func (e *executor) graph(ctx, killCtx context.Context) (string, error) {
|
||||||
|
|
||||||
func (e *executor) apply(
|
func (e *executor) apply(
|
||||||
ctx, killCtx context.Context,
|
ctx, killCtx context.Context,
|
||||||
plan []byte,
|
|
||||||
env []string,
|
env []string,
|
||||||
logr logSink,
|
logr logSink,
|
||||||
) (*proto.Provision_Response, error) {
|
) (*proto.ApplyComplete, error) {
|
||||||
ctx, span := e.server.startTrace(ctx, tracing.FuncName())
|
ctx, span := e.server.startTrace(ctx, tracing.FuncName())
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
e.mut.Lock()
|
e.mut.Lock()
|
||||||
defer e.mut.Unlock()
|
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{
|
args := []string{
|
||||||
"apply",
|
"apply",
|
||||||
"-no-color",
|
"-no-color",
|
||||||
"-auto-approve",
|
"-auto-approve",
|
||||||
"-input=false",
|
"-input=false",
|
||||||
"-json",
|
"-json",
|
||||||
planFile.Name(),
|
getPlanFilePath(e.workdir),
|
||||||
}
|
}
|
||||||
|
|
||||||
outWriter, doneOut := provisionLogWriter(logr)
|
outWriter, doneOut := provisionLogWriter(logr)
|
||||||
|
@ -395,7 +390,7 @@ func (e *executor) apply(
|
||||||
<-doneErr
|
<-doneErr
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err = e.execWriteOutput(ctx, killCtx, args, env, outWriter, errWriter)
|
err := e.execWriteOutput(ctx, killCtx, args, env, outWriter, errWriter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("terraform apply: %w", err)
|
return nil, xerrors.Errorf("terraform apply: %w", err)
|
||||||
}
|
}
|
||||||
|
@ -408,15 +403,11 @@ func (e *executor) apply(
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("read statefile %q: %w", statefilePath, err)
|
return nil, xerrors.Errorf("read statefile %q: %w", statefilePath, err)
|
||||||
}
|
}
|
||||||
return &proto.Provision_Response{
|
return &proto.ApplyComplete{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Parameters: state.Parameters,
|
||||||
Complete: &proto.Provision_Complete{
|
Resources: state.Resources,
|
||||||
Parameters: state.Parameters,
|
GitAuthProviders: state.GitAuthProviders,
|
||||||
Resources: state.Resources,
|
State: stateContent,
|
||||||
GitAuthProviders: state.GitAuthProviders,
|
|
||||||
State: stateContent,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -461,48 +452,28 @@ func (e *executor) state(ctx, killCtx context.Context) (*tfjson.State, error) {
|
||||||
return state, nil
|
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() {
|
go func() {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
|
var err error
|
||||||
switch runtime.GOOS {
|
switch runtime.GOOS {
|
||||||
case "windows":
|
case "windows":
|
||||||
// Interrupts aren't supported by Windows.
|
// Interrupts aren't supported by Windows.
|
||||||
_ = cmd.Process.Kill()
|
err = cmd.Process.Kill()
|
||||||
default:
|
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():
|
case <-killCtx.Done():
|
||||||
|
logger.Debug(ctx, "kill context ended", slog.F("args", cmd.Args))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
type logSink interface {
|
type logSink interface {
|
||||||
Log(*proto.Log)
|
ProvisionLog(l proto.LogLevel, o string)
|
||||||
}
|
|
||||||
|
|
||||||
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),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// logWriter creates a WriteCloser that will log each line of text at the given level. The WriteCloser must be closed
|
// 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
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
sink.Log(&proto.Log{Level: level, Output: scanner.Text()})
|
sink.ProvisionLog(level, scanner.Text())
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -543,7 +514,7 @@ func readAndLog(sink logSink, r io.Reader, done chan<- any, level proto.LogLevel
|
||||||
if logLevel == proto.LogLevel_INFO {
|
if logLevel == proto.LogLevel_INFO {
|
||||||
logLevel = proto.LogLevel_DEBUG
|
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)
|
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 the diagnostic is provided, let's provide a bit more info!
|
||||||
if log.Diagnostic == nil {
|
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)
|
logLevel = convertTerraformLogLevel(string(log.Diagnostic.Severity), sink)
|
||||||
for _, diagLine := range strings.Split(FormatDiagnostic(log.Diagnostic), "\n") {
|
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":
|
case "error":
|
||||||
return proto.LogLevel_ERROR
|
return proto.LogLevel_ERROR
|
||||||
default:
|
default:
|
||||||
sink.Log(&proto.Log{
|
sink.ProvisionLog(proto.LogLevel_WARN, fmt.Sprintf("unable to convert log level %s", logLevel))
|
||||||
Level: proto.LogLevel_WARN,
|
|
||||||
Output: fmt.Sprintf("unable to convert log level %s", logLevel),
|
|
||||||
})
|
|
||||||
return proto.LogLevel_INFO
|
return proto.LogLevel_INFO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,8 @@ type mockLogger struct {
|
||||||
|
|
||||||
var _ logSink = &mockLogger{}
|
var _ logSink = &mockLogger{}
|
||||||
|
|
||||||
func (m *mockLogger) Log(l *proto.Log) {
|
func (m *mockLogger) ProvisionLog(l proto.LogLevel, o string) {
|
||||||
m.logs = append(m.logs, l)
|
m.logs = append(m.logs, &proto.Log{Level: l, Output: o})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLogWriter_Mainline(t *testing.T) {
|
func TestLogWriter_Mainline(t *testing.T) {
|
||||||
|
|
|
@ -12,18 +12,20 @@ import (
|
||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
"github.com/coder/coder/v2/coderd/tracing"
|
"github.com/coder/coder/v2/coderd/tracing"
|
||||||
|
"github.com/coder/coder/v2/provisionersdk"
|
||||||
"github.com/coder/coder/v2/provisionersdk/proto"
|
"github.com/coder/coder/v2/provisionersdk/proto"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Parse extracts Terraform variables from source-code.
|
// Parse extracts Terraform variables from source-code.
|
||||||
func (s *server) Parse(request *proto.Parse_Request, stream proto.DRPCProvisioner_ParseStream) error {
|
func (s *server) Parse(sess *provisionersdk.Session, _ *proto.ParseRequest, _ <-chan struct{}) *proto.ParseComplete {
|
||||||
_, span := s.startTrace(stream.Context(), tracing.FuncName())
|
ctx := sess.Context()
|
||||||
|
_, span := s.startTrace(ctx, tracing.FuncName())
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
// Load the module and print any parse errors.
|
// Load the module and print any parse errors.
|
||||||
module, diags := tfconfig.LoadModule(request.Directory)
|
module, diags := tfconfig.LoadModule(sess.WorkDirectory)
|
||||||
if diags.HasErrors() {
|
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
|
// 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 {
|
for _, v := range variables {
|
||||||
mv, err := convertTerraformVariable(v)
|
mv, err := convertTerraformVariable(v)
|
||||||
if err != nil {
|
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)
|
templateVariables = append(templateVariables, mv)
|
||||||
}
|
}
|
||||||
return stream.Send(&proto.Parse_Response{
|
return &proto.ParseComplete{
|
||||||
Type: &proto.Parse_Response_Complete{
|
TemplateVariables: templateVariables,
|
||||||
Complete: &proto.Parse_Complete{
|
}
|
||||||
TemplateVariables: templateVariables,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts a Terraform variable to a template-wide variable, processed by Coder.
|
// Converts a Terraform variable to a template-wide variable, processed by Coder.
|
||||||
|
|
|
@ -4,8 +4,6 @@ package terraform_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -21,9 +19,8 @@ func TestParse(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
Name string
|
Name string
|
||||||
Files map[string]string
|
Files map[string]string
|
||||||
Response *proto.Parse_Response
|
Response *proto.ParseComplete
|
||||||
// If ErrorContains is not empty, then response.Recv() should return an
|
// If ErrorContains is not empty, then the ParseComplete should have an Error containing the given string
|
||||||
// error containing this string before a Complete response is returned.
|
|
||||||
ErrorContains string
|
ErrorContains string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
|
@ -33,16 +30,12 @@ func TestParse(t *testing.T) {
|
||||||
description = "Testing!"
|
description = "Testing!"
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
Response: &proto.Parse_Response{
|
Response: &proto.ParseComplete{
|
||||||
Type: &proto.Parse_Response_Complete{
|
TemplateVariables: []*proto.TemplateVariable{
|
||||||
Complete: &proto.Parse_Complete{
|
{
|
||||||
TemplateVariables: []*proto.TemplateVariable{
|
Name: "A",
|
||||||
{
|
Description: "Testing!",
|
||||||
Name: "A",
|
Required: true,
|
||||||
Description: "Testing!",
|
|
||||||
Required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -54,15 +47,11 @@ func TestParse(t *testing.T) {
|
||||||
default = "wow"
|
default = "wow"
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
Response: &proto.Parse_Response{
|
Response: &proto.ParseComplete{
|
||||||
Type: &proto.Parse_Response_Complete{
|
TemplateVariables: []*proto.TemplateVariable{
|
||||||
Complete: &proto.Parse_Complete{
|
{
|
||||||
TemplateVariables: []*proto.TemplateVariable{
|
Name: "A",
|
||||||
{
|
DefaultValue: "wow",
|
||||||
Name: "A",
|
|
||||||
DefaultValue: "wow",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -76,15 +65,11 @@ func TestParse(t *testing.T) {
|
||||||
}
|
}
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
Response: &proto.Parse_Response{
|
Response: &proto.ParseComplete{
|
||||||
Type: &proto.Parse_Response_Complete{
|
TemplateVariables: []*proto.TemplateVariable{
|
||||||
Complete: &proto.Parse_Complete{
|
{
|
||||||
TemplateVariables: []*proto.TemplateVariable{
|
Name: "A",
|
||||||
{
|
Required: true,
|
||||||
Name: "A",
|
|
||||||
Required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -104,27 +89,23 @@ func TestParse(t *testing.T) {
|
||||||
"main2.tf": `variable "baz" { }
|
"main2.tf": `variable "baz" { }
|
||||||
variable "quux" { }`,
|
variable "quux" { }`,
|
||||||
},
|
},
|
||||||
Response: &proto.Parse_Response{
|
Response: &proto.ParseComplete{
|
||||||
Type: &proto.Parse_Response_Complete{
|
TemplateVariables: []*proto.TemplateVariable{
|
||||||
Complete: &proto.Parse_Complete{
|
{
|
||||||
TemplateVariables: []*proto.TemplateVariable{
|
Name: "foo",
|
||||||
{
|
Required: true,
|
||||||
Name: "foo",
|
},
|
||||||
Required: true,
|
{
|
||||||
},
|
Name: "bar",
|
||||||
{
|
Required: true,
|
||||||
Name: "bar",
|
},
|
||||||
Required: true,
|
{
|
||||||
},
|
Name: "baz",
|
||||||
{
|
Required: true,
|
||||||
Name: "baz",
|
},
|
||||||
Required: true,
|
{
|
||||||
},
|
Name: "quux",
|
||||||
{
|
Required: true,
|
||||||
Name: "quux",
|
|
||||||
Required: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -139,19 +120,15 @@ func TestParse(t *testing.T) {
|
||||||
sensitive = true
|
sensitive = true
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
Response: &proto.Parse_Response{
|
Response: &proto.ParseComplete{
|
||||||
Type: &proto.Parse_Response_Complete{
|
TemplateVariables: []*proto.TemplateVariable{
|
||||||
Complete: &proto.Parse_Complete{
|
{
|
||||||
TemplateVariables: []*proto.TemplateVariable{
|
Name: "A",
|
||||||
{
|
Description: "Testing!",
|
||||||
Name: "A",
|
Type: "bool",
|
||||||
Description: "Testing!",
|
DefaultValue: "true",
|
||||||
Type: "bool",
|
Required: false,
|
||||||
DefaultValue: "true",
|
Sensitive: true,
|
||||||
Required: false,
|
|
||||||
Sensitive: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -166,19 +143,15 @@ func TestParse(t *testing.T) {
|
||||||
sensitive = true
|
sensitive = true
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
Response: &proto.Parse_Response{
|
Response: &proto.ParseComplete{
|
||||||
Type: &proto.Parse_Response_Complete{
|
TemplateVariables: []*proto.TemplateVariable{
|
||||||
Complete: &proto.Parse_Complete{
|
{
|
||||||
TemplateVariables: []*proto.TemplateVariable{
|
Name: "A",
|
||||||
{
|
Description: "Testing!",
|
||||||
Name: "A",
|
Type: "string",
|
||||||
Description: "Testing!",
|
DefaultValue: "abc",
|
||||||
Type: "string",
|
Required: false,
|
||||||
DefaultValue: "abc",
|
Sensitive: true,
|
||||||
Required: false,
|
|
||||||
Sensitive: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -193,19 +166,15 @@ func TestParse(t *testing.T) {
|
||||||
sensitive = true
|
sensitive = true
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
Response: &proto.Parse_Response{
|
Response: &proto.ParseComplete{
|
||||||
Type: &proto.Parse_Response_Complete{
|
TemplateVariables: []*proto.TemplateVariable{
|
||||||
Complete: &proto.Parse_Complete{
|
{
|
||||||
TemplateVariables: []*proto.TemplateVariable{
|
Name: "A",
|
||||||
{
|
Description: "Testing!",
|
||||||
Name: "A",
|
Type: "string",
|
||||||
Description: "Testing!",
|
DefaultValue: "",
|
||||||
Type: "string",
|
Required: false,
|
||||||
DefaultValue: "",
|
Sensitive: true,
|
||||||
Required: false,
|
|
||||||
Sensitive: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -219,19 +188,15 @@ func TestParse(t *testing.T) {
|
||||||
sensitive = true
|
sensitive = true
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
Response: &proto.Parse_Response{
|
Response: &proto.ParseComplete{
|
||||||
Type: &proto.Parse_Response_Complete{
|
TemplateVariables: []*proto.TemplateVariable{
|
||||||
Complete: &proto.Parse_Complete{
|
{
|
||||||
TemplateVariables: []*proto.TemplateVariable{
|
Name: "A",
|
||||||
{
|
Description: "Testing!",
|
||||||
Name: "A",
|
Type: "string",
|
||||||
Description: "Testing!",
|
DefaultValue: "",
|
||||||
Type: "string",
|
Required: true,
|
||||||
DefaultValue: "",
|
Sensitive: true,
|
||||||
Required: true,
|
|
||||||
Sensitive: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -243,40 +208,31 @@ func TestParse(t *testing.T) {
|
||||||
t.Run(testCase.Name, func(t *testing.T) {
|
t.Run(testCase.Name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
// Write all files to the temporary test directory.
|
session := configure(ctx, t, api, &proto.Config{
|
||||||
directory := t.TempDir()
|
TemplateSourceArchive: makeTar(t, testCase.Files),
|
||||||
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,
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
err := session.Send(&proto.Request{Type: &proto.Request_Parse{Parse: &proto.ParseRequest{}}})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
msg, err := response.Recv()
|
msg, err := session.Recv()
|
||||||
if err != nil {
|
require.NoError(t, err)
|
||||||
if testCase.ErrorContains != "" {
|
|
||||||
require.ErrorContains(t, err, testCase.ErrorContains)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if msg.GetComplete() == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if testCase.ErrorContains != "" {
|
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!
|
// Ensure the want and got are equivalent!
|
||||||
want, err := json.Marshal(testCase.Response)
|
want, err := json.Marshal(testCase.Response)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
got, err := json.Marshal(msg)
|
got, err := json.Marshal(msg.GetParse())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, string(want), string(got))
|
require.Equal(t, string(want), string(got))
|
||||||
|
|
|
@ -4,11 +4,10 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"golang.org/x/xerrors"
|
"cdr.dev/slog"
|
||||||
|
|
||||||
"github.com/coder/coder/v2/coderd/tracing"
|
"github.com/coder/coder/v2/coderd/tracing"
|
||||||
"github.com/coder/coder/v2/provisionersdk"
|
"github.com/coder/coder/v2/provisionersdk"
|
||||||
|
@ -16,48 +15,23 @@ import (
|
||||||
"github.com/coder/terraform-provider-coder/provider"
|
"github.com/coder/terraform-provider-coder/provider"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Provision executes `terraform apply` or `terraform plan` for dry runs.
|
func (s *server) setupContexts(parent context.Context, canceledOrComplete <-chan struct{}) (
|
||||||
func (s *server) Provision(stream proto.DRPCProvisioner_ProvisionStream) error {
|
ctx context.Context, cancel func(), killCtx context.Context, kill func(),
|
||||||
ctx, span := s.startTrace(stream.Context(), tracing.FuncName())
|
) {
|
||||||
defer span.End()
|
// Create a context for graceful cancellation bound to the session
|
||||||
|
|
||||||
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
|
|
||||||
// context. This ensures that we will perform graceful cancellation
|
// context. This ensures that we will perform graceful cancellation
|
||||||
// even on connection loss.
|
// even on connection loss.
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel = context.WithCancel(parent)
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
// Create a separate context for forceful cancellation not tied to
|
// Create a separate context for forceful cancellation not tied to
|
||||||
// the stream so that we can control when to terminate the process.
|
// the stream so that we can control when to terminate the process.
|
||||||
killCtx, kill := context.WithCancel(context.Background())
|
killCtx, kill = context.WithCancel(context.Background())
|
||||||
defer kill()
|
|
||||||
|
|
||||||
// Ensure processes are eventually cleaned up on graceful
|
// Ensure processes are eventually cleaned up on graceful
|
||||||
// cancellation or disconnect.
|
// cancellation or disconnect.
|
||||||
go func() {
|
go func() {
|
||||||
<-ctx.Done()
|
<-ctx.Done()
|
||||||
|
s.logger.Debug(ctx, "graceful context done")
|
||||||
|
|
||||||
// TODO(mafredri): We should track this provision request as
|
// TODO(mafredri): We should track this provision request as
|
||||||
// part of graceful server shutdown procedure. Waiting on a
|
// 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()
|
defer t.Stop()
|
||||||
select {
|
select {
|
||||||
case <-t.C:
|
case <-t.C:
|
||||||
|
s.logger.Debug(ctx, "exit timeout hit")
|
||||||
kill()
|
kill()
|
||||||
case <-killCtx.Done():
|
case <-killCtx.Done():
|
||||||
|
s.logger.Debug(ctx, "kill context done")
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
// Process cancel
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
<-canceledOrComplete
|
||||||
request, err := stream.Recv()
|
s.logger.Debug(ctx, "canceledOrComplete closed")
|
||||||
if err != nil {
|
cancel()
|
||||||
return
|
|
||||||
}
|
|
||||||
if request.GetCancel() == nil {
|
|
||||||
// We only process cancellation requests here.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cancel()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}()
|
}()
|
||||||
|
return ctx, cancel, killCtx, kill
|
||||||
|
}
|
||||||
|
|
||||||
sink := streamLogSink{
|
func (s *server) Plan(
|
||||||
logger: s.logger.Named("execution_logs"),
|
sess *provisionersdk.Session, request *proto.PlanRequest, canceledOrComplete <-chan struct{},
|
||||||
stream: stream,
|
) *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)
|
e := s.executor(sess.WorkDirectory)
|
||||||
if err = e.checkMinVersion(ctx); err != nil {
|
if err := e.checkMinVersion(ctx); err != nil {
|
||||||
return err
|
return provisionersdk.PlanErrorf(err.Error())
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
logTerraformEnvVars(sess)
|
||||||
|
|
||||||
// If we're destroying, exit early if there's no state. This is necessary to
|
// 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
|
// 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
|
// 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
|
// contingency, in the future we will try harder to prevent workspaces being
|
||||||
// broken this hard.
|
// broken this hard.
|
||||||
if config.Metadata.WorkspaceTransition == proto.WorkspaceTransition_DESTROY && len(config.State) == 0 {
|
if request.Metadata.GetWorkspaceTransition() == proto.WorkspaceTransition_DESTROY && len(sess.Config.State) == 0 {
|
||||||
_ = stream.Send(&proto.Provision_Response{
|
sess.ProvisionLog(proto.LogLevel_INFO, "The terraform state does not exist, there is nothing to do")
|
||||||
Type: &proto.Provision_Response_Log{
|
return &proto.PlanComplete{}
|
||||||
Log: &proto.Log{
|
}
|
||||||
Level: proto.LogLevel_INFO,
|
|
||||||
Output: "The terraform state does not exist, there is nothing to do",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return stream.Send(&proto.Provision_Response{
|
statefilePath := getStateFilePath(sess.WorkDirectory)
|
||||||
Type: &proto.Provision_Response_Complete{
|
if len(sess.Config.State) > 0 {
|
||||||
Complete: &proto.Provision_Complete{},
|
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")
|
s.logger.Debug(ctx, "running initialization")
|
||||||
err = e.init(ctx, killCtx, sink)
|
err := e.init(ctx, killCtx, sess)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ctx.Err() != nil {
|
s.logger.Debug(ctx, "init failed", slog.Error(err))
|
||||||
return stream.Send(&proto.Provision_Response{
|
return provisionersdk.PlanErrorf("initialize terraform: %s", err)
|
||||||
Type: &proto.Provision_Response_Complete{
|
|
||||||
Complete: &proto.Provision_Complete{
|
|
||||||
Error: err.Error(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return xerrors.Errorf("initialize terraform: %w", err)
|
|
||||||
}
|
}
|
||||||
s.logger.Debug(ctx, "ran initialization")
|
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 {
|
if err != nil {
|
||||||
return err
|
return provisionersdk.PlanErrorf("setup env: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
var resp *proto.Provision_Response
|
vars, err := planVars(request)
|
||||||
if planRequest != nil {
|
if err != nil {
|
||||||
vars, err := planVars(planRequest)
|
return provisionersdk.PlanErrorf("plan vars: %s", err)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
// Must be apply
|
|
||||||
resp, err = e.apply(
|
resp, err := e.plan(
|
||||||
ctx, killCtx, applyRequest.Plan, env, sink,
|
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 {
|
if err != nil {
|
||||||
errorMessage := err.Error()
|
errorMessage := err.Error()
|
||||||
// Terraform can fail and apply and still need to store it's state.
|
// Terraform can fail and apply and still need to store it's state.
|
||||||
// In this case, we return Complete with an explicit error message.
|
// In this case, we return Complete with an explicit error message.
|
||||||
stateData, _ := os.ReadFile(statefilePath)
|
stateData, _ := os.ReadFile(statefilePath)
|
||||||
return stream.Send(&proto.Provision_Response{
|
return &proto.ApplyComplete{
|
||||||
Type: &proto.Provision_Response_Complete{
|
State: stateData,
|
||||||
Complete: &proto.Provision_Complete{
|
Error: errorMessage,
|
||||||
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{}
|
vars := []string{}
|
||||||
for _, variable := range plan.VariableValues {
|
for _, variable := range plan.VariableValues {
|
||||||
vars = append(vars, fmt.Sprintf("%s=%s", variable.Name, variable.Value))
|
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
|
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 := safeEnviron()
|
||||||
env = append(env,
|
env = append(env,
|
||||||
"CODER_AGENT_URL="+config.Metadata.CoderUrl,
|
"CODER_AGENT_URL="+metadata.GetCoderUrl(),
|
||||||
"CODER_WORKSPACE_TRANSITION="+strings.ToLower(config.Metadata.WorkspaceTransition.String()),
|
"CODER_WORKSPACE_TRANSITION="+strings.ToLower(metadata.GetWorkspaceTransition().String()),
|
||||||
"CODER_WORKSPACE_NAME="+config.Metadata.WorkspaceName,
|
"CODER_WORKSPACE_NAME="+metadata.GetWorkspaceName(),
|
||||||
"CODER_WORKSPACE_OWNER="+config.Metadata.WorkspaceOwner,
|
"CODER_WORKSPACE_OWNER="+metadata.GetWorkspaceOwner(),
|
||||||
"CODER_WORKSPACE_OWNER_EMAIL="+config.Metadata.WorkspaceOwnerEmail,
|
"CODER_WORKSPACE_OWNER_EMAIL="+metadata.GetWorkspaceOwnerEmail(),
|
||||||
"CODER_WORKSPACE_OWNER_OIDC_ACCESS_TOKEN="+config.Metadata.WorkspaceOwnerOidcAccessToken,
|
"CODER_WORKSPACE_OWNER_OIDC_ACCESS_TOKEN="+metadata.GetWorkspaceOwnerOidcAccessToken(),
|
||||||
"CODER_WORKSPACE_ID="+config.Metadata.WorkspaceId,
|
"CODER_WORKSPACE_ID="+metadata.GetWorkspaceId(),
|
||||||
"CODER_WORKSPACE_OWNER_ID="+config.Metadata.WorkspaceOwnerId,
|
"CODER_WORKSPACE_OWNER_ID="+metadata.GetWorkspaceOwnerId(),
|
||||||
"CODER_WORKSPACE_OWNER_SESSION_TOKEN="+config.Metadata.WorkspaceOwnerSessionToken,
|
"CODER_WORKSPACE_OWNER_SESSION_TOKEN="+metadata.GetWorkspaceOwnerSessionToken(),
|
||||||
)
|
)
|
||||||
for key, value := range provisionersdk.AgentScriptEnv() {
|
for key, value := range provisionersdk.AgentScriptEnv() {
|
||||||
env = append(env, key+"="+value)
|
env = append(env, key+"="+value)
|
||||||
|
@ -258,10 +232,10 @@ func logTerraformEnvVars(sink logSink) {
|
||||||
if !tfEnvSafeToPrint[parts[0]] {
|
if !tfEnvSafeToPrint[parts[0]] {
|
||||||
parts[1] = "<value redacted>"
|
parts[1] = "<value redacted>"
|
||||||
}
|
}
|
||||||
sink.Log(&proto.Log{
|
sink.ProvisionLog(
|
||||||
Level: proto.LogLevel_WARN,
|
proto.LogLevel_WARN,
|
||||||
Output: fmt.Sprintf("terraform environment variable: %s=%s", parts[0], parts[1]),
|
fmt.Sprintf("terraform environment variable: %s=%s", parts[0], parts[1]),
|
||||||
})
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
package terraform_test
|
package terraform_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"archive/tar"
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
@ -35,6 +37,7 @@ func setupProvisioner(t *testing.T, opts *provisionerServeOptions) (context.Cont
|
||||||
opts = &provisionerServeOptions{}
|
opts = &provisionerServeOptions{}
|
||||||
}
|
}
|
||||||
cachePath := t.TempDir()
|
cachePath := t.TempDir()
|
||||||
|
workDir := t.TempDir()
|
||||||
client, server := provisionersdk.MemTransportPipe()
|
client, server := provisionersdk.MemTransportPipe()
|
||||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||||
serverErr := make(chan error, 1)
|
serverErr := make(chan error, 1)
|
||||||
|
@ -50,40 +53,75 @@ func setupProvisioner(t *testing.T, opts *provisionerServeOptions) (context.Cont
|
||||||
go func() {
|
go func() {
|
||||||
serverErr <- terraform.Serve(ctx, &terraform.ServeOptions{
|
serverErr <- terraform.Serve(ctx, &terraform.ServeOptions{
|
||||||
ServeOptions: &provisionersdk.ServeOptions{
|
ServeOptions: &provisionersdk.ServeOptions{
|
||||||
Listener: server,
|
Listener: server,
|
||||||
|
Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
|
||||||
|
WorkDirectory: workDir,
|
||||||
},
|
},
|
||||||
BinaryPath: opts.binaryPath,
|
BinaryPath: opts.binaryPath,
|
||||||
CachePath: cachePath,
|
CachePath: cachePath,
|
||||||
Logger: slogtest.Make(t, nil).Leveled(slog.LevelDebug),
|
|
||||||
ExitTimeout: opts.exitTimeout,
|
ExitTimeout: opts.exitTimeout,
|
||||||
})
|
})
|
||||||
}()
|
}()
|
||||||
api := proto.NewDRPCProvisionerClient(client)
|
api := proto.NewDRPCProvisionerClient(client)
|
||||||
|
|
||||||
return ctx, api
|
return ctx, api
|
||||||
}
|
}
|
||||||
|
|
||||||
func readProvisionLog(t *testing.T, response proto.DRPCProvisioner_ProvisionClient) (
|
func makeTar(t *testing.T, files map[string]string) []byte {
|
||||||
string,
|
t.Helper()
|
||||||
*proto.Provision_Complete,
|
var buffer bytes.Buffer
|
||||||
) {
|
writer := tar.NewWriter(&buffer)
|
||||||
var (
|
for name, content := range files {
|
||||||
logBuf strings.Builder
|
err := writer.WriteHeader(&tar.Header{
|
||||||
c *proto.Provision_Complete
|
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 {
|
for {
|
||||||
msg, err := response.Recv()
|
msg, err := response.Recv()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
if log := msg.GetLog(); log != nil {
|
if log := msg.GetLog(); log != nil {
|
||||||
t.Log(log.Level.String(), log.Output)
|
t.Log(log.Level.String(), log.Output)
|
||||||
_, _ = logBuf.WriteString(log.Output)
|
_, err = logBuf.WriteString(log.Output)
|
||||||
}
|
require.NoError(t, err)
|
||||||
if c = msg.GetComplete(); c != nil {
|
continue
|
||||||
require.Empty(t, c.Error)
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
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) {
|
func TestProvision_Cancel(t *testing.T) {
|
||||||
|
@ -109,9 +147,10 @@ func TestProvision_Cancel(t *testing.T) {
|
||||||
wantLog: []string{"interrupt", "exit"},
|
wantLog: []string{"interrupt", "exit"},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "Cancel apply",
|
// Provisioner requires a plan before an apply, so test cancel with plan.
|
||||||
mode: "apply",
|
name: "Cancel plan",
|
||||||
startSequence: []string{"init", "apply_start"},
|
mode: "plan",
|
||||||
|
startSequence: []string{"init", "plan_start"},
|
||||||
wantLog: []string{"interrupt", "exit"},
|
wantLog: []string{"interrupt", "exit"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -131,24 +170,16 @@ func TestProvision_Cancel(t *testing.T) {
|
||||||
ctx, api := setupProvisioner(t, &provisionerServeOptions{
|
ctx, api := setupProvisioner(t, &provisionerServeOptions{
|
||||||
binaryPath: binPath,
|
binaryPath: binPath,
|
||||||
})
|
})
|
||||||
|
sess := configure(ctx, t, api, &proto.Config{
|
||||||
response, err := api.Provision(ctx)
|
TemplateSourceArchive: makeTar(t, nil),
|
||||||
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{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
err = sendPlan(sess, proto.WorkspaceTransition_START)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for _, line := range tt.startSequence {
|
for _, line := range tt.startSequence {
|
||||||
LoopStart:
|
LoopStart:
|
||||||
msg, err := response.Recv()
|
msg, err := sess.Recv()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
t.Log(msg.Type)
|
t.Log(msg.Type)
|
||||||
|
@ -160,22 +191,22 @@ func TestProvision_Cancel(t *testing.T) {
|
||||||
require.Equal(t, line, log.Output)
|
require.Equal(t, line, log.Output)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = response.Send(&proto.Provision_Request{
|
err = sess.Send(&proto.Request{
|
||||||
Type: &proto.Provision_Request_Cancel{
|
Type: &proto.Request_Cancel{
|
||||||
Cancel: &proto.Provision_Cancel{},
|
Cancel: &proto.CancelRequest{},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
var gotLog []string
|
var gotLog []string
|
||||||
for {
|
for {
|
||||||
msg, err := response.Recv()
|
msg, err := sess.Recv()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
if log := msg.GetLog(); log != nil {
|
if log := msg.GetLog(); log != nil {
|
||||||
gotLog = append(gotLog, log.Output)
|
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")
|
require.Contains(t, c.Error, "exit status 1")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -208,23 +239,17 @@ func TestProvision_CancelTimeout(t *testing.T) {
|
||||||
exitTimeout: time.Second,
|
exitTimeout: time.Second,
|
||||||
})
|
})
|
||||||
|
|
||||||
response, err := api.Provision(ctx)
|
sess := configure(ctx, t, api, &proto.Config{
|
||||||
require.NoError(t, err)
|
TemplateSourceArchive: makeTar(t, nil),
|
||||||
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{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// provisioner requires plan before apply, so test cancel with plan.
|
||||||
|
err = sendPlan(sess, proto.WorkspaceTransition_START)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for _, line := range []string{"init", "apply_start"} {
|
for _, line := range []string{"init", "plan_start"} {
|
||||||
LoopStart:
|
LoopStart:
|
||||||
msg, err := response.Recv()
|
msg, err := sess.Recv()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
t.Log(msg.Type)
|
t.Log(msg.Type)
|
||||||
|
@ -236,18 +261,14 @@ func TestProvision_CancelTimeout(t *testing.T) {
|
||||||
require.Equal(t, line, log.Output)
|
require.Equal(t, line, log.Output)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = response.Send(&proto.Provision_Request{
|
err = sess.Send(&proto.Request{Type: &proto.Request_Cancel{Cancel: &proto.CancelRequest{}}})
|
||||||
Type: &proto.Provision_Request_Cancel{
|
|
||||||
Cancel: &proto.Provision_Cancel{},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
msg, err := response.Recv()
|
msg, err := sess.Recv()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
if c := msg.GetComplete(); c != nil {
|
if c := msg.GetPlan(); c != nil {
|
||||||
require.Contains(t, c.Error, "killed")
|
require.Contains(t, c.Error, "killed")
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -258,17 +279,18 @@ func TestProvision(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
Name string
|
Name string
|
||||||
Files map[string]string
|
Files map[string]string
|
||||||
Request *proto.Provision_Plan
|
Metadata *proto.Metadata
|
||||||
|
Request *proto.PlanRequest
|
||||||
// Response may be nil to not check the response.
|
// Response may be nil to not check the response.
|
||||||
Response *proto.Provision_Response
|
Response *proto.PlanComplete
|
||||||
// If ErrorContains is not empty, then response.Recv() should return an
|
// If ErrorContains is not empty, PlanComplete should have an Error containing the given string
|
||||||
// error containing this string before a Complete response is returned.
|
|
||||||
ErrorContains string
|
ErrorContains string
|
||||||
// If ExpectLogContains is not empty, then the logs should contain it.
|
// If ExpectLogContains is not empty, then the logs should contain it.
|
||||||
ExpectLogContains string
|
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",
|
Name: "missing-variable",
|
||||||
|
@ -293,15 +315,11 @@ func TestProvision(t *testing.T) {
|
||||||
Files: map[string]string{
|
Files: map[string]string{
|
||||||
"main.tf": `resource "null_resource" "A" {}`,
|
"main.tf": `resource "null_resource" "A" {}`,
|
||||||
},
|
},
|
||||||
Response: &proto.Provision_Response{
|
Response: &proto.PlanComplete{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Resources: []*proto.Resource{{
|
||||||
Complete: &proto.Provision_Complete{
|
Name: "A",
|
||||||
Resources: []*proto.Resource{{
|
Type: "null_resource",
|
||||||
Name: "A",
|
}},
|
||||||
Type: "null_resource",
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -309,15 +327,11 @@ func TestProvision(t *testing.T) {
|
||||||
Files: map[string]string{
|
Files: map[string]string{
|
||||||
"main.tf": `resource "null_resource" "A" {}`,
|
"main.tf": `resource "null_resource" "A" {}`,
|
||||||
},
|
},
|
||||||
Response: &proto.Provision_Response{
|
Response: &proto.PlanComplete{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Resources: []*proto.Resource{{
|
||||||
Complete: &proto.Provision_Complete{
|
Name: "A",
|
||||||
Resources: []*proto.Resource{{
|
Type: "null_resource",
|
||||||
Name: "A",
|
}},
|
||||||
Type: "null_resource",
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Apply: true,
|
Apply: true,
|
||||||
},
|
},
|
||||||
|
@ -334,15 +348,11 @@ func TestProvision(t *testing.T) {
|
||||||
}
|
}
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
Response: &proto.Provision_Response{
|
Response: &proto.PlanComplete{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Resources: []*proto.Resource{{
|
||||||
Complete: &proto.Provision_Complete{
|
Name: "A",
|
||||||
Resources: []*proto.Resource{{
|
Type: "null_resource",
|
||||||
Name: "A",
|
}},
|
||||||
Type: "null_resource",
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Apply: true,
|
Apply: true,
|
||||||
},
|
},
|
||||||
|
@ -367,12 +377,8 @@ func TestProvision(t *testing.T) {
|
||||||
Files: map[string]string{
|
Files: map[string]string{
|
||||||
"main.tf": `resource "null_resource" "A" {}`,
|
"main.tf": `resource "null_resource" "A" {}`,
|
||||||
},
|
},
|
||||||
Request: &proto.Provision_Plan{
|
Metadata: &proto.Metadata{
|
||||||
Config: &proto.Provision_Config{
|
WorkspaceTransition: proto.WorkspaceTransition_DESTROY,
|
||||||
Metadata: &proto.Provision_Metadata{
|
|
||||||
WorkspaceTransition: proto.WorkspaceTransition_DESTROY,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
ExpectLogContains: "nothing to do",
|
ExpectLogContains: "nothing to do",
|
||||||
},
|
},
|
||||||
|
@ -406,7 +412,7 @@ func TestProvision(t *testing.T) {
|
||||||
}
|
}
|
||||||
}`,
|
}`,
|
||||||
},
|
},
|
||||||
Request: &proto.Provision_Plan{
|
Request: &proto.PlanRequest{
|
||||||
RichParameterValues: []*proto.RichParameterValue{
|
RichParameterValues: []*proto.RichParameterValue{
|
||||||
{
|
{
|
||||||
Name: "Example",
|
Name: "Example",
|
||||||
|
@ -418,27 +424,23 @@ func TestProvision(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Response: &proto.Provision_Response{
|
Response: &proto.PlanComplete{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Parameters: []*proto.RichParameter{
|
||||||
Complete: &proto.Provision_Complete{
|
{
|
||||||
Parameters: []*proto.RichParameter{
|
Name: "Example",
|
||||||
{
|
Type: "string",
|
||||||
Name: "Example",
|
DefaultValue: "foobar",
|
||||||
Type: "string",
|
},
|
||||||
DefaultValue: "foobar",
|
{
|
||||||
},
|
Name: "Sample",
|
||||||
{
|
Type: "string",
|
||||||
Name: "Sample",
|
DefaultValue: "foobaz",
|
||||||
Type: "string",
|
|
||||||
DefaultValue: "foobaz",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Resources: []*proto.Resource{{
|
|
||||||
Name: "example",
|
|
||||||
Type: "null_resource",
|
|
||||||
}},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
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{
|
RichParameterValues: []*proto.RichParameterValue{
|
||||||
{
|
{
|
||||||
Name: "Example",
|
Name: "Example",
|
||||||
|
@ -500,27 +502,23 @@ func TestProvision(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Response: &proto.Provision_Response{
|
Response: &proto.PlanComplete{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Parameters: []*proto.RichParameter{
|
||||||
Complete: &proto.Provision_Complete{
|
{
|
||||||
Parameters: []*proto.RichParameter{
|
Name: "Example",
|
||||||
{
|
Type: "string",
|
||||||
Name: "Example",
|
DefaultValue: "foobar",
|
||||||
Type: "string",
|
},
|
||||||
DefaultValue: "foobar",
|
{
|
||||||
},
|
Name: "Sample",
|
||||||
{
|
Type: "string",
|
||||||
Name: "Sample",
|
DefaultValue: "foobaz",
|
||||||
Type: "string",
|
|
||||||
DefaultValue: "foobaz",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Resources: []*proto.Resource{{
|
|
||||||
Name: "example",
|
|
||||||
Type: "null_resource",
|
|
||||||
}},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
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{{
|
GitAuthProviders: []*proto.GitAuthProvider{{
|
||||||
Id: "github",
|
Id: "github",
|
||||||
AccessToken: "some-value",
|
AccessToken: "some-value",
|
||||||
}},
|
}},
|
||||||
},
|
},
|
||||||
Response: &proto.Provision_Response{
|
Response: &proto.PlanComplete{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Resources: []*proto.Resource{{
|
||||||
Complete: &proto.Provision_Complete{
|
Name: "example",
|
||||||
Resources: []*proto.Resource{{
|
Type: "null_resource",
|
||||||
Name: "example",
|
Metadata: []*proto.Resource_Metadata{{
|
||||||
Type: "null_resource",
|
Key: "token",
|
||||||
Metadata: []*proto.Resource_Metadata{{
|
Value: "some-value",
|
||||||
Key: "token",
|
}},
|
||||||
Value: "some-value",
|
}},
|
||||||
}},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -579,50 +573,26 @@ func TestProvision(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
ctx, api := setupProvisioner(t, nil)
|
ctx, api := setupProvisioner(t, nil)
|
||||||
|
sess := configure(ctx, t, api, &proto.Config{
|
||||||
|
TemplateSourceArchive: makeTar(t, testCase.Files),
|
||||||
|
})
|
||||||
|
|
||||||
directory := t.TempDir()
|
planRequest := &proto.Request{Type: &proto.Request_Plan{Plan: &proto.PlanRequest{
|
||||||
for path, content := range testCase.Files {
|
Metadata: testCase.Metadata,
|
||||||
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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
if testCase.Request != nil {
|
if testCase.Request != nil {
|
||||||
if planRequest.GetPlan().GetConfig() == nil {
|
planRequest = &proto.Request{Type: &proto.Request_Plan{Plan: testCase.Request}}
|
||||||
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{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gotExpectedLog := testCase.ExpectLogContains == ""
|
gotExpectedLog := testCase.ExpectLogContains == ""
|
||||||
|
|
||||||
provision := func(req *proto.Provision_Request) *proto.Provision_Complete {
|
provision := func(req *proto.Request) *proto.Response {
|
||||||
response, err := api.Provision(ctx)
|
err := sess.Send(req)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
err = response.Send(req)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var complete *proto.Provision_Complete
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
msg, err := response.Recv()
|
msg, err := sess.Recv()
|
||||||
if msg != nil && msg.GetLog() != nil {
|
require.NoError(t, err)
|
||||||
|
if msg.GetLog() != nil {
|
||||||
if testCase.ExpectLogContains != "" && strings.Contains(msg.GetLog().Output, testCase.ExpectLogContains) {
|
if testCase.ExpectLogContains != "" && strings.Contains(msg.GetLog().Output, testCase.ExpectLogContains) {
|
||||||
gotExpectedLog = true
|
gotExpectedLog = true
|
||||||
}
|
}
|
||||||
|
@ -630,67 +600,51 @@ func TestProvision(t *testing.T) {
|
||||||
t.Logf("log: [%s] %s", msg.GetLog().Level, msg.GetLog().Output)
|
t.Logf("log: [%s] %s", msg.GetLog().Level, msg.GetLog().Output)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if testCase.ErrorContains != "" {
|
return msg
|
||||||
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 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 {
|
if testCase.Apply {
|
||||||
require.NotNil(t, planComplete.Plan)
|
resp = provision(&proto.Request{Type: &proto.Request_Apply{Apply: &proto.ApplyRequest{
|
||||||
provision(&proto.Provision_Request{
|
Metadata: &proto.Metadata{WorkspaceTransition: proto.WorkspaceTransition_START},
|
||||||
Type: &proto.Provision_Request_Apply{
|
}}})
|
||||||
Apply: &proto.Provision_Apply{
|
applyComplete := resp.GetApply()
|
||||||
Config: planRequest.GetPlan().GetConfig(),
|
require.NotNil(t, applyComplete)
|
||||||
Plan: planComplete.Plan,
|
|
||||||
},
|
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 {
|
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
|
// nolint:paralleltest
|
||||||
func TestProvision_ExtraEnv(t *testing.T) {
|
func TestProvision_ExtraEnv(t *testing.T) {
|
||||||
// #nosec
|
// #nosec
|
||||||
|
@ -708,31 +678,15 @@ func TestProvision_ExtraEnv(t *testing.T) {
|
||||||
t.Setenv("TF_SUPERSECRET", secretValue)
|
t.Setenv("TF_SUPERSECRET", secretValue)
|
||||||
|
|
||||||
ctx, api := setupProvisioner(t, nil)
|
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()
|
err := sendPlan(sess, proto.WorkspaceTransition_START)
|
||||||
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)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
found := false
|
found := false
|
||||||
for {
|
for {
|
||||||
msg, err := response.Recv()
|
msg, err := sess.Recv()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
if log := msg.GetLog(); log != nil {
|
if log := msg.GetLog(); log != nil {
|
||||||
|
@ -742,7 +696,7 @@ func TestProvision_ExtraEnv(t *testing.T) {
|
||||||
}
|
}
|
||||||
require.NotContains(t, log.Output, secretValue)
|
require.NotContains(t, log.Output, secretValue)
|
||||||
}
|
}
|
||||||
if c := msg.GetComplete(); c != nil {
|
if c := msg.GetPlan(); c != nil {
|
||||||
require.Empty(t, c.Error)
|
require.Empty(t, c.Error)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
@ -774,48 +728,19 @@ func TestProvision_SafeEnv(t *testing.T) {
|
||||||
`
|
`
|
||||||
|
|
||||||
ctx, api := setupProvisioner(t, nil)
|
ctx, api := setupProvisioner(t, nil)
|
||||||
|
sess := configure(ctx, t, api, &proto.Config{
|
||||||
directory := t.TempDir()
|
TemplateSourceArchive: makeTar(t, map[string]string{"main.tf": echoResource}),
|
||||||
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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
err := sendPlan(sess, proto.WorkspaceTransition_START)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, complete := readProvisionLog(t, response)
|
_ = readProvisionLog(t, sess)
|
||||||
|
|
||||||
response, err = api.Provision(ctx)
|
err = sendApply(sess, proto.WorkspaceTransition_START)
|
||||||
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(),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
log, _ := readProvisionLog(t, response)
|
log := readProvisionLog(t, sess)
|
||||||
require.Contains(t, log, passedValue)
|
require.Contains(t, log, passedValue)
|
||||||
require.NotContains(t, log, secretValue)
|
require.NotContains(t, log, secretValue)
|
||||||
require.Contains(t, log, "CODER_")
|
require.Contains(t, log, "CODER_")
|
||||||
|
|
|
@ -24,7 +24,6 @@ type ServeOptions struct {
|
||||||
BinaryPath string
|
BinaryPath string
|
||||||
// CachePath must not be used by multiple processes at once.
|
// CachePath must not be used by multiple processes at once.
|
||||||
CachePath string
|
CachePath string
|
||||||
Logger slog.Logger
|
|
||||||
Tracer trace.Tracer
|
Tracer trace.Tracer
|
||||||
|
|
||||||
// ExitTimeout defines how long we will wait for a running Terraform
|
// 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,
|
binaryPath: s.binaryPath,
|
||||||
cachePath: s.cachePath,
|
cachePath: s.cachePath,
|
||||||
workdir: workdir,
|
workdir: workdir,
|
||||||
|
logger: s.logger.Named("executor"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,8 +22,9 @@ version)
|
||||||
;;
|
;;
|
||||||
init)
|
init)
|
||||||
case "$MODE" in
|
case "$MODE" in
|
||||||
apply)
|
plan)
|
||||||
echo "init"
|
echo "init"
|
||||||
|
exit 0
|
||||||
;;
|
;;
|
||||||
init)
|
init)
|
||||||
sleep 10 &
|
sleep 10 &
|
||||||
|
@ -39,7 +40,7 @@ init)
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
apply)
|
plan)
|
||||||
sleep 10 &
|
sleep 10 &
|
||||||
sleep_pid=$!
|
sleep_pid=$!
|
||||||
|
|
||||||
|
@ -47,14 +48,14 @@ apply)
|
||||||
trap 'json_print interrupt; exit 1' INT
|
trap 'json_print interrupt; exit 1' INT
|
||||||
trap 'json_print terminate; exit 2' TERM
|
trap 'json_print terminate; exit 2' TERM
|
||||||
|
|
||||||
json_print apply_start
|
json_print plan_start
|
||||||
wait
|
wait
|
||||||
json_print apply_end
|
json_print plan_end
|
||||||
;;
|
;;
|
||||||
plan)
|
apply)
|
||||||
echo "plan not supported"
|
echo "apply not supported"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
exit 0
|
exit 10
|
||||||
|
|
|
@ -23,19 +23,19 @@ init)
|
||||||
echo "init"
|
echo "init"
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
apply)
|
plan)
|
||||||
trap 'json_print interrupt' INT
|
trap 'json_print interrupt' INT
|
||||||
|
|
||||||
json_print apply_start
|
json_print plan_start
|
||||||
sleep 10 2>/dev/null >/dev/null
|
sleep 10 2>/dev/null >/dev/null
|
||||||
json_print apply_end
|
json_print plan_end
|
||||||
|
|
||||||
exit 0
|
exit 0
|
||||||
;;
|
;;
|
||||||
plan)
|
apply)
|
||||||
echo "plan not supported"
|
echo "apply not supported"
|
||||||
exit 1
|
exit 1
|
||||||
;;
|
;;
|
||||||
esac
|
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"`
|
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"`
|
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"`
|
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"`
|
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"`
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *AcquiredJob_WorkspaceBuild) GetMetadata() *proto.Provision_Metadata {
|
func (x *AcquiredJob_WorkspaceBuild) GetMetadata() *proto.Metadata {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Metadata
|
return x.Metadata
|
||||||
}
|
}
|
||||||
|
@ -917,8 +917,8 @@ type AcquiredJob_TemplateImport struct {
|
||||||
sizeCache protoimpl.SizeCache
|
sizeCache protoimpl.SizeCache
|
||||||
unknownFields protoimpl.UnknownFields
|
unknownFields protoimpl.UnknownFields
|
||||||
|
|
||||||
Metadata *proto.Provision_Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,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"`
|
UserVariableValues []*proto.VariableValue `protobuf:"bytes,2,rep,name=user_variable_values,json=userVariableValues,proto3" json:"user_variable_values,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *AcquiredJob_TemplateImport) Reset() {
|
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}
|
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 {
|
if x != nil {
|
||||||
return x.Metadata
|
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"`
|
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"`
|
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() {
|
func (x *AcquiredJob_TemplateDryRun) Reset() {
|
||||||
|
@ -1023,7 +1023,7 @@ func (x *AcquiredJob_TemplateDryRun) GetVariableValues() []*proto.VariableValue
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *AcquiredJob_TemplateDryRun) GetMetadata() *proto.Provision_Metadata {
|
func (x *AcquiredJob_TemplateDryRun) GetMetadata() *proto.Metadata {
|
||||||
if x != nil {
|
if x != nil {
|
||||||
return x.Metadata
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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,
|
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, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
|
0x74, 0x61, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
|
||||||
0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
|
0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52,
|
||||||
0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64,
|
0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61,
|
||||||
0x61, 0x74, 0x61, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x08, 0x20, 0x01,
|
0x74, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12,
|
||||||
0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x67,
|
0x1b, 0x0a, 0x09, 0x6c, 0x6f, 0x67, 0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x09, 0x20, 0x01,
|
||||||
0x5f, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f,
|
0x28, 0x09, 0x52, 0x08, 0x6c, 0x6f, 0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x4a, 0x04, 0x08, 0x03,
|
||||||
0x67, 0x4c, 0x65, 0x76, 0x65, 0x6c, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x1a, 0x9b, 0x01, 0x0a,
|
0x10, 0x04, 0x1a, 0x91, 0x01, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49,
|
||||||
0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12,
|
0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
|
||||||
0x3b, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28,
|
0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73,
|
||||||
0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
|
0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08,
|
||||||
0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
|
0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x4c, 0x0a, 0x14, 0x75, 0x73, 0x65, 0x72,
|
||||||
0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x4c, 0x0a, 0x14,
|
0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73,
|
||||||
0x75, 0x73, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61,
|
0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
|
||||||
0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f,
|
0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c,
|
||||||
0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c,
|
0x75, 0x65, 0x52, 0x12, 0x75, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65,
|
||||||
0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x75, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, 0x69,
|
0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x1a, 0xe3, 0x01, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c,
|
||||||
0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x1a, 0xed, 0x01, 0x0a, 0x0e, 0x54,
|
0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x53, 0x0a, 0x15, 0x72, 0x69, 0x63,
|
||||||
0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x53, 0x0a,
|
0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x6c, 0x75,
|
||||||
0x15, 0x72, 0x69, 0x63, 0x68, 0x5f, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x5f,
|
0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
|
||||||
0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70,
|
0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d,
|
||||||
0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x69, 0x63, 0x68, 0x50,
|
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, 0x52, 0x13, 0x72,
|
0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x43,
|
||||||
0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x56, 0x61, 0x6c, 0x75,
|
0x0a, 0x0f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65,
|
||||||
0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76,
|
0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73,
|
||||||
0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72,
|
0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61,
|
||||||
0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62,
|
0x6c, 0x75, 0x65, 0x52, 0x0e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c,
|
||||||
0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c,
|
0x75, 0x65, 0x73, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18,
|
||||||
0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x3b, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64,
|
0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
|
||||||
0x61, 0x74, 0x61, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76,
|
0x6e, 0x65, 0x72, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65,
|
||||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
|
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x1a, 0x40, 0x0a, 0x12,
|
||||||
0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61,
|
0x54, 0x72, 0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74,
|
||||||
0x64, 0x61, 0x74, 0x61, 0x4a, 0x04, 0x08, 0x01, 0x10, 0x02, 0x1a, 0x40, 0x0a, 0x12, 0x54, 0x72,
|
0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
0x61, 0x63, 0x65, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x45, 0x6e, 0x74, 0x72, 0x79,
|
0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20,
|
||||||
0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
|
0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x06,
|
||||||
0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
|
0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xa5, 0x03, 0x0a, 0x09, 0x46, 0x61, 0x69, 0x6c, 0x65,
|
||||||
0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x06, 0x0a, 0x04,
|
0x64, 0x4a, 0x6f, 0x62, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01,
|
||||||
0x74, 0x79, 0x70, 0x65, 0x22, 0xa5, 0x03, 0x0a, 0x09, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a,
|
0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65,
|
||||||
0x6f, 0x62, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
|
0x72, 0x72, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f,
|
||||||
0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72, 0x72,
|
0x72, 0x12, 0x51, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x62,
|
||||||
0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x12,
|
0x75, 0x69, 0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x72, 0x6f,
|
||||||
0x51, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x5f, 0x62, 0x75, 0x69,
|
0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64,
|
||||||
0x6c, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
|
0x4a, 0x6f, 0x62, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69,
|
||||||
0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f,
|
0x6c, 0x64, 0x48, 0x00, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42,
|
||||||
0x62, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64,
|
0x75, 0x69, 0x6c, 0x64, 0x12, 0x51, 0x0a, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65,
|
||||||
0x48, 0x00, 0x52, 0x0e, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69,
|
0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e,
|
||||||
0x6c, 0x64, 0x12, 0x51, 0x0a, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x69,
|
0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x46, 0x61, 0x69,
|
||||||
0x6d, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x70, 0x72,
|
0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49,
|
||||||
0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65,
|
0x6d, 0x70, 0x6f, 0x72, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74,
|
||||||
0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70,
|
0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x52, 0x0a, 0x10, 0x74, 0x65, 0x6d, 0x70, 0x6c,
|
||||||
0x6f, 0x72, 0x74, 0x48, 0x00, 0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49,
|
0x61, 0x74, 0x65, 0x5f, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28,
|
||||||
0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x52, 0x0a, 0x10, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74,
|
0x0b, 0x32, 0x26, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64,
|
||||||
0x65, 0x5f, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c,
|
||||||
0x26, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x46,
|
0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x74, 0x65, 0x6d,
|
||||||
0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74,
|
0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x65,
|
||||||
0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6c,
|
0x72, 0x72, 0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52,
|
||||||
0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x65, 0x72, 0x72,
|
0x09, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x1a, 0x26, 0x0a, 0x0e, 0x57, 0x6f,
|
||||||
0x6f, 0x72, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x65,
|
0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05,
|
||||||
0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x1a, 0x26, 0x0a, 0x0e, 0x57, 0x6f, 0x72, 0x6b,
|
0x73, 0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61,
|
||||||
0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73, 0x74,
|
0x74, 0x65, 0x1a, 0x10, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d,
|
||||||
0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74, 0x65,
|
0x70, 0x6f, 0x72, 0x74, 0x1a, 0x10, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65,
|
||||||
0x1a, 0x10, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f,
|
0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xd8,
|
||||||
0x72, 0x74, 0x1a, 0x10, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72,
|
0x05, 0x0a, 0x0c, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x12,
|
||||||
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,
|
|
||||||
0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
|
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,
|
0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64, 0x12, 0x54, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70,
|
||||||
0x20, 0x03, 0x28, 0x0b, 0x32, 0x11, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
|
0x61, 0x63, 0x65, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||||
0x65, 0x72, 0x64, 0x2e, 0x4c, 0x6f, 0x67, 0x52, 0x04, 0x6c, 0x6f, 0x67, 0x73, 0x12, 0x4c, 0x0a,
|
0x29, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43,
|
||||||
0x12, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62,
|
0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x57, 0x6f, 0x72, 0x6b,
|
||||||
0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x70, 0x72, 0x6f, 0x76,
|
0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x48, 0x00, 0x52, 0x0e, 0x77, 0x6f,
|
||||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65,
|
0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x54, 0x0a, 0x0f,
|
||||||
0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x11, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61,
|
0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x69, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x18,
|
||||||
0x74, 0x65, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x73, 0x12, 0x4c, 0x0a, 0x14, 0x75,
|
0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f,
|
||||||
0x73, 0x65, 0x72, 0x5f, 0x76, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c,
|
0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f,
|
||||||
0x75, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76,
|
0x62, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74,
|
||||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65,
|
0x48, 0x00, 0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f,
|
||||||
0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x75, 0x73, 0x65, 0x72, 0x56, 0x61, 0x72, 0x69, 0x61,
|
0x72, 0x74, 0x12, 0x55, 0x0a, 0x10, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x5f, 0x64,
|
||||||
0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61,
|
0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x70,
|
||||||
0x64, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x65, 0x61, 0x64, 0x6d,
|
0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x70,
|
||||||
0x65, 0x4a, 0x04, 0x08, 0x03, 0x10, 0x04, 0x22, 0x7a, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74,
|
0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x2e, 0x54, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74,
|
||||||
0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08,
|
0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x48, 0x00, 0x52, 0x0e, 0x74, 0x65, 0x6d, 0x70, 0x6c,
|
||||||
0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08,
|
0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x1a, 0x5b, 0x0a, 0x0e, 0x57, 0x6f, 0x72,
|
||||||
0x63, 0x61, 0x6e, 0x63, 0x65, 0x6c, 0x65, 0x64, 0x12, 0x43, 0x0a, 0x0f, 0x76, 0x61, 0x72, 0x69,
|
0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x73,
|
||||||
0x61, 0x62, 0x6c, 0x65, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28,
|
0x74, 0x61, 0x74, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x73, 0x74, 0x61, 0x74,
|
||||||
0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
|
0x65, 0x12, 0x33, 0x0a, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02,
|
||||||
0x56, 0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x0e, 0x76,
|
0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
|
||||||
0x61, 0x72, 0x69, 0x61, 0x62, 0x6c, 0x65, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x4a, 0x04, 0x08,
|
0x65, 0x72, 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73,
|
||||||
0x02, 0x10, 0x03, 0x22, 0x4a, 0x0a, 0x12, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f,
|
0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x1a, 0x81, 0x02, 0x0a, 0x0e, 0x54, 0x65, 0x6d, 0x70, 0x6c,
|
||||||
0x74, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x15, 0x0a, 0x06, 0x6a, 0x6f, 0x62,
|
0x61, 0x74, 0x65, 0x49, 0x6d, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x3e, 0x0a, 0x0f, 0x73, 0x74, 0x61,
|
||||||
0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6a, 0x6f, 0x62, 0x49, 0x64,
|
0x72, 0x74, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03,
|
||||||
0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x5f, 0x63, 0x6f, 0x73, 0x74, 0x18, 0x02,
|
0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
|
||||||
0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x64, 0x61, 0x69, 0x6c, 0x79, 0x43, 0x6f, 0x73, 0x74, 0x22,
|
0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0e, 0x73, 0x74, 0x61, 0x72, 0x74,
|
||||||
0x68, 0x0a, 0x13, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65,
|
0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x3c, 0x0a, 0x0e, 0x73, 0x74, 0x6f,
|
||||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x6f, 0x6b, 0x18, 0x01, 0x20, 0x01,
|
0x70, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28,
|
||||||
0x28, 0x08, 0x52, 0x02, 0x6f, 0x6b, 0x12, 0x29, 0x0a, 0x10, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74,
|
0x0b, 0x32, 0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e,
|
||||||
0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05,
|
0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x0d, 0x73, 0x74, 0x6f, 0x70, 0x52, 0x65,
|
||||||
0x52, 0x0f, 0x63, 0x72, 0x65, 0x64, 0x69, 0x74, 0x73, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65,
|
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x72, 0x69, 0x63, 0x68, 0x5f,
|
||||||
0x64, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28,
|
0x70, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b,
|
||||||
0x05, 0x52, 0x06, 0x62, 0x75, 0x64, 0x67, 0x65, 0x74, 0x2a, 0x34, 0x0a, 0x09, 0x4c, 0x6f, 0x67,
|
0x32, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52,
|
||||||
0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x16, 0x0a, 0x12, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53,
|
0x69, 0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x52, 0x0e, 0x72, 0x69,
|
||||||
0x49, 0x4f, 0x4e, 0x45, 0x52, 0x5f, 0x44, 0x41, 0x45, 0x4d, 0x4f, 0x4e, 0x10, 0x00, 0x12, 0x0f,
|
0x63, 0x68, 0x50, 0x61, 0x72, 0x61, 0x6d, 0x65, 0x74, 0x65, 0x72, 0x73, 0x12, 0x2c, 0x0a, 0x12,
|
||||||
0x0a, 0x0b, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x53, 0x49, 0x4f, 0x4e, 0x45, 0x52, 0x10, 0x01, 0x32,
|
0x67, 0x69, 0x74, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x5f, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65,
|
||||||
0xec, 0x02, 0x0a, 0x11, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x44,
|
0x72, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x10, 0x67, 0x69, 0x74, 0x41, 0x75, 0x74,
|
||||||
0x61, 0x65, 0x6d, 0x6f, 0x6e, 0x12, 0x3c, 0x0a, 0x0a, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65,
|
0x68, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x73, 0x1a, 0x45, 0x0a, 0x0e, 0x54, 0x65,
|
||||||
0x4a, 0x6f, 0x62, 0x12, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65,
|
0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x44, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x12, 0x33, 0x0a, 0x09,
|
||||||
0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x19, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69,
|
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32,
|
||||||
0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x41, 0x63, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64,
|
0x15, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x2e, 0x52, 0x65,
|
||||||
0x4a, 0x6f, 0x62, 0x12, 0x52, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f,
|
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65,
|
||||||
0x74, 0x61, 0x12, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
|
0x73, 0x42, 0x06, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x22, 0xb0, 0x01, 0x0a, 0x03, 0x4c, 0x6f,
|
||||||
0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74, 0x61, 0x52, 0x65, 0x71,
|
0x67, 0x12, 0x2f, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
|
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, 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, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
|
||||||
0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
|
0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x51, 0x75, 0x6f, 0x74,
|
||||||
0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x71,
|
0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x4c, 0x0a, 0x09, 0x55, 0x70, 0x64,
|
||||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e,
|
0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
|
||||||
0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52, 0x65, 0x73,
|
0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52,
|
||||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x46, 0x61, 0x69, 0x6c, 0x4a, 0x6f, 0x62,
|
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69,
|
||||||
0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e,
|
0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x52,
|
||||||
0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76,
|
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x46, 0x61, 0x69, 0x6c, 0x4a,
|
||||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x3e,
|
0x6f, 0x62, 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72,
|
||||||
0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12, 0x1a, 0x2e,
|
0x64, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72,
|
||||||
0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43, 0x6f, 0x6d,
|
0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||||
0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x76,
|
0x12, 0x3e, 0x0a, 0x0b, 0x43, 0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x4a, 0x6f, 0x62, 0x12,
|
||||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x42, 0x2e,
|
0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x43,
|
||||||
0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x64,
|
0x6f, 0x6d, 0x70, 0x6c, 0x65, 0x74, 0x65, 0x64, 0x4a, 0x6f, 0x62, 0x1a, 0x13, 0x2e, 0x70, 0x72,
|
||||||
0x65, 0x72, 0x2f, 0x63, 0x6f, 0x64, 0x65, 0x72, 0x2f, 0x76, 0x32, 0x2f, 0x70, 0x72, 0x6f, 0x76,
|
0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79,
|
||||||
0x69, 0x73, 0x69, 0x6f, 0x6e, 0x65, 0x72, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06,
|
0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63,
|
||||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
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 (
|
var (
|
||||||
|
@ -1618,7 +1616,7 @@ var file_provisionerd_proto_provisionerd_proto_goTypes = []interface{}{
|
||||||
(*proto.VariableValue)(nil), // 22: provisioner.VariableValue
|
(*proto.VariableValue)(nil), // 22: provisioner.VariableValue
|
||||||
(*proto.RichParameterValue)(nil), // 23: provisioner.RichParameterValue
|
(*proto.RichParameterValue)(nil), // 23: provisioner.RichParameterValue
|
||||||
(*proto.GitAuthProvider)(nil), // 24: provisioner.GitAuthProvider
|
(*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.Resource)(nil), // 26: provisioner.Resource
|
||||||
(*proto.RichParameter)(nil), // 27: provisioner.RichParameter
|
(*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
|
23, // 16: provisionerd.AcquiredJob.WorkspaceBuild.rich_parameter_values:type_name -> provisioner.RichParameterValue
|
||||||
22, // 17: provisionerd.AcquiredJob.WorkspaceBuild.variable_values:type_name -> provisioner.VariableValue
|
22, // 17: provisionerd.AcquiredJob.WorkspaceBuild.variable_values:type_name -> provisioner.VariableValue
|
||||||
24, // 18: provisionerd.AcquiredJob.WorkspaceBuild.git_auth_providers:type_name -> provisioner.GitAuthProvider
|
24, // 18: provisionerd.AcquiredJob.WorkspaceBuild.git_auth_providers:type_name -> provisioner.GitAuthProvider
|
||||||
25, // 19: provisionerd.AcquiredJob.WorkspaceBuild.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.Provision.Metadata
|
25, // 20: provisionerd.AcquiredJob.TemplateImport.metadata:type_name -> provisioner.Metadata
|
||||||
22, // 21: provisionerd.AcquiredJob.TemplateImport.user_variable_values:type_name -> provisioner.VariableValue
|
22, // 21: provisionerd.AcquiredJob.TemplateImport.user_variable_values:type_name -> provisioner.VariableValue
|
||||||
23, // 22: provisionerd.AcquiredJob.TemplateDryRun.rich_parameter_values:type_name -> provisioner.RichParameterValue
|
23, // 22: provisionerd.AcquiredJob.TemplateDryRun.rich_parameter_values:type_name -> provisioner.RichParameterValue
|
||||||
22, // 23: provisionerd.AcquiredJob.TemplateDryRun.variable_values:type_name -> provisioner.VariableValue
|
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, // 25: provisionerd.CompletedJob.WorkspaceBuild.resources:type_name -> provisioner.Resource
|
||||||
26, // 26: provisionerd.CompletedJob.TemplateImport.start_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
|
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.RichParameterValue rich_parameter_values = 4;
|
||||||
repeated provisioner.VariableValue variable_values = 5;
|
repeated provisioner.VariableValue variable_values = 5;
|
||||||
repeated provisioner.GitAuthProvider git_auth_providers = 6;
|
repeated provisioner.GitAuthProvider git_auth_providers = 6;
|
||||||
provisioner.Provision.Metadata metadata = 7;
|
provisioner.Metadata metadata = 7;
|
||||||
bytes state = 8;
|
bytes state = 8;
|
||||||
string log_level = 9;
|
string log_level = 9;
|
||||||
}
|
}
|
||||||
message TemplateImport {
|
message TemplateImport {
|
||||||
provisioner.Provision.Metadata metadata = 1;
|
provisioner.Metadata metadata = 1;
|
||||||
repeated provisioner.VariableValue user_variable_values = 2;
|
repeated provisioner.VariableValue user_variable_values = 2;
|
||||||
}
|
}
|
||||||
message TemplateDryRun {
|
message TemplateDryRun {
|
||||||
|
@ -32,7 +32,7 @@ message AcquiredJob {
|
||||||
|
|
||||||
repeated provisioner.RichParameterValue rich_parameter_values = 2;
|
repeated provisioner.RichParameterValue rich_parameter_values = 2;
|
||||||
repeated provisioner.VariableValue variable_values = 3;
|
repeated provisioner.VariableValue variable_values = 3;
|
||||||
provisioner.Provision.Metadata metadata = 4;
|
provisioner.Metadata metadata = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
string job_id = 1;
|
string job_id = 1;
|
||||||
|
@ -45,9 +45,9 @@ message AcquiredJob {
|
||||||
TemplateImport template_import = 7;
|
TemplateImport template_import = 7;
|
||||||
TemplateDryRun template_dry_run = 8;
|
TemplateDryRun template_dry_run = 8;
|
||||||
}
|
}
|
||||||
// trace_metadata is currently used for tracing information only. It allows
|
// trace_metadata is currently used for tracing information only. It allows
|
||||||
// jobs to be tied to the request that created them.
|
// jobs to be tied to the request that created them.
|
||||||
map<string, string> trace_metadata = 9;
|
map<string, string> trace_metadata = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
message FailedJob {
|
message FailedJob {
|
||||||
|
@ -113,7 +113,7 @@ message UpdateJobRequest {
|
||||||
string job_id = 1;
|
string job_id = 1;
|
||||||
repeated Log logs = 2;
|
repeated Log logs = 2;
|
||||||
repeated provisioner.TemplateVariable template_variables = 4;
|
repeated provisioner.TemplateVariable template_variables = 4;
|
||||||
repeated provisioner.VariableValue user_variable_values = 5;
|
repeated provisioner.VariableValue user_variable_values = 5;
|
||||||
bytes readme = 6;
|
bytes readme = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +121,7 @@ message UpdateJobResponse {
|
||||||
reserved 2;
|
reserved 2;
|
||||||
|
|
||||||
bool canceled = 1;
|
bool canceled = 1;
|
||||||
repeated provisioner.VariableValue variable_values = 3;
|
repeated provisioner.VariableValue variable_values = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message CommitQuotaRequest {
|
message CommitQuotaRequest {
|
||||||
|
|
|
@ -12,7 +12,6 @@ import (
|
||||||
"github.com/hashicorp/yamux"
|
"github.com/hashicorp/yamux"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/prometheus/client_golang/prometheus/promauto"
|
"github.com/prometheus/client_golang/prometheus/promauto"
|
||||||
"github.com/spf13/afero"
|
|
||||||
"github.com/valyala/fasthttp/fasthttputil"
|
"github.com/valyala/fasthttp/fasthttputil"
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
semconv "go.opentelemetry.io/otel/semconv/v1.14.0"
|
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.
|
// Options provides customizations to the behavior of a provisioner daemon.
|
||||||
type Options struct {
|
type Options struct {
|
||||||
Filesystem afero.Fs
|
|
||||||
Logger slog.Logger
|
Logger slog.Logger
|
||||||
TracerProvider trace.TracerProvider
|
TracerProvider trace.TracerProvider
|
||||||
Metrics *Metrics
|
Metrics *Metrics
|
||||||
|
@ -56,8 +54,6 @@ type Options struct {
|
||||||
JobPollJitter time.Duration
|
JobPollJitter time.Duration
|
||||||
JobPollDebounce time.Duration
|
JobPollDebounce time.Duration
|
||||||
Provisioners Provisioners
|
Provisioners Provisioners
|
||||||
// WorkDirectory must not be used by multiple processes at once.
|
|
||||||
WorkDirectory string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates and starts a provisioner daemon.
|
// New creates and starts a provisioner daemon.
|
||||||
|
@ -80,9 +76,6 @@ func New(clientDialer Dialer, opts *Options) *Server {
|
||||||
if opts.LogBufferInterval == 0 {
|
if opts.LogBufferInterval == 0 {
|
||||||
opts.LogBufferInterval = 250 * time.Millisecond
|
opts.LogBufferInterval = 250 * time.Millisecond
|
||||||
}
|
}
|
||||||
if opts.Filesystem == nil {
|
|
||||||
opts.Filesystem = afero.NewOsFs()
|
|
||||||
}
|
|
||||||
if opts.TracerProvider == nil {
|
if opts.TracerProvider == nil {
|
||||||
opts.TracerProvider = trace.NewNoopTracerProvider()
|
opts.TracerProvider = trace.NewNoopTracerProvider()
|
||||||
}
|
}
|
||||||
|
@ -405,8 +398,6 @@ func (p *Server) acquireJob(ctx context.Context) {
|
||||||
Updater: p,
|
Updater: p,
|
||||||
QuotaCommitter: p,
|
QuotaCommitter: p,
|
||||||
Logger: p.opts.Logger.Named("runner"),
|
Logger: p.opts.Logger.Named("runner"),
|
||||||
Filesystem: p.opts.Filesystem,
|
|
||||||
WorkDirectory: p.opts.WorkDirectory,
|
|
||||||
Provisioner: provisioner,
|
Provisioner: provisioner,
|
||||||
UpdateInterval: p.opts.UpdateInterval,
|
UpdateInterval: p.opts.UpdateInterval,
|
||||||
ForceCancelInterval: p.opts.ForceCancelInterval,
|
ForceCancelInterval: p.opts.ForceCancelInterval,
|
||||||
|
|
|
@ -25,7 +25,6 @@ import (
|
||||||
"cdr.dev/slog/sloggers/slogtest"
|
"cdr.dev/slog/sloggers/slogtest"
|
||||||
"github.com/coder/coder/v2/provisionerd"
|
"github.com/coder/coder/v2/provisionerd"
|
||||||
"github.com/coder/coder/v2/provisionerd/proto"
|
"github.com/coder/coder/v2/provisionerd/proto"
|
||||||
"github.com/coder/coder/v2/provisionerd/runner"
|
|
||||||
"github.com/coder/coder/v2/provisionersdk"
|
"github.com/coder/coder/v2/provisionersdk"
|
||||||
sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
|
sdkproto "github.com/coder/coder/v2/provisionersdk/proto"
|
||||||
"github.com/coder/coder/v2/testutil"
|
"github.com/coder/coder/v2/testutil"
|
||||||
|
@ -129,7 +128,7 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
Type: &proto.AcquiredJob_TemplateImport_{
|
Type: &proto.AcquiredJob_TemplateImport_{
|
||||||
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
||||||
Metadata: &sdkproto.Provision_Metadata{},
|
Metadata: &sdkproto.Metadata{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -144,10 +143,15 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}), nil
|
}), nil
|
||||||
}, provisionerd.Provisioners{
|
}, provisionerd.Provisioners{
|
||||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
"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()
|
closerMutex.Lock()
|
||||||
defer closerMutex.Unlock()
|
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_{
|
Type: &proto.AcquiredJob_TemplateImport_{
|
||||||
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
||||||
Metadata: &sdkproto.Provision_Metadata{},
|
Metadata: &sdkproto.Metadata{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -220,7 +224,7 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
Type: &proto.AcquiredJob_TemplateImport_{
|
Type: &proto.AcquiredJob_TemplateImport_{
|
||||||
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
||||||
Metadata: &sdkproto.Provision_Metadata{},
|
Metadata: &sdkproto.Metadata{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -235,9 +239,13 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}), nil
|
}), nil
|
||||||
}, provisionerd.Provisioners{
|
}, provisionerd.Provisioners{
|
||||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||||
parse: func(request *sdkproto.Parse_Request, stream sdkproto.DRPCProvisioner_ParseStream) error {
|
parse: func(
|
||||||
<-stream.Context().Done()
|
_ *provisionersdk.Session,
|
||||||
return nil
|
_ *sdkproto.ParseRequest,
|
||||||
|
cancelOrComplete <-chan struct{},
|
||||||
|
) *sdkproto.ParseComplete {
|
||||||
|
<-cancelOrComplete
|
||||||
|
return &sdkproto.ParseComplete{}
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
@ -255,7 +263,6 @@ func TestProvisionerd(t *testing.T) {
|
||||||
didComplete atomic.Bool
|
didComplete atomic.Bool
|
||||||
didLog atomic.Bool
|
didLog atomic.Bool
|
||||||
didAcquireJob atomic.Bool
|
didAcquireJob atomic.Bool
|
||||||
didDryRun = atomic.NewBool(true)
|
|
||||||
didReadme atomic.Bool
|
didReadme atomic.Bool
|
||||||
completeChan = make(chan struct{})
|
completeChan = make(chan struct{})
|
||||||
completeOnce sync.Once
|
completeOnce sync.Once
|
||||||
|
@ -273,12 +280,12 @@ func TestProvisionerd(t *testing.T) {
|
||||||
JobId: "test",
|
JobId: "test",
|
||||||
Provisioner: "someprovisioner",
|
Provisioner: "someprovisioner",
|
||||||
TemplateSourceArchive: createTar(t, map[string]string{
|
TemplateSourceArchive: createTar(t, map[string]string{
|
||||||
"test.txt": "content",
|
"test.txt": "content",
|
||||||
runner.ReadmeFile: "# A cool template 😎\n",
|
provisionersdk.ReadmeFile: "# A cool template 😎\n",
|
||||||
}),
|
}),
|
||||||
Type: &proto.AcquiredJob_TemplateImport_{
|
Type: &proto.AcquiredJob_TemplateImport_{
|
||||||
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
TemplateImport: &proto.AcquiredJob_TemplateImport{
|
||||||
Metadata: &sdkproto.Provision_Metadata{},
|
Metadata: &sdkproto.Metadata{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -299,54 +306,34 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}), nil
|
}), nil
|
||||||
}, provisionerd.Provisioners{
|
}, provisionerd.Provisioners{
|
||||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||||
parse: func(request *sdkproto.Parse_Request, stream sdkproto.DRPCProvisioner_ParseStream) error {
|
parse: func(
|
||||||
data, err := os.ReadFile(filepath.Join(request.Directory, "test.txt"))
|
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.NoError(t, err)
|
||||||
require.Equal(t, "content", string(data))
|
require.Equal(t, "content", string(data))
|
||||||
|
s.ProvisionLog(sdkproto.LogLevel_INFO, "hello")
|
||||||
err = stream.Send(&sdkproto.Parse_Response{
|
return &sdkproto.ParseComplete{}
|
||||||
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
|
|
||||||
},
|
},
|
||||||
provision: func(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
plan: func(
|
||||||
request, err := stream.Recv()
|
s *provisionersdk.Session,
|
||||||
require.NoError(t, err)
|
_ *sdkproto.PlanRequest,
|
||||||
if request.GetApply() != nil {
|
cancelOrComplete <-chan struct{},
|
||||||
didDryRun.Store(false)
|
) *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{
|
apply: func(
|
||||||
Log: &sdkproto.Log{
|
_ *provisionersdk.Session,
|
||||||
Level: sdkproto.LogLevel_INFO,
|
_ *sdkproto.ApplyRequest,
|
||||||
Output: "hello",
|
_ <-chan struct{},
|
||||||
},
|
) *sdkproto.ApplyComplete {
|
||||||
},
|
t.Error("dry run should not apply")
|
||||||
})
|
return &sdkproto.ApplyComplete{}
|
||||||
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
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
@ -355,7 +342,6 @@ func TestProvisionerd(t *testing.T) {
|
||||||
require.NoError(t, closer.Close())
|
require.NoError(t, closer.Close())
|
||||||
assert.True(t, didLog.Load(), "should log some updates")
|
assert.True(t, didLog.Load(), "should log some updates")
|
||||||
assert.True(t, didComplete.Load(), "should complete the job")
|
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) {
|
t.Run("TemplateDryRun", func(t *testing.T) {
|
||||||
|
@ -371,7 +357,7 @@ func TestProvisionerd(t *testing.T) {
|
||||||
completeChan = make(chan struct{})
|
completeChan = make(chan struct{})
|
||||||
completeOnce sync.Once
|
completeOnce sync.Once
|
||||||
|
|
||||||
metadata = &sdkproto.Provision_Metadata{}
|
metadata = &sdkproto.Metadata{}
|
||||||
)
|
)
|
||||||
|
|
||||||
closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
closer := createProvisionerd(t, func(ctx context.Context) (proto.DRPCProvisionerDaemonClient, error) {
|
||||||
|
@ -414,16 +400,22 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}), nil
|
}), nil
|
||||||
}, provisionerd.Provisioners{
|
}, provisionerd.Provisioners{
|
||||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||||
provision: func(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
plan: func(
|
||||||
err := stream.Send(&sdkproto.Provision_Response{
|
_ *provisionersdk.Session,
|
||||||
Type: &sdkproto.Provision_Response_Complete{
|
_ *sdkproto.PlanRequest,
|
||||||
Complete: &sdkproto.Provision_Complete{
|
_ <-chan struct{},
|
||||||
Resources: []*sdkproto.Resource{},
|
) *sdkproto.PlanComplete {
|
||||||
},
|
return &sdkproto.PlanComplete{
|
||||||
},
|
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{}
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
@ -464,7 +456,7 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
||||||
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
||||||
Metadata: &sdkproto.Provision_Metadata{},
|
Metadata: &sdkproto.Metadata{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -482,24 +474,20 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}), nil
|
}), nil
|
||||||
}, provisionerd.Provisioners{
|
}, provisionerd.Provisioners{
|
||||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||||
provision: func(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
plan: func(
|
||||||
err := stream.Send(&sdkproto.Provision_Response{
|
s *provisionersdk.Session,
|
||||||
Type: &sdkproto.Provision_Response_Log{
|
_ *sdkproto.PlanRequest,
|
||||||
Log: &sdkproto.Log{
|
cancelOrComplete <-chan struct{},
|
||||||
Level: sdkproto.LogLevel_DEBUG,
|
) *sdkproto.PlanComplete {
|
||||||
Output: "wow",
|
s.ProvisionLog(sdkproto.LogLevel_DEBUG, "wow")
|
||||||
},
|
return &sdkproto.PlanComplete{}
|
||||||
},
|
},
|
||||||
})
|
apply: func(
|
||||||
require.NoError(t, err)
|
_ *provisionersdk.Session,
|
||||||
|
_ *sdkproto.ApplyRequest,
|
||||||
err = stream.Send(&sdkproto.Provision_Response{
|
_ <-chan struct{},
|
||||||
Type: &sdkproto.Provision_Response_Complete{
|
) *sdkproto.ApplyComplete {
|
||||||
Complete: &sdkproto.Provision_Complete{},
|
return &sdkproto.ApplyComplete{}
|
||||||
},
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
@ -540,7 +528,7 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
||||||
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
||||||
Metadata: &sdkproto.Provision_Metadata{},
|
Metadata: &sdkproto.Metadata{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -567,40 +555,46 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}), nil
|
}), nil
|
||||||
}, provisionerd.Provisioners{
|
}, provisionerd.Provisioners{
|
||||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||||
provision: func(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
plan: func(
|
||||||
err := stream.Send(&sdkproto.Provision_Response{
|
s *provisionersdk.Session,
|
||||||
Type: &sdkproto.Provision_Response_Log{
|
_ *sdkproto.PlanRequest,
|
||||||
Log: &sdkproto.Log{
|
cancelOrComplete <-chan struct{},
|
||||||
Level: sdkproto.LogLevel_DEBUG,
|
) *sdkproto.PlanComplete {
|
||||||
Output: "wow",
|
s.ProvisionLog(sdkproto.LogLevel_DEBUG, "wow")
|
||||||
|
return &sdkproto.PlanComplete{
|
||||||
|
Resources: []*sdkproto.Resource{
|
||||||
|
{
|
||||||
|
DailyCost: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DailyCost: 15,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
require.NoError(t, err)
|
},
|
||||||
|
apply: func(
|
||||||
err = stream.Send(&sdkproto.Provision_Response{
|
_ *provisionersdk.Session,
|
||||||
Type: &sdkproto.Provision_Response_Complete{
|
_ *sdkproto.ApplyRequest,
|
||||||
Complete: &sdkproto.Provision_Complete{
|
_ <-chan struct{},
|
||||||
Resources: []*sdkproto.Resource{
|
) *sdkproto.ApplyComplete {
|
||||||
{
|
t.Error("should not apply when resources exceed quota")
|
||||||
DailyCost: 10,
|
return &sdkproto.ApplyComplete{
|
||||||
},
|
Resources: []*sdkproto.Resource{
|
||||||
{
|
{
|
||||||
DailyCost: 15,
|
DailyCost: 10,
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
|
DailyCost: 15,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
}
|
||||||
require.NoError(t, err)
|
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
require.Condition(t, closedWithin(completeChan, testutil.WaitShort))
|
require.Condition(t, closedWithin(completeChan, testutil.WaitShort))
|
||||||
require.NoError(t, closer.Close())
|
require.NoError(t, closer.Close())
|
||||||
assert.True(t, didLog.Load(), "should log some updates")
|
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")
|
assert.True(t, didFail.Load(), "should fail the job")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -633,7 +627,7 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
||||||
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
||||||
Metadata: &sdkproto.Provision_Metadata{},
|
Metadata: &sdkproto.Metadata{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -646,14 +640,24 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}), nil
|
}), nil
|
||||||
}, provisionerd.Provisioners{
|
}, provisionerd.Provisioners{
|
||||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||||
provision: func(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
plan: func(
|
||||||
return stream.Send(&sdkproto.Provision_Response{
|
s *provisionersdk.Session,
|
||||||
Type: &sdkproto.Provision_Response_Complete{
|
_ *sdkproto.PlanRequest,
|
||||||
Complete: &sdkproto.Provision_Complete{
|
cancelOrComplete <-chan struct{},
|
||||||
Error: "some error",
|
) *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_{
|
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
||||||
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
||||||
Metadata: &sdkproto.Provision_Metadata{},
|
Metadata: &sdkproto.Metadata{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -712,31 +716,24 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}), nil
|
}), nil
|
||||||
}, provisionerd.Provisioners{
|
}, provisionerd.Provisioners{
|
||||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||||
provision: func(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
plan: func(
|
||||||
// Ignore the first provision message!
|
s *provisionersdk.Session,
|
||||||
_, _ = stream.Recv()
|
_ *sdkproto.PlanRequest,
|
||||||
|
canceledOrComplete <-chan struct{},
|
||||||
err := stream.Send(&sdkproto.Provision_Response{
|
) *sdkproto.PlanComplete {
|
||||||
Type: &sdkproto.Provision_Response_Log{
|
s.ProvisionLog(sdkproto.LogLevel_DEBUG, "in progress")
|
||||||
Log: &sdkproto.Log{
|
<-canceledOrComplete
|
||||||
Level: sdkproto.LogLevel_DEBUG,
|
return &sdkproto.PlanComplete{
|
||||||
Output: "in progress",
|
Error: "some error",
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
})
|
apply: func(
|
||||||
require.NoError(t, err)
|
_ *provisionersdk.Session,
|
||||||
|
_ *sdkproto.ApplyRequest,
|
||||||
msg, err := stream.Recv()
|
_ <-chan struct{},
|
||||||
require.NoError(t, err)
|
) *sdkproto.ApplyComplete {
|
||||||
require.NotNil(t, msg.GetCancel())
|
t.Error("should not apply when shut down during plan")
|
||||||
|
return &sdkproto.ApplyComplete{}
|
||||||
return stream.Send(&sdkproto.Provision_Response{
|
|
||||||
Type: &sdkproto.Provision_Response_Complete{
|
|
||||||
Complete: &sdkproto.Provision_Complete{
|
|
||||||
Error: "some error",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
@ -768,7 +765,7 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
||||||
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
||||||
Metadata: &sdkproto.Provision_Metadata{},
|
Metadata: &sdkproto.Metadata{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -805,31 +802,24 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}), nil
|
}), nil
|
||||||
}, provisionerd.Provisioners{
|
}, provisionerd.Provisioners{
|
||||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||||
provision: func(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
plan: func(
|
||||||
// Ignore the first provision message!
|
s *provisionersdk.Session,
|
||||||
_, _ = stream.Recv()
|
_ *sdkproto.PlanRequest,
|
||||||
|
canceledOrComplete <-chan struct{},
|
||||||
err := stream.Send(&sdkproto.Provision_Response{
|
) *sdkproto.PlanComplete {
|
||||||
Type: &sdkproto.Provision_Response_Log{
|
s.ProvisionLog(sdkproto.LogLevel_DEBUG, "in progress")
|
||||||
Log: &sdkproto.Log{
|
<-canceledOrComplete
|
||||||
Level: sdkproto.LogLevel_DEBUG,
|
return &sdkproto.PlanComplete{
|
||||||
Output: "in progress",
|
Error: "some error",
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
})
|
apply: func(
|
||||||
require.NoError(t, err)
|
_ *provisionersdk.Session,
|
||||||
|
_ *sdkproto.ApplyRequest,
|
||||||
msg, err := stream.Recv()
|
_ <-chan struct{},
|
||||||
require.NoError(t, err)
|
) *sdkproto.ApplyComplete {
|
||||||
require.NotNil(t, msg.GetCancel())
|
t.Error("should not apply when shut down during plan")
|
||||||
|
return &sdkproto.ApplyComplete{}
|
||||||
return stream.Send(&sdkproto.Provision_Response{
|
|
||||||
Type: &sdkproto.Provision_Response_Complete{
|
|
||||||
Complete: &sdkproto.Provision_Complete{
|
|
||||||
Error: "some error",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
@ -867,7 +857,7 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}),
|
}),
|
||||||
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
||||||
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
||||||
Metadata: &sdkproto.Provision_Metadata{},
|
Metadata: &sdkproto.Metadata{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -898,16 +888,22 @@ func TestProvisionerd(t *testing.T) {
|
||||||
return client, nil
|
return client, nil
|
||||||
}, provisionerd.Provisioners{
|
}, provisionerd.Provisioners{
|
||||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||||
provision: func(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
plan: func(
|
||||||
// Ignore the first provision message!
|
_ *provisionersdk.Session,
|
||||||
_, _ = stream.Recv()
|
_ *sdkproto.PlanRequest,
|
||||||
return stream.Send(&sdkproto.Provision_Response{
|
_ <-chan struct{},
|
||||||
Type: &sdkproto.Provision_Response_Complete{
|
) *sdkproto.PlanComplete {
|
||||||
Complete: &sdkproto.Provision_Complete{
|
return &sdkproto.PlanComplete{
|
||||||
Error: "some error",
|
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_{
|
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
||||||
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
||||||
Metadata: &sdkproto.Provision_Metadata{},
|
Metadata: &sdkproto.Metadata{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -977,14 +973,19 @@ func TestProvisionerd(t *testing.T) {
|
||||||
return client, nil
|
return client, nil
|
||||||
}, provisionerd.Provisioners{
|
}, provisionerd.Provisioners{
|
||||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||||
provision: func(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
plan: func(
|
||||||
// Ignore the first provision message!
|
_ *provisionersdk.Session,
|
||||||
_, _ = stream.Recv()
|
_ *sdkproto.PlanRequest,
|
||||||
return stream.Send(&sdkproto.Provision_Response{
|
_ <-chan struct{},
|
||||||
Type: &sdkproto.Provision_Response_Complete{
|
) *sdkproto.PlanComplete {
|
||||||
Complete: &sdkproto.Provision_Complete{},
|
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_{
|
Type: &proto.AcquiredJob_WorkspaceBuild_{
|
||||||
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
WorkspaceBuild: &proto.AcquiredJob_WorkspaceBuild{
|
||||||
Metadata: &sdkproto.Provision_Metadata{},
|
Metadata: &sdkproto.Metadata{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -1056,24 +1057,21 @@ func TestProvisionerd(t *testing.T) {
|
||||||
}), nil
|
}), nil
|
||||||
}, provisionerd.Provisioners{
|
}, provisionerd.Provisioners{
|
||||||
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
"someprovisioner": createProvisionerClient(t, done, provisionerTestServer{
|
||||||
provision: func(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
plan: func(
|
||||||
err := stream.Send(&sdkproto.Provision_Response{
|
s *provisionersdk.Session,
|
||||||
Type: &sdkproto.Provision_Response_Log{
|
_ *sdkproto.PlanRequest,
|
||||||
Log: &sdkproto.Log{
|
_ <-chan struct{},
|
||||||
Level: sdkproto.LogLevel_DEBUG,
|
) *sdkproto.PlanComplete {
|
||||||
Output: "wow",
|
s.ProvisionLog(sdkproto.LogLevel_DEBUG, "wow")
|
||||||
},
|
return &sdkproto.PlanComplete{}
|
||||||
},
|
},
|
||||||
})
|
apply: func(
|
||||||
require.NoError(t, err)
|
s *provisionersdk.Session,
|
||||||
|
_ *sdkproto.ApplyRequest,
|
||||||
err = stream.Send(&sdkproto.Provision_Response{
|
_ <-chan struct{},
|
||||||
Type: &sdkproto.Provision_Response_Complete{
|
) *sdkproto.ApplyComplete {
|
||||||
Complete: &sdkproto.Provision_Complete{},
|
s.ProvisionLog(sdkproto.LogLevel_DEBUG, "wow")
|
||||||
},
|
return &sdkproto.ApplyComplete{}
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
return nil
|
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
@ -1111,7 +1109,6 @@ func createProvisionerd(t *testing.T, dialer provisionerd.Dialer, provisioners p
|
||||||
JobPollInterval: 50 * time.Millisecond,
|
JobPollInterval: 50 * time.Millisecond,
|
||||||
UpdateInterval: 50 * time.Millisecond,
|
UpdateInterval: 50 * time.Millisecond,
|
||||||
Provisioners: provisioners,
|
Provisioners: provisioners,
|
||||||
WorkDirectory: t.TempDir(),
|
|
||||||
})
|
})
|
||||||
t.Cleanup(func() {
|
t.Cleanup(func() {
|
||||||
_ = server.Close()
|
_ = server.Close()
|
||||||
|
@ -1172,15 +1169,15 @@ func createProvisionerClient(t *testing.T, done <-chan struct{}, server provisio
|
||||||
_ = clientPipe.Close()
|
_ = clientPipe.Close()
|
||||||
_ = serverPipe.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())
|
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||||
closed := make(chan struct{})
|
closed := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
defer close(closed)
|
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() {
|
t.Cleanup(func() {
|
||||||
cancelFunc()
|
cancelFunc()
|
||||||
|
@ -1200,16 +1197,21 @@ func createProvisionerClient(t *testing.T, done <-chan struct{}, server provisio
|
||||||
}
|
}
|
||||||
|
|
||||||
type provisionerTestServer struct {
|
type provisionerTestServer struct {
|
||||||
parse func(request *sdkproto.Parse_Request, stream sdkproto.DRPCProvisioner_ParseStream) error
|
parse func(s *provisionersdk.Session, r *sdkproto.ParseRequest, canceledOrComplete <-chan struct{}) *sdkproto.ParseComplete
|
||||||
provision func(stream sdkproto.DRPCProvisioner_ProvisionStream) error
|
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 {
|
func (p *provisionerTestServer) Parse(s *provisionersdk.Session, r *sdkproto.ParseRequest, canceledOrComplete <-chan struct{}) *sdkproto.ParseComplete {
|
||||||
return p.parse(request, stream)
|
return p.parse(s, r, canceledOrComplete)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *provisionerTestServer) Provision(stream sdkproto.DRPCProvisioner_ProvisionStream) error {
|
func (p *provisionerTestServer) Plan(s *provisionersdk.Session, r *sdkproto.PlanRequest, canceledOrComplete <-chan struct{}) *sdkproto.PlanComplete {
|
||||||
return p.provision(stream)
|
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
|
// Fulfills the protobuf interface for a ProvisionerDaemon with
|
||||||
|
|
|
@ -1,15 +1,9 @@
|
||||||
package runner
|
package runner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"archive/tar"
|
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -18,7 +12,6 @@ import (
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
"github.com/spf13/afero"
|
|
||||||
"go.opentelemetry.io/otel/attribute"
|
"go.opentelemetry.io/otel/attribute"
|
||||||
"go.opentelemetry.io/otel/codes"
|
"go.opentelemetry.io/otel/codes"
|
||||||
semconv "go.opentelemetry.io/otel/semconv/v1.14.0"
|
semconv "go.opentelemetry.io/otel/semconv/v1.14.0"
|
||||||
|
@ -54,14 +47,14 @@ type Runner struct {
|
||||||
sender JobUpdater
|
sender JobUpdater
|
||||||
quotaCommitter QuotaCommitter
|
quotaCommitter QuotaCommitter
|
||||||
logger slog.Logger
|
logger slog.Logger
|
||||||
filesystem afero.Fs
|
|
||||||
workDirectory string
|
|
||||||
provisioner sdkproto.DRPCProvisionerClient
|
provisioner sdkproto.DRPCProvisionerClient
|
||||||
lastUpdate atomic.Pointer[time.Time]
|
lastUpdate atomic.Pointer[time.Time]
|
||||||
updateInterval time.Duration
|
updateInterval time.Duration
|
||||||
forceCancelInterval time.Duration
|
forceCancelInterval time.Duration
|
||||||
logBufferInterval 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.
|
// closed when the Runner is finished sending any updates/failed/complete.
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
// active as long as we are not canceled
|
// active as long as we are not canceled
|
||||||
|
@ -108,8 +101,6 @@ type Options struct {
|
||||||
Updater JobUpdater
|
Updater JobUpdater
|
||||||
QuotaCommitter QuotaCommitter
|
QuotaCommitter QuotaCommitter
|
||||||
Logger slog.Logger
|
Logger slog.Logger
|
||||||
Filesystem afero.Fs
|
|
||||||
WorkDirectory string
|
|
||||||
Provisioner sdkproto.DRPCProvisionerClient
|
Provisioner sdkproto.DRPCProvisionerClient
|
||||||
UpdateInterval time.Duration
|
UpdateInterval time.Duration
|
||||||
ForceCancelInterval time.Duration
|
ForceCancelInterval time.Duration
|
||||||
|
@ -149,8 +140,6 @@ func New(
|
||||||
sender: opts.Updater,
|
sender: opts.Updater,
|
||||||
quotaCommitter: opts.QuotaCommitter,
|
quotaCommitter: opts.QuotaCommitter,
|
||||||
logger: logger,
|
logger: logger,
|
||||||
filesystem: opts.Filesystem,
|
|
||||||
workDirectory: opts.WorkDirectory,
|
|
||||||
provisioner: opts.Provisioner,
|
provisioner: opts.Provisioner,
|
||||||
updateInterval: opts.UpdateInterval,
|
updateInterval: opts.UpdateInterval,
|
||||||
forceCancelInterval: opts.ForceCancelInterval,
|
forceCancelInterval: opts.ForceCancelInterval,
|
||||||
|
@ -386,6 +375,14 @@ func (r *Runner) doCleanFinish(ctx context.Context) {
|
||||||
r.setComplete(completedJob)
|
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() {
|
defer func() {
|
||||||
ctx, span := r.startTrace(ctx, tracing.FuncName())
|
ctx, span := r.startTrace(ctx, tracing.FuncName())
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
@ -396,23 +393,6 @@ func (r *Runner) doCleanFinish(ctx context.Context) {
|
||||||
Stage: "Cleaning Up",
|
Stage: "Cleaning Up",
|
||||||
CreatedAt: time.Now().UnixMilli(),
|
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)
|
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())
|
ctx, span := r.startTrace(ctx, tracing.FuncName())
|
||||||
defer span.End()
|
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{
|
r.queueLog(ctx, &proto.Log{
|
||||||
Source: proto.LogSource_PROVISIONER_DAEMON,
|
Source: proto.LogSource_PROVISIONER_DAEMON,
|
||||||
Level: sdkproto.LogLevel_INFO,
|
Level: sdkproto.LogLevel_INFO,
|
||||||
Stage: "Setting up",
|
Stage: "Setting up",
|
||||||
CreatedAt: time.Now().UnixMilli(),
|
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) {
|
switch jobType := r.job.Type.(type) {
|
||||||
case *proto.AcquiredJob_TemplateImport_:
|
case *proto.AcquiredJob_TemplateImport_:
|
||||||
r.logger.Debug(context.Background(), "acquired job is template import",
|
r.logger.Debug(context.Background(), "acquired job is template import",
|
||||||
slog.F("user_variable_values", redactVariableValues(jobType.TemplateImport.UserVariableValues)),
|
slog.F("user_variable_values", redactVariableValues(jobType.TemplateImport.UserVariableValues)),
|
||||||
)
|
)
|
||||||
|
|
||||||
failedJob := r.runReadmeParse(ctx)
|
|
||||||
if failedJob != nil {
|
|
||||||
return nil, failedJob
|
|
||||||
}
|
|
||||||
return r.runTemplateImport(ctx)
|
return r.runTemplateImport(ctx)
|
||||||
case *proto.AcquiredJob_TemplateDryRun_:
|
case *proto.AcquiredJob_TemplateDryRun_:
|
||||||
r.logger.Debug(context.Background(), "acquired job is template dry-run",
|
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
|
// 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
|
// from assuming the job is stalled, and allows the runner to learn if the job
|
||||||
// has been canceled by the user.
|
// 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) {
|
func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *proto.FailedJob) {
|
||||||
ctx, span := r.startTrace(ctx, tracing.FuncName())
|
ctx, span := r.startTrace(ctx, tracing.FuncName())
|
||||||
defer span.End()
|
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
|
// Parse parameters and update the job with the parameter specs
|
||||||
r.queueLog(ctx, &proto.Log{
|
r.queueLog(ctx, &proto.Log{
|
||||||
Source: proto.LogSource_PROVISIONER_DAEMON,
|
Source: proto.LogSource_PROVISIONER_DAEMON,
|
||||||
|
@ -623,7 +517,7 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p
|
||||||
Stage: "Parsing template parameters",
|
Stage: "Parsing template parameters",
|
||||||
CreatedAt: time.Now().UnixMilli(),
|
CreatedAt: time.Now().UnixMilli(),
|
||||||
})
|
})
|
||||||
templateVariables, err := r.runTemplateImportParse(ctx)
|
templateVariables, readme, err := r.runTemplateImportParse(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, r.failedJobf("run parse: %s", err)
|
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,
|
JobId: r.job.JobId,
|
||||||
TemplateVariables: templateVariables,
|
TemplateVariables: templateVariables,
|
||||||
UserVariableValues: r.job.GetTemplateImport().GetUserVariableValues(),
|
UserVariableValues: r.job.GetTemplateImport().GetUserVariableValues(),
|
||||||
|
Readme: readme,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, r.failedJobf("update job: %s", err)
|
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",
|
Stage: "Detecting persistent resources",
|
||||||
CreatedAt: time.Now().UnixMilli(),
|
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,
|
CoderUrl: r.job.GetTemplateImport().Metadata.CoderUrl,
|
||||||
WorkspaceTransition: sdkproto.WorkspaceTransition_START,
|
WorkspaceTransition: sdkproto.WorkspaceTransition_START,
|
||||||
})
|
})
|
||||||
|
@ -661,7 +556,7 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p
|
||||||
Stage: "Detecting ephemeral resources",
|
Stage: "Detecting ephemeral resources",
|
||||||
CreatedAt: time.Now().UnixMilli(),
|
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,
|
CoderUrl: r.job.GetTemplateImport().Metadata.CoderUrl,
|
||||||
WorkspaceTransition: sdkproto.WorkspaceTransition_STOP,
|
WorkspaceTransition: sdkproto.WorkspaceTransition_STOP,
|
||||||
})
|
})
|
||||||
|
@ -682,25 +577,24 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parses template variables and parameter schemas from source.
|
// Parses template variables and README from source.
|
||||||
func (r *Runner) runTemplateImportParse(ctx context.Context) ([]*sdkproto.TemplateVariable, error) {
|
func (r *Runner) runTemplateImportParse(ctx context.Context) (
|
||||||
|
vars []*sdkproto.TemplateVariable, readme []byte, err error,
|
||||||
|
) {
|
||||||
ctx, span := r.startTrace(ctx, tracing.FuncName())
|
ctx, span := r.startTrace(ctx, tracing.FuncName())
|
||||||
defer span.End()
|
defer span.End()
|
||||||
|
|
||||||
stream, err := r.provisioner.Parse(ctx, &sdkproto.Parse_Request{
|
err = r.session.Send(&sdkproto.Request{Type: &sdkproto.Request_Parse{Parse: &sdkproto.ParseRequest{}}})
|
||||||
Directory: r.workDirectory,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("parse source: %w", err)
|
return nil, nil, xerrors.Errorf("parse source: %w", err)
|
||||||
}
|
}
|
||||||
defer stream.Close()
|
|
||||||
for {
|
for {
|
||||||
msg, err := stream.Recv()
|
msg, err := r.session.Recv()
|
||||||
if err != nil {
|
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) {
|
switch msgType := msg.Type.(type) {
|
||||||
case *sdkproto.Parse_Response_Log:
|
case *sdkproto.Response_Log:
|
||||||
r.logger.Debug(context.Background(), "parse job logged",
|
r.logger.Debug(context.Background(), "parse job logged",
|
||||||
slog.F("level", msgType.Log.Level),
|
slog.F("level", msgType.Log.Level),
|
||||||
slog.F("output", msgType.Log.Output),
|
slog.F("output", msgType.Log.Output),
|
||||||
|
@ -713,14 +607,20 @@ func (r *Runner) runTemplateImportParse(ctx context.Context) ([]*sdkproto.Templa
|
||||||
Output: msgType.Log.Output,
|
Output: msgType.Log.Output,
|
||||||
Stage: "Parse parameters",
|
Stage: "Parse parameters",
|
||||||
})
|
})
|
||||||
case *sdkproto.Parse_Response_Complete:
|
case *sdkproto.Response_Parse:
|
||||||
|
pc := msgType.Parse
|
||||||
r.logger.Debug(context.Background(), "parse complete",
|
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:
|
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())
|
reflect.TypeOf(msg.Type).String())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -735,13 +635,18 @@ type templateImportProvision struct {
|
||||||
// Performs a dry-run provision when importing a template.
|
// 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.
|
// 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.
|
// 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)
|
return r.runTemplateImportProvisionWithRichParameters(ctx, variableValues, nil, metadata)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Performs a dry-run provision with provided rich parameters.
|
// 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.
|
// 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())
|
ctx, span := r.startTrace(ctx, tracing.FuncName())
|
||||||
defer span.End()
|
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
|
// 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
|
// 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 {
|
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() {
|
go func() {
|
||||||
select {
|
select {
|
||||||
|
case <-nevermind:
|
||||||
|
return
|
||||||
case <-r.notStopped.Done():
|
case <-r.notStopped.Done():
|
||||||
return
|
return
|
||||||
case <-r.notCanceled.Done():
|
case <-r.notCanceled.Done():
|
||||||
_ = stream.Send(&sdkproto.Provision_Request{
|
_ = r.session.Send(&sdkproto.Request{
|
||||||
Type: &sdkproto.Provision_Request_Cancel{
|
Type: &sdkproto.Request_Cancel{
|
||||||
Cancel: &sdkproto.Provision_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 {
|
for {
|
||||||
msg, err := stream.Recv()
|
msg, err := r.session.Recv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, xerrors.Errorf("recv import provision: %w", err)
|
return nil, xerrors.Errorf("recv import provision: %w", err)
|
||||||
}
|
}
|
||||||
switch msgType := msg.Type.(type) {
|
switch msgType := msg.Type.(type) {
|
||||||
case *sdkproto.Provision_Response_Log:
|
case *sdkproto.Response_Log:
|
||||||
r.logger.Debug(context.Background(), "template import provision job logged",
|
r.logger.Debug(context.Background(), "template import provision job logged",
|
||||||
slog.F("level", msgType.Log.Level),
|
slog.F("level", msgType.Log.Level),
|
||||||
slog.F("output", msgType.Log.Output),
|
slog.F("output", msgType.Log.Output),
|
||||||
|
@ -805,25 +702,25 @@ func (r *Runner) runTemplateImportProvisionWithRichParameters(ctx context.Contex
|
||||||
Output: msgType.Log.Output,
|
Output: msgType.Log.Output,
|
||||||
Stage: stage,
|
Stage: stage,
|
||||||
})
|
})
|
||||||
case *sdkproto.Provision_Response_Complete:
|
case *sdkproto.Response_Plan:
|
||||||
if msgType.Complete.Error != "" {
|
c := msgType.Plan
|
||||||
|
if c.Error != "" {
|
||||||
r.logger.Info(context.Background(), "dry-run provision failure",
|
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",
|
r.logger.Info(context.Background(), "parse dry-run provision successful",
|
||||||
slog.F("resource_count", len(msgType.Complete.Resources)),
|
slog.F("resource_count", len(c.Resources)),
|
||||||
slog.F("resources", msgType.Complete.Resources),
|
slog.F("resources", c.Resources),
|
||||||
slog.F("state_length", len(msgType.Complete.State)),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return &templateImportProvision{
|
return &templateImportProvision{
|
||||||
Resources: msgType.Complete.Resources,
|
Resources: c.Resources,
|
||||||
Parameters: msgType.Complete.Parameters,
|
Parameters: c.Parameters,
|
||||||
GitAuthProviders: msgType.Complete.GitAuthProviders,
|
GitAuthProviders: c.GitAuthProviders,
|
||||||
}, nil
|
}, nil
|
||||||
default:
|
default:
|
||||||
return nil, xerrors.Errorf("invalid message type %q received from provisioner",
|
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()
|
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.
|
// Run the template import provision task since it's already a dry run.
|
||||||
provision, err := r.runTemplateImportProvisionWithRichParameters(ctx,
|
provision, err := r.runTemplateImportProvisionWithRichParameters(ctx,
|
||||||
r.job.GetTemplateDryRun().GetVariableValues(),
|
r.job.GetTemplateDryRun().GetVariableValues(),
|
||||||
|
@ -884,41 +788,39 @@ func (r *Runner) runTemplateDryRun(ctx context.Context) (*proto.CompletedJob, *p
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Runner) buildWorkspace(ctx context.Context, stage string, req *sdkproto.Provision_Request) (
|
func (r *Runner) buildWorkspace(ctx context.Context, stage string, req *sdkproto.Request) (
|
||||||
*sdkproto.Provision_Complete, *proto.FailedJob,
|
*sdkproto.Response, *proto.FailedJob,
|
||||||
) {
|
) {
|
||||||
// use the notStopped so that if we attempt to gracefully cancel, the stream
|
// 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
|
// 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 {
|
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() {
|
go func() {
|
||||||
select {
|
select {
|
||||||
|
case <-nevermind:
|
||||||
|
return
|
||||||
case <-r.notStopped.Done():
|
case <-r.notStopped.Done():
|
||||||
return
|
return
|
||||||
case <-r.notCanceled.Done():
|
case <-r.notCanceled.Done():
|
||||||
_ = stream.Send(&sdkproto.Provision_Request{
|
_ = r.session.Send(&sdkproto.Request{
|
||||||
Type: &sdkproto.Provision_Request_Cancel{
|
Type: &sdkproto.Request_Cancel{
|
||||||
Cancel: &sdkproto.Provision_Cancel{},
|
Cancel: &sdkproto.CancelRequest{},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
err = stream.Send(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, r.failedWorkspaceBuildf("start provision: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
msg, err := stream.Recv()
|
msg, err := r.session.Recv()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, r.failedWorkspaceBuildf("recv workspace provision: %s", err)
|
return nil, r.failedWorkspaceBuildf("recv workspace provision: %s", err)
|
||||||
}
|
}
|
||||||
switch msgType := msg.Type.(type) {
|
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",
|
r.logProvisionerJobLog(context.Background(), msgType.Log.Level, "workspace provisioner job logged",
|
||||||
slog.F("level", msgType.Log.Level),
|
slog.F("level", msgType.Log.Level),
|
||||||
slog.F("output", msgType.Log.Output),
|
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,
|
Output: msgType.Log.Output,
|
||||||
Stage: stage,
|
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:
|
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"
|
applyStage = "Destroying workspace"
|
||||||
}
|
}
|
||||||
|
|
||||||
config := &sdkproto.Provision_Config{
|
failedJob := r.configure(&sdkproto.Config{
|
||||||
Directory: r.workDirectory,
|
TemplateSourceArchive: r.job.GetTemplateSourceArchive(),
|
||||||
Metadata: r.job.GetWorkspaceBuild().Metadata,
|
State: r.job.GetWorkspaceBuild().State,
|
||||||
State: r.job.GetWorkspaceBuild().State,
|
ProvisionerLogLevel: r.job.GetWorkspaceBuild().LogLevel,
|
||||||
|
})
|
||||||
ProvisionerLogLevel: r.job.GetWorkspaceBuild().LogLevel,
|
if failedJob != nil {
|
||||||
|
return nil, failedJob
|
||||||
}
|
}
|
||||||
|
|
||||||
completedPlan, failed := r.buildWorkspace(ctx, "Planning infrastructure", &sdkproto.Provision_Request{
|
resp, failed := r.buildWorkspace(ctx, "Planning infrastructure", &sdkproto.Request{
|
||||||
Type: &sdkproto.Provision_Request_Plan{
|
Type: &sdkproto.Request_Plan{
|
||||||
Plan: &sdkproto.Provision_Plan{
|
Plan: &sdkproto.PlanRequest{
|
||||||
Config: config,
|
Metadata: r.job.GetWorkspaceBuild().Metadata,
|
||||||
RichParameterValues: r.job.GetWorkspaceBuild().RichParameterValues,
|
RichParameterValues: r.job.GetWorkspaceBuild().RichParameterValues,
|
||||||
VariableValues: r.job.GetWorkspaceBuild().VariableValues,
|
VariableValues: r.job.GetWorkspaceBuild().VariableValues,
|
||||||
GitAuthProviders: r.job.GetWorkspaceBuild().GitAuthProviders,
|
GitAuthProviders: r.job.GetWorkspaceBuild().GitAuthProviders,
|
||||||
|
@ -1056,9 +935,31 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p
|
||||||
if failed != nil {
|
if failed != nil {
|
||||||
return nil, failed
|
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)
|
r.flushQueuedLogs(ctx)
|
||||||
if commitQuota {
|
if commitQuota {
|
||||||
failed = r.commitQuota(ctx, completedPlan.GetResources())
|
failed = r.commitQuota(ctx, planComplete.Resources)
|
||||||
r.flushQueuedLogs(ctx)
|
r.flushQueuedLogs(ctx)
|
||||||
if failed != nil {
|
if failed != nil {
|
||||||
return nil, failed
|
return nil, failed
|
||||||
|
@ -1072,25 +973,50 @@ func (r *Runner) runWorkspaceBuild(ctx context.Context) (*proto.CompletedJob, *p
|
||||||
CreatedAt: time.Now().UnixMilli(),
|
CreatedAt: time.Now().UnixMilli(),
|
||||||
})
|
})
|
||||||
|
|
||||||
completedApply, failed := r.buildWorkspace(ctx, applyStage, &sdkproto.Provision_Request{
|
resp, failed = r.buildWorkspace(ctx, applyStage, &sdkproto.Request{
|
||||||
Type: &sdkproto.Provision_Request_Apply{
|
Type: &sdkproto.Request_Apply{
|
||||||
Apply: &sdkproto.Provision_Apply{
|
Apply: &sdkproto.ApplyRequest{
|
||||||
Config: config,
|
Metadata: r.job.GetWorkspaceBuild().Metadata,
|
||||||
Plan: completedPlan.GetPlan(),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if failed != nil {
|
if failed != nil {
|
||||||
return nil, failed
|
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)
|
r.flushQueuedLogs(ctx)
|
||||||
|
|
||||||
return &proto.CompletedJob{
|
return &proto.CompletedJob{
|
||||||
JobId: r.job.JobId,
|
JobId: r.job.JobId,
|
||||||
Type: &proto.CompletedJob_WorkspaceBuild_{
|
Type: &proto.CompletedJob_WorkspaceBuild_{
|
||||||
WorkspaceBuild: &proto.CompletedJob_WorkspaceBuild{
|
WorkspaceBuild: &proto.CompletedJob_WorkspaceBuild{
|
||||||
State: completedApply.GetState(),
|
State: applyComplete.State,
|
||||||
Resources: completedApply.GetResources(),
|
Resources: applyComplete.Resources,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, nil
|
}, 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 {
|
message GitAuthProvider {
|
||||||
string id = 1;
|
string id = 1;
|
||||||
string access_token = 2;
|
string access_token = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Agent represents a running agent on the workspace.
|
// Agent represents a running agent on the workspace.
|
||||||
|
@ -110,15 +110,15 @@ message Agent {
|
||||||
string token = 9;
|
string token = 9;
|
||||||
string instance_id = 10;
|
string instance_id = 10;
|
||||||
}
|
}
|
||||||
int32 connection_timeout_seconds = 11;
|
int32 connection_timeout_seconds = 11;
|
||||||
string troubleshooting_url = 12;
|
string troubleshooting_url = 12;
|
||||||
string motd_file = 13;
|
string motd_file = 13;
|
||||||
// Field 14 was bool login_before_ready = 14, now removed.
|
// Field 14 was bool login_before_ready = 14, now removed.
|
||||||
int32 startup_script_timeout_seconds = 15;
|
int32 startup_script_timeout_seconds = 15;
|
||||||
string shutdown_script = 16;
|
string shutdown_script = 16;
|
||||||
int32 shutdown_script_timeout_seconds = 17;
|
int32 shutdown_script_timeout_seconds = 17;
|
||||||
repeated Metadata metadata = 18;
|
repeated Metadata metadata = 18;
|
||||||
string startup_script_behavior = 19;
|
string startup_script_behavior = 19;
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AppSharingLevel {
|
enum AppSharingLevel {
|
||||||
|
@ -168,96 +168,111 @@ message Resource {
|
||||||
int32 daily_cost = 8;
|
int32 daily_cost = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse consumes source-code from a directory to produce inputs.
|
// WorkspaceTransition is the desired outcome of a build
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum WorkspaceTransition {
|
enum WorkspaceTransition {
|
||||||
START = 0;
|
START = 0;
|
||||||
STOP = 1;
|
STOP = 1;
|
||||||
DESTROY = 2;
|
DESTROY = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Provision consumes source-code from a directory to produce resources.
|
// Metadata is information about a workspace used in the execution of a build
|
||||||
// Exactly one of Plan or Apply must be provided in a single session.
|
message Metadata {
|
||||||
message Provision {
|
string coder_url = 1;
|
||||||
message Metadata {
|
WorkspaceTransition workspace_transition = 2;
|
||||||
string coder_url = 1;
|
string workspace_name = 3;
|
||||||
WorkspaceTransition workspace_transition = 2;
|
string workspace_owner = 4;
|
||||||
string workspace_name = 3;
|
string workspace_id = 5;
|
||||||
string workspace_owner = 4;
|
string workspace_owner_id = 6;
|
||||||
string workspace_id = 5;
|
string workspace_owner_email = 7;
|
||||||
string workspace_owner_id = 6;
|
string template_name = 8;
|
||||||
string workspace_owner_email = 7;
|
string template_version = 9;
|
||||||
string template_name = 8;
|
string workspace_owner_oidc_access_token = 10;
|
||||||
string template_version = 9;
|
string workspace_owner_session_token = 11;
|
||||||
string workspace_owner_oidc_access_token = 10;
|
}
|
||||||
string workspace_owner_session_token = 11;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Config represents execution configuration shared by both Plan and
|
// Config represents execution configuration shared by all subsequent requests in the Session
|
||||||
// Apply commands.
|
message Config {
|
||||||
message Config {
|
// template_source_archive is a tar of the template source files
|
||||||
string directory = 1;
|
bytes template_source_archive = 1;
|
||||||
bytes state = 2;
|
// state is the provisioner state (if any)
|
||||||
Metadata metadata = 3;
|
bytes state = 2;
|
||||||
|
string provisioner_log_level = 3;
|
||||||
|
}
|
||||||
|
|
||||||
string provisioner_log_level = 4;
|
// ParseRequest consumes source-code to produce inputs.
|
||||||
}
|
message ParseRequest {
|
||||||
|
}
|
||||||
|
|
||||||
message Plan {
|
// ParseComplete indicates a request to parse completed.
|
||||||
reserved 2;
|
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;
|
Config config = 1;
|
||||||
repeated RichParameterValue rich_parameter_values = 3;
|
ParseRequest parse = 2;
|
||||||
repeated VariableValue variable_values = 4;
|
PlanRequest plan = 3;
|
||||||
repeated GitAuthProvider git_auth_providers = 5;
|
ApplyRequest apply = 4;
|
||||||
|
CancelRequest cancel = 5;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
message Apply {
|
message Response {
|
||||||
Config config = 1;
|
oneof type {
|
||||||
bytes plan = 2;
|
Log log = 1;
|
||||||
}
|
ParseComplete parse = 2;
|
||||||
|
PlanComplete plan = 3;
|
||||||
message Cancel {}
|
ApplyComplete apply = 4;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
service Provisioner {
|
service Provisioner {
|
||||||
rpc Parse(Parse.Request) returns (stream Parse.Response);
|
// Session represents provisioning a single template import or workspace. The daemon always sends Config followed
|
||||||
rpc Provision(stream Provision.Request) returns (stream Provision.Response);
|
// 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 {
|
type DRPCProvisionerClient interface {
|
||||||
DRPCConn() drpc.Conn
|
DRPCConn() drpc.Conn
|
||||||
|
|
||||||
Parse(ctx context.Context, in *Parse_Request) (DRPCProvisioner_ParseClient, error)
|
Session(ctx context.Context) (DRPCProvisioner_SessionClient, error)
|
||||||
Provision(ctx context.Context) (DRPCProvisioner_ProvisionClient, error)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type drpcProvisionerClient struct {
|
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) DRPCConn() drpc.Conn { return c.cc }
|
||||||
|
|
||||||
func (c *drpcProvisionerClient) Parse(ctx context.Context, in *Parse_Request) (DRPCProvisioner_ParseClient, error) {
|
func (c *drpcProvisionerClient) Session(ctx context.Context) (DRPCProvisioner_SessionClient, error) {
|
||||||
stream, err := c.cc.NewStream(ctx, "/provisioner.Provisioner/Parse", drpcEncoding_File_provisionersdk_proto_provisioner_proto{})
|
stream, err := c.cc.NewStream(ctx, "/provisioner.Provisioner/Session", drpcEncoding_File_provisionersdk_proto_provisioner_proto{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
x := &drpcProvisioner_ParseClient{stream}
|
x := &drpcProvisioner_SessionClient{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
|
|
||||||
}
|
|
||||||
return x, nil
|
return x, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type DRPCProvisioner_ParseClient interface {
|
type DRPCProvisioner_SessionClient interface {
|
||||||
drpc.Stream
|
drpc.Stream
|
||||||
Recv() (*Parse_Response, error)
|
Send(*Request) error
|
||||||
|
Recv() (*Response, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type drpcProvisioner_ParseClient struct {
|
type drpcProvisioner_SessionClient struct {
|
||||||
drpc.Stream
|
drpc.Stream
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *drpcProvisioner_ParseClient) GetStream() drpc.Stream {
|
func (x *drpcProvisioner_SessionClient) GetStream() drpc.Stream {
|
||||||
return x.Stream
|
return x.Stream
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *drpcProvisioner_ParseClient) Recv() (*Parse_Response, error) {
|
func (x *drpcProvisioner_SessionClient) Send(m *Request) 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 {
|
|
||||||
return x.MsgSend(m, drpcEncoding_File_provisionersdk_proto_provisioner_proto{})
|
return x.MsgSend(m, drpcEncoding_File_provisionersdk_proto_provisioner_proto{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *drpcProvisioner_ProvisionClient) Recv() (*Provision_Response, error) {
|
func (x *drpcProvisioner_SessionClient) Recv() (*Response, error) {
|
||||||
m := new(Provision_Response)
|
m := new(Response)
|
||||||
if err := x.MsgRecv(m, drpcEncoding_File_provisionersdk_proto_provisioner_proto{}); err != nil {
|
if err := x.MsgRecv(m, drpcEncoding_File_provisionersdk_proto_provisioner_proto{}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return m, nil
|
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{})
|
return x.MsgRecv(m, drpcEncoding_File_provisionersdk_proto_provisioner_proto{})
|
||||||
}
|
}
|
||||||
|
|
||||||
type DRPCProvisionerServer interface {
|
type DRPCProvisionerServer interface {
|
||||||
Parse(*Parse_Request, DRPCProvisioner_ParseStream) error
|
Session(DRPCProvisioner_SessionStream) error
|
||||||
Provision(DRPCProvisioner_ProvisionStream) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type DRPCProvisionerUnimplementedServer struct{}
|
type DRPCProvisionerUnimplementedServer struct{}
|
||||||
|
|
||||||
func (s *DRPCProvisionerUnimplementedServer) Parse(*Parse_Request, DRPCProvisioner_ParseStream) error {
|
func (s *DRPCProvisionerUnimplementedServer) Session(DRPCProvisioner_SessionStream) error {
|
||||||
return drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *DRPCProvisionerUnimplementedServer) Provision(DRPCProvisioner_ProvisionStream) error {
|
|
||||||
return drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
|
return drpcerr.WithCode(errors.New("Unimplemented"), drpcerr.Unimplemented)
|
||||||
}
|
}
|
||||||
|
|
||||||
type DRPCProvisionerDescription struct{}
|
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) {
|
func (DRPCProvisionerDescription) Method(n int) (string, drpc.Encoding, drpc.Receiver, interface{}, bool) {
|
||||||
switch n {
|
switch n {
|
||||||
case 0:
|
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) {
|
func(srv interface{}, ctx context.Context, in1, in2 interface{}) (drpc.Message, error) {
|
||||||
return nil, srv.(DRPCProvisionerServer).
|
return nil, srv.(DRPCProvisionerServer).
|
||||||
Parse(
|
Session(
|
||||||
in1.(*Parse_Request),
|
&drpcProvisioner_SessionStream{in1.(drpc.Stream)},
|
||||||
&drpcProvisioner_ParseStream{in2.(drpc.Stream)},
|
|
||||||
)
|
)
|
||||||
}, DRPCProvisionerServer.Parse, true
|
}, DRPCProvisionerServer.Session, 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
|
|
||||||
default:
|
default:
|
||||||
return "", nil, nil, nil, false
|
return "", nil, nil, nil, false
|
||||||
}
|
}
|
||||||
|
@ -178,41 +123,28 @@ func DRPCRegisterProvisioner(mux drpc.Mux, impl DRPCProvisionerServer) error {
|
||||||
return mux.Register(impl, DRPCProvisionerDescription{})
|
return mux.Register(impl, DRPCProvisionerDescription{})
|
||||||
}
|
}
|
||||||
|
|
||||||
type DRPCProvisioner_ParseStream interface {
|
type DRPCProvisioner_SessionStream interface {
|
||||||
drpc.Stream
|
drpc.Stream
|
||||||
Send(*Parse_Response) error
|
Send(*Response) error
|
||||||
|
Recv() (*Request, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type drpcProvisioner_ParseStream struct {
|
type drpcProvisioner_SessionStream struct {
|
||||||
drpc.Stream
|
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{})
|
return x.MsgSend(m, drpcEncoding_File_provisionersdk_proto_provisioner_proto{})
|
||||||
}
|
}
|
||||||
|
|
||||||
type DRPCProvisioner_ProvisionStream interface {
|
func (x *drpcProvisioner_SessionStream) Recv() (*Request, error) {
|
||||||
drpc.Stream
|
m := new(Request)
|
||||||
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)
|
|
||||||
if err := x.MsgRecv(m, drpcEncoding_File_provisionersdk_proto_provisioner_proto{}); err != nil {
|
if err := x.MsgRecv(m, drpcEncoding_File_provisionersdk_proto_provisioner_proto{}); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return m, nil
|
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{})
|
return x.MsgRecv(m, drpcEncoding_File_provisionersdk_proto_provisioner_proto{})
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,8 @@ import (
|
||||||
"storj.io/drpc/drpcmux"
|
"storj.io/drpc/drpcmux"
|
||||||
"storj.io/drpc/drpcserver"
|
"storj.io/drpc/drpcserver"
|
||||||
|
|
||||||
|
"cdr.dev/slog"
|
||||||
|
|
||||||
"github.com/coder/coder/v2/coderd/tracing"
|
"github.com/coder/coder/v2/coderd/tracing"
|
||||||
"github.com/coder/coder/v2/provisionersdk/proto"
|
"github.com/coder/coder/v2/provisionersdk/proto"
|
||||||
)
|
)
|
||||||
|
@ -20,11 +22,19 @@ import (
|
||||||
// ServeOptions are configurations to serve a provisioner.
|
// ServeOptions are configurations to serve a provisioner.
|
||||||
type ServeOptions struct {
|
type ServeOptions struct {
|
||||||
// Conn specifies a custom transport to serve the dRPC connection.
|
// 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.
|
// 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 {
|
if options == nil {
|
||||||
options = &ServeOptions{}
|
options = &ServeOptions{}
|
||||||
}
|
}
|
||||||
|
@ -45,11 +55,22 @@ func Serve(ctx context.Context, server proto.DRPCProvisionerServer, options *Ser
|
||||||
}()
|
}()
|
||||||
options.Listener = stdio
|
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.
|
// 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
|
// See: https://www.storj.io/blog/introducing-drpc-our-replacement-for-grpc
|
||||||
mux := drpcmux.New()
|
mux := drpcmux.New()
|
||||||
err := proto.DRPCRegisterProvisioner(mux, server)
|
ps := &protoServer{
|
||||||
|
server: server,
|
||||||
|
opts: *options,
|
||||||
|
}
|
||||||
|
err := proto.DRPCRegisterProvisioner(mux, ps)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return xerrors.Errorf("register provisioner: %w", err)
|
return xerrors.Errorf("register provisioner: %w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"go.uber.org/goleak"
|
"go.uber.org/goleak"
|
||||||
"storj.io/drpc/drpcerr"
|
|
||||||
|
|
||||||
"github.com/coder/coder/v2/provisionersdk"
|
"github.com/coder/coder/v2/provisionersdk"
|
||||||
"github.com/coder/coder/v2/provisionersdk/proto"
|
"github.com/coder/coder/v2/provisionersdk/proto"
|
||||||
|
@ -28,17 +27,37 @@ func TestProvisionerSDK(t *testing.T) {
|
||||||
ctx, cancelFunc := context.WithCancel(context.Background())
|
ctx, cancelFunc := context.WithCancel(context.Background())
|
||||||
defer cancelFunc()
|
defer cancelFunc()
|
||||||
go func() {
|
go func() {
|
||||||
err := provisionersdk.Serve(ctx, &proto.DRPCProvisionerUnimplementedServer{}, &provisionersdk.ServeOptions{
|
err := provisionersdk.Serve(ctx, unimplementedServer{}, &provisionersdk.ServeOptions{
|
||||||
Listener: server,
|
Listener: server,
|
||||||
|
WorkDirectory: t.TempDir(),
|
||||||
})
|
})
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
api := proto.NewDRPCProvisionerClient(client)
|
api := proto.NewDRPCProvisionerClient(client)
|
||||||
stream, err := api.Parse(context.Background(), &proto.Parse_Request{})
|
s, err := api.Session(ctx)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
_, err = stream.Recv()
|
err = s.Send(&proto.Request{Type: &proto.Request_Config{Config: &proto.Config{}}})
|
||||||
require.Equal(t, drpcerr.Unimplemented, int(drpcerr.Code(err)))
|
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) {
|
t.Run("ServeClosedPipe", func(t *testing.T) {
|
||||||
|
@ -47,9 +66,24 @@ func TestProvisionerSDK(t *testing.T) {
|
||||||
_ = client.Close()
|
_ = client.Close()
|
||||||
_ = server.Close()
|
_ = server.Close()
|
||||||
|
|
||||||
err := provisionersdk.Serve(context.Background(), &proto.DRPCProvisionerUnimplementedServer{}, &provisionersdk.ServeOptions{
|
err := provisionersdk.Serve(context.Background(), unimplementedServer{}, &provisionersdk.ServeOptions{
|
||||||
Listener: server,
|
Listener: server,
|
||||||
|
WorkDirectory: t.TempDir(),
|
||||||
})
|
})
|
||||||
require.NoError(t, err)
|
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
|
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 {
|
func MultiplexedConn(session *yamux.Session) drpc.Conn {
|
||||||
return &multiplexedDRPC{session}
|
return &multiplexedDRPC{session}
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,10 +231,10 @@ func setupRunnerTest(t *testing.T) (client *codersdk.Client, agentID uuid.UUID)
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
|
|
@ -48,10 +48,10 @@ func Test_Runner(t *testing.T) {
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{
|
ProvisionApply: []*proto.Response{
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Log{
|
Type: &proto.Response_Log{
|
||||||
Log: &proto.Log{
|
Log: &proto.Log{
|
||||||
Level: proto.LogLevel_INFO,
|
Level: proto.LogLevel_INFO,
|
||||||
Output: "hello from logs",
|
Output: "hello from logs",
|
||||||
|
@ -59,8 +59,8 @@ func Test_Runner(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{
|
Resources: []*proto.Resource{
|
||||||
{
|
{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
|
@ -170,10 +170,10 @@ func Test_Runner(t *testing.T) {
|
||||||
|
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{
|
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()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{
|
ProvisionApply: []*proto.Response{
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Log{
|
Type: &proto.Response_Log{
|
||||||
Log: &proto.Log{
|
Log: &proto.Log{
|
||||||
Level: proto.LogLevel_INFO,
|
Level: proto.LogLevel_INFO,
|
||||||
Output: "hello from logs",
|
Output: "hello from logs",
|
||||||
|
@ -293,8 +293,8 @@ func Test_Runner(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{
|
Resources: []*proto.Resource{
|
||||||
{
|
{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
|
@ -407,11 +407,11 @@ func Test_Runner(t *testing.T) {
|
||||||
|
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{
|
ProvisionApply: []*proto.Response{
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Error: "test error",
|
Error: "test error",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -252,10 +252,10 @@ func setupRunnerTest(t *testing.T) (client *codersdk.Client, agentID uuid.UUID)
|
||||||
authToken := uuid.NewString()
|
authToken := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
|
|
@ -45,10 +45,10 @@ func Test_Runner(t *testing.T) {
|
||||||
authToken3 := uuid.NewString()
|
authToken3 := uuid.NewString()
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{
|
ProvisionApply: []*proto.Response{
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Log{
|
Type: &proto.Response_Log{
|
||||||
Log: &proto.Log{
|
Log: &proto.Log{
|
||||||
Level: proto.LogLevel_INFO,
|
Level: proto.LogLevel_INFO,
|
||||||
Output: "hello from logs",
|
Output: "hello from logs",
|
||||||
|
@ -56,8 +56,8 @@ func Test_Runner(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{
|
Resources: []*proto.Resource{
|
||||||
{
|
{
|
||||||
Name: "example1",
|
Name: "example1",
|
||||||
|
@ -199,11 +199,11 @@ func Test_Runner(t *testing.T) {
|
||||||
|
|
||||||
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{
|
ProvisionApply: []*proto.Response{
|
||||||
{
|
{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Error: "test error",
|
Error: "test error",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -41,10 +41,10 @@ func TestRun(t *testing.T) {
|
||||||
agentName = "agent"
|
agentName = "agent"
|
||||||
version = coderdtest.CreateTemplateVersion(t, client, firstUser.OrganizationID, &echo.Responses{
|
version = coderdtest.CreateTemplateVersion(t, client, firstUser.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
@ -154,10 +154,10 @@ func TestRun(t *testing.T) {
|
||||||
agentName = "agent"
|
agentName = "agent"
|
||||||
version = coderdtest.CreateTemplateVersion(t, client, firstUser.OrganizationID, &echo.Responses{
|
version = coderdtest.CreateTemplateVersion(t, client, firstUser.OrganizationID, &echo.Responses{
|
||||||
Parse: echo.ParseComplete,
|
Parse: echo.ParseComplete,
|
||||||
ProvisionPlan: echo.ProvisionComplete,
|
ProvisionPlan: echo.PlanComplete,
|
||||||
ProvisionApply: []*proto.Provision_Response{{
|
ProvisionApply: []*proto.Response{{
|
||||||
Type: &proto.Provision_Response_Complete{
|
Type: &proto.Response_Apply{
|
||||||
Complete: &proto.Provision_Complete{
|
Apply: &proto.ApplyComplete{
|
||||||
Resources: []*proto.Resource{{
|
Resources: []*proto.Resource{{
|
||||||
Name: "example",
|
Name: "example",
|
||||||
Type: "aws_instance",
|
Type: "aws_instance",
|
||||||
|
|
|
@ -8,10 +8,10 @@ import {
|
||||||
Agent,
|
Agent,
|
||||||
App,
|
App,
|
||||||
AppSharingLevel,
|
AppSharingLevel,
|
||||||
Parse_Complete,
|
Response,
|
||||||
Parse_Response,
|
ParseComplete,
|
||||||
Provision_Complete,
|
PlanComplete,
|
||||||
Provision_Response,
|
ApplyComplete,
|
||||||
Resource,
|
Resource,
|
||||||
RichParameter,
|
RichParameter,
|
||||||
} from "./provisionerGenerated"
|
} from "./provisionerGenerated"
|
||||||
|
@ -337,11 +337,11 @@ type RecursivePartial<T> = {
|
||||||
|
|
||||||
interface EchoProvisionerResponses {
|
interface EchoProvisionerResponses {
|
||||||
// parse is for observing any Terraform variables
|
// parse is for observing any Terraform variables
|
||||||
parse?: RecursivePartial<Parse_Response>[]
|
parse?: RecursivePartial<Response>[]
|
||||||
// plan occurs when the template is imported
|
// plan occurs when the template is imported
|
||||||
plan?: RecursivePartial<Provision_Response>[]
|
plan?: RecursivePartial<Response>[]
|
||||||
// apply occurs when the workspace is built
|
// apply occurs when the workspace is built
|
||||||
apply?: RecursivePartial<Provision_Response>[]
|
apply?: RecursivePartial<Response>[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// createTemplateVersionTar consumes a series of echo provisioner protobufs and
|
// createTemplateVersionTar consumes a series of echo provisioner protobufs and
|
||||||
|
@ -353,109 +353,133 @@ const createTemplateVersionTar = async (
|
||||||
responses = {}
|
responses = {}
|
||||||
}
|
}
|
||||||
if (!responses.parse) {
|
if (!responses.parse) {
|
||||||
responses.parse = [{}]
|
responses.parse = [
|
||||||
|
{
|
||||||
|
parse: {},
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
if (!responses.apply) {
|
if (!responses.apply) {
|
||||||
responses.apply = [{}]
|
responses.apply = [
|
||||||
|
{
|
||||||
|
apply: {},
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
if (!responses.plan) {
|
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()
|
const tar = new TarWriter()
|
||||||
responses.parse.forEach((response, index) => {
|
responses.parse.forEach((response, index) => {
|
||||||
response.complete = {
|
response.parse = {
|
||||||
templateVariables: [],
|
templateVariables: [],
|
||||||
...response.complete,
|
error: "",
|
||||||
} as Parse_Complete
|
readme: new Uint8Array(),
|
||||||
|
...response.parse,
|
||||||
|
} as ParseComplete
|
||||||
tar.addFile(
|
tar.addFile(
|
||||||
`${index}.parse.protobuf`,
|
`${index}.parse.protobuf`,
|
||||||
Parse_Response.encode(response as Parse_Response).finish(),
|
Response.encode(response as Response).finish(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const fillProvisionResponse = (
|
const fillResource = (resource: RecursivePartial<Resource>) => {
|
||||||
response: RecursivePartial<Provision_Response>,
|
if (resource.agents) {
|
||||||
) => {
|
resource.agents = resource.agents?.map(
|
||||||
response.complete = {
|
(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: "",
|
error: "",
|
||||||
state: new Uint8Array(),
|
state: new Uint8Array(),
|
||||||
resources: [],
|
resources: [],
|
||||||
parameters: [],
|
parameters: [],
|
||||||
gitAuthProviders: [],
|
gitAuthProviders: [],
|
||||||
plan: new Uint8Array(),
|
...response.apply,
|
||||||
...response.complete,
|
} as ApplyComplete
|
||||||
} as Provision_Complete
|
response.apply.resources = response.apply.resources?.map(fillResource)
|
||||||
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)
|
|
||||||
|
|
||||||
tar.addFile(
|
tar.addFile(
|
||||||
`${index}.provision.apply.protobuf`,
|
`${index}.apply.protobuf`,
|
||||||
Provision_Response.encode(response as Provision_Response).finish(),
|
Response.encode(response as Response).finish(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
responses.plan.forEach((response, index) => {
|
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(
|
tar.addFile(
|
||||||
`${index}.provision.plan.protobuf`,
|
`${index}.plan.protobuf`,
|
||||||
Provision_Response.encode(response as Provision_Response).finish(),
|
Response.encode(response as Response).finish(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
const tarFile = await tar.write()
|
const tarFile = await tar.write()
|
||||||
|
@ -512,16 +536,21 @@ export const echoResponsesWithParameters = (
|
||||||
richParameters: RichParameter[],
|
richParameters: RichParameter[],
|
||||||
): EchoProvisionerResponses => {
|
): EchoProvisionerResponses => {
|
||||||
return {
|
return {
|
||||||
|
parse: [
|
||||||
|
{
|
||||||
|
parse: {},
|
||||||
|
},
|
||||||
|
],
|
||||||
plan: [
|
plan: [
|
||||||
{
|
{
|
||||||
complete: {
|
plan: {
|
||||||
parameters: richParameters,
|
parameters: richParameters,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
apply: [
|
apply: [
|
||||||
{
|
{
|
||||||
complete: {
|
apply: {
|
||||||
resources: [
|
resources: [
|
||||||
{
|
{
|
||||||
name: "example",
|
name: "example",
|
||||||
|
|
|
@ -21,6 +21,7 @@ export enum AppSharingLevel {
|
||||||
UNRECOGNIZED = -1,
|
UNRECOGNIZED = -1,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** WorkspaceTransition is the desired outcome of a build */
|
||||||
export enum WorkspaceTransition {
|
export enum WorkspaceTransition {
|
||||||
START = 0,
|
START = 0,
|
||||||
STOP = 1,
|
STOP = 1,
|
||||||
|
@ -177,29 +178,8 @@ export interface Resource_Metadata {
|
||||||
isNull: boolean
|
isNull: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Parse consumes source-code from a directory to produce inputs. */
|
/** Metadata is information about a workspace used in the execution of a build */
|
||||||
export interface Parse {}
|
export interface Metadata {
|
||||||
|
|
||||||
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 {
|
|
||||||
coderUrl: string
|
coderUrl: string
|
||||||
workspaceTransition: WorkspaceTransition
|
workspaceTransition: WorkspaceTransition
|
||||||
workspaceName: string
|
workspaceName: string
|
||||||
|
@ -213,49 +193,74 @@ export interface Provision_Metadata {
|
||||||
workspaceOwnerSessionToken: string
|
workspaceOwnerSessionToken: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Config represents execution configuration shared by all subsequent requests in the Session */
|
||||||
* Config represents execution configuration shared by both Plan and
|
export interface Config {
|
||||||
* Apply commands.
|
/** template_source_archive is a tar of the template source files */
|
||||||
*/
|
templateSourceArchive: Uint8Array
|
||||||
export interface Provision_Config {
|
/** state is the provisioner state (if any) */
|
||||||
directory: string
|
|
||||||
state: Uint8Array
|
state: Uint8Array
|
||||||
metadata: Provision_Metadata | undefined
|
|
||||||
provisionerLogLevel: string
|
provisionerLogLevel: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Provision_Plan {
|
/** ParseRequest consumes source-code to produce inputs. */
|
||||||
config: Provision_Config | undefined
|
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[]
|
richParameterValues: RichParameterValue[]
|
||||||
variableValues: VariableValue[]
|
variableValues: VariableValue[]
|
||||||
gitAuthProviders: GitAuthProvider[]
|
gitAuthProviders: GitAuthProvider[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Provision_Apply {
|
/** PlanComplete indicates a request to plan completed. */
|
||||||
config: Provision_Config | undefined
|
export interface PlanComplete {
|
||||||
plan: Uint8Array
|
error: string
|
||||||
|
resources: Resource[]
|
||||||
|
parameters: RichParameter[]
|
||||||
|
gitAuthProviders: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Provision_Cancel {}
|
/**
|
||||||
|
* ApplyRequest asks the provisioner to apply the changes. Apply MUST be preceded by a successful plan request/response
|
||||||
export interface Provision_Request {
|
* in the same Session. The plan data is not transmitted over the wire and is cached by the provisioner in the Session.
|
||||||
plan?: Provision_Plan | undefined
|
*/
|
||||||
apply?: Provision_Apply | undefined
|
export interface ApplyRequest {
|
||||||
cancel?: Provision_Cancel | undefined
|
metadata: Metadata | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Provision_Complete {
|
/** ApplyComplete indicates a request to apply completed. */
|
||||||
|
export interface ApplyComplete {
|
||||||
state: Uint8Array
|
state: Uint8Array
|
||||||
error: string
|
error: string
|
||||||
resources: Resource[]
|
resources: Resource[]
|
||||||
parameters: RichParameter[]
|
parameters: RichParameter[]
|
||||||
gitAuthProviders: string[]
|
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
|
log?: Log | undefined
|
||||||
complete?: Provision_Complete | undefined
|
parse?: ParseComplete | undefined
|
||||||
|
plan?: PlanComplete | undefined
|
||||||
|
apply?: ApplyComplete | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Empty = {
|
export const Empty = {
|
||||||
|
@ -648,60 +653,9 @@ export const Resource_Metadata = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Parse = {
|
export const Metadata = {
|
||||||
encode(_: Parse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
|
|
||||||
return writer
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Parse_Request = {
|
|
||||||
encode(
|
encode(
|
||||||
message: Parse_Request,
|
message: Metadata,
|
||||||
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,
|
|
||||||
writer: _m0.Writer = _m0.Writer.create(),
|
writer: _m0.Writer = _m0.Writer.create(),
|
||||||
): _m0.Writer {
|
): _m0.Writer {
|
||||||
if (message.coderUrl !== "") {
|
if (message.coderUrl !== "") {
|
||||||
|
@ -741,96 +695,108 @@ export const Provision_Metadata = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Provision_Config = {
|
export const Config = {
|
||||||
encode(
|
encode(
|
||||||
message: Provision_Config,
|
message: Config,
|
||||||
writer: _m0.Writer = _m0.Writer.create(),
|
writer: _m0.Writer = _m0.Writer.create(),
|
||||||
): _m0.Writer {
|
): _m0.Writer {
|
||||||
if (message.directory !== "") {
|
if (message.templateSourceArchive.length !== 0) {
|
||||||
writer.uint32(10).string(message.directory)
|
writer.uint32(10).bytes(message.templateSourceArchive)
|
||||||
}
|
}
|
||||||
if (message.state.length !== 0) {
|
if (message.state.length !== 0) {
|
||||||
writer.uint32(18).bytes(message.state)
|
writer.uint32(18).bytes(message.state)
|
||||||
}
|
}
|
||||||
if (message.metadata !== undefined) {
|
|
||||||
Provision_Metadata.encode(
|
|
||||||
message.metadata,
|
|
||||||
writer.uint32(26).fork(),
|
|
||||||
).ldelim()
|
|
||||||
}
|
|
||||||
if (message.provisionerLogLevel !== "") {
|
if (message.provisionerLogLevel !== "") {
|
||||||
writer.uint32(34).string(message.provisionerLogLevel)
|
writer.uint32(26).string(message.provisionerLogLevel)
|
||||||
}
|
}
|
||||||
return writer
|
return writer
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Provision_Plan = {
|
export const ParseRequest = {
|
||||||
encode(
|
encode(
|
||||||
message: Provision_Plan,
|
_: ParseRequest,
|
||||||
writer: _m0.Writer = _m0.Writer.create(),
|
writer: _m0.Writer = _m0.Writer.create(),
|
||||||
): _m0.Writer {
|
): _m0.Writer {
|
||||||
if (message.config !== undefined) {
|
return writer
|
||||||
Provision_Config.encode(message.config, writer.uint32(10).fork()).ldelim()
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
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) {
|
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) {
|
for (const v of message.gitAuthProviders) {
|
||||||
GitAuthProvider.encode(v!, writer.uint32(42).fork()).ldelim()
|
GitAuthProvider.encode(v!, writer.uint32(34).fork()).ldelim()
|
||||||
}
|
}
|
||||||
return writer
|
return writer
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Provision_Apply = {
|
export const PlanComplete = {
|
||||||
encode(
|
encode(
|
||||||
message: Provision_Apply,
|
message: PlanComplete,
|
||||||
writer: _m0.Writer = _m0.Writer.create(),
|
writer: _m0.Writer = _m0.Writer.create(),
|
||||||
): _m0.Writer {
|
): _m0.Writer {
|
||||||
if (message.config !== undefined) {
|
if (message.error !== "") {
|
||||||
Provision_Config.encode(message.config, writer.uint32(10).fork()).ldelim()
|
writer.uint32(10).string(message.error)
|
||||||
}
|
}
|
||||||
if (message.plan.length !== 0) {
|
for (const v of message.resources) {
|
||||||
writer.uint32(18).bytes(message.plan)
|
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
|
return writer
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Provision_Cancel = {
|
export const ApplyRequest = {
|
||||||
encode(
|
encode(
|
||||||
_: Provision_Cancel,
|
message: ApplyRequest,
|
||||||
writer: _m0.Writer = _m0.Writer.create(),
|
writer: _m0.Writer = _m0.Writer.create(),
|
||||||
): _m0.Writer {
|
): _m0.Writer {
|
||||||
return writer
|
if (message.metadata !== undefined) {
|
||||||
},
|
Metadata.encode(message.metadata, writer.uint32(10).fork()).ldelim()
|
||||||
}
|
|
||||||
|
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
return writer
|
return writer
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Provision_Complete = {
|
export const ApplyComplete = {
|
||||||
encode(
|
encode(
|
||||||
message: Provision_Complete,
|
message: ApplyComplete,
|
||||||
writer: _m0.Writer = _m0.Writer.create(),
|
writer: _m0.Writer = _m0.Writer.create(),
|
||||||
): _m0.Writer {
|
): _m0.Writer {
|
||||||
if (message.state.length !== 0) {
|
if (message.state.length !== 0) {
|
||||||
|
@ -848,34 +814,76 @@ export const Provision_Complete = {
|
||||||
for (const v of message.gitAuthProviders) {
|
for (const v of message.gitAuthProviders) {
|
||||||
writer.uint32(42).string(v!)
|
writer.uint32(42).string(v!)
|
||||||
}
|
}
|
||||||
if (message.plan.length !== 0) {
|
return writer
|
||||||
writer.uint32(50).bytes(message.plan)
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
return writer
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Provision_Response = {
|
export const Response = {
|
||||||
encode(
|
encode(
|
||||||
message: Provision_Response,
|
message: Response,
|
||||||
writer: _m0.Writer = _m0.Writer.create(),
|
writer: _m0.Writer = _m0.Writer.create(),
|
||||||
): _m0.Writer {
|
): _m0.Writer {
|
||||||
if (message.log !== undefined) {
|
if (message.log !== undefined) {
|
||||||
Log.encode(message.log, writer.uint32(10).fork()).ldelim()
|
Log.encode(message.log, writer.uint32(10).fork()).ldelim()
|
||||||
}
|
}
|
||||||
if (message.complete !== undefined) {
|
if (message.parse !== undefined) {
|
||||||
Provision_Complete.encode(
|
ParseComplete.encode(message.parse, writer.uint32(18).fork()).ldelim()
|
||||||
message.complete,
|
}
|
||||||
writer.uint32(18).fork(),
|
if (message.plan !== undefined) {
|
||||||
).ldelim()
|
PlanComplete.encode(message.plan, writer.uint32(26).fork()).ldelim()
|
||||||
|
}
|
||||||
|
if (message.apply !== undefined) {
|
||||||
|
ApplyComplete.encode(message.apply, writer.uint32(34).fork()).ldelim()
|
||||||
}
|
}
|
||||||
return writer
|
return writer
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Provisioner {
|
export interface Provisioner {
|
||||||
Parse(request: Parse_Request): Observable<Parse_Response>
|
/**
|
||||||
Provision(
|
* Session represents provisioning a single template import or workspace. The daemon always sends Config followed
|
||||||
request: Observable<Provision_Request>,
|
* by one of the requests (ParseRequest, PlanRequest, ApplyRequest). The provisioner should respond with a stream
|
||||||
): Observable<Provision_Response>
|
* 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, {
|
const template = await createTemplate(page, {
|
||||||
apply: [
|
apply: [
|
||||||
{
|
{
|
||||||
complete: {
|
apply: {
|
||||||
resources: [
|
resources: [
|
||||||
{
|
{
|
||||||
agents: [
|
agents: [
|
||||||
|
|
|
@ -21,7 +21,7 @@ test("create workspace", async ({ page }) => {
|
||||||
const template = await createTemplate(page, {
|
const template = await createTemplate(page, {
|
||||||
apply: [
|
apply: [
|
||||||
{
|
{
|
||||||
complete: {
|
apply: {
|
||||||
resources: [
|
resources: [
|
||||||
{
|
{
|
||||||
name: "example",
|
name: "example",
|
||||||
|
|
|
@ -15,7 +15,7 @@ test("ssh with agent " + agentVersion, async ({ page }) => {
|
||||||
const template = await createTemplate(page, {
|
const template = await createTemplate(page, {
|
||||||
apply: [
|
apply: [
|
||||||
{
|
{
|
||||||
complete: {
|
apply: {
|
||||||
resources: [
|
resources: [
|
||||||
{
|
{
|
||||||
agents: [
|
agents: [
|
||||||
|
|
|
@ -15,7 +15,7 @@ test("ssh with client " + clientVersion, async ({ page }) => {
|
||||||
const template = await createTemplate(page, {
|
const template = await createTemplate(page, {
|
||||||
apply: [
|
apply: [
|
||||||
{
|
{
|
||||||
complete: {
|
apply: {
|
||||||
resources: [
|
resources: [
|
||||||
{
|
{
|
||||||
agents: [
|
agents: [
|
||||||
|
|
|
@ -7,7 +7,7 @@ test("web terminal", async ({ context, page }) => {
|
||||||
const template = await createTemplate(page, {
|
const template = await createTemplate(page, {
|
||||||
apply: [
|
apply: [
|
||||||
{
|
{
|
||||||
complete: {
|
apply: {
|
||||||
resources: [
|
resources: [
|
||||||
{
|
{
|
||||||
agents: [
|
agents: [
|
||||||
|
|
Loading…
Reference in New Issue