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

Commitafe3400

Browse files
committed
chore: allow pushing only inactive coderd_template versions
1 parent19fbf75 commitafe3400

File tree

5 files changed

+382
-43
lines changed

5 files changed

+382
-43
lines changed

‎docs/resources/template.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ resource "coderd_template" "ubuntu-main" {
7777
-`description` (String) A description of the template.
7878
-`display_name` (String) The display name of the template. Defaults to the template name.
7979
-`failure_ttl_ms` (Number) (Enterprise) The max lifetime before Coder stops all resources for failed workspaces created from this template, in milliseconds.
80-
-`icon` (String) Relative path or external URL thatspecifes an icon to be displayed in the dashboard.
80+
-`icon` (String) Relative path or external URL thatspecifies an icon to be displayed in the dashboard.
8181
-`max_port_share_level` (String) (Enterprise) The maximum port share level for workspaces created from this template. Defaults to`owner` on an Enterprise deployment, or`public` otherwise.
8282
-`organization_id` (String) The ID of the organization. Defaults to the provider's default organization
8383
-`require_active_version` (Boolean) (Enterprise) Whether workspaces must be created from the active version of this template. Defaults to false.

‎integration/integration.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,10 @@ func StartCoder(ctx context.Context, t *testing.T, name string, useLicense bool)
5151
ctr,err:=cli.ContainerCreate(ctx,&container.Config{
5252
Image:coderImg+":"+coderVersion,
5353
Env: []string{
54-
"CODER_HTTP_ADDRESS=0.0.0.0:3000",// Listen on all interfaces inside the container
55-
"CODER_ACCESS_URL=http://localhost:3000",// Set explicitly to avoid creating try.coder.app URLs.
56-
"CODER_TELEMETRY_ENABLE=false",// Avoid creating noise.
54+
"CODER_HTTP_ADDRESS=0.0.0.0:3000",// Listen on all interfaces inside the container
55+
"CODER_ACCESS_URL=http://localhost:3000",// Set explicitly to avoid creating try.coder.app URLs.
56+
"CODER_TELEMETRY_ENABLE=false",// Avoid creating noise.
57+
"CODER_DANGEROUS_DISABLE_RATE_LIMITS=true",// Avoid hitting file rate limit in tests.
5758
},
5859
Labels:map[string]string{},
5960
ExposedPorts:map[nat.Port]struct{}{nat.Port("3000/tcp"): {}},

‎integration/integration_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ func TestIntegration(t *testing.T) {
166166
tfCmd.Stderr=&buf
167167
tt.preF(t,client)
168168
iferr:=tfCmd.Run();!assert.NoError(t,err) {
169-
t.Logf("%s",buf.String())
169+
t.Log(buf.String())
170170
t.FailNow()
171171
}
172172
tt.assertF(t,client)

‎internal/provider/template_resource.go

Lines changed: 93 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaReques
286286
},
287287
},
288288
"icon": schema.StringAttribute{
289-
MarkdownDescription:"Relative path or external URL thatspecifes an icon to be displayed in the dashboard.",
289+
MarkdownDescription:"Relative path or external URL thatspecifies an icon to be displayed in the dashboard.",
290290
Optional:true,
291291
Computed:true,
292292
Default:stringdefault.StaticString(""),
@@ -404,7 +404,7 @@ func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaReques
404404
Required:true,
405405
Validators: []validator.List{
406406
listvalidator.SizeAtLeast(1),
407-
NewActiveVersionValidator(),
407+
NewVersionsValidator(),
408408
},
409409
NestedObject: schema.NestedAttributeObject{
410410
Attributes:map[string]schema.Attribute{
@@ -867,24 +867,24 @@ func (r *TemplateResource) ConfigValidators(context.Context) []resource.ConfigVa
867867
return []resource.ConfigValidator{}
868868
}
869869

870-
typeactiveVersionValidatorstruct{}
870+
typeversionsValidatorstruct{}
871871

872-
funcNewActiveVersionValidator() validator.List {
873-
return&activeVersionValidator{}
872+
funcNewVersionsValidator() validator.List {
873+
return&versionsValidator{}
874874
}
875875

876876
// Description implements validator.List.
877-
func (a*activeVersionValidator)Description(ctx context.Context)string {
877+
func (a*versionsValidator)Description(ctx context.Context)string {
878878
returna.MarkdownDescription(ctx)
879879
}
880880

881881
// MarkdownDescription implements validator.List.
882-
func (a*activeVersionValidator)MarkdownDescription(context.Context)string {
883-
return"Validate thatexactly onetemplate versionhas active set to true."
882+
func (a*versionsValidator)MarkdownDescription(context.Context)string {
883+
return"Validate that template versionnames are unique and that at most one version is active."
884884
}
885885

886886
// ValidateList implements validator.List.
887-
func (a*activeVersionValidator)ValidateList(ctx context.Context,req validator.ListRequest,resp*validator.ListResponse) {
887+
func (a*versionsValidator)ValidateList(ctx context.Context,req validator.ListRequest,resp*validator.ListResponse) {
888888
ifreq.ConfigValue.IsNull()||req.ConfigValue.IsUnknown() {
889889
return
890890
}
@@ -908,13 +908,13 @@ func (a *activeVersionValidator) ValidateList(ctx context.Context, req validator
908908
uniqueNames[version.Name.ValueString()]=struct{}{}
909909
}
910910

911-
//Check if only oneitem in Version hasactive set to true
911+
//Ensure at most oneversion isactive
912912
active:=false
913913
for_,version:=rangedata {
914-
// `active`is required, so if it's null or unknown, this is Terraform
914+
// `active`defaults to false, so if it's null or unknown, this is Terraform
915915
// requesting an early validation.
916916
ifversion.Active.IsNull()||version.Active.IsUnknown() {
917-
return
917+
continue
918918
}
919919
ifversion.Active.ValueBool() {
920920
ifactive {
@@ -924,12 +924,9 @@ func (a *activeVersionValidator) ValidateList(ctx context.Context, req validator
924924
active=true
925925
}
926926
}
927-
if!active {
928-
resp.Diagnostics.AddError("Client Error","At least one template version must be active.")
929-
}
930927
}
931928

932-
var_ validator.List=&activeVersionValidator{}
929+
var_ validator.List=&versionsValidator{}
933930

934931
typeversionsPlanModifierstruct{}
935932

@@ -956,6 +953,12 @@ func (d *versionsPlanModifier) PlanModifyList(ctx context.Context, req planmodif
956953
return
957954
}
958955

956+
hasActiveVersion,diag:=hasOneActiveVersion(configVersions)
957+
ifdiag.HasError() {
958+
resp.Diagnostics.Append(diag...)
959+
return
960+
}
961+
959962
fori:=rangeplanVersions {
960963
hash,err:=computeDirectoryHash(planVersions[i].Directory.ValueString())
961964
iferr!=nil {
@@ -974,6 +977,13 @@ func (d *versionsPlanModifier) PlanModifyList(ctx context.Context, req planmodif
974977
// If this is the first read, init the private state value
975978
iflvBytes==nil {
976979
lv=make(LastVersionsByHash)
980+
// If there's no prior private state, this might be resource creation,
981+
// in which case one version must be active.
982+
if!hasActiveVersion {
983+
resp.Diagnostics.AddError("Client Error","At least one template version must be active when creating a"+
984+
" `coderd_template` resource.\n(Subsequent resource updates can be made without an active template in the list).")
985+
return
986+
}
977987
}else {
978988
err:=json.Unmarshal(lvBytes,&lv)
979989
iferr!=nil {
@@ -982,9 +992,37 @@ func (d *versionsPlanModifier) PlanModifyList(ctx context.Context, req planmodif
982992
}
983993
}
984994

985-
planVersions.reconcileVersionIDs(lv,configVersions)
995+
diag=planVersions.reconcileVersionIDs(lv,configVersions,hasActiveVersion)
996+
ifdiag.HasError() {
997+
resp.Diagnostics.Append(diag...)
998+
return
999+
}
1000+
1001+
resp.PlanValue,diag=types.ListValueFrom(ctx,req.PlanValue.ElementType(ctx),planVersions)
1002+
ifdiag.HasError() {
1003+
resp.Diagnostics.Append(diag...)
1004+
}
1005+
}
9861006

987-
resp.PlanValue,resp.Diagnostics=types.ListValueFrom(ctx,req.PlanValue.ElementType(ctx),planVersions)
1007+
funchasOneActiveVersion(dataVersions) (hasActiveVersionbool,diags diag.Diagnostics) {
1008+
active:=false
1009+
for_,version:=rangedata {
1010+
ifversion.Active.IsNull()||version.Active.IsUnknown() {
1011+
// If null or unknown, the value will be defaulted to false
1012+
continue
1013+
}
1014+
ifversion.Active.ValueBool() {
1015+
ifactive {
1016+
diags.AddError("Client Error","Only one template version can be active at a time.")
1017+
return
1018+
}
1019+
active=true
1020+
}
1021+
}
1022+
if!active {
1023+
returnfalse,diags
1024+
}
1025+
returntrue,diags
9881026
}
9891027

9901028
funcNewVersionsPlanModifier() planmodifier.List {
@@ -1309,6 +1347,7 @@ type PreviousTemplateVersion struct {
13091347
ID uuid.UUID`json:"id"`
13101348
Namestring`json:"name"`
13111349
TFVarsmap[string]string`json:"tf_vars"`
1350+
Activebool`json:"active"`
13121351
}
13131352

13141353
typeprivateStateinterface {
@@ -1331,13 +1370,15 @@ func (v Versions) setPrivateState(ctx context.Context, ps privateState) (diags d
13311370
ID:version.ID.ValueUUID(),
13321371
Name:version.Name.ValueString(),
13331372
TFVars:tfVars,
1373+
Active:version.Active.ValueBool(),
13341374
})
13351375
}else {
13361376
lv[version.DirectoryHash.ValueString()]= []PreviousTemplateVersion{
13371377
{
13381378
ID:version.ID.ValueUUID(),
13391379
Name:version.Name.ValueString(),
13401380
TFVars:tfVars,
1381+
Active:version.Active.ValueBool(),
13411382
},
13421383
}
13431384
}
@@ -1350,7 +1391,7 @@ func (v Versions) setPrivateState(ctx context.Context, ps privateState) (diags d
13501391
returnps.SetKey(ctx,LastVersionsKey,lvBytes)
13511392
}
13521393

1353-
func (planVersionsVersions)reconcileVersionIDs(lvLastVersionsByHash,configVersionsVersions) {
1394+
func (planVersionsVersions)reconcileVersionIDs(lvLastVersionsByHash,configVersionsVersions,hasOneActiveVersionbool) (diag diag.Diagnostics) {
13541395
// We remove versions that we've matched from `lv`, so make a copy for
13551396
// resolving tfvar changes at the end.
13561397
fullLv:=make(LastVersionsByHash)
@@ -1420,6 +1461,39 @@ func (planVersions Versions) reconcileVersionIDs(lv LastVersionsByHash, configVe
14201461
}
14211462
}
14221463
}
1464+
1465+
// If a version was deactivated, and no active version was set, we need to
1466+
// return an error to avoid a post-apply plan being non-empty.
1467+
if!hasOneActiveVersion {
1468+
fori:=rangeplanVersions {
1469+
if!planVersions[i].ID.IsUnknown() {
1470+
prevs,ok:=fullLv[planVersions[i].DirectoryHash.ValueString()]
1471+
if!ok {
1472+
continue
1473+
}
1474+
ifversionDeactivated(prevs,&planVersions[i]) {
1475+
diag.AddError("Client Error","Plan could not determine which version should be active.\n"+
1476+
"Either specify an active version or modify the contents of the previously active version before marking it as inactive.")
1477+
returndiag
1478+
}
1479+
}
1480+
}
1481+
}
1482+
returndiag
1483+
}
1484+
1485+
funcversionDeactivated(prevs []PreviousTemplateVersion,planned*TemplateVersion)bool {
1486+
for_,prev:=rangeprevs {
1487+
ifprev.ID==planned.ID.ValueUUID() {
1488+
ifprev.Active&&
1489+
!planned.Active.IsNull()&&
1490+
!planned.Active.IsUnknown()&&
1491+
!planned.Active.ValueBool() {
1492+
returntrue
1493+
}
1494+
}
1495+
}
1496+
returnfalse
14231497
}
14241498

14251499
functfVariablesChanged(prevs []PreviousTemplateVersion,planned*TemplateVersion)bool {

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp