Skip to content

Commit 34c67e8

Browse files
authored
test: add unit test helper to create templates in second organizations (#13628)
* chore: add coderdtest helpers
1 parent e4333c0 commit 34c67e8

File tree

3 files changed

+162
-0
lines changed

3 files changed

+162
-0
lines changed

coderd/coderdtest/coderdtest.go

+44
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,18 @@ func NewTaggedProvisionerDaemon(t testing.TB, coderAPI *coderd.API, name string,
600600
}
601601

602602
func NewExternalProvisionerDaemon(t testing.TB, client *codersdk.Client, org uuid.UUID, tags map[string]string) io.Closer {
603+
t.Helper()
604+
605+
// Without this check, the provisioner will silently fail.
606+
entitlements, err := client.Entitlements(context.Background())
607+
if err == nil {
608+
feature := entitlements.Features[codersdk.FeatureExternalProvisionerDaemons]
609+
if !feature.Enabled || feature.Entitlement != codersdk.EntitlementEntitled {
610+
require.NoError(t, xerrors.Errorf("external provisioner daemons require an entitled license"))
611+
return nil
612+
}
613+
}
614+
603615
echoClient, echoServer := drpc.MemTransportPipe()
604616
ctx, cancelFunc := context.WithCancel(context.Background())
605617
serveDone := make(chan struct{})
@@ -638,6 +650,7 @@ func NewExternalProvisionerDaemon(t testing.TB, client *codersdk.Client, org uui
638650
t.Cleanup(func() {
639651
_ = closer.Close()
640652
})
653+
641654
return closer
642655
}
643656

@@ -790,6 +803,37 @@ func createAnotherUserRetry(t testing.TB, client *codersdk.Client, organizationI
790803
return other, user
791804
}
792805

806+
type CreateOrganizationOptions struct {
807+
// IncludeProvisionerDaemon will spin up an external provisioner for the organization.
808+
// This requires enterprise and the feature 'codersdk.FeatureExternalProvisionerDaemons'
809+
IncludeProvisionerDaemon bool
810+
}
811+
812+
func CreateOrganization(t *testing.T, client *codersdk.Client, opts CreateOrganizationOptions, mutators ...func(*codersdk.CreateOrganizationRequest)) codersdk.Organization {
813+
ctx := testutil.Context(t, testutil.WaitMedium)
814+
req := codersdk.CreateOrganizationRequest{
815+
Name: strings.ReplaceAll(strings.ToLower(namesgenerator.GetRandomName(0)), "_", "-"),
816+
DisplayName: namesgenerator.GetRandomName(1),
817+
Description: namesgenerator.GetRandomName(1),
818+
Icon: "",
819+
}
820+
for _, mutator := range mutators {
821+
mutator(&req)
822+
}
823+
824+
org, err := client.CreateOrganization(ctx, req)
825+
require.NoError(t, err)
826+
827+
if opts.IncludeProvisionerDaemon {
828+
closer := NewExternalProvisionerDaemon(t, client, org.ID, map[string]string{})
829+
t.Cleanup(func() {
830+
_ = closer.Close()
831+
})
832+
}
833+
834+
return org
835+
}
836+
793837
// CreateTemplateVersion creates a template import provisioner job
794838
// with the responses provided. It uses the "echo" provisioner for compatibility
795839
// with testing.

enterprise/cli/templatecreate_test.go

+67
Original file line numberDiff line numberDiff line change
@@ -134,4 +134,71 @@ func TestTemplateCreate(t *testing.T) {
134134
require.Error(t, err)
135135
require.Contains(t, err.Error(), "your license is not entitled to use enterprise access control, so you cannot set --require-active-version")
136136
})
137+
138+
// Create a template in a second organization via custom role
139+
t.Run("SecondOrganization", func(t *testing.T) {
140+
t.Parallel()
141+
142+
dv := coderdtest.DeploymentValues(t)
143+
dv.Experiments = []string{string(codersdk.ExperimentCustomRoles)}
144+
ownerClient, _ := coderdenttest.New(t, &coderdenttest.Options{
145+
Options: &coderdtest.Options{
146+
DeploymentValues: dv,
147+
// This only affects the first org.
148+
IncludeProvisionerDaemon: false,
149+
},
150+
LicenseOptions: &coderdenttest.LicenseOptions{
151+
Features: license.Features{
152+
codersdk.FeatureAccessControl: 1,
153+
codersdk.FeatureCustomRoles: 1,
154+
codersdk.FeatureExternalProvisionerDaemons: 1,
155+
},
156+
},
157+
})
158+
159+
// Create the second organization
160+
secondOrg := coderdtest.CreateOrganization(t, ownerClient, coderdtest.CreateOrganizationOptions{
161+
IncludeProvisionerDaemon: true,
162+
})
163+
164+
ctx := testutil.Context(t, testutil.WaitMedium)
165+
166+
//nolint:gocritic // owner required to make custom roles
167+
orgTemplateAdminRole, err := ownerClient.PatchOrganizationRole(ctx, secondOrg.ID, codersdk.Role{
168+
Name: "org-template-admin",
169+
OrganizationID: secondOrg.ID.String(),
170+
OrganizationPermissions: codersdk.CreatePermissions(map[codersdk.RBACResource][]codersdk.RBACAction{
171+
codersdk.ResourceTemplate: codersdk.RBACResourceActions[codersdk.ResourceTemplate],
172+
}),
173+
})
174+
require.NoError(t, err, "create admin role")
175+
176+
orgTemplateAdmin, _ := coderdtest.CreateAnotherUser(t, ownerClient, secondOrg.ID, rbac.RoleIdentifier{
177+
Name: orgTemplateAdminRole.Name,
178+
OrganizationID: secondOrg.ID,
179+
})
180+
181+
source := clitest.CreateTemplateVersionSource(t, &echo.Responses{
182+
Parse: echo.ParseComplete,
183+
ProvisionApply: echo.ApplyComplete,
184+
})
185+
186+
const templateName = "new-template"
187+
inv, conf := newCLI(t, "templates",
188+
"push", templateName,
189+
"--directory", source,
190+
"--test.provisioner", string(database.ProvisionerTypeEcho),
191+
"-y",
192+
)
193+
194+
clitest.SetupConfig(t, orgTemplateAdmin, conf)
195+
196+
err = inv.Run()
197+
require.NoError(t, err)
198+
199+
ctx = testutil.Context(t, testutil.WaitMedium)
200+
template, err := orgTemplateAdmin.TemplateByName(ctx, secondOrg.ID, templateName)
201+
require.NoError(t, err)
202+
require.Equal(t, template.OrganizationID, secondOrg.ID)
203+
})
137204
}

enterprise/coderd/templates_test.go

+51
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,57 @@ func TestTemplates(t *testing.T) {
717717
_, err = owner.Template(ctx, template.ID)
718718
require.NoError(t, err)
719719
})
720+
721+
// Create a template in a second organization via custom role
722+
t.Run("SecondOrganization", func(t *testing.T) {
723+
t.Parallel()
724+
725+
dv := coderdtest.DeploymentValues(t)
726+
dv.Experiments = []string{string(codersdk.ExperimentCustomRoles)}
727+
ownerClient, _ := coderdenttest.New(t, &coderdenttest.Options{
728+
Options: &coderdtest.Options{
729+
DeploymentValues: dv,
730+
IncludeProvisionerDaemon: false,
731+
},
732+
LicenseOptions: &coderdenttest.LicenseOptions{
733+
Features: license.Features{
734+
codersdk.FeatureAccessControl: 1,
735+
codersdk.FeatureCustomRoles: 1,
736+
codersdk.FeatureExternalProvisionerDaemons: 1,
737+
},
738+
},
739+
})
740+
741+
ctx := testutil.Context(t, testutil.WaitMedium)
742+
secondOrg := coderdtest.CreateOrganization(t, ownerClient, coderdtest.CreateOrganizationOptions{
743+
IncludeProvisionerDaemon: true,
744+
})
745+
746+
//nolint:gocritic // owner required to make custom roles
747+
orgTemplateAdminRole, err := ownerClient.PatchOrganizationRole(ctx, secondOrg.ID, codersdk.Role{
748+
Name: "org-template-admin",
749+
OrganizationID: secondOrg.ID.String(),
750+
OrganizationPermissions: codersdk.CreatePermissions(map[codersdk.RBACResource][]codersdk.RBACAction{
751+
codersdk.ResourceTemplate: codersdk.RBACResourceActions[codersdk.ResourceTemplate],
752+
}),
753+
})
754+
require.NoError(t, err, "create admin role")
755+
756+
orgTemplateAdmin, _ := coderdtest.CreateAnotherUser(t, ownerClient, secondOrg.ID, rbac.RoleIdentifier{
757+
Name: orgTemplateAdminRole.Name,
758+
OrganizationID: secondOrg.ID,
759+
})
760+
761+
version := coderdtest.CreateTemplateVersion(t, orgTemplateAdmin, secondOrg.ID, &echo.Responses{
762+
Parse: echo.ParseComplete,
763+
ProvisionApply: echo.ApplyComplete,
764+
ProvisionPlan: echo.PlanComplete,
765+
})
766+
coderdtest.AwaitTemplateVersionJobCompleted(t, orgTemplateAdmin, version.ID)
767+
768+
template := coderdtest.CreateTemplate(t, orgTemplateAdmin, secondOrg.ID, version.ID)
769+
require.Equal(t, template.OrganizationID, secondOrg.ID)
770+
})
720771
}
721772

722773
func TestTemplateACL(t *testing.T) {

0 commit comments

Comments
 (0)