Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commita5e6e53

Browse files
committed
fix: validate resources against available features before creating
1 parentef38461 commita5e6e53

11 files changed

+241
-14
lines changed

‎docs/resources/group.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
page_title:"coderd_group Resource - coderd"
44
subcategory:""
55
description:|-
6-
A group on the Coder deployment. If you want to have a group resource with unmanaged members, but still want to read the members in Terraform, use the data.coderd_group data source.
6+
A group on the Coder deployment. If you want to have a group resource with unmanaged members, but still want to read the members in Terraform, use the data.coderd_group data source. Requires an Enterprise license.
77
---
88

99
#coderd_group (Resource)
1010

11-
A group on the Coder deployment. If you want to have a group resource with unmanaged members, but still want to read the members in Terraform, use the`data.coderd_group` data source.
11+
A group on the Coder deployment. If you want to have a group resource with unmanaged members, but still want to read the members in Terraform, use the`data.coderd_group` data source. Creating groups requires an Enterprise license.
1212

1313

1414

‎docs/resources/template.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ A Coder template
3333
-`deprecation_message` (String) If set, the template will be marked as deprecated and users will be blocked from creating new workspaces from it.
3434
-`description` (String) A description of the template.
3535
-`display_name` (String) The display name of the template. Defaults to the template name.
36-
-`failure_ttl_ms` (Number) The max lifetime before Coder stops all resources for failed workspaces created from this template, in milliseconds.
36+
-`failure_ttl_ms` (Number) The max lifetime before Coder stops all resources for failed workspaces created from this template, in milliseconds. Requires an enterprise Coder deployment.
3737
-`icon` (String) Relative path or external URL that specifes an icon to be displayed in the dashboard.
3838
-`organization_id` (String) The ID of the organization. Defaults to the provider's default organization
39-
-`require_active_version` (Boolean) Whether workspaces must be created from the active version of this template. Defaults to false.
40-
-`time_til_dormant_autodelete_ms` (Number) The max lifetime before Coder permanently deletes dormant workspaces created from this template.
41-
-`time_til_dormant_ms` (Number) The max lifetime before Coder locks inactive workspaces created from this template, in milliseconds.
39+
-`require_active_version` (Boolean) Whether workspaces must be created from the active version of this template. Defaults to false. Requires an enterprise Coder deployment.
40+
-`time_til_dormant_autodelete_ms` (Number) The max lifetime before Coder permanently deletes dormant workspaces created from this template. Requires an enterprise Coder deployment.
41+
-`time_til_dormant_ms` (Number) The max lifetime before Coder locks inactive workspaces created from this template, in milliseconds. Requires an enterprise Coder deployment.
4242

4343
###Read-Only
4444

‎internal/provider/group_data_source.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,11 @@ func (d *GroupDataSource) Read(ctx context.Context, req datasource.ReadRequest,
168168
return
169169
}
170170

171+
resp.Diagnostics.Append(CheckGroupEntitlements(ctx,d.data.Features)...)
172+
ifresp.Diagnostics.HasError() {
173+
return
174+
}
175+
171176
client:=d.data.Client
172177

173178
ifdata.OrganizationID.IsNull() {

‎internal/provider/group_resource.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"github.com/coder/coder/v2/codersdk"
1111
"github.com/google/uuid"
1212
"github.com/hashicorp/terraform-plugin-framework/attr"
13+
"github.com/hashicorp/terraform-plugin-framework/diag"
1314
"github.com/hashicorp/terraform-plugin-framework/path"
1415
"github.com/hashicorp/terraform-plugin-framework/resource"
1516
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
@@ -46,13 +47,21 @@ type GroupResourceModel struct {
4647
Members types.Set`tfsdk:"members"`
4748
}
4849

50+
funcCheckGroupEntitlements(ctx context.Context,featuresmap[codersdk.FeatureName]codersdk.Feature) (diags diag.Diagnostics) {
51+
if!features[codersdk.FeatureTemplateRBAC].Enabled {
52+
diags.AddError("Feature not enabled","Your license is not entitled to create groups.")
53+
return
54+
}
55+
returnnil
56+
}
57+
4958
func (r*GroupResource)Metadata(ctx context.Context,req resource.MetadataRequest,resp*resource.MetadataResponse) {
5059
resp.TypeName=req.ProviderTypeName+"_group"
5160
}
5261

5362
func (r*GroupResource)Schema(ctx context.Context,req resource.SchemaRequest,resp*resource.SchemaResponse) {
5463
resp.Schema= schema.Schema{
55-
MarkdownDescription:"A group on the Coder deployment. If you want to have a group resource with unmanaged members, but still want to read the members in Terraform, use the `data.coderd_group` data source.",
64+
MarkdownDescription:"A group on the Coder deployment. If you want to have a group resource with unmanaged members, but still want to read the members in Terraform, use the `data.coderd_group` data source. Requires an Enterprise license.",
5665

5766
Attributes:map[string]schema.Attribute{
5867
"id": schema.StringAttribute{
@@ -134,6 +143,11 @@ func (r *GroupResource) Create(ctx context.Context, req resource.CreateRequest,
134143
return
135144
}
136145

146+
resp.Diagnostics.Append(CheckGroupEntitlements(ctx,r.data.Features)...)
147+
ifresp.Diagnostics.HasError() {
148+
return
149+
}
150+
137151
client:=r.data.Client
138152

139153
ifdata.OrganizationID.IsUnknown() {

‎internal/provider/group_resource_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package provider
66
import (
77
"context"
88
"os"
9+
"regexp"
910
"strings"
1011
"testing"
1112
"text/template"
@@ -127,6 +128,39 @@ func TestAccGroupResource(t *testing.T) {
127128
})
128129
}
129130

131+
funcTestAccGroupResourceAGPL(t*testing.T) {
132+
ifos.Getenv("TF_ACC")=="" {
133+
t.Skip("Acceptance tests are disabled.")
134+
}
135+
ctx:=context.Background()
136+
client:=integration.StartCoder(ctx,t,"group_acc_agpl",false)
137+
firstUser,err:=client.User(ctx,codersdk.Me)
138+
require.NoError(t,err)
139+
140+
cfg1:=testAccGroupResourceconfig{
141+
URL:client.URL.String(),
142+
Token:client.SessionToken(),
143+
Name:PtrTo("example-group"),
144+
DisplayName:PtrTo("Example Group"),
145+
AvatarUrl:PtrTo("https://google.com"),
146+
QuotaAllowance:PtrTo(int32(100)),
147+
Members:PtrTo([]string{firstUser.ID.String()}),
148+
}
149+
150+
resource.Test(t, resource.TestCase{
151+
IsUnitTest:true,
152+
PreCheck:func() {testAccPreCheck(t) },
153+
ProtoV6ProviderFactories:testAccProtoV6ProviderFactories,
154+
Steps: []resource.TestStep{
155+
{
156+
Config:cfg1.String(t),
157+
ExpectError:regexp.MustCompile("Your license is not entitled to create groups."),
158+
},
159+
},
160+
})
161+
162+
}
163+
130164
typetestAccGroupResourceconfigstruct {
131165
URLstring
132166
Tokenstring

‎internal/provider/organization_data_source_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ func TestAccOrganizationDataSource(t *testing.T) {
1919
t.Skip("Acceptance tests are disabled.")
2020
}
2121
ctx:=context.Background()
22-
client:=integration.StartCoder(ctx,t,"org_data_acc",true)
22+
client:=integration.StartCoder(ctx,t,"org_data_acc",false)
2323
firstUser,err:=client.User(ctx,codersdk.Me)
2424
require.NoError(t,err)
2525

‎internal/provider/provider.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type CoderdProvider struct {
3434
typeCoderdProviderDatastruct {
3535
Client*codersdk.Client
3636
DefaultOrganizationID uuid.UUID
37+
Featuresmap[codersdk.FeatureName]codersdk.Feature
3738
}
3839

3940
// CoderdProviderModel describes the provider data model.
@@ -111,9 +112,15 @@ func (p *CoderdProvider) Configure(ctx context.Context, req provider.ConfigureRe
111112
}
112113
data.DefaultOrganizationID=UUIDValue(user.OrganizationIDs[0])
113114
}
115+
entitlements,err:=client.Entitlements(ctx)
116+
iferr!=nil {
117+
resp.Diagnostics.AddError("Client Error","failed to get deployment entitlements: "+err.Error())
118+
}
119+
114120
providerData:=&CoderdProviderData{
115121
Client:client,
116122
DefaultOrganizationID:data.DefaultOrganizationID.ValueUUID(),
123+
Features:entitlements.Features,
117124
}
118125
resp.DataSourceData=providerData
119126
resp.ResourceData=providerData

‎internal/provider/template_resource.go

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ type TemplateResourceModel struct {
7575
}
7676

7777
// EqualTemplateMetadata returns true if two templates have identical metadata (excluding ACL).
78-
func (mTemplateResourceModel)EqualTemplateMetadata(otherTemplateResourceModel)bool {
78+
func (m*TemplateResourceModel)EqualTemplateMetadata(other*TemplateResourceModel)bool {
7979
returnm.Name.Equal(other.Name)&&
8080
m.DisplayName.Equal(other.DisplayName)&&
8181
m.Description.Equal(other.Description)&&
@@ -94,6 +94,47 @@ func (m TemplateResourceModel) EqualTemplateMetadata(other TemplateResourceModel
9494
m.RequireActiveVersion.Equal(other.RequireActiveVersion)
9595
}
9696

97+
func (m*TemplateResourceModel)CheckEntitlements(ctx context.Context,featuresmap[codersdk.FeatureName]codersdk.Feature) (diags diag.Diagnostics) {
98+
varautoStopAutostopRequirement
99+
diags.Append(m.AutostopRequirement.As(ctx,&autoStop, basetypes.ObjectAsOptions{})...)
100+
ifdiags.HasError() {
101+
returndiags
102+
}
103+
requiresScheduling:=len(autoStop.DaysOfWeek)>0||
104+
!m.AllowUserAutostart.ValueBool()||
105+
!m.AllowUserAutostop.ValueBool()||
106+
m.FailureTTLMillis.ValueInt64()!=0||
107+
m.TimeTilDormantAutoDeleteMillis.ValueInt64()!=0||
108+
m.TimeTilDormantMillis.ValueInt64()!=0||
109+
len(m.AutostartPermittedDaysOfWeek.Elements())!=7
110+
requiresActiveVersion:=m.RequireActiveVersion.ValueBool()
111+
requiresACL:=!m.ACL.IsNull()
112+
ifrequiresScheduling||requiresActiveVersion||requiresACL {
113+
ifrequiresScheduling&&!features[codersdk.FeatureAdvancedTemplateScheduling].Enabled {
114+
diags.AddError(
115+
"Feature not enabled",
116+
"Your license is not entitled to use advanced template scheduling, so you cannot modify any of the following fields from their defaults: auto_stop_requirement, auto_start_permitted_days_of_week, allow_user_auto_start, allow_user_auto_stop, failure_ttl_ms, time_til_dormant_ms, time_til_dormant_autodelete_ms.",
117+
)
118+
return
119+
}
120+
ifrequiresActiveVersion&&!features[codersdk.FeatureAccessControl].Enabled {
121+
diags.AddError(
122+
"Feature not enabled",
123+
"Your license is not entitled to use access control, so you cannot set require_active_version.",
124+
)
125+
return
126+
}
127+
ifrequiresACL&&!features[codersdk.FeatureTemplateRBAC].Enabled {
128+
diags.AddError(
129+
"Feature not enabled",
130+
"Your license is not entitled to use template access control, so you cannot set acl.",
131+
)
132+
return
133+
}
134+
}
135+
return
136+
}
137+
97138
typeTemplateVersionstruct {
98139
IDUUID`tfsdk:"id"`
99140
Name types.String`tfsdk:"name"`
@@ -297,25 +338,25 @@ func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaReques
297338
Default:booldefault.StaticBool(true),
298339
},
299340
"failure_ttl_ms": schema.Int64Attribute{
300-
MarkdownDescription:"The max lifetime before Coder stops all resources for failed workspaces created from this template, in milliseconds.",
341+
MarkdownDescription:"The max lifetime before Coder stops all resources for failed workspaces created from this template, in milliseconds. Requires an enterprise Coder deployment.",
301342
Optional:true,
302343
Computed:true,
303344
Default:int64default.StaticInt64(0),
304345
},
305346
"time_til_dormant_ms": schema.Int64Attribute{
306-
MarkdownDescription:"The max lifetime before Coder locks inactive workspaces created from this template, in milliseconds.",
347+
MarkdownDescription:"The max lifetime before Coder locks inactive workspaces created from this template, in milliseconds. Requires an enterprise Coder deployment.",
307348
Optional:true,
308349
Computed:true,
309350
Default:int64default.StaticInt64(0),
310351
},
311352
"time_til_dormant_autodelete_ms": schema.Int64Attribute{
312-
MarkdownDescription:"The max lifetime before Coder permanently deletes dormant workspaces created from this template.",
353+
MarkdownDescription:"The max lifetime before Coder permanently deletes dormant workspaces created from this template. Requires an enterprise Coder deployment.",
313354
Optional:true,
314355
Computed:true,
315356
Default:int64default.StaticInt64(0),
316357
},
317358
"require_active_version": schema.BoolAttribute{
318-
MarkdownDescription:"Whether workspaces must be created from the active version of this template. Defaults to false.",
359+
MarkdownDescription:"Whether workspaces must be created from the active version of this template. Defaults to false. Requires an enterprise Coder deployment.",
319360
Optional:true,
320361
Computed:true,
321362
Default:booldefault.StaticBool(false),
@@ -430,6 +471,11 @@ func (r *TemplateResource) Create(ctx context.Context, req resource.CreateReques
430471
data.DisplayName=data.Name
431472
}
432473

474+
resp.Diagnostics.Append(data.CheckEntitlements(ctx,r.data.Features)...)
475+
ifresp.Diagnostics.HasError() {
476+
return
477+
}
478+
433479
client:=r.data.Client
434480
orgID:=data.OrganizationID.ValueUUID()
435481
vartemplateResp codersdk.Template
@@ -601,13 +647,18 @@ func (r *TemplateResource) Update(ctx context.Context, req resource.UpdateReques
601647
newState.DisplayName=newState.Name
602648
}
603649

650+
resp.Diagnostics.Append(newState.CheckEntitlements(ctx,r.data.Features)...)
651+
ifresp.Diagnostics.HasError() {
652+
return
653+
}
654+
604655
orgID:=newState.OrganizationID.ValueUUID()
605656

606657
templateID:=newState.ID.ValueUUID()
607658

608659
client:=r.data.Client
609660

610-
templateMetadataChanged:=!newState.EqualTemplateMetadata(curState)
661+
templateMetadataChanged:=!newState.EqualTemplateMetadata(&curState)
611662
// This is required, as the API will reject no-diff updates.
612663
iftemplateMetadataChanged {
613664
tflog.Trace(ctx,"change in template metadata detected, updating.")

‎internal/provider/template_resource_test.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,87 @@ func TestAccTemplateResource(t *testing.T) {
372372
})
373373
}
374374

375+
funcTestAccTemplateResourceAGPL(t*testing.T) {
376+
ifos.Getenv("TF_ACC")=="" {
377+
t.Skip("Acceptance tests are disabled.")
378+
}
379+
ctx:=context.Background()
380+
client:=integration.StartCoder(ctx,t,"template_acc",false)
381+
firstUser,err:=client.User(ctx,codersdk.Me)
382+
require.NoError(t,err)
383+
384+
cfg1:=testAccTemplateResourceConfig{
385+
URL:client.URL.String(),
386+
Token:client.SessionToken(),
387+
Name:PtrTo("example-template"),
388+
Versions: []testAccTemplateVersionConfig{
389+
{
390+
// Auto-generated version name
391+
Directory:PtrTo("../../integration/template-test/example-template/"),
392+
Active:PtrTo(true),
393+
},
394+
},
395+
AllowUserAutostart:PtrTo(false),
396+
}
397+
398+
cfg2:=cfg1
399+
cfg2.AllowUserAutostart=nil
400+
cfg2.AutostopRequirement.DaysOfWeek=PtrTo([]string{"monday","tuesday"})
401+
402+
cfg3:=cfg2
403+
cfg3.AutostopRequirement.null=true
404+
cfg3.AutostartRequirement=PtrTo([]string{})
405+
406+
cfg4:=cfg3
407+
cfg4.FailureTTL=PtrTo(int64(1))
408+
409+
cfg5:=cfg4
410+
cfg5.FailureTTL=nil
411+
cfg5.AutostartRequirement=nil
412+
cfg5.RequireActiveVersion=PtrTo(true)
413+
414+
cfg6:=cfg5
415+
cfg6.RequireActiveVersion=nil
416+
cfg6.ACL=testAccTemplateACLConfig{
417+
GroupACL: []testAccTemplateKeyValueConfig{
418+
{
419+
Key:PtrTo(firstUser.OrganizationIDs[0].String()),
420+
Value:PtrTo("use"),
421+
},
422+
},
423+
}
424+
425+
for_,cfg:=range []testAccTemplateResourceConfig{cfg1,cfg2,cfg3,cfg4} {
426+
resource.Test(t, resource.TestCase{
427+
PreCheck:func() {testAccPreCheck(t) },
428+
IsUnitTest:true,
429+
ProtoV6ProviderFactories:testAccProtoV6ProviderFactories,
430+
Steps: []resource.TestStep{
431+
{
432+
Config:cfg.String(t),
433+
ExpectError:regexp.MustCompile("Your license is not entitled to use advanced template scheduling"),
434+
},
435+
},
436+
})
437+
}
438+
439+
resource.Test(t, resource.TestCase{
440+
PreCheck:func() {testAccPreCheck(t) },
441+
IsUnitTest:true,
442+
ProtoV6ProviderFactories:testAccProtoV6ProviderFactories,
443+
Steps: []resource.TestStep{
444+
{
445+
Config:cfg5.String(t),
446+
ExpectError:regexp.MustCompile("Your license is not entitled to use access control"),
447+
},
448+
{
449+
Config:cfg6.String(t),
450+
ExpectError:regexp.MustCompile("Your license is not entitled to use template access control"),
451+
},
452+
},
453+
})
454+
}
455+
375456
typetestAccTemplateResourceConfigstruct {
376457
URLstring
377458
Tokenstring

‎internal/provider/workspace_proxy_resource.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ func (r *WorkspaceProxyResource) Create(ctx context.Context, req resource.Create
103103
return
104104
}
105105

106+
if!r.data.Features[codersdk.FeatureWorkspaceProxy].Enabled {
107+
resp.Diagnostics.AddError("Feature not enabled","Your license is not entitled to create workspace proxies.")
108+
return
109+
}
110+
106111
client:=r.data.Client
107112
wsp,err:=client.CreateWorkspaceProxy(ctx, codersdk.CreateWorkspaceProxyRequest{
108113
Name:data.Name.ValueString(),

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp