From 3e47158b4312d2666e6922270380d3e852812c4d Mon Sep 17 00:00:00 2001 From: chrisghill Date: Thu, 12 Mar 2026 16:24:53 -0600 Subject: [PATCH 1/3] Always regen _massdriver_variables.tf on bundle build --- pkg/bundle/build_test.go | 12 ++--- pkg/bundle/combine_test.go | 11 ++-- pkg/bundle/schemas/metadata-schema.json | 27 ++-------- pkg/provisioners/opentofu.go | 37 ++++++++----- pkg/provisioners/opentofu_test.go | 72 ++++++++++++++++++++++--- 5 files changed, 100 insertions(+), 59 deletions(-) diff --git a/pkg/bundle/build_test.go b/pkg/bundle/build_test.go index e3470d0e..c115f038 100644 --- a/pkg/bundle/build_test.go +++ b/pkg/bundle/build_test.go @@ -157,7 +157,9 @@ var expectedSchemaContents = map[string][]byte{ } var expectedTFContent = map[string][]byte{ - "_massdriver_variables.tf": []byte(`// Auto-generated variable declarations from massdriver.yaml + "_massdriver_variables.tf": []byte(`// This file is auto-generated by massdriver from your massdriver.yaml file. +// Any changes made directly to this file will be overwritten on the next build. +// To opt a variable out of regeneration, move it to another file (e.g. variables.tf). variable "draft_node_foo" { type = object({ foo = optional(object({ @@ -176,13 +178,7 @@ variable "foo" { } variable "md_metadata" { type = object({ - default_tags = object({ - managed-by = string - md-manifest = string - md-package = string - md-project = string - md-target = string - }) + default_tags = map(string) deployment = object({ id = string }) diff --git a/pkg/bundle/combine_test.go b/pkg/bundle/combine_test.go index 820444e5..6e1ca567 100644 --- a/pkg/bundle/combine_test.go +++ b/pkg/bundle/combine_test.go @@ -10,15 +10,10 @@ import ( var mdMetadataMap = map[string]any{ "properties": map[string]any{ "default_tags": map[string]any{ - "properties": map[string]any{ - "managed-by": map[string]any{"type": "string"}, - "md-manifest": map[string]any{"type": "string"}, - "md-package": map[string]any{"type": "string"}, - "md-project": map[string]any{"type": "string"}, - "md-target": map[string]any{"type": "string"}, + "type": "object", + "additionalProperties": map[string]any{ + "type": "string", }, - "required": []any{"managed-by", "md-manifest", "md-package", "md-project", "md-target"}, - "type": "object", }, "deployment": map[string]any{ "properties": map[string]any{ diff --git a/pkg/bundle/schemas/metadata-schema.json b/pkg/bundle/schemas/metadata-schema.json index 0276495b..e6d24ec1 100644 --- a/pkg/bundle/schemas/metadata-schema.json +++ b/pkg/bundle/schemas/metadata-schema.json @@ -19,30 +19,9 @@ "properties": { "default_tags": { "type": "object", - "properties": { - "managed-by": { - "type": "string" - }, - "md-manifest": { - "type": "string" - }, - "md-package": { - "type": "string" - }, - "md-project": { - "type": "string" - }, - "md-target": { - "type": "string" - } - }, - "required": [ - "managed-by", - "md-manifest", - "md-package", - "md-project", - "md-target" - ] + "additionalProperties": { + "type": "string" + } }, "deployment": { "type": "object", diff --git a/pkg/provisioners/opentofu.go b/pkg/provisioners/opentofu.go index bea9e9d3..dc208996 100644 --- a/pkg/provisioners/opentofu.go +++ b/pkg/provisioners/opentofu.go @@ -12,8 +12,28 @@ import ( type OpentofuProvisioner struct{} -func (p *OpentofuProvisioner) ExportMassdriverInputs(stepPath string, variables map[string]any) error { - // read existing OpenTofu variables for this step +func (p *OpentofuProvisioner) ExportMassdriverInputs(stepPath string, variables map[string]any) (retErr error) { + massdriverVarsFile := path.Join(stepPath, "_massdriver_variables.tf") + massdriverVarsBackup := massdriverVarsFile + ".bak" + + // If _massdriver_variables.tf already exists, rename it so airlock won't read it + // during the scan. This allows variables declared there to be regenerated. + // The defer below restores the backup on any error, or removes it on success. + if _, statErr := os.Stat(massdriverVarsFile); statErr == nil { + if renameErr := os.Rename(massdriverVarsFile, massdriverVarsBackup); renameErr != nil { + return renameErr + } + defer func() { + if retErr != nil { + os.Remove(massdriverVarsFile) //nolint:errcheck + os.Rename(massdriverVarsBackup, massdriverVarsFile) //nolint:errcheck + } else { + os.Remove(massdriverVarsBackup) //nolint:errcheck + } + }() + } + + // read existing OpenTofu variables for this step (excludes _massdriver_variables.tf) tofuVarsImport := opentofu.TofuToSchema(stepPath) if tofuVarsImport.Schema == nil { return errors.New("failed to read existing OpenTofu variable declarations: " + tofuVarsImport.PrettyDiags()) @@ -34,17 +54,10 @@ func (p *OpentofuProvisioner) ExportMassdriverInputs(stepPath string, variables return transpileErr } - comment := []byte("// Auto-generated variable declarations from massdriver.yaml\n") - content = append(comment, content...) - filename := "/_massdriver_variables.tf" - fh, openErr := os.OpenFile(path.Join(stepPath, filename), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if openErr != nil { - return openErr - } - defer fh.Close() + 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") + content = append(header, content...) - _, writeErr := fh.Write(content) - if writeErr != nil { + if writeErr := os.WriteFile(massdriverVarsFile, content, 0644); writeErr != nil { return writeErr } diff --git a/pkg/provisioners/opentofu_test.go b/pkg/provisioners/opentofu_test.go index e458636a..6b75a8a4 100644 --- a/pkg/provisioners/opentofu_test.go +++ b/pkg/provisioners/opentofu_test.go @@ -13,12 +13,16 @@ import ( "github.com/massdriver-cloud/mass/pkg/provisioners" ) +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" + func TestOpentofuExportMassdriverInputs(t *testing.T) { type test struct { - name string - variables map[string]any - want string - errString string + name string + tfFile string // testdata fixture to use as variables.tf; defaults to tc.name + variables map[string]any + existingMassdriverVars string // pre-populate _massdriver_variables.tf with this content + want string + errString string } tests := []test{ { @@ -49,12 +53,54 @@ func TestOpentofuExportMassdriverInputs(t *testing.T) { }, }, }, - want: `// Auto-generated variable declarations from massdriver.yaml -variable "bar" { + want: autoGeneratedHeader + `variable "bar" { + type = string +} +`, + }, + { + name: "regenerate", + tfFile: "missingopentofu", + variables: map[string]any{ + "required": []any{"bar", "foo"}, + "properties": map[string]any{ + "bar": map[string]any{ + "type": "string", + }, + "foo": map[string]any{ + "type": "string", + }, + }, + }, + existingMassdriverVars: autoGeneratedHeader + `variable "bar" { + type = string +} +`, + want: autoGeneratedHeader + `variable "bar" { type = string } `, }, + { + name: "regenerateclean", + tfFile: "same", + variables: map[string]any{ + "required": []any{"bar", "foo"}, + "properties": map[string]any{ + "bar": map[string]any{ + "type": "string", + }, + "foo": map[string]any{ + "type": "string", + }, + }, + }, + existingMassdriverVars: autoGeneratedHeader + `variable "bar" { + type = string +} +`, + want: ``, + }, { name: "missingmassdriver", variables: map[string]any{ @@ -77,7 +123,12 @@ variable "bar" { t.Run(tc.name, func(t *testing.T) { testDir := t.TempDir() - content, err := os.ReadFile(path.Join("testdata", "opentofu", fmt.Sprintf("%s.tf", tc.name))) + tfFile := tc.tfFile + if tfFile == "" { + tfFile = tc.name + } + + content, err := os.ReadFile(path.Join("testdata", "opentofu", fmt.Sprintf("%s.tf", tfFile))) if err != nil { t.Fatalf("%d, unexpected error", err) } @@ -87,6 +138,13 @@ variable "bar" { t.Fatalf("%d, unexpected error", err) } + if tc.existingMassdriverVars != "" { + err = os.WriteFile(path.Join(testDir, "_massdriver_variables.tf"), []byte(tc.existingMassdriverVars), 0644) + if err != nil { + t.Fatalf("unexpected error writing existing massdriver vars: %s", err) + } + } + prov := provisioners.OpentofuProvisioner{} err = prov.ExportMassdriverInputs(testDir, tc.variables) if tc.errString == "" && err != nil { From a3c148531600eff0cfea58f5a284e29c0f3dcd05 Mon Sep 17 00:00:00 2001 From: chrisghill Date: Thu, 12 Mar 2026 16:34:54 -0600 Subject: [PATCH 2/3] PR findings --- pkg/provisioners/opentofu.go | 2 ++ pkg/provisioners/opentofu_test.go | 8 ++++---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/provisioners/opentofu.go b/pkg/provisioners/opentofu.go index dc208996..7d137fdb 100644 --- a/pkg/provisioners/opentofu.go +++ b/pkg/provisioners/opentofu.go @@ -31,6 +31,8 @@ func (p *OpentofuProvisioner) ExportMassdriverInputs(stepPath string, variables os.Remove(massdriverVarsBackup) //nolint:errcheck } }() + } else if !errors.Is(statErr, os.ErrNotExist) { + return statErr } // read existing OpenTofu variables for this step (excludes _massdriver_variables.tf) diff --git a/pkg/provisioners/opentofu_test.go b/pkg/provisioners/opentofu_test.go index 6b75a8a4..c6f9a617 100644 --- a/pkg/provisioners/opentofu_test.go +++ b/pkg/provisioners/opentofu_test.go @@ -130,12 +130,12 @@ func TestOpentofuExportMassdriverInputs(t *testing.T) { content, err := os.ReadFile(path.Join("testdata", "opentofu", fmt.Sprintf("%s.tf", tfFile))) if err != nil { - t.Fatalf("%d, unexpected error", err) + t.Fatalf("unexpected error: %v", err) } err = os.WriteFile(path.Join(testDir, "variables.tf"), content, 0644) if err != nil { - t.Fatalf("%d, unexpected error", err) + t.Fatalf("unexpected error: %v", err) } if tc.existingMassdriverVars != "" { @@ -208,12 +208,12 @@ func TestOpentofuReadProvisionerInputs(t *testing.T) { content, err := os.ReadFile(path.Join("testdata", "opentofu", fmt.Sprintf("%s.tf", tc.name))) if err != nil { - t.Fatalf("%d, unexpected error", err) + t.Fatalf("unexpected error: %v", err) } err = os.WriteFile(path.Join(testDir, "variables.tf"), content, 0644) if err != nil { - t.Fatalf("%d, unexpected error", err) + t.Fatalf("unexpected error: %v", err) } prov := provisioners.OpentofuProvisioner{} From 27f770df490b215fd48fef01c1105dc3b0c0b576 Mon Sep 17 00:00:00 2001 From: chrisghill Date: Thu, 12 Mar 2026 16:51:25 -0600 Subject: [PATCH 3/3] PR finding --- pkg/provisioners/opentofu_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/provisioners/opentofu_test.go b/pkg/provisioners/opentofu_test.go index c6f9a617..cbd98d66 100644 --- a/pkg/provisioners/opentofu_test.go +++ b/pkg/provisioners/opentofu_test.go @@ -141,7 +141,7 @@ func TestOpentofuExportMassdriverInputs(t *testing.T) { if tc.existingMassdriverVars != "" { err = os.WriteFile(path.Join(testDir, "_massdriver_variables.tf"), []byte(tc.existingMassdriverVars), 0644) if err != nil { - t.Fatalf("unexpected error writing existing massdriver vars: %s", err) + t.Fatalf("unexpected error writing existing massdriver vars: %v", err) } }