Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
11 changes: 11 additions & 0 deletions internal/addrs/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,17 @@ func (r AbsResourceInstance) Less(o AbsResourceInstance) bool {
}
}

// InModule returns a new AbsResourceInstance with the module path
// prefixed by module
func (r AbsResourceInstance) InModule(module ModuleInstance) AbsResourceInstance {
modPath := make(ModuleInstance, 0, len(module)+len(r.Module))
modPath = append(modPath, module...)
modPath = append(modPath, r.Module...)
ret := r // copy address
ret.Module = modPath
return ret
}

// AbsResourceInstance is a Checkable
func (r AbsResourceInstance) checkableSigil() {}

Expand Down
83 changes: 83 additions & 0 deletions internal/configs/import.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package configs

import (
"fmt"

"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/zclconf/go-cty/cty"
)

type Import struct {
ID string
To addrs.AbsResourceInstance

DeclRange hcl.Range
}

func decodeImportBlock(block *hcl.Block) (*Import, hcl.Diagnostics) {
var diags hcl.Diagnostics
imp := &Import{
DeclRange: block.DefRange,
}

content, moreDiags := block.Body.Content(importBlockSchema)
diags = append(diags, moreDiags...)

if attr, exists := content.Attributes["id"]; exists {
id, idDiags := decodeId(attr.Expr)
diags = append(diags, idDiags...)
imp.ID = id
}

if attr, exists := content.Attributes["to"]; exists {
to, traversalDiags := hcl.AbsTraversalForExpr(attr.Expr)
diags = append(diags, traversalDiags...)
if !traversalDiags.HasErrors() {
to, toDiags := addrs.ParseAbsResourceInstance(to)
diags = append(diags, toDiags.ToHCL()...)
imp.To = to
if !toDiags.HasErrors() && to.Resource.Resource.Mode != addrs.ManagedResourceMode {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid Resource",
Detail: fmt.Sprintf("%v is not a managed resource. Importing into a data source is not allowed.", to),
Subject: attr.Expr.Range().Ptr(),
})
}
}
}

return imp, diags

}

func decodeId(expr hcl.Expression) (string, hcl.Diagnostics) {
id, diags := expr.Value(nil)
if diags.HasErrors() {
return "", diags
}
if id.Type() != cty.String {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Invalid Attribute",
Detail: fmt.Sprintf("Invalid attribute value for import id: %#v", id),
Subject: expr.Range().Ptr(),
})
return "", diags
}
return id.AsString(), diags
}

var importBlockSchema = &hcl.BodySchema{
Attributes: []hcl.AttributeSchema{
{
Name: "id",
Required: true,
},
{
Name: "to",
Required: true,
},
},
}
181 changes: 181 additions & 0 deletions internal/configs/import_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
package configs

import (
"testing"

"github.com/google/go-cmp/cmp"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/hcltest"
"github.com/hashicorp/terraform/internal/addrs"
"github.com/zclconf/go-cty/cty"
)

func TestImportBlock_decode(t *testing.T) {
blockRange := hcl.Range{
Filename: "mock.tf",
Start: hcl.Pos{Line: 3, Column: 12, Byte: 27},
End: hcl.Pos{Line: 3, Column: 19, Byte: 34},
}

id := "test1"
id_expr := hcltest.MockExprLiteral(cty.StringVal(id))

res_expr := hcltest.MockExprTraversalSrc("test_instance.foo")

index_expr := hcltest.MockExprTraversalSrc("test_instance.foo[1]")

mod_expr := hcltest.MockExprTraversalSrc("module.foo.test_instance.this")

tests := map[string]struct {
input *hcl.Block
want *Import
err string
}{
"success": {
&hcl.Block{
Type: "import",
Body: hcltest.MockBody(&hcl.BodyContent{
Attributes: hcl.Attributes{
"id": {
Name: "id",
Expr: id_expr,
},
"to": {
Name: "to",
Expr: res_expr,
},
},
}),
DefRange: blockRange,
},
&Import{
ID: id,
To: mustImportEndpointFromExpr(res_expr),
DeclRange: blockRange,
},
``,
},
"indexed_resource": {
&hcl.Block{
Type: "import",
Body: hcltest.MockBody(&hcl.BodyContent{
Attributes: hcl.Attributes{
"id": {
Name: "id",
Expr: id_expr,
},
"to": {
Name: "to",
Expr: index_expr,
},
},
}),
DefRange: blockRange,
},
&Import{
ID: id,
To: mustImportEndpointFromExpr(index_expr),
DeclRange: blockRange,
},
``,
},
"module": {
&hcl.Block{
Type: "import",
Body: hcltest.MockBody(&hcl.BodyContent{
Attributes: hcl.Attributes{
"id": {
Name: "id",
Expr: id_expr,
},
"to": {
Name: "to",
Expr: mod_expr,
},
},
}),
DefRange: blockRange,
},
&Import{
ID: id,
To: mustImportEndpointFromExpr(mod_expr),
DeclRange: blockRange,
},
``,
},
"error: missing argument": {
&hcl.Block{
Type: "import",
Body: hcltest.MockBody(&hcl.BodyContent{
Attributes: hcl.Attributes{
"id": {
Name: "id",
Expr: id_expr,
},
},
}),
DefRange: blockRange,
},
&Import{
ID: id,
DeclRange: blockRange,
},
"Missing required argument",
},
"error: type mismatch": {
&hcl.Block{
Type: "import",
Body: hcltest.MockBody(&hcl.BodyContent{
Attributes: hcl.Attributes{
"id": {
Name: "id",
Expr: hcltest.MockExprLiteral(cty.NumberIntVal(0)),
},
"to": {
Name: "to",
Expr: res_expr,
},
},
}),
DefRange: blockRange,
},
&Import{
To: mustImportEndpointFromExpr(res_expr),
DeclRange: blockRange,
},
"Invalid Attribute",
},
}

for name, test := range tests {
t.Run(name, func(t *testing.T) {
got, diags := decodeImportBlock(test.input)

if diags.HasErrors() {
if test.err == "" {
t.Fatalf("unexpected error: %s", diags.Errs())
}
if gotErr := diags[0].Summary; gotErr != test.err {
t.Errorf("wrong error, got %q, want %q", gotErr, test.err)
}
} else if test.err != "" {
t.Fatalf("expected error")
}
if !cmp.Equal(got, test.want, cmp.AllowUnexported(addrs.AbsResourceInstance{})) {
t.Fatalf("wrong result: %s", cmp.Diff(got, test.want))
}
})
}
}

func mustImportEndpointFromExpr(expr hcl.Expression) addrs.AbsResourceInstance {
traversal, hcldiags := hcl.AbsTraversalForExpr(expr)
if hcldiags.HasErrors() {
panic(hcldiags.Errs())
}
ep, diags := addrs.ParseAbsResourceInstance(traversal)
if diags.HasErrors() {
panic(diags.Err())
}
return ep
}
18 changes: 15 additions & 3 deletions internal/configs/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ type Module struct {
ManagedResources map[string]*Resource
DataResources map[string]*Resource

Moved []*Moved
Moved []*Moved
Import []*Import
}

// File describes the contents of a single configuration file.
Expand Down Expand Up @@ -78,7 +79,8 @@ type File struct {
ManagedResources []*Resource
DataResources []*Resource

Moved []*Moved
Moved []*Moved
Import []*Import
}

// NewModule takes a list of primary files and a list of override files and
Expand Down Expand Up @@ -357,10 +359,11 @@ func (m *Module) appendFile(file *File) hcl.Diagnostics {
}
}

// "Moved" blocks just append, because they are all independent
// "Moved" and "Import" blocks just append, because they are all independent
// of one another at this level. (We handle any references between
// them at runtime.)
m.Moved = append(m.Moved, file.Moved...)
m.Import = append(m.Import, file.Import...)

return diags
}
Expand Down Expand Up @@ -543,6 +546,15 @@ func (m *Module) mergeFile(file *File) hcl.Diagnostics {
})
}

for _, i := range file.Import {
diags = append(diags, &hcl.Diagnostic{
Severity: hcl.DiagError,
Summary: "Cannot override 'import' blocks",
Detail: "Records of import objects can appear only in normal files, not in override files.",
Subject: i.DeclRange.Ptr(),
})
}

return diags
}

Expand Down
10 changes: 10 additions & 0 deletions internal/configs/parser_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,13 @@ func (p *Parser) loadConfigFile(path string, override bool) (*File, hcl.Diagnost
file.Moved = append(file.Moved, cfg)
}

case "import":
cfg, cfgDiags := decodeImportBlock(block)
diags = append(diags, cfgDiags...)
if cfg != nil {
file.Import = append(file.Import, cfg)
}

default:
// Should never happen because the above cases should be exhaustive
// for all block type names in our schema.
Expand Down Expand Up @@ -252,6 +259,9 @@ var configFileSchema = &hcl.BodySchema{
{
Type: "moved",
},
{
Type: "import",
},
},
}

Expand Down
Loading