Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bf558f5

Browse files
fix: create new template version when tfvars change (#98)
Closes #97.
1 parent 09e0394 commit bf558f5

File tree

3 files changed

+319
-77
lines changed

3 files changed

+319
-77
lines changed

docs/resources/template.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ page_title: "coderd_template Resource - terraform-provider-coderd"
44
subcategory: ""
55
description: |-
66
A Coder template.
7-
Logs from building template versions are streamed from the provisioner when the TF_LOG environment variable is INFO or higher.
7+
Logs from building template versions can be optionally streamed from the provisioner by setting the TF_LOG environment variable to INFO or higher.
88
When importing, the ID supplied can be either a template UUID retrieved via the API or <organization-name>/<template-name>.
99
---
1010

1111
# coderd_template (Resource)
1212

1313
A Coder template.
1414

15-
Logs from building template versions are streamed from the provisioner when the `TF_LOG` environment variable is `INFO` or higher.
15+
Logs from building template versions can be optionally streamed from the provisioner by setting the `TF_LOG` environment variable to `INFO` or higher.
1616

1717
When importing, the ID supplied can be either a template UUID retrieved via the API or `<organization-name>/<template-name>`.
1818

@@ -101,7 +101,7 @@ Optional:
101101

102102
- `active` (Boolean) Whether this version is the active version of the template. Only one version can be active at a time.
103103
- `message` (String) A message describing the changes in this version of the template. Messages longer than 72 characters will be truncated.
104-
- `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.
104+
- `name` (String) The name of the template version. Automatically generated if not provided. If provided, the name *must* change each time the directory contents, or the `tf_vars` attribute are updated.
105105
- `provisioner_tags` (Attributes Set) Provisioner tags for the template version. (see [below for nested schema](#nestedatt--versions--provisioner_tags))
106106
- `tf_vars` (Attributes Set) Terraform variables for the template version. (see [below for nested schema](#nestedatt--versions--tf_vars))
107107

internal/provider/template_resource.go

+68-10
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"encoding/json"
77
"fmt"
88
"io"
9+
"slices"
910
"strings"
1011

1112
"cdr.dev/slog"
@@ -230,8 +231,8 @@ func (r *TemplateResource) Metadata(ctx context.Context, req resource.MetadataRe
230231

231232
func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
232233
resp.Schema = schema.Schema{
233-
MarkdownDescription: "A Coder template.\n\nLogs from building template versions are streamed from the provisioner " +
234-
"when the `TF_LOG` environment variable is `INFO` or higher.\n\n" +
234+
MarkdownDescription: "A Coder template.\n\nLogs from building template versions can be optionally streamed from the provisioner " +
235+
"by setting the `TF_LOG` environment variable to `INFO` or higher.\n\n" +
235236
"When importing, the ID supplied can be either a template UUID retrieved via the API or `<organization-name>/<template-name>`.",
236237

237238
Attributes: map[string]schema.Attribute{
@@ -395,7 +396,7 @@ func (r *TemplateResource) Schema(ctx context.Context, req resource.SchemaReques
395396
Computed: true,
396397
},
397398
"name": schema.StringAttribute{
398-
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.",
399+
MarkdownDescription: "The name of the template version. Automatically generated if not provided. If provided, the name *must* change each time the directory contents, or the `tf_vars` attribute are updated.",
399400
Optional: true,
400401
Computed: true,
401402
Validators: []validator.String{
@@ -1058,7 +1059,7 @@ func markActive(ctx context.Context, client *codersdk.Client, templateID uuid.UU
10581059
ID: versionID,
10591060
})
10601061
if err != nil {
1061-
return fmt.Errorf("Failed to update active template version: %s", err)
1062+
return fmt.Errorf("failed to update active template version: %s", err)
10621063
}
10631064
tflog.Info(ctx, "marked template version as active")
10641065
return nil
@@ -1236,8 +1237,9 @@ type LastVersionsByHash = map[string][]PreviousTemplateVersion
12361237
var LastVersionsKey = "last_versions"
12371238

12381239
type PreviousTemplateVersion struct {
1239-
ID uuid.UUID `json:"id"`
1240-
Name string `json:"name"`
1240+
ID uuid.UUID `json:"id"`
1241+
Name string `json:"name"`
1242+
TFVars map[string]string `json:"tf_vars"`
12411243
}
12421244

12431245
type privateState interface {
@@ -1249,18 +1251,24 @@ func (v Versions) setPrivateState(ctx context.Context, ps privateState) (diags d
12491251
lv := make(LastVersionsByHash)
12501252
for _, version := range v {
12511253
vbh, ok := lv[version.DirectoryHash.ValueString()]
1254+
tfVars := make(map[string]string, len(version.TerraformVariables))
1255+
for _, tfVar := range version.TerraformVariables {
1256+
tfVars[tfVar.Name.ValueString()] = tfVar.Value.ValueString()
1257+
}
12521258
// Store the IDs and names of all versions with the same directory hash,
12531259
// in the order they appear
12541260
if ok {
12551261
lv[version.DirectoryHash.ValueString()] = append(vbh, PreviousTemplateVersion{
1256-
ID: version.ID.ValueUUID(),
1257-
Name: version.Name.ValueString(),
1262+
ID: version.ID.ValueUUID(),
1263+
Name: version.Name.ValueString(),
1264+
TFVars: tfVars,
12581265
})
12591266
} else {
12601267
lv[version.DirectoryHash.ValueString()] = []PreviousTemplateVersion{
12611268
{
1262-
ID: version.ID.ValueUUID(),
1263-
Name: version.Name.ValueString(),
1269+
ID: version.ID.ValueUUID(),
1270+
Name: version.Name.ValueString(),
1271+
TFVars: tfVars,
12641272
},
12651273
}
12661274
}
@@ -1274,6 +1282,13 @@ func (v Versions) setPrivateState(ctx context.Context, ps privateState) (diags d
12741282
}
12751283

12761284
func (planVersions Versions) reconcileVersionIDs(lv LastVersionsByHash, configVersions Versions) {
1285+
// We remove versions that we've matched from `lv`, so make a copy for
1286+
// resolving tfvar changes at the end.
1287+
fullLv := make(LastVersionsByHash)
1288+
for k, v := range lv {
1289+
fullLv[k] = slices.Clone(v)
1290+
}
1291+
12771292
for i := range planVersions {
12781293
prevList, ok := lv[planVersions[i].DirectoryHash.ValueString()]
12791294
// If not in state, mark as known after apply since we'll create a new version.
@@ -1313,6 +1328,49 @@ func (planVersions Versions) reconcileVersionIDs(lv LastVersionsByHash, configVe
13131328
lv[planVersions[i].DirectoryHash.ValueString()] = prevList[1:]
13141329
}
13151330
}
1331+
1332+
// If only the Terraform variables have changed,
1333+
// we need to create a new version with the new variables.
1334+
for i := range planVersions {
1335+
if !planVersions[i].ID.IsUnknown() {
1336+
prevs, ok := fullLv[planVersions[i].DirectoryHash.ValueString()]
1337+
if !ok {
1338+
continue
1339+
}
1340+
if tfVariablesChanged(prevs, &planVersions[i]) {
1341+
planVersions[i].ID = NewUUIDUnknown()
1342+
// We could always set the name to unknown here, to generate a
1343+
// random one (this is what the Web UI currently does when
1344+
// only updating tfvars).
1345+
// However, I think it'd be weird if the provider just started
1346+
// ignoring the name you set in the config, we'll instead
1347+
// require that users update the name if they update the tfvars.
1348+
if configVersions[i].Name.IsNull() {
1349+
planVersions[i].Name = types.StringUnknown()
1350+
}
1351+
}
1352+
}
1353+
}
1354+
}
1355+
1356+
func tfVariablesChanged(prevs []PreviousTemplateVersion, planned *TemplateVersion) bool {
1357+
for _, prev := range prevs {
1358+
if prev.ID == planned.ID.ValueUUID() {
1359+
// If the previous version has no TFVars, then it was created using
1360+
// an older provider version.
1361+
if prev.TFVars == nil {
1362+
return true
1363+
}
1364+
for _, tfVar := range planned.TerraformVariables {
1365+
if prev.TFVars[tfVar.Name.ValueString()] != tfVar.Value.ValueString() {
1366+
return true
1367+
}
1368+
}
1369+
return false
1370+
}
1371+
}
1372+
return true
1373+
13161374
}
13171375

13181376
func formatLogs(err error, logs []codersdk.ProvisionerJobLog) string {

0 commit comments

Comments
 (0)