Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add the Variable struct #47

Merged
merged 4 commits into from
Jun 1, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion earlydecoder/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (

"github.com/hashicorp/go-version"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/terraform-registry-address"
tfaddr "github.com/hashicorp/terraform-registry-address"
"github.com/hashicorp/terraform-schema/module"
)

Expand Down Expand Up @@ -127,10 +127,15 @@ func LoadModule(path string, files map[string]*hcl.File) (*module.Meta, hcl.Diag
}
}

variables := make(map[string]module.Variable)
for key, variable := range mod.Variables {
variables[key] = *variable
}
return &module.Meta{
Path: path,
ProviderReferences: refs,
ProviderRequirements: providerRequirements,
CoreRequirements: coreRequirements,
Variables: variables,
}, diags
}
140 changes: 132 additions & 8 deletions earlydecoder/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,30 @@ import (
"github.com/hashicorp/go-version"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/hashicorp/terraform-registry-address"
tfaddr "github.com/hashicorp/terraform-registry-address"
"github.com/hashicorp/terraform-schema/module"
"github.com/zclconf/go-cty-debug/ctydebug"
"github.com/zclconf/go-cty/cty"
)

type testCase struct {
name string
cfg string
expectedMeta *module.Meta
}

func TestLoadModule(t *testing.T) {
path := t.TempDir()

testCases := []struct {
name string
cfg string
expectedMeta *module.Meta
}{
testCases := []testCase{
{
"empty config",
``,
&module.Meta{
Path: path,
ProviderReferences: map[module.ProviderRef]tfaddr.Provider{},
ProviderRequirements: map[tfaddr.Provider]version.Constraints{},
Variables: map[string]module.Variable{},
},
},
{
Expand All @@ -40,6 +45,7 @@ terraform {
CoreRequirements: mustConstraints(t, "~> 0.12"),
ProviderReferences: map[module.ProviderRef]tfaddr.Provider{},
ProviderRequirements: map[tfaddr.Provider]version.Constraints{},
Variables: map[string]module.Variable{},
},
},
{
Expand Down Expand Up @@ -76,6 +82,7 @@ provider "grafana" {
tfaddr.NewLegacyProvider("google"): {},
tfaddr.NewLegacyProvider("grafana"): {},
},
Variables: map[string]module.Variable{},
},
},
{
Expand Down Expand Up @@ -112,6 +119,7 @@ provider "grafana" {
tfaddr.NewLegacyProvider("google"): mustConstraints(t, ">= 3.0.0"),
tfaddr.NewLegacyProvider("grafana"): {},
},
Variables: map[string]module.Variable{},
},
},
{
Expand Down Expand Up @@ -152,6 +160,7 @@ provider "grafana" {
tfaddr.NewLegacyProvider("google"): mustConstraints(t, ">= 3.0.0"),
tfaddr.NewLegacyProvider("grafana"): {},
},
Variables: map[string]module.Variable{},
},
},
{
Expand Down Expand Up @@ -222,6 +231,7 @@ provider "grafana" {
Type: "grafana",
}: mustConstraints(t, "2.1.0"),
},
Variables: map[string]module.Variable{},
},
},
{
Expand Down Expand Up @@ -282,6 +292,7 @@ resource "google_storage_bucket" "bucket" {
Type: "google",
}: mustConstraints(t, "2.0.0"),
},
Variables: map[string]module.Variable{},
},
},
{
Expand Down Expand Up @@ -343,6 +354,7 @@ resource "google_storage_bucket" "bucket" {
Type: "google",
}: mustConstraints(t, "2.0.0"),
},
Variables: map[string]module.Variable{},
},
},
{
Expand Down Expand Up @@ -396,6 +408,7 @@ provider "aws" {
Type: "google",
}: mustConstraints(t, "2.0.0"),
},
Variables: map[string]module.Variable{},
},
},
{
Expand Down Expand Up @@ -455,6 +468,7 @@ provider "aws" {
Type: "google",
}: mustConstraints(t, "2.0.0"),
},
Variables: map[string]module.Variable{},
},
},
{
Expand Down Expand Up @@ -489,11 +503,114 @@ resource "google_something" "test" {
Type: "google-beta",
}: mustConstraints(t, "2.0.0"),
},
Variables: map[string]module.Variable{},
},
},
}

opts := cmp.Comparer(compareVersionConstraint)
executeTestCases(testCases, t, path)
}

func TestLoadModule_Variables(t *testing.T) {
path := t.TempDir()

testCases := []testCase{
{
"empty variables",
`
variable "name" {
}`,
&module.Meta{
Path: path,
ProviderReferences: map[module.ProviderRef]tfaddr.Provider{},
ProviderRequirements: map[tfaddr.Provider]version.Constraints{},
Variables: map[string]module.Variable{
"name": {
Type: cty.DynamicPseudoType,
},
},
},
},
{
"variables with type",
`
variable "name" {
type = string
}`,
&module.Meta{
Path: path,
ProviderReferences: map[module.ProviderRef]tfaddr.Provider{},
ProviderRequirements: map[tfaddr.Provider]version.Constraints{},
Variables: map[string]module.Variable{
"name": {
Type: cty.String,
},
},
},
},
{
"variables with description",
`
variable "name" {
description = "description"
}`,
&module.Meta{
Path: path,
ProviderReferences: map[module.ProviderRef]tfaddr.Provider{},
ProviderRequirements: map[tfaddr.Provider]version.Constraints{},
Variables: map[string]module.Variable{
"name": {
Type: cty.DynamicPseudoType,
Description: "description",
},
},
},
},
{
"variables with sensitive",
`
variable "name" {
sensitive = true
}`,
&module.Meta{
Path: path,
ProviderReferences: map[module.ProviderRef]tfaddr.Provider{},
ProviderRequirements: map[tfaddr.Provider]version.Constraints{},
Variables: map[string]module.Variable{
"name": {
Type: cty.DynamicPseudoType,
IsSensitive: true,
},
},
},
},
{
"variables with type and description and sensitive",
`
variable "name" {
type = string
description = "description"
sensitive = true
}`,
&module.Meta{
Path: path,
ProviderReferences: map[module.ProviderRef]tfaddr.Provider{},
ProviderRequirements: map[tfaddr.Provider]version.Constraints{},
Variables: map[string]module.Variable{
"name": {
Type: cty.String,
Description: "description",
IsSensitive: true,
},
},
},
},
}
executeTestCases(testCases, t, path)

}
func executeTestCases(testCases []testCase, t *testing.T, path string) {
opts := getCustomComparers()

for i, tc := range testCases {
t.Run(fmt.Sprintf("%d-%s", i, tc.name), func(t *testing.T) {
Expand All @@ -510,7 +627,7 @@ resource "google_something" "test" {
t.Fatal(diags)
}

if diff := cmp.Diff(tc.expectedMeta, meta, opts); diff != "" {
if diff := cmp.Diff(tc.expectedMeta, meta, opts...); diff != "" {
t.Fatalf("module meta doesn't match: %s", diff)
}
})
Expand All @@ -528,3 +645,10 @@ func mustConstraints(t *testing.T, vc string) version.Constraints {
func compareVersionConstraint(x, y version.Constraint) bool {
return x.String() == y.String()
}

func getCustomComparers() []cmp.Option {
return []cmp.Option{
cmp.Comparer(compareVersionConstraint),
ctydebug.CmpOptions,
}
}
40 changes: 36 additions & 4 deletions earlydecoder/load_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import (
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/hashicorp/terraform-schema/internal/typeexpr"
"github.com/hashicorp/terraform-schema/module"
"github.com/zclconf/go-cty/cty"
)

// decodedModule is the type representing a decoded Terraform module.
Expand All @@ -16,15 +18,17 @@ type decodedModule struct {
ProviderConfigs map[string]*providerConfig
Resources map[string]*resource
DataSources map[string]*dataSource
Variables map[string]*module.Variable
}

func newDecodedModule() *decodedModule {
return &decodedModule{
RequiredCore: make([]string, 0),
ProviderRequirements: make(map[string]*providerRequirement, 0),
ProviderConfigs: make(map[string]*providerConfig, 0),
Resources: make(map[string]*resource, 0),
DataSources: make(map[string]*dataSource, 0),
ProviderRequirements: make(map[string]*providerRequirement),
ProviderConfigs: make(map[string]*providerConfig),
Resources: make(map[string]*resource),
DataSources: make(map[string]*dataSource),
Variables: make(map[string]*module.Variable),
}
}

Expand Down Expand Up @@ -166,7 +170,35 @@ func loadModuleFromFile(file *hcl.File, mod *decodedModule) hcl.Diagnostics {
LocalName: inferProviderNameFromType(r.Type),
}
}

case "variable":
content, _, contentDiags := block.Body.PartialContent(variableSchema)
diags = append(diags, contentDiags...)
name := block.Labels[0]
description := ""
isSensitive := false
var valDiags hcl.Diagnostics
if attr, defined := content.Attributes["description"]; defined {
valDiags = gohcl.DecodeExpression(attr.Expr, nil, &description)
diags = append(diags, valDiags...)
}
varType := cty.DynamicPseudoType
if attr, defined := content.Attributes["type"]; defined {
varType, valDiags = typeexpr.TypeConstraint(attr.Expr)
diags = append(diags, valDiags...)
}
if attr, defined := content.Attributes["sensitive"]; defined {
valDiags = gohcl.DecodeExpression(attr.Expr, nil, &isSensitive)
diags = append(diags, valDiags...)
}
mod.Variables[name] = &module.Variable{
Type: varType,
Description: description,
IsSensitive: isSensitive,
}

}

}

return diags
Expand Down
21 changes: 21 additions & 0 deletions earlydecoder/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ var rootSchema = &hcl.BodySchema{
Type: "data",
LabelNames: []string{"type", "name"},
},
{
Type: "variable",
LabelNames: []string{"name"},
},
},
}

Expand Down Expand Up @@ -55,3 +59,20 @@ var resourceSchema = &hcl.BodySchema{
},
},
}

var variableSchema = &hcl.BodySchema{
Attributes: []hcl.AttributeSchema{
{
Name: "description",
},
{
Name: "type",
},
{
Name: "sensitive",
},
{
Name: "default",
},
},
}
10 changes: 10 additions & 0 deletions internal/typeexpr/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Package typeexpr is a fork of github.com/hashicorp/hcl/v2/ext/typeexpr
// which has additional experimental support for optional attributes.
//
// This is here as part of the module_variable_optional_attrs experiment.
// If that experiment is successful, the changes here may be upstreamed into
// HCL itself or, if we deem it to be Terraform-specific, we should at least
// update this documentation to reflect that this is now the primary
// Terraform-specific type expression implementation, separate from the
// upstream HCL one.
package typeexpr
Loading