@@ -3,6 +3,7 @@ package provider
3
3
import (
4
4
"bufio"
5
5
"context"
6
+ "encoding/json"
6
7
"fmt"
7
8
"io"
8
9
@@ -339,7 +340,7 @@ func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaReques
339
340
Computed : true ,
340
341
},
341
342
"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. " ,
343
344
Optional : true ,
344
345
Computed : true ,
345
346
},
@@ -495,6 +496,10 @@ func (r *TemplateResource) Create(ctx context.Context, req resource.CreateReques
495
496
data .ID = UUIDValue (templateResp .ID )
496
497
data .DisplayName = types .StringValue (templateResp .DisplayName )
497
498
499
+ resp .Diagnostics .Append (data .Versions .writePrivateState (func (key string , value []byte ) diag.Diagnostics {
500
+ return resp .Private .SetKey (ctx , key , value )
501
+ })... )
502
+
498
503
// Save data into Terraform sutate
499
504
resp .Diagnostics .Append (resp .State .Set (ctx , & data )... )
500
505
}
@@ -562,11 +567,11 @@ func (r *TemplateResource) Read(ctx context.Context, req resource.ReadRequest, r
562
567
}
563
568
564
569
func (r * TemplateResource ) Update (ctx context.Context , req resource.UpdateRequest , resp * resource.UpdateResponse ) {
565
- var planState TemplateResourceModel
570
+ var newState TemplateResourceModel
566
571
var curState TemplateResourceModel
567
572
568
573
// Read Terraform plan data into the model
569
- resp .Diagnostics .Append (req .Plan .Get (ctx , & planState )... )
574
+ resp .Diagnostics .Append (req .Plan .Get (ctx , & newState )... )
570
575
571
576
if resp .Diagnostics .HasError () {
572
577
return
@@ -578,25 +583,25 @@ func (r *TemplateResource) Update(ctx context.Context, req resource.UpdateReques
578
583
return
579
584
}
580
585
581
- if planState .OrganizationID .IsUnknown () {
582
- planState .OrganizationID = UUIDValue (r .data .DefaultOrganizationID )
586
+ if newState .OrganizationID .IsUnknown () {
587
+ newState .OrganizationID = UUIDValue (r .data .DefaultOrganizationID )
583
588
}
584
589
585
- if planState .DisplayName .IsUnknown () {
586
- planState .DisplayName = planState .Name
590
+ if newState .DisplayName .IsUnknown () {
591
+ newState .DisplayName = newState .Name
587
592
}
588
593
589
- orgID := planState .OrganizationID .ValueUUID ()
594
+ orgID := newState .OrganizationID .ValueUUID ()
590
595
591
- templateID := planState .ID .ValueUUID ()
596
+ templateID := newState .ID .ValueUUID ()
592
597
593
598
client := r .data .Client
594
599
595
- templateMetadataChanged := ! planState .EqualTemplateMetadata (curState )
600
+ templateMetadataChanged := ! newState .EqualTemplateMetadata (curState )
596
601
// This is required, as the API will reject no-diff updates.
597
602
if templateMetadataChanged {
598
603
tflog .Trace (ctx , "change in template metadata detected, updating." )
599
- updateReq := planState .toUpdateRequest (ctx , resp )
604
+ updateReq := newState .toUpdateRequest (ctx , resp )
600
605
if resp .Diagnostics .HasError () {
601
606
return
602
607
}
@@ -611,9 +616,9 @@ func (r *TemplateResource) Update(ctx context.Context, req resource.UpdateReques
611
616
612
617
// Since the everyone group always gets deleted by `DisableEveryoneGroupAccess`, we need to run this even if there
613
618
// were no ACL changes but the template metadata was updated.
614
- if ! planState .ACL .IsNull () && (! curState .ACL .Equal (planState .ACL ) || templateMetadataChanged ) {
619
+ if ! newState .ACL .IsNull () && (! curState .ACL .Equal (newState .ACL ) || templateMetadataChanged ) {
615
620
var acl ACL
616
- resp .Diagnostics .Append (planState .ACL .As (ctx , & acl , basetypes.ObjectAsOptions {})... )
621
+ resp .Diagnostics .Append (newState .ACL .As (ctx , & acl , basetypes.ObjectAsOptions {})... )
617
622
if resp .Diagnostics .HasError () {
618
623
return
619
624
}
@@ -625,51 +630,64 @@ func (r *TemplateResource) Update(ctx context.Context, req resource.UpdateReques
625
630
tflog .Trace (ctx , "successfully updated template ACL" )
626
631
}
627
632
628
- for idx , plannedVersion := range planState .Versions {
629
- var curVersionID 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
- if foundVersion == nil || foundVersion .DirectoryHash != plannedVersion .DirectoryHash {
633
+ // Populate version IDs, based off what we created last `apply`
634
+ diags := readPrivateState (newState .Versions , func (key string ) ([]byte , diag.Diagnostics ) {
635
+ return req .Private .GetKey (ctx , key )
636
+ })
637
+ if diags .HasError () {
638
+ resp .Diagnostics .Append (diags ... )
639
+ return
640
+ }
641
+ for idx := range newState .Versions {
642
+ if newState .Versions [idx ].ID .IsUnknown () {
634
643
tflog .Trace (ctx , "discovered a new or modified template version" )
635
- versionResp , err := newVersion (ctx , client , newVersionRequest {
636
- Version : & plannedVersion ,
644
+ uploadResp , err := newVersion (ctx , client , newVersionRequest {
645
+ Version : & newState . Versions [ idx ] ,
637
646
OrganizationID : orgID ,
638
647
TemplateID : & templateID ,
639
648
})
640
649
if err != nil {
641
650
resp .Diagnostics .AddError ("Client Error" , err .Error ())
642
651
return
643
652
}
644
- curVersionID = versionResp .ID
653
+ versionResp , err := client .TemplateVersion (ctx , uploadResp .ID )
654
+ if err != nil {
655
+ resp .Diagnostics .AddError ("Client Error" , fmt .Sprintf ("Failed to get template version: %s" , err ))
656
+ return
657
+ }
658
+ newState .Versions [idx ].ID = UUIDValue (versionResp .ID )
645
659
} 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
- if err != nil {
651
- resp .Diagnostics .AddError ("Client Error" , fmt .Sprintf ("Failed to get template version: %s" , err ))
652
- return
660
+ _ , err := client .UpdateTemplateVersion (ctx , newState .Versions [idx ].ID .ValueUUID (), codersdk.PatchTemplateVersionRequest {
661
+ Name : newState .Versions [idx ].Name .ValueString (),
662
+ Message : newState .Versions [idx ].Message .ValueStringPointer (),
663
+ })
664
+ if err != nil {
665
+ resp .Diagnostics .AddError ("Client Error" , fmt .Sprintf ("Failed to update template version metadata: %s" , err ))
666
+ return
667
+ }
653
668
}
654
- if plannedVersion .Active .ValueBool () {
669
+ if newState . Versions [ idx ] .Active .ValueBool () {
655
670
tflog .Trace (ctx , "marking template version as active" , map [string ]any {
656
- "version_id" : versionResp . ID ,
657
- "template_id" : templateID ,
671
+ "version_id" : newState . Versions [ idx ]. ID . ValueString () ,
672
+ "template_id" : templateID . String () ,
658
673
})
659
674
err := client .UpdateActiveTemplateVersion (ctx , templateID , codersdk.UpdateActiveTemplateVersion {
660
- ID : versionResp . ID ,
675
+ ID : newState . Versions [ idx ]. ID . ValueUUID () ,
661
676
})
662
677
if err != nil {
663
678
resp .Diagnostics .AddError ("Client Error" , fmt .Sprintf ("Failed to update active template version: %s" , err ))
664
679
return
665
680
}
666
681
tflog .Trace (ctx , "marked template version as active" )
667
682
}
668
- planState .Versions [idx ].ID = UUIDValue (versionResp .ID )
669
683
}
670
684
685
+ resp .Diagnostics .Append (newState .Versions .writePrivateState (func (key string , value []byte ) diag.Diagnostics {
686
+ return resp .Private .SetKey (ctx , key , value )
687
+ })... )
688
+
671
689
// Save updated data into Terraform state
672
- resp .Diagnostics .Append (resp .State .Set (ctx , & planState )... )
690
+ resp .Diagnostics .Append (resp .State .Set (ctx , & newState )... )
673
691
}
674
692
675
693
func (r * TemplateResource ) Delete (ctx context.Context , req resource.DeleteRequest , resp * resource.DeleteResponse ) {
@@ -1053,3 +1071,53 @@ func (r *TemplateResourceModel) toCreateRequest(ctx context.Context, resp *resou
1053
1071
DisableEveryoneGroupAccess : ! r .ACL .IsNull (),
1054
1072
}
1055
1073
}
1074
+
1075
+ type PreviousTemplateVersion struct {
1076
+ ID uuid.UUID `json:"id"`
1077
+ Name string `json:"name"`
1078
+ }
1079
+
1080
+ func (v Versions ) writePrivateState (privateStateWriter func (key string , value []byte ) diag.Diagnostics ) (diags diag.Diagnostics ) {
1081
+ for _ , version := range v {
1082
+ prevBytes , err := json .Marshal (PreviousTemplateVersion {ID : version .ID .ValueUUID (), Name : version .Name .ValueString ()})
1083
+ if err != nil {
1084
+ diags .AddError ("Client Error" , fmt .Sprintf ("Failed to marshal name to json bytes: %s" , err ))
1085
+ return diags
1086
+ }
1087
+ diag := privateStateWriter (version .DirectoryHash .ValueString (), prevBytes )
1088
+ if diag .HasError () {
1089
+ return diag
1090
+ }
1091
+ }
1092
+ return diags
1093
+ }
1094
+
1095
+ func readPrivateState (v Versions , privateStateReader func (key string ) ([]byte , diag.Diagnostics )) (diags diag.Diagnostics ) {
1096
+ for idx , version := range v {
1097
+ jsonBytes , diag := privateStateReader (version .DirectoryHash .ValueString ())
1098
+ if diag .HasError () {
1099
+ return diag
1100
+ }
1101
+ // If not in state, create it
1102
+ if jsonBytes == nil {
1103
+ continue
1104
+ }
1105
+ var prev PreviousTemplateVersion
1106
+ err := json .Unmarshal (jsonBytes , & prev )
1107
+ if err != nil {
1108
+ diags .AddError ("Client Error" , fmt .Sprintf ("Failed to unmarshal name from json bytes: %s" , err ))
1109
+ return diags
1110
+ }
1111
+ // If in the state, but with a different name, create it
1112
+ if prev .Name != version .Name .ValueString () {
1113
+ continue
1114
+ }
1115
+ // If in the state, but with no name, create it
1116
+ if prev .Name == "" {
1117
+ continue
1118
+ }
1119
+ // Otherwise, use the ID from last time
1120
+ v [idx ].ID = UUIDValue (prev .ID )
1121
+ }
1122
+ return
1123
+ }
0 commit comments