@@ -69,6 +69,7 @@ type TemplateResourceModel struct {
69
69
TimeTilDormantAutoDeleteMillis types.Int64 `tfsdk:"time_til_dormant_autodelete_ms"`
70
70
RequireActiveVersion types.Bool `tfsdk:"require_active_version"`
71
71
DeprecationMessage types.String `tfsdk:"deprecation_message"`
72
+ MaxPortShareLevel types.String `tfsdk:"max_port_share_level"`
72
73
73
74
// If null, we are not managing ACL via Terraform (such as for AGPL).
74
75
ACL types.Object `tfsdk:"acl"`
@@ -92,7 +93,9 @@ func (m *TemplateResourceModel) EqualTemplateMetadata(other *TemplateResourceMod
92
93
m .FailureTTLMillis .Equal (other .FailureTTLMillis ) &&
93
94
m .TimeTilDormantMillis .Equal (other .TimeTilDormantMillis ) &&
94
95
m .TimeTilDormantAutoDeleteMillis .Equal (other .TimeTilDormantAutoDeleteMillis ) &&
95
- m .RequireActiveVersion .Equal (other .RequireActiveVersion )
96
+ m .RequireActiveVersion .Equal (other .RequireActiveVersion ) &&
97
+ m .DeprecationMessage .Equal (other .DeprecationMessage ) &&
98
+ m .MaxPortShareLevel .Equal (other .MaxPortShareLevel )
96
99
}
97
100
98
101
func (m * TemplateResourceModel ) CheckEntitlements (ctx context.Context , features map [codersdk.FeatureName ]codersdk.Feature ) (diags diag.Diagnostics ) {
@@ -110,7 +113,8 @@ func (m *TemplateResourceModel) CheckEntitlements(ctx context.Context, features
110
113
len (m .AutostartPermittedDaysOfWeek .Elements ()) != 7
111
114
requiresActiveVersion := m .RequireActiveVersion .ValueBool ()
112
115
requiresACL := ! m .ACL .IsNull ()
113
- if requiresScheduling || requiresActiveVersion || requiresACL {
116
+ requiresSharedPortsControl := m .MaxPortShareLevel .ValueString () != "" && m .MaxPortShareLevel .ValueString () != string (codersdk .WorkspaceAgentPortShareLevelPublic )
117
+ if requiresScheduling || requiresActiveVersion || requiresACL || requiresSharedPortsControl {
114
118
if requiresScheduling && ! features [codersdk .FeatureAdvancedTemplateScheduling ].Enabled {
115
119
diags .AddError (
116
120
"Feature not enabled" ,
@@ -132,6 +136,13 @@ func (m *TemplateResourceModel) CheckEntitlements(ctx context.Context, features
132
136
)
133
137
return
134
138
}
139
+ if requiresSharedPortsControl && ! features [codersdk .FeatureControlSharedPorts ].Enabled {
140
+ diags .AddError (
141
+ "Feature not enabled" ,
142
+ "Your license is not entitled to use port sharing control, so you cannot set max_port_share_level." ,
143
+ )
144
+ return
145
+ }
135
146
}
136
147
return
137
148
}
@@ -369,6 +380,14 @@ func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaReques
369
380
Computed : true ,
370
381
Default : booldefault .StaticBool (false ),
371
382
},
383
+ "max_port_share_level" : schema.StringAttribute {
384
+ MarkdownDescription : "(Enterprise) The maximum port share level for workspaces created from this template. Defaults to `owner` on an Enterprise deployment, or `public` otherwise." ,
385
+ Optional : true ,
386
+ Computed : true ,
387
+ Validators : []validator.String {
388
+ stringvalidator .OneOfCaseInsensitive (string (codersdk .WorkspaceAgentPortShareLevelAuthenticated ), string (codersdk .WorkspaceAgentPortShareLevelOwner ), string (codersdk .WorkspaceAgentPortShareLevelPublic )),
389
+ },
390
+ },
372
391
"deprecation_message" : schema.StringAttribute {
373
392
MarkdownDescription : "If set, the template will be marked as deprecated with the provided message and users will be blocked from creating new workspaces from it. Does nothing if set when the resource is created." ,
374
393
Optional : true ,
@@ -553,6 +572,23 @@ func (r *TemplateResource) Create(ctx context.Context, req resource.CreateReques
553
572
data .ID = UUIDValue (templateResp .ID )
554
573
data .DisplayName = types .StringValue (templateResp .DisplayName )
555
574
575
+ // TODO: Remove this update call once this provider requires a Coder
576
+ // deployment running `v2.15.0` or later.
577
+ if data .MaxPortShareLevel .IsUnknown () {
578
+ data .MaxPortShareLevel = types .StringValue (string (templateResp .MaxPortShareLevel ))
579
+ } else {
580
+ mpslReq := data .toUpdateRequest (ctx , & resp .Diagnostics )
581
+ if resp .Diagnostics .HasError () {
582
+ return
583
+ }
584
+ mpslResp , err := client .UpdateTemplateMeta (ctx , data .ID .ValueUUID (), * mpslReq )
585
+ if err != nil {
586
+ resp .Diagnostics .AddError ("Client Error" , fmt .Sprintf ("Failed to set max port share level via update: %s" , err ))
587
+ return
588
+ }
589
+ data .MaxPortShareLevel = types .StringValue (string (mpslResp .MaxPortShareLevel ))
590
+ }
591
+
556
592
resp .Diagnostics .Append (data .Versions .setPrivateState (ctx , resp .Private )... )
557
593
if resp .Diagnostics .HasError () {
558
594
return
@@ -591,6 +627,7 @@ func (r *TemplateResource) Read(ctx context.Context, req resource.ReadRequest, r
591
627
resp .Diagnostics .Append (diag ... )
592
628
return
593
629
}
630
+ data .MaxPortShareLevel = types .StringValue (string (template .MaxPortShareLevel ))
594
631
595
632
if ! data .ACL .IsNull () {
596
633
tflog .Info (ctx , "reading template ACL" )
@@ -665,11 +702,16 @@ func (r *TemplateResource) Update(ctx context.Context, req resource.UpdateReques
665
702
666
703
client := r .data .Client
667
704
705
+ // TODO(ethanndickson): Remove this once the provider requires a Coder
706
+ // deployment running `v2.15.0` or later.
707
+ if newState .MaxPortShareLevel .IsUnknown () {
708
+ newState .MaxPortShareLevel = curState .MaxPortShareLevel
709
+ }
668
710
templateMetadataChanged := ! newState .EqualTemplateMetadata (& curState )
669
711
// This is required, as the API will reject no-diff updates.
670
712
if templateMetadataChanged {
671
713
tflog .Info (ctx , "change in template metadata detected, updating." )
672
- updateReq := newState .toUpdateRequest (ctx , resp )
714
+ updateReq := newState .toUpdateRequest (ctx , & resp . Diagnostics )
673
715
if resp .Diagnostics .HasError () {
674
716
return
675
717
}
@@ -758,6 +800,14 @@ func (r *TemplateResource) Update(ctx context.Context, req resource.UpdateReques
758
800
}
759
801
}
760
802
}
803
+ // TODO(ethanndickson): Remove this once the provider requires a Coder
804
+ // deployment running `v2.15.0` or later.
805
+ templateResp , err := client .Template (ctx , templateID )
806
+ if err != nil {
807
+ resp .Diagnostics .AddError ("Client Error" , fmt .Sprintf ("Failed to get template: %s" , err ))
808
+ return
809
+ }
810
+ newState .MaxPortShareLevel = types .StringValue (string (templateResp .MaxPortShareLevel ))
761
811
762
812
resp .Diagnostics .Append (newState .Versions .setPrivateState (ctx , resp .Private )... )
763
813
if resp .Diagnostics .HasError () {
@@ -1147,25 +1197,27 @@ func (r *TemplateResourceModel) readResponse(ctx context.Context, template *code
1147
1197
r .TimeTilDormantAutoDeleteMillis = types .Int64Value (template .TimeTilDormantAutoDeleteMillis )
1148
1198
r .RequireActiveVersion = types .BoolValue (template .RequireActiveVersion )
1149
1199
r .DeprecationMessage = types .StringValue (template .DeprecationMessage )
1200
+ // TODO(ethanndickson): MaxPortShareLevel deliberately omitted, as it can't
1201
+ // be set during a create request, and we call this during `Create`.
1150
1202
return nil
1151
1203
}
1152
1204
1153
- func (r * TemplateResourceModel ) toUpdateRequest (ctx context.Context , resp * resource. UpdateResponse ) * codersdk.UpdateTemplateMeta {
1205
+ func (r * TemplateResourceModel ) toUpdateRequest (ctx context.Context , diag * diag. Diagnostics ) * codersdk.UpdateTemplateMeta {
1154
1206
var days []string
1155
- resp . Diagnostics .Append (
1207
+ diag .Append (
1156
1208
r .AutostartPermittedDaysOfWeek .ElementsAs (ctx , & days , false )... ,
1157
1209
)
1158
- if resp . Diagnostics .HasError () {
1210
+ if diag .HasError () {
1159
1211
return nil
1160
1212
}
1161
1213
autoStart := & codersdk.TemplateAutostartRequirement {
1162
1214
DaysOfWeek : days ,
1163
1215
}
1164
1216
var reqs AutostopRequirement
1165
- resp . Diagnostics .Append (
1217
+ diag .Append (
1166
1218
r .AutostopRequirement .As (ctx , & reqs , basetypes.ObjectAsOptions {})... ,
1167
1219
)
1168
- if resp . Diagnostics .HasError () {
1220
+ if diag .HasError () {
1169
1221
return nil
1170
1222
}
1171
1223
autoStop := & codersdk.TemplateAutostopRequirement {
@@ -1189,6 +1241,7 @@ func (r *TemplateResourceModel) toUpdateRequest(ctx context.Context, resp *resou
1189
1241
TimeTilDormantAutoDeleteMillis : r .TimeTilDormantAutoDeleteMillis .ValueInt64 (),
1190
1242
RequireActiveVersion : r .RequireActiveVersion .ValueBool (),
1191
1243
DeprecationMessage : r .DeprecationMessage .ValueStringPointer (),
1244
+ MaxPortShareLevel : PtrTo (codersdk .WorkspaceAgentPortShareLevel (r .MaxPortShareLevel .ValueString ())),
1192
1245
// If we're managing ACL, we want to delete the everyone group
1193
1246
DisableEveryoneGroupAccess : ! r .ACL .IsNull (),
1194
1247
}
0 commit comments