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

Skip to content

Commit b704ee6

Browse files
committed
fix: create new template version when tfvars change
1 parent ee9ac22 commit b704ee6

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{
@@ -1053,7 +1054,7 @@ func markActive(ctx context.Context, client *codersdk.Client, templateID uuid.UU
10531054
ID: versionID,
10541055
})
10551056
if err != nil {
1056-
return fmt.Errorf("Failed to update active template version: %s", err)
1057+
return fmt.Errorf("failed to update active template version: %s", err)
10571058
}
10581059
tflog.Info(ctx, "marked template version as active")
10591060
return nil
@@ -1231,8 +1232,9 @@ type LastVersionsByHash = map[string][]PreviousTemplateVersion
12311232
var LastVersionsKey = "last_versions"
12321233

12331234
type PreviousTemplateVersion struct {
1234-
ID uuid.UUID `json:"id"`
1235-
Name string `json:"name"`
1235+
ID uuid.UUID `json:"id"`
1236+
Name string `json:"name"`
1237+
TFVars map[string]string `json:"tf_vars"`
12361238
}
12371239

12381240
type privateState interface {
@@ -1244,18 +1246,24 @@ func (v Versions) setPrivateState(ctx context.Context, ps privateState) (diags d
12441246
lv := make(LastVersionsByHash)
12451247
for _, version := range v {
12461248
vbh, ok := lv[version.DirectoryHash.ValueString()]
1249+
tfVars := make(map[string]string, len(version.TerraformVariables))
1250+
for _, tfVar := range version.TerraformVariables {
1251+
tfVars[tfVar.Name.ValueString()] = tfVar.Value.ValueString()
1252+
}
12471253
// Store the IDs and names of all versions with the same directory hash,
12481254
// in the order they appear
12491255
if ok {
12501256
lv[version.DirectoryHash.ValueString()] = append(vbh, PreviousTemplateVersion{
1251-
ID: version.ID.ValueUUID(),
1252-
Name: version.Name.ValueString(),
1257+
ID: version.ID.ValueUUID(),
1258+
Name: version.Name.ValueString(),
1259+
TFVars: tfVars,
12531260
})
12541261
} else {
12551262
lv[version.DirectoryHash.ValueString()] = []PreviousTemplateVersion{
12561263
{
1257-
ID: version.ID.ValueUUID(),
1258-
Name: version.Name.ValueString(),
1264+
ID: version.ID.ValueUUID(),
1265+
Name: version.Name.ValueString(),
1266+
TFVars: tfVars,
12591267
},
12601268
}
12611269
}
@@ -1269,6 +1277,13 @@ func (v Versions) setPrivateState(ctx context.Context, ps privateState) (diags d
12691277
}
12701278

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

0 commit comments

Comments
 (0)