Skip to content

Commit dad9c11

Browse files
authored
Always regen _massdriver_variables.tf on bundle build (#218)
* Always regen _massdriver_variables.tf on bundle build * PR findings * PR finding
1 parent bb76277 commit dad9c11

5 files changed

Lines changed: 106 additions & 63 deletions

File tree

pkg/bundle/build_test.go

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,9 @@ var expectedSchemaContents = map[string][]byte{
157157
}
158158

159159
var expectedTFContent = map[string][]byte{
160-
"_massdriver_variables.tf": []byte(`// Auto-generated variable declarations from massdriver.yaml
160+
"_massdriver_variables.tf": []byte(`// This file is auto-generated by massdriver from your massdriver.yaml file.
161+
// Any changes made directly to this file will be overwritten on the next build.
162+
// To opt a variable out of regeneration, move it to another file (e.g. variables.tf).
161163
variable "draft_node_foo" {
162164
type = object({
163165
foo = optional(object({
@@ -176,13 +178,7 @@ variable "foo" {
176178
}
177179
variable "md_metadata" {
178180
type = object({
179-
default_tags = object({
180-
managed-by = string
181-
md-manifest = string
182-
md-package = string
183-
md-project = string
184-
md-target = string
185-
})
181+
default_tags = map(string)
186182
deployment = object({
187183
id = string
188184
})

pkg/bundle/combine_test.go

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,10 @@ import (
1010
var mdMetadataMap = map[string]any{
1111
"properties": map[string]any{
1212
"default_tags": map[string]any{
13-
"properties": map[string]any{
14-
"managed-by": map[string]any{"type": "string"},
15-
"md-manifest": map[string]any{"type": "string"},
16-
"md-package": map[string]any{"type": "string"},
17-
"md-project": map[string]any{"type": "string"},
18-
"md-target": map[string]any{"type": "string"},
13+
"type": "object",
14+
"additionalProperties": map[string]any{
15+
"type": "string",
1916
},
20-
"required": []any{"managed-by", "md-manifest", "md-package", "md-project", "md-target"},
21-
"type": "object",
2217
},
2318
"deployment": map[string]any{
2419
"properties": map[string]any{

pkg/bundle/schemas/metadata-schema.json

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,30 +19,9 @@
1919
"properties": {
2020
"default_tags": {
2121
"type": "object",
22-
"properties": {
23-
"managed-by": {
24-
"type": "string"
25-
},
26-
"md-manifest": {
27-
"type": "string"
28-
},
29-
"md-package": {
30-
"type": "string"
31-
},
32-
"md-project": {
33-
"type": "string"
34-
},
35-
"md-target": {
36-
"type": "string"
37-
}
38-
},
39-
"required": [
40-
"managed-by",
41-
"md-manifest",
42-
"md-package",
43-
"md-project",
44-
"md-target"
45-
]
22+
"additionalProperties": {
23+
"type": "string"
24+
}
4625
},
4726
"deployment": {
4827
"type": "object",

pkg/provisioners/opentofu.go

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,30 @@ import (
1212

1313
type OpentofuProvisioner struct{}
1414

15-
func (p *OpentofuProvisioner) ExportMassdriverInputs(stepPath string, variables map[string]any) error {
16-
// read existing OpenTofu variables for this step
15+
func (p *OpentofuProvisioner) ExportMassdriverInputs(stepPath string, variables map[string]any) (retErr error) {
16+
massdriverVarsFile := path.Join(stepPath, "_massdriver_variables.tf")
17+
massdriverVarsBackup := massdriverVarsFile + ".bak"
18+
19+
// If _massdriver_variables.tf already exists, rename it so airlock won't read it
20+
// during the scan. This allows variables declared there to be regenerated.
21+
// The defer below restores the backup on any error, or removes it on success.
22+
if _, statErr := os.Stat(massdriverVarsFile); statErr == nil {
23+
if renameErr := os.Rename(massdriverVarsFile, massdriverVarsBackup); renameErr != nil {
24+
return renameErr
25+
}
26+
defer func() {
27+
if retErr != nil {
28+
os.Remove(massdriverVarsFile) //nolint:errcheck
29+
os.Rename(massdriverVarsBackup, massdriverVarsFile) //nolint:errcheck
30+
} else {
31+
os.Remove(massdriverVarsBackup) //nolint:errcheck
32+
}
33+
}()
34+
} else if !errors.Is(statErr, os.ErrNotExist) {
35+
return statErr
36+
}
37+
38+
// read existing OpenTofu variables for this step (excludes _massdriver_variables.tf)
1739
tofuVarsImport := opentofu.TofuToSchema(stepPath)
1840
if tofuVarsImport.Schema == nil {
1941
return errors.New("failed to read existing OpenTofu variable declarations: " + tofuVarsImport.PrettyDiags())
@@ -34,17 +56,10 @@ func (p *OpentofuProvisioner) ExportMassdriverInputs(stepPath string, variables
3456
return transpileErr
3557
}
3658

37-
comment := []byte("// Auto-generated variable declarations from massdriver.yaml\n")
38-
content = append(comment, content...)
39-
filename := "/_massdriver_variables.tf"
40-
fh, openErr := os.OpenFile(path.Join(stepPath, filename), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
41-
if openErr != nil {
42-
return openErr
43-
}
44-
defer fh.Close()
59+
header := []byte("// This file is auto-generated by massdriver from your massdriver.yaml file.\n// Any changes made directly to this file will be overwritten on the next build.\n// To opt a variable out of regeneration, move it to another file (e.g. variables.tf).\n")
60+
content = append(header, content...)
4561

46-
_, writeErr := fh.Write(content)
47-
if writeErr != nil {
62+
if writeErr := os.WriteFile(massdriverVarsFile, content, 0644); writeErr != nil {
4863
return writeErr
4964
}
5065

pkg/provisioners/opentofu_test.go

Lines changed: 69 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,16 @@ import (
1313
"github.com/massdriver-cloud/mass/pkg/provisioners"
1414
)
1515

16+
const autoGeneratedHeader = "// This file is auto-generated by massdriver from your massdriver.yaml file.\n// Any changes made directly to this file will be overwritten on the next build.\n// To opt a variable out of regeneration, move it to another file (e.g. variables.tf).\n"
17+
1618
func TestOpentofuExportMassdriverInputs(t *testing.T) {
1719
type test struct {
18-
name string
19-
variables map[string]any
20-
want string
21-
errString string
20+
name string
21+
tfFile string // testdata fixture to use as variables.tf; defaults to tc.name
22+
variables map[string]any
23+
existingMassdriverVars string // pre-populate _massdriver_variables.tf with this content
24+
want string
25+
errString string
2226
}
2327
tests := []test{
2428
{
@@ -49,12 +53,54 @@ func TestOpentofuExportMassdriverInputs(t *testing.T) {
4953
},
5054
},
5155
},
52-
want: `// Auto-generated variable declarations from massdriver.yaml
53-
variable "bar" {
56+
want: autoGeneratedHeader + `variable "bar" {
57+
type = string
58+
}
59+
`,
60+
},
61+
{
62+
name: "regenerate",
63+
tfFile: "missingopentofu",
64+
variables: map[string]any{
65+
"required": []any{"bar", "foo"},
66+
"properties": map[string]any{
67+
"bar": map[string]any{
68+
"type": "string",
69+
},
70+
"foo": map[string]any{
71+
"type": "string",
72+
},
73+
},
74+
},
75+
existingMassdriverVars: autoGeneratedHeader + `variable "bar" {
76+
type = string
77+
}
78+
`,
79+
want: autoGeneratedHeader + `variable "bar" {
5480
type = string
5581
}
5682
`,
5783
},
84+
{
85+
name: "regenerateclean",
86+
tfFile: "same",
87+
variables: map[string]any{
88+
"required": []any{"bar", "foo"},
89+
"properties": map[string]any{
90+
"bar": map[string]any{
91+
"type": "string",
92+
},
93+
"foo": map[string]any{
94+
"type": "string",
95+
},
96+
},
97+
},
98+
existingMassdriverVars: autoGeneratedHeader + `variable "bar" {
99+
type = string
100+
}
101+
`,
102+
want: ``,
103+
},
58104
{
59105
name: "missingmassdriver",
60106
variables: map[string]any{
@@ -77,14 +123,26 @@ variable "bar" {
77123
t.Run(tc.name, func(t *testing.T) {
78124
testDir := t.TempDir()
79125

80-
content, err := os.ReadFile(path.Join("testdata", "opentofu", fmt.Sprintf("%s.tf", tc.name)))
126+
tfFile := tc.tfFile
127+
if tfFile == "" {
128+
tfFile = tc.name
129+
}
130+
131+
content, err := os.ReadFile(path.Join("testdata", "opentofu", fmt.Sprintf("%s.tf", tfFile)))
81132
if err != nil {
82-
t.Fatalf("%d, unexpected error", err)
133+
t.Fatalf("unexpected error: %v", err)
83134
}
84135

85136
err = os.WriteFile(path.Join(testDir, "variables.tf"), content, 0644)
86137
if err != nil {
87-
t.Fatalf("%d, unexpected error", err)
138+
t.Fatalf("unexpected error: %v", err)
139+
}
140+
141+
if tc.existingMassdriverVars != "" {
142+
err = os.WriteFile(path.Join(testDir, "_massdriver_variables.tf"), []byte(tc.existingMassdriverVars), 0644)
143+
if err != nil {
144+
t.Fatalf("unexpected error writing existing massdriver vars: %v", err)
145+
}
88146
}
89147

90148
prov := provisioners.OpentofuProvisioner{}
@@ -150,12 +208,12 @@ func TestOpentofuReadProvisionerInputs(t *testing.T) {
150208

151209
content, err := os.ReadFile(path.Join("testdata", "opentofu", fmt.Sprintf("%s.tf", tc.name)))
152210
if err != nil {
153-
t.Fatalf("%d, unexpected error", err)
211+
t.Fatalf("unexpected error: %v", err)
154212
}
155213

156214
err = os.WriteFile(path.Join(testDir, "variables.tf"), content, 0644)
157215
if err != nil {
158-
t.Fatalf("%d, unexpected error", err)
216+
t.Fatalf("unexpected error: %v", err)
159217
}
160218

161219
prov := provisioners.OpentofuProvisioner{}

0 commit comments

Comments
 (0)