Skip to content

Commit cd32c42

Browse files
authored
fix(cli): inherit provisioner tags from last template version (#13462)
1 parent e527bc6 commit cd32c42

File tree

4 files changed

+151
-3
lines changed

4 files changed

+151
-3
lines changed

cli/templatepush.go

+10
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,16 @@ func (r *RootCmd) templatePush() *serpent.Command {
100100
return err
101101
}
102102

103+
// If user hasn't provided new provisioner tags, inherit ones from the active template version.
104+
if len(tags) == 0 && template.ActiveVersionID != uuid.Nil {
105+
templateVersion, err := client.TemplateVersion(inv.Context(), template.ActiveVersionID)
106+
if err != nil {
107+
return err
108+
}
109+
tags = templateVersion.Job.Tags
110+
inv.Logger.Info(inv.Context(), "reusing existing provisioner tags", "tags", tags)
111+
}
112+
103113
userVariableValues, err := ParseUserVariableValues(
104114
varsFiles,
105115
variablesFile,

cli/templatepush_test.go

+129
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,135 @@ func TestTemplatePush(t *testing.T) {
403403
assert.NotEqual(t, template.ActiveVersionID, templateVersions[1].ID)
404404
})
405405

406+
t.Run("ProvisionerTags", func(t *testing.T) {
407+
t.Parallel()
408+
409+
t.Run("ChangeTags", func(t *testing.T) {
410+
t.Parallel()
411+
412+
// Start the first provisioner
413+
client, provisionerDocker, api := coderdtest.NewWithAPI(t, &coderdtest.Options{
414+
IncludeProvisionerDaemon: true,
415+
ProvisionerDaemonTags: map[string]string{
416+
"docker": "true",
417+
},
418+
})
419+
defer provisionerDocker.Close()
420+
421+
// Start the second provisioner
422+
provisionerFoobar := coderdtest.NewTaggedProvisionerDaemon(t, api, "provisioner-foobar", map[string]string{
423+
"foobar": "foobaz",
424+
})
425+
defer provisionerFoobar.Close()
426+
427+
owner := coderdtest.CreateFirstUser(t, client)
428+
templateAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleTemplateAdmin())
429+
430+
// Create the template with initial tagged template version.
431+
templateVersion := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil, func(ctvr *codersdk.CreateTemplateVersionRequest) {
432+
ctvr.ProvisionerTags = map[string]string{
433+
"docker": "true",
434+
}
435+
})
436+
templateVersion = coderdtest.AwaitTemplateVersionJobCompleted(t, client, templateVersion.ID)
437+
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, templateVersion.ID)
438+
439+
// Push new template version without provisioner tags. CLI should reuse tags from the previous version.
440+
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
441+
Parse: echo.ParseComplete,
442+
ProvisionApply: echo.ApplyComplete,
443+
})
444+
inv, root := clitest.New(t, "templates", "push", template.Name, "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho), "--name", template.Name,
445+
"--provisioner-tag", "foobar=foobaz")
446+
clitest.SetupConfig(t, templateAdmin, root)
447+
pty := ptytest.New(t).Attach(inv)
448+
449+
execDone := make(chan error)
450+
go func() {
451+
execDone <- inv.Run()
452+
}()
453+
454+
matches := []struct {
455+
match string
456+
write string
457+
}{
458+
{match: "Upload", write: "yes"},
459+
}
460+
for _, m := range matches {
461+
pty.ExpectMatch(m.match)
462+
pty.WriteLine(m.write)
463+
}
464+
465+
require.NoError(t, <-execDone)
466+
467+
// Verify template version tags
468+
template, err := client.Template(context.Background(), template.ID)
469+
require.NoError(t, err)
470+
471+
templateVersion, err = client.TemplateVersion(context.Background(), template.ActiveVersionID)
472+
require.NoError(t, err)
473+
require.EqualValues(t, map[string]string{"foobar": "foobaz", "owner": "", "scope": "organization"}, templateVersion.Job.Tags)
474+
})
475+
476+
t.Run("DoNotChangeTags", func(t *testing.T) {
477+
t.Parallel()
478+
479+
// Start the tagged provisioner
480+
client := coderdtest.New(t, &coderdtest.Options{
481+
IncludeProvisionerDaemon: true,
482+
ProvisionerDaemonTags: map[string]string{
483+
"docker": "true",
484+
},
485+
})
486+
owner := coderdtest.CreateFirstUser(t, client)
487+
templateAdmin, _ := coderdtest.CreateAnotherUser(t, client, owner.OrganizationID, rbac.RoleTemplateAdmin())
488+
489+
// Create the template with initial tagged template version.
490+
templateVersion := coderdtest.CreateTemplateVersion(t, client, owner.OrganizationID, nil, func(ctvr *codersdk.CreateTemplateVersionRequest) {
491+
ctvr.ProvisionerTags = map[string]string{
492+
"docker": "true",
493+
}
494+
})
495+
templateVersion = coderdtest.AwaitTemplateVersionJobCompleted(t, client, templateVersion.ID)
496+
template := coderdtest.CreateTemplate(t, client, owner.OrganizationID, templateVersion.ID)
497+
498+
// Push new template version without provisioner tags. CLI should reuse tags from the previous version.
499+
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
500+
Parse: echo.ParseComplete,
501+
ProvisionApply: echo.ApplyComplete,
502+
})
503+
inv, root := clitest.New(t, "templates", "push", template.Name, "--directory", source, "--test.provisioner", string(database.ProvisionerTypeEcho), "--name", template.Name)
504+
clitest.SetupConfig(t, templateAdmin, root)
505+
pty := ptytest.New(t).Attach(inv)
506+
507+
execDone := make(chan error)
508+
go func() {
509+
execDone <- inv.Run()
510+
}()
511+
512+
matches := []struct {
513+
match string
514+
write string
515+
}{
516+
{match: "Upload", write: "yes"},
517+
}
518+
for _, m := range matches {
519+
pty.ExpectMatch(m.match)
520+
pty.WriteLine(m.write)
521+
}
522+
523+
require.NoError(t, <-execDone)
524+
525+
// Verify template version tags
526+
template, err := client.Template(context.Background(), template.ID)
527+
require.NoError(t, err)
528+
529+
templateVersion, err = client.TemplateVersion(context.Background(), template.ActiveVersionID)
530+
require.NoError(t, err)
531+
require.EqualValues(t, map[string]string{"docker": "true", "owner": "", "scope": "organization"}, templateVersion.Job.Tags)
532+
})
533+
})
534+
406535
t.Run("Variables", func(t *testing.T) {
407536
t.Parallel()
408537

coderd/coderd.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -1373,6 +1373,10 @@ func compressHandler(h http.Handler) http.Handler {
13731373
// CreateInMemoryProvisionerDaemon is an in-memory connection to a provisionerd.
13741374
// Useful when starting coderd and provisionerd in the same process.
13751375
func (api *API) CreateInMemoryProvisionerDaemon(dialCtx context.Context, name string, provisionerTypes []codersdk.ProvisionerType) (client proto.DRPCProvisionerDaemonClient, err error) {
1376+
return api.CreateInMemoryTaggedProvisionerDaemon(dialCtx, name, provisionerTypes, nil)
1377+
}
1378+
1379+
func (api *API) CreateInMemoryTaggedProvisionerDaemon(dialCtx context.Context, name string, provisionerTypes []codersdk.ProvisionerType, provisionerTags map[string]string) (client proto.DRPCProvisionerDaemonClient, err error) {
13761380
tracer := api.TracerProvider.Tracer(tracing.TracerName)
13771381
clientSession, serverSession := drpc.MemTransportPipe()
13781382
defer func() {
@@ -1400,7 +1404,7 @@ func (api *API) CreateInMemoryProvisionerDaemon(dialCtx context.Context, name st
14001404
OrganizationID: defaultOrg.ID,
14011405
CreatedAt: dbtime.Now(),
14021406
Provisioners: dbTypes,
1403-
Tags: provisionersdk.MutateTags(uuid.Nil, nil),
1407+
Tags: provisionersdk.MutateTags(uuid.Nil, provisionerTags),
14041408
LastSeenAt: sql.NullTime{Time: dbtime.Now(), Valid: true},
14051409
Version: buildinfo.Version(),
14061410
APIVersion: proto.CurrentVersion.String(),

coderd/coderdtest/coderdtest.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ type Options struct {
125125

126126
// IncludeProvisionerDaemon when true means to start an in-memory provisionerD
127127
IncludeProvisionerDaemon bool
128+
ProvisionerDaemonTags map[string]string
128129
MetricsCacheRefreshInterval time.Duration
129130
AgentStatsRefreshInterval time.Duration
130131
DeploymentValues *codersdk.DeploymentValues
@@ -512,7 +513,7 @@ func NewWithAPI(t testing.TB, options *Options) (*codersdk.Client, io.Closer, *c
512513
setHandler(coderAPI.RootHandler)
513514
var provisionerCloser io.Closer = nopcloser{}
514515
if options.IncludeProvisionerDaemon {
515-
provisionerCloser = NewProvisionerDaemon(t, coderAPI)
516+
provisionerCloser = NewTaggedProvisionerDaemon(t, coderAPI, "test", options.ProvisionerDaemonTags)
516517
}
517518
client := codersdk.New(serverURL)
518519
t.Cleanup(func() {
@@ -552,6 +553,10 @@ func (c *provisionerdCloser) Close() error {
552553
// well with coderd testing. It registers the "echo" provisioner for
553554
// quick testing.
554555
func NewProvisionerDaemon(t testing.TB, coderAPI *coderd.API) io.Closer {
556+
return NewTaggedProvisionerDaemon(t, coderAPI, "test", nil)
557+
}
558+
559+
func NewTaggedProvisionerDaemon(t testing.TB, coderAPI *coderd.API, name string, provisionerTags map[string]string) io.Closer {
555560
t.Helper()
556561

557562
// t.Cleanup runs in last added, first called order. t.TempDir() will delete
@@ -578,7 +583,7 @@ func NewProvisionerDaemon(t testing.TB, coderAPI *coderd.API) io.Closer {
578583
}()
579584

580585
daemon := provisionerd.New(func(dialCtx context.Context) (provisionerdproto.DRPCProvisionerDaemonClient, error) {
581-
return coderAPI.CreateInMemoryProvisionerDaemon(dialCtx, "test", []codersdk.ProvisionerType{codersdk.ProvisionerTypeEcho})
586+
return coderAPI.CreateInMemoryTaggedProvisionerDaemon(dialCtx, name, []codersdk.ProvisionerType{codersdk.ProvisionerTypeEcho}, provisionerTags)
582587
}, &provisionerd.Options{
583588
Logger: coderAPI.Logger.Named("provisionerd").Leveled(slog.LevelDebug),
584589
UpdateInterval: 250 * time.Millisecond,

0 commit comments

Comments
 (0)