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

Commite54b6f5

Browse files
committed
fix: template version replacement & metadata updates
1 parentbf81000 commite54b6f5

File tree

3 files changed

+119
-39
lines changed

3 files changed

+119
-39
lines changed

‎docs/resources/template.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Optional:
5454

5555
-`active` (Boolean) Whether this version is the active version of the template. Only one version can be active at a time.
5656
-`message` (String) A message describing the changes in this version of the template. Messages longer than 72 characters will be truncated.
57-
-`name` (String) The name of the template version. Automatically generated if not provided.
57+
-`name` (String) The name of the template version. Automatically generated if not provided. If provided, the name*must* change each time the directory contents are updated.
5858
-`provisioner_tags` (Attributes Set) Provisioner tags for the template version. (see[below for nested schema](#nestedatt--versions--provisioner_tags))
5959
-`tf_vars` (Attributes Set) Terraform variables for the template version. (see[below for nested schema](#nestedatt--versions--tf_vars))
6060

‎internal/provider/template_resource.go‎

Lines changed: 99 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package provider
33
import (
44
"bufio"
55
"context"
6+
"encoding/json"
67
"fmt"
78
"io"
89

@@ -339,7 +340,7 @@ func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaReques
339340
Computed:true,
340341
},
341342
"name": schema.StringAttribute{
342-
MarkdownDescription:"The name of the template version. Automatically generated if not provided.",
343+
MarkdownDescription:"The name of the template version. Automatically generated if not provided. If provided, the name *must* change each time the directory contents are updated.",
343344
Optional:true,
344345
Computed:true,
345346
},
@@ -495,6 +496,11 @@ func (r *TemplateResource) Create(ctx context.Context, req resource.CreateReques
495496
data.ID=UUIDValue(templateResp.ID)
496497
data.DisplayName=types.StringValue(templateResp.DisplayName)
497498

499+
resp.Diagnostics.Append(data.Versions.writePrivateState(ctx,resp.Private)...)
500+
ifresp.Diagnostics.HasError() {
501+
return
502+
}
503+
498504
// Save data into Terraform sutate
499505
resp.Diagnostics.Append(resp.State.Set(ctx,&data)...)
500506
}
@@ -562,11 +568,11 @@ func (r *TemplateResource) Read(ctx context.Context, req resource.ReadRequest, r
562568
}
563569

564570
func (r*TemplateResource)Update(ctx context.Context,req resource.UpdateRequest,resp*resource.UpdateResponse) {
565-
varplanStateTemplateResourceModel
571+
varnewStateTemplateResourceModel
566572
varcurStateTemplateResourceModel
567573

568574
// Read Terraform plan data into the model
569-
resp.Diagnostics.Append(req.Plan.Get(ctx,&planState)...)
575+
resp.Diagnostics.Append(req.Plan.Get(ctx,&newState)...)
570576

571577
ifresp.Diagnostics.HasError() {
572578
return
@@ -578,25 +584,25 @@ func (r *TemplateResource) Update(ctx context.Context, req resource.UpdateReques
578584
return
579585
}
580586

581-
ifplanState.OrganizationID.IsUnknown() {
582-
planState.OrganizationID=UUIDValue(r.data.DefaultOrganizationID)
587+
ifnewState.OrganizationID.IsUnknown() {
588+
newState.OrganizationID=UUIDValue(r.data.DefaultOrganizationID)
583589
}
584590

585-
ifplanState.DisplayName.IsUnknown() {
586-
planState.DisplayName=planState.Name
591+
ifnewState.DisplayName.IsUnknown() {
592+
newState.DisplayName=newState.Name
587593
}
588594

589-
orgID:=planState.OrganizationID.ValueUUID()
595+
orgID:=newState.OrganizationID.ValueUUID()
590596

591-
templateID:=planState.ID.ValueUUID()
597+
templateID:=newState.ID.ValueUUID()
592598

593599
client:=r.data.Client
594600

595-
templateMetadataChanged:=!planState.EqualTemplateMetadata(curState)
601+
templateMetadataChanged:=!newState.EqualTemplateMetadata(curState)
596602
// This is required, as the API will reject no-diff updates.
597603
iftemplateMetadataChanged {
598604
tflog.Trace(ctx,"change in template metadata detected, updating.")
599-
updateReq:=planState.toUpdateRequest(ctx,resp)
605+
updateReq:=newState.toUpdateRequest(ctx,resp)
600606
ifresp.Diagnostics.HasError() {
601607
return
602608
}
@@ -611,9 +617,9 @@ func (r *TemplateResource) Update(ctx context.Context, req resource.UpdateReques
611617

612618
// Since the everyone group always gets deleted by `DisableEveryoneGroupAccess`, we need to run this even if there
613619
// were no ACL changes but the template metadata was updated.
614-
if!planState.ACL.IsNull()&& (!curState.ACL.Equal(planState.ACL)||templateMetadataChanged) {
620+
if!newState.ACL.IsNull()&& (!curState.ACL.Equal(newState.ACL)||templateMetadataChanged) {
615621
varaclACL
616-
resp.Diagnostics.Append(planState.ACL.As(ctx,&acl, basetypes.ObjectAsOptions{})...)
622+
resp.Diagnostics.Append(newState.ACL.As(ctx,&acl, basetypes.ObjectAsOptions{})...)
617623
ifresp.Diagnostics.HasError() {
618624
return
619625
}
@@ -625,51 +631,62 @@ func (r *TemplateResource) Update(ctx context.Context, req resource.UpdateReques
625631
tflog.Trace(ctx,"successfully updated template ACL")
626632
}
627633

628-
foridx,plannedVersion:=rangeplanState.Versions {
629-
varcurVersionID uuid.UUID
630-
// All versions in the state are guaranteed to have known IDs
631-
foundVersion:=curState.Versions.ByID(plannedVersion.ID)
632-
// If the version is new, or if the directory hash has changed, create a new version
633-
iffoundVersion==nil||foundVersion.DirectoryHash!=plannedVersion.DirectoryHash {
634+
// Populate version IDs, based off previously created template versions stored in private state.
635+
resp.Diagnostics.Append(readPrivateState(ctx,newState.Versions,req.Private)...)
636+
ifresp.Diagnostics.HasError() {
637+
return
638+
}
639+
foridx:=rangenewState.Versions {
640+
ifnewState.Versions[idx].ID.IsUnknown() {
634641
tflog.Trace(ctx,"discovered a new or modified template version")
635-
versionResp,err:=newVersion(ctx,client,newVersionRequest{
636-
Version:&plannedVersion,
642+
uploadResp,err:=newVersion(ctx,client,newVersionRequest{
643+
Version:&newState.Versions[idx],
637644
OrganizationID:orgID,
638645
TemplateID:&templateID,
639646
})
640647
iferr!=nil {
641648
resp.Diagnostics.AddError("Client Error",err.Error())
642649
return
643650
}
644-
curVersionID=versionResp.ID
651+
versionResp,err:=client.TemplateVersion(ctx,uploadResp.ID)
652+
iferr!=nil {
653+
resp.Diagnostics.AddError("Client Error",fmt.Sprintf("Failed to get template version: %s",err))
654+
return
655+
}
656+
newState.Versions[idx].ID=UUIDValue(versionResp.ID)
645657
}else {
646-
// Or if it's an existing version, get the ID
647-
curVersionID=plannedVersion.ID.ValueUUID()
648-
}
649-
versionResp,err:=client.TemplateVersion(ctx,curVersionID)
650-
iferr!=nil {
651-
resp.Diagnostics.AddError("Client Error",fmt.Sprintf("Failed to get template version: %s",err))
652-
return
658+
_,err:=client.UpdateTemplateVersion(ctx,newState.Versions[idx].ID.ValueUUID(), codersdk.PatchTemplateVersionRequest{
659+
Name:newState.Versions[idx].Name.ValueString(),
660+
Message:newState.Versions[idx].Message.ValueStringPointer(),
661+
})
662+
iferr!=nil {
663+
resp.Diagnostics.AddError("Client Error",fmt.Sprintf("Failed to update template version metadata: %s",err))
664+
return
665+
}
653666
}
654-
ifplannedVersion.Active.ValueBool() {
667+
ifnewState.Versions[idx].Active.ValueBool() {
655668
tflog.Trace(ctx,"marking template version as active",map[string]any{
656-
"version_id":versionResp.ID,
657-
"template_id":templateID,
669+
"version_id":newState.Versions[idx].ID.ValueString(),
670+
"template_id":templateID.String(),
658671
})
659672
err:=client.UpdateActiveTemplateVersion(ctx,templateID, codersdk.UpdateActiveTemplateVersion{
660-
ID:versionResp.ID,
673+
ID:newState.Versions[idx].ID.ValueUUID(),
661674
})
662675
iferr!=nil {
663676
resp.Diagnostics.AddError("Client Error",fmt.Sprintf("Failed to update active template version: %s",err))
664677
return
665678
}
666679
tflog.Trace(ctx,"marked template version as active")
667680
}
668-
planState.Versions[idx].ID=UUIDValue(versionResp.ID)
681+
}
682+
683+
resp.Diagnostics.Append(newState.Versions.writePrivateState(ctx,resp.Private)...)
684+
ifresp.Diagnostics.HasError() {
685+
return
669686
}
670687

671688
// Save updated data into Terraform state
672-
resp.Diagnostics.Append(resp.State.Set(ctx,&planState)...)
689+
resp.Diagnostics.Append(resp.State.Set(ctx,&newState)...)
673690
}
674691

675692
func (r*TemplateResource)Delete(ctx context.Context,req resource.DeleteRequest,resp*resource.DeleteResponse) {
@@ -1053,3 +1070,50 @@ func (r *TemplateResourceModel) toCreateRequest(ctx context.Context, resp *resou
10531070
DisableEveryoneGroupAccess:!r.ACL.IsNull(),
10541071
}
10551072
}
1073+
1074+
typePreviousTemplateVersionstruct {
1075+
ID uuid.UUID`json:"id"`
1076+
Namestring`json:"name"`
1077+
}
1078+
1079+
typeprivateStateinterface {
1080+
GetKey(ctx context.Context,keystring) ([]byte, diag.Diagnostics)
1081+
SetKey(ctx context.Context,keystring,value []byte) diag.Diagnostics
1082+
}
1083+
1084+
func (vVersions)writePrivateState(ctx context.Context,psprivateState) (diags diag.Diagnostics) {
1085+
for_,version:=rangev {
1086+
prevBytes,err:=json.Marshal(PreviousTemplateVersion{ID:version.ID.ValueUUID(),Name:version.Name.ValueString()})
1087+
iferr!=nil {
1088+
diags.AddError("Client Error",fmt.Sprintf("Failed to marshal name to json bytes: %s",err))
1089+
returndiags
1090+
}
1091+
diag:=ps.SetKey(ctx,version.DirectoryHash.ValueString(),prevBytes)
1092+
ifdiag.HasError() {
1093+
returndiag
1094+
}
1095+
}
1096+
returndiags
1097+
}
1098+
1099+
funcreadPrivateState(ctx context.Context,vVersions,psprivateState) (diags diag.Diagnostics) {
1100+
foridx,version:=rangev {
1101+
jsonBytes,diag:=ps.GetKey(ctx,version.DirectoryHash.ValueString())
1102+
ifdiag.HasError() {
1103+
returndiag
1104+
}
1105+
// If not in state, create it
1106+
ifjsonBytes==nil {
1107+
continue
1108+
}
1109+
varprevPreviousTemplateVersion
1110+
err:=json.Unmarshal(jsonBytes,&prev)
1111+
iferr!=nil {
1112+
diags.AddError("Client Error",fmt.Sprintf("Failed to unmarshal name from json bytes: %s",err))
1113+
returndiags
1114+
}
1115+
// Otherwise, use the existing ID
1116+
v[idx].ID=UUIDValue(prev.ID)
1117+
}
1118+
return
1119+
}

‎internal/provider/template_resource_test.go‎

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ func TestAccTemplateResource(t *testing.T) {
2929
Name:PtrTo("example-template"),
3030
Versions: []testAccTemplateVersionConfig{
3131
{
32-
Name:PtrTo("main"),
32+
// Auto-generated version name
3333
Directory:PtrTo("../../integration/template-test/example-template/"),
3434
Active:PtrTo(true),
3535
// TODO(ethanndickson): Remove this when we add in `*.tfvars` parsing
@@ -123,8 +123,8 @@ func TestAccTemplateResource(t *testing.T) {
123123
resource.TestCheckResourceAttr("coderd_template.test","time_til_dormant_autodelete","0"),
124124
resource.TestCheckResourceAttr("coderd_template.test","require_active_version","false"),
125125
resource.TestMatchTypeSetElemNestedAttrs("coderd_template.test","versions.*",map[string]*regexp.Regexp{
126-
"name":regexp.MustCompile("main"),
127-
"id":regexp.MustCompile(".*"),
126+
"name":regexp.MustCompile(".+"),
127+
"id":regexp.MustCompile(".+"),
128128
"directory_hash":regexp.MustCompile(".+"),
129129
"message":regexp.MustCompile(""),
130130
}),
@@ -137,6 +137,7 @@ func TestAccTemplateResource(t *testing.T) {
137137
ImportState:true,
138138
ImportStateVerify:true,
139139
// In the real world, `versions` needs to be added to the configuration after importing
140+
// We can't import ACL as we can't currently differentiate between managed and unmanaged ACL
140141
ImportStateVerifyIgnore: []string{"versions","acl"},
141142
},
142143
// Update existing version & metadata
@@ -214,6 +215,21 @@ func TestAccTemplateResource(t *testing.T) {
214215
resource.TestCheckNoResourceAttr("coderd_template.test","acl"),
215216
),
216217
},
218+
// Check orphaned versions
219+
{
220+
Config:cfg7.String(t),
221+
PreConfig:func() {
222+
templates,err:=client.Templates(ctx)
223+
require.NoError(t,err)
224+
require.Len(t,templates,1)
225+
versions,err:=client.TemplateVersionsByTemplate(ctx, codersdk.TemplateVersionsByTemplateRequest{
226+
TemplateID:templates[0].ID,
227+
})
228+
require.NoError(t,err)
229+
require.Len(t,versions,2)
230+
},
231+
},
232+
// Resource deleted
217233
},
218234
})
219235
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp