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

Lines changed: 44 additions & 0 deletions
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

Lines changed: 67 additions & 0 deletions
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

Lines changed: 51 additions & 0 deletions
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy