Skip to content

Commit 81449d0

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

File tree

3 files changed

+319
-77
lines changed

3 files changed

+319
-77
lines changed

docs/resources/template.md

Lines changed: 3 additions & 3 deletions
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

Lines changed: 68 additions & 10 deletions
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
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)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy