Skip to content

Commit

Permalink
(TF-18671) Enable references for variables in deployment inputs (Depl…
Browse files Browse the repository at this point in the history
…oy) (#389)

* feat: Do early decoding for deployments in deploy files
* add OriginForTarget for stack variable references in deployment files
  • Loading branch information
ansgarm authored Aug 15, 2024
1 parent 8191b5e commit 662d8a7
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 8 deletions.
28 changes: 25 additions & 3 deletions earlydecoder/stacks/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package earlydecoder
import (
"fmt"
"sort"
"strings"

"github.com/hashicorp/go-version"
"github.com/hashicorp/hcl/v2"
Expand All @@ -20,9 +21,14 @@ func LoadStack(path string, files map[string]*hcl.File) (*stack.Meta, hcl.Diagno
mod := newDecodedStack()
for filename, f := range files {
filenames = append(filenames, filename)
// TODO need more stack metas
fDiags := loadStackFromFile(f, mod)
diags = append(diags, fDiags...)

if isStackFilename(filename) {
fDiags := loadStackFromFile(f, mod)
diags = append(diags, fDiags...)
} else if isDeployFilename(filename) {
fDiags := loadDeployFromFile(f, mod)
diags = append(diags, fDiags...)
}
}

sort.Strings(filenames)
Expand Down Expand Up @@ -73,12 +79,28 @@ func LoadStack(path string, files map[string]*hcl.File) (*stack.Meta, hcl.Diagno
}
}

deployments := make(map[string]stack.Deployment)
for key, deployment := range mod.Deployments {
deployments[key] = *deployment
}

return &stack.Meta{
Path: path,
Filenames: filenames,
Components: components,
Variables: variables,
Outputs: outputs,
ProviderRequirements: providerRequirements,
Deployments: deployments,
}, diags
}

func isStackFilename(name string) bool {
return strings.HasSuffix(name, ".tfstack.hcl") ||
strings.HasSuffix(name, ".tfstack.json")
}

func isDeployFilename(name string) bool {
return strings.HasSuffix(name, ".tfdeploy.hcl") ||
strings.HasSuffix(name, ".tfdeploy.json")
}
13 changes: 8 additions & 5 deletions earlydecoder/stacks/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@ func TestLoadStack(t *testing.T) {
``,
&stack.Meta{
Path: path,
Filenames: []string{"test.tf"},
Filenames: []string{"test.tfstack.hcl"},
Components: map[string]stack.Component{},
Variables: map[string]stack.Variable{},
Outputs: map[string]stack.Output{},
ProviderRequirements: map[string]stack.ProviderRequirement{},
Deployments: map[string]stack.Deployment{},
},
nil,
},
Expand All @@ -57,7 +58,7 @@ func TestLoadStack(t *testing.T) {
}`,
&stack.Meta{
Path: path,
Filenames: []string{"test.tf"},
Filenames: []string{"test.tfstack.hcl"},
Components: map[string]stack.Component{
"test": {
Source: "github.com/acme/infra/core",
Expand All @@ -68,6 +69,7 @@ func TestLoadStack(t *testing.T) {
Variables: map[string]stack.Variable{},
Outputs: map[string]stack.Output{},
ProviderRequirements: map[string]stack.ProviderRequirement{},
Deployments: map[string]stack.Deployment{},
},
nil,
},
Expand All @@ -85,14 +87,15 @@ func TestLoadStack(t *testing.T) {
}`,
&stack.Meta{
Path: path,
Filenames: []string{"test.tf"},
Filenames: []string{"test.tfstack.hcl"},
Components: map[string]stack.Component{},
Variables: map[string]stack.Variable{},
Outputs: map[string]stack.Output{},
ProviderRequirements: map[string]stack.ProviderRequirement{
"aws": {Source: tfaddr.MustParseProviderSource("hashicorp/aws"), VersionConstraints: version.MustConstraints(version.NewConstraint("~> 5.7.0"))},
"random": {Source: tfaddr.MustParseProviderSource("hashicorp/random"), VersionConstraints: version.MustConstraints(version.NewConstraint("~> 3.5.1"))},
},
Deployments: map[string]stack.Deployment{},
},
nil,
},
Expand All @@ -104,12 +107,12 @@ func TestLoadStack(t *testing.T) {
func runTestCases(testCases []testCase, t *testing.T, path string) {
for i, tc := range testCases {
t.Run(fmt.Sprintf("%d-%s", i, tc.name), func(t *testing.T) {
f, diags := hclsyntax.ParseConfig([]byte(tc.cfg), "test.tf", hcl.InitialPos)
f, diags := hclsyntax.ParseConfig([]byte(tc.cfg), "test.tfstack.hcl", hcl.InitialPos)
if len(diags) > 0 {
t.Fatal(diags)
}
files := map[string]*hcl.File{
"test.tf": f,
"test.tfstack.hcl": f,
}

meta, diags := LoadStack(path, files)
Expand Down
47 changes: 47 additions & 0 deletions earlydecoder/stacks/load_deploy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package earlydecoder

import (
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/terraform-schema/stack"
"github.com/zclconf/go-cty/cty"
)

// loadDeployFromFile reads given file, interprets it and stores in given stack
// This is useful for any caller which does tokenization/parsing on its own
// e.g. because it will reuse these parsed files later for more detailed
// interpretation.
func loadDeployFromFile(file *hcl.File, ds *decodedStack) hcl.Diagnostics {
var diags hcl.Diagnostics

content, _, contentDiags := file.Body.PartialContent(deploymentRootSchema)
diags = append(diags, contentDiags...)
for _, block := range content.Blocks {
switch block.Type {
case "deployment":
content, _, contentDiags := block.Body.PartialContent(deploymentSchema)
diags = append(diags, contentDiags...)

if len(block.Labels) != 1 || block.Labels[0] == "" {
continue
}

name := block.Labels[0]

inputs := make(map[string]cty.Value)
if attr, defined := content.Attributes["inputs"]; defined {
valDiags := gohcl.DecodeExpression(attr.Expr, nil, &inputs)
diags = append(diags, valDiags...)
}

ds.Deployments[name] = &stack.Deployment{
Inputs: inputs,
}
}
}

return diags
}
3 changes: 3 additions & 0 deletions earlydecoder/stacks/load_stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ type decodedStack struct {
Variables map[string]*stack.Variable
Outputs map[string]*stack.Output
ProviderRequirements map[string]*providerRequirement

Deployments map[string]*stack.Deployment
}

func newDecodedStack() *decodedStack {
Expand All @@ -30,6 +32,7 @@ func newDecodedStack() *decodedStack {
Variables: make(map[string]*stack.Variable),
Outputs: make(map[string]*stack.Output),
ProviderRequirements: make(map[string]*providerRequirement),
Deployments: make(map[string]*stack.Deployment),
}
}

Expand Down
17 changes: 17 additions & 0 deletions earlydecoder/stacks/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,15 @@ var rootSchema = &hcl.BodySchema{
},
}

var deploymentRootSchema = &hcl.BodySchema{
Blocks: []hcl.BlockHeaderSchema{
{
Type: "deployment",
LabelNames: []string{"name"},
},
},
}

var componentSchema = &hcl.BodySchema{
Attributes: []hcl.AttributeSchema{
{
Expand Down Expand Up @@ -65,3 +74,11 @@ var outputSchema = &hcl.BodySchema{
},
},
}

var deploymentSchema = &hcl.BodySchema{
Attributes: []hcl.AttributeSchema{
{
Name: "inputs",
},
},
}
16 changes: 16 additions & 0 deletions schema/stacks/deploy_schema_merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package schema
import (
"github.com/hashicorp/hcl-lang/lang"
"github.com/hashicorp/hcl-lang/schema"
"github.com/hashicorp/terraform-schema/internal/schema/refscope"
tfschema "github.com/hashicorp/terraform-schema/schema"
"github.com/hashicorp/terraform-schema/stack"
"github.com/zclconf/go-cty/cty"
Expand Down Expand Up @@ -51,6 +52,21 @@ func constraintForDeploymentInputs(stackMeta stack.Meta) (schema.Constraint, err
aSchema := StackVarToAttribute(variable)
aSchema.Constraint = tfschema.ConvertAttributeTypeToConstraint(varType)

aSchema.OriginForTarget = &schema.PathTarget{
Address: schema.Address{
schema.StaticStep{Name: "var"},
schema.AttrNameStep{},
},
Path: lang.Path{
Path: stackMeta.Path,
LanguageID: tfschema.StackLanguageID,
},
Constraints: schema.Constraints{
ScopeId: refscope.VariableScope,
Type: varType,
},
}

inputs[name] = aSchema
}

Expand Down
10 changes: 10 additions & 0 deletions stack/deployment.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package stack

import "github.com/zclconf/go-cty/cty"

type Deployment struct {
Inputs map[string]cty.Value
}
2 changes: 2 additions & 0 deletions stack/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ type Meta struct {
Variables map[string]Variable
Outputs map[string]Output
ProviderRequirements map[string]ProviderRequirement

Deployments map[string]Deployment
}

0 comments on commit 662d8a7

Please sign in to comment.