Skip to content

Commit

Permalink
earlydecoder: Decode backend block (#78)
Browse files Browse the repository at this point in the history
  • Loading branch information
radeksimko authored Oct 21, 2021
1 parent eaee833 commit 21dfff1
Show file tree
Hide file tree
Showing 7 changed files with 220 additions and 3 deletions.
21 changes: 21 additions & 0 deletions backend/backend.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package backend

type BackendData interface {
Copy() BackendData
}

type UnknownBackendData struct{}

func (*UnknownBackendData) Copy() BackendData {
return &UnknownBackendData{}
}

type Remote struct {
Hostname string
}

func (r *Remote) Copy() BackendData {
return &Remote{
Hostname: r.Hostname,
}
}
29 changes: 29 additions & 0 deletions earlydecoder/backend.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package earlydecoder

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

func decodeBackendsBlock(block *hcl.Block) (backend.BackendData, hcl.Diagnostics) {
bType := block.Labels[0]
attrs, diags := block.Body.JustAttributes()

switch bType {
case "remote":
if attr, ok := attrs["hostname"]; ok {
val, vDiags := attr.Expr.Value(nil)
diags = append(diags, vDiags...)
if val.IsWhollyKnown() && val.Type() == cty.String {
return &backend.Remote{
Hostname: val.AsString(),
}, nil
}
}

return &backend.Remote{}, nil
}

return &backend.UnknownBackendData{}, diags
}
22 changes: 22 additions & 0 deletions earlydecoder/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,27 @@ func LoadModule(path string, files map[string]*hcl.File) (*module.Meta, hcl.Diag
coreRequirements = append(coreRequirements, c...)
}

var backend *module.Backend
if len(mod.Backends) == 1 {
for bType, data := range mod.Backends {
backend = &module.Backend{
Type: bType,
Data: data,
}
}
} else if len(mod.Backends) > 1 {
backendTypes := make([]string, len(mod.Backends))
for bType := range mod.Backends {
backendTypes = append(backendTypes, bType)
}

diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Unable to parse backend configuration",
Detail: fmt.Sprintf("Multiple backend definitions: %q", backendTypes),
})
}

var (
providerRequirements = make(map[tfaddr.Provider]version.Constraints, 0)
refs = make(map[module.ProviderRef]tfaddr.Provider, 0)
Expand Down Expand Up @@ -139,6 +160,7 @@ func LoadModule(path string, files map[string]*hcl.File) (*module.Meta, hcl.Diag

return &module.Meta{
Path: path,
Backend: backend,
ProviderReferences: refs,
ProviderRequirements: providerRequirements,
CoreRequirements: coreRequirements,
Expand Down
119 changes: 116 additions & 3 deletions earlydecoder/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hclsyntax"
tfaddr "github.com/hashicorp/terraform-registry-address"
"github.com/hashicorp/terraform-schema/backend"
"github.com/hashicorp/terraform-schema/module"
"github.com/zclconf/go-cty-debug/ctydebug"
"github.com/zclconf/go-cty/cty"
Expand Down Expand Up @@ -536,7 +537,7 @@ resource "google_something" "test" {
},
}

executeTestCases(testCases, t, path)
runTestCases(testCases, t, path)
}

func TestLoadModule_Variables(t *testing.T) {
Expand Down Expand Up @@ -787,11 +788,123 @@ output "name" {
nil,
},
}
executeTestCases(testCases, t, path)

runTestCases(testCases, t, path)
}
func executeTestCases(testCases []testCase, t *testing.T, path string) {

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

testCases := []testCase{
{
"no backend",
`
terraform {
}`,
&module.Meta{
Path: path,
Backend: nil,
ProviderReferences: map[module.ProviderRef]tfaddr.Provider{},
ProviderRequirements: map[tfaddr.Provider]version.Constraints{},
Variables: map[string]module.Variable{},
Outputs: map[string]module.Output{},
},
nil,
},
{
"s3 backend",
`
terraform {
backend "s3" {
blah = "test"
}
}`,
&module.Meta{
Path: path,
Backend: &module.Backend{
Type: "s3",
Data: &backend.UnknownBackendData{},
},
ProviderReferences: map[module.ProviderRef]tfaddr.Provider{},
ProviderRequirements: map[tfaddr.Provider]version.Constraints{},
Variables: map[string]module.Variable{},
Outputs: map[string]module.Output{},
},
nil,
},
{
"empty remote backend",
`
terraform {
backend "remote" {}
}`,
&module.Meta{
Path: path,
Backend: &module.Backend{
Type: "remote",
Data: &backend.Remote{},
},
ProviderReferences: map[module.ProviderRef]tfaddr.Provider{},
ProviderRequirements: map[tfaddr.Provider]version.Constraints{},
Variables: map[string]module.Variable{},
Outputs: map[string]module.Output{},
},
nil,
},
{
"remote backend with hostname",
`
terraform {
backend "remote" {
hostname = "app.terraform.io"
}
}`,
&module.Meta{
Path: path,
Backend: &module.Backend{
Type: "remote",
Data: &backend.Remote{Hostname: "app.terraform.io"},
},
ProviderReferences: map[module.ProviderRef]tfaddr.Provider{},
ProviderRequirements: map[tfaddr.Provider]version.Constraints{},
Variables: map[string]module.Variable{},
Outputs: map[string]module.Output{},
},
nil,
},
{
"remote backend with hostname and more attributes",
`
terraform {
backend "remote" {
hostname = "app.terraform.io"
organization = "test"
workspaces {
name = "test"
}
}
}`,
&module.Meta{
Path: path,
Backend: &module.Backend{
Type: "remote",
Data: &backend.Remote{Hostname: "app.terraform.io"},
},
ProviderReferences: map[module.ProviderRef]tfaddr.Provider{},
ProviderRequirements: map[tfaddr.Provider]version.Constraints{},
Variables: map[string]module.Variable{},
Outputs: map[string]module.Output{},
},
nil,
},
}

runTestCases(testCases, t, path)
}

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)
Expand Down
21 changes: 21 additions & 0 deletions earlydecoder/load_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/hashicorp/terraform-schema/backend"
"github.com/hashicorp/terraform-schema/internal/typeexpr"
"github.com/hashicorp/terraform-schema/module"
"github.com/zclconf/go-cty/cty"
Expand All @@ -15,6 +16,7 @@ import (
// decodedModule is the type representing a decoded Terraform module.
type decodedModule struct {
RequiredCore []string
Backends map[string]backend.BackendData
ProviderRequirements map[string]*providerRequirement
ProviderConfigs map[string]*providerConfig
Resources map[string]*resource
Expand All @@ -26,6 +28,7 @@ type decodedModule struct {
func newDecodedModule() *decodedModule {
return &decodedModule{
RequiredCore: make([]string, 0),
Backends: make(map[string]backend.BackendData),
ProviderRequirements: make(map[string]*providerRequirement),
ProviderConfigs: make(map[string]*providerConfig),
Resources: make(map[string]*resource),
Expand Down Expand Up @@ -68,6 +71,24 @@ func loadModuleFromFile(file *hcl.File, mod *decodedModule) hcl.Diagnostics {

for _, innerBlock := range content.Blocks {
switch innerBlock.Type {
case "backend":
bType := innerBlock.Labels[0]

data, bDiags := decodeBackendsBlock(innerBlock)
diags = append(diags, bDiags...)

if _, exists := mod.Backends[bType]; exists {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Multiple backend definitions",
Detail: fmt.Sprintf("Found multiple backend definitions for %q. Only one is allowed.", bType),
Subject: &innerBlock.DefRange,
})
continue
}

mod.Backends[bType] = data

case "required_providers":
reqs, reqsDiags := decodeRequiredProvidersBlock(innerBlock)
diags = append(diags, reqsDiags...)
Expand Down
4 changes: 4 additions & 0 deletions earlydecoder/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ var terraformBlockSchema = &hcl.BodySchema{
{
Type: "required_providers",
},
{
Type: "backend",
LabelNames: []string{"type"},
},
},
}

Expand Down
7 changes: 7 additions & 0 deletions module/meta.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,25 @@ package module
import (
"github.com/hashicorp/go-version"
tfaddr "github.com/hashicorp/terraform-registry-address"
"github.com/hashicorp/terraform-schema/backend"
)

type Meta struct {
Path string

Backend *Backend
ProviderReferences map[ProviderRef]tfaddr.Provider
ProviderRequirements map[tfaddr.Provider]version.Constraints
CoreRequirements version.Constraints
Variables map[string]Variable
Outputs map[string]Output
}

type Backend struct {
Type string
Data backend.BackendData
}

type ProviderRef struct {
LocalName string

Expand Down

0 comments on commit 21dfff1

Please sign in to comment.