config: "ResourceMode" concept for resources

Previously resources were assumed to always support the full set of
create, read, update and delete operations, and Terraform's resource
management lifecycle.

Data sources introduce a new kind of resource that only supports the
"read" operation. To support this, a new "Mode" field is added to
the Resource concept within the config layer, which can be set to
ManagedResourceMode (to indicate the only mode previously possible) or
DataResourceMode (to indicate that only "read" is supported).

To support both managed and data resources in the tests, the
stringification of resources in config_string.go is adjusted slightly
to use the Id() method rather than the unusual type[name] serialization
from before, causing a simple mechanical adjustment to the loader tests'
expected result strings.
This commit is contained in:
Martin Atkins 2016-05-01 14:43:05 -07:00
parent f331240601
commit fc4fa10981
6 changed files with 68 additions and 32 deletions

View File

@ -67,9 +67,11 @@ type ProviderConfig struct {
} }
// A resource represents a single Terraform resource in the configuration. // A resource represents a single Terraform resource in the configuration.
// A Terraform resource is something that represents some component that // A Terraform resource is something that supports some or all of the
// can be created and managed, and has some properties associated with it. // usual "create, read, update, delete" operations, depending on
// the given Mode.
type Resource struct { type Resource struct {
Mode ResourceMode // which operations the resource supports
Name string Name string
Type string Type string
RawCount *RawConfig RawCount *RawConfig
@ -85,6 +87,7 @@ type Resource struct {
// interpolation. // interpolation.
func (r *Resource) Copy() *Resource { func (r *Resource) Copy() *Resource {
n := &Resource{ n := &Resource{
Mode: r.Mode,
Name: r.Name, Name: r.Name,
Type: r.Type, Type: r.Type,
RawCount: r.RawCount.Copy(), RawCount: r.RawCount.Copy(),
@ -210,7 +213,14 @@ func (r *Resource) Count() (int, error) {
// A unique identifier for this resource. // A unique identifier for this resource.
func (r *Resource) Id() string { func (r *Resource) Id() string {
switch r.Mode {
case ManagedResourceMode:
return fmt.Sprintf("%s.%s", r.Type, r.Name) return fmt.Sprintf("%s.%s", r.Type, r.Name)
case DataResourceMode:
return fmt.Sprintf("data.%s.%s", r.Type, r.Name)
default:
panic(fmt.Errorf("unknown resource mode %s", r.Mode))
}
} }
// Validate does some basic semantic checking of the configuration. // Validate does some basic semantic checking of the configuration.
@ -804,13 +814,14 @@ func (c *ProviderConfig) mergerMerge(m merger) merger {
} }
func (r *Resource) mergerName() string { func (r *Resource) mergerName() string {
return fmt.Sprintf("%s.%s", r.Type, r.Name) return r.Id()
} }
func (r *Resource) mergerMerge(m merger) merger { func (r *Resource) mergerMerge(m merger) merger {
r2 := m.(*Resource) r2 := m.(*Resource)
result := *r result := *r
result.Mode = r2.Mode
result.Name = r2.Name result.Name = r2.Name
result.Type = r2.Type result.Type = r2.Type
result.RawConfig = result.RawConfig.merge(r2.RawConfig) result.RawConfig = result.RawConfig.merge(r2.RawConfig)

View File

@ -178,7 +178,7 @@ func resourcesStr(rs []*Resource) string {
ks := make([]string, 0, len(rs)) ks := make([]string, 0, len(rs))
mapping := make(map[string]int) mapping := make(map[string]int)
for i, r := range rs { for i, r := range rs {
k := fmt.Sprintf("%s[%s]", r.Type, r.Name) k := r.Id()
ks = append(ks, k) ks = append(ks, k)
mapping[k] = i mapping[k] = i
} }
@ -190,9 +190,8 @@ func resourcesStr(rs []*Resource) string {
for _, i := range order { for _, i := range order {
r := rs[i] r := rs[i]
result += fmt.Sprintf( result += fmt.Sprintf(
"%s[%s] (x%s)\n", "%s (x%s)\n",
r.Type, r.Id(),
r.Name,
r.RawCount.Value()) r.RawCount.Value())
ks := make([]string, 0, len(r.RawConfig.Raw)) ks := make([]string, 0, len(r.RawConfig.Raw))

View File

@ -566,6 +566,7 @@ func loadResourcesHcl(list *ast.ObjectList) ([]*Resource, error) {
} }
result = append(result, &Resource{ result = append(result, &Resource{
Mode: ManagedResourceMode,
Name: k, Name: k,
Type: t, Type: t,
RawCount: countConfig, RawCount: countConfig,

View File

@ -742,13 +742,13 @@ func TestLoad_jsonAttributes(t *testing.T) {
} }
const jsonAttributeStr = ` const jsonAttributeStr = `
cloudstack_firewall[test] (x1) cloudstack_firewall.test (x1)
ipaddress ipaddress
rule rule
` `
const windowsHeredocResourcesStr = ` const windowsHeredocResourcesStr = `
aws_instance[test] (x1) aws_instance.test (x1)
user_data user_data
` `
@ -759,17 +759,17 @@ aws
` `
const heredocResourcesStr = ` const heredocResourcesStr = `
aws_iam_policy[policy] (x1) aws_iam_policy.policy (x1)
description description
name name
path path
policy policy
aws_instance[heredocwithnumbers] (x1) aws_instance.heredocwithnumbers (x1)
ami ami
provisioners provisioners
local-exec local-exec
command command
aws_instance[test] (x1) aws_instance.test (x1)
ami ami
provisioners provisioners
remote-exec remote-exec
@ -777,7 +777,7 @@ aws_instance[test] (x1)
` `
const escapedquotesResourcesStr = ` const escapedquotesResourcesStr = `
aws_instance[quotes] (x1) aws_instance.quotes (x1)
ami ami
vars vars
user: var.ami user: var.ami
@ -800,7 +800,7 @@ do
` `
const basicResourcesStr = ` const basicResourcesStr = `
aws_instance[db] (x1) aws_instance.db (x1)
VPC VPC
security_groups security_groups
provisioners provisioners
@ -811,7 +811,7 @@ aws_instance[db] (x1)
aws_instance.web aws_instance.web
vars vars
resource: aws_security_group.firewall.*.id resource: aws_security_group.firewall.*.id
aws_instance[web] (x1) aws_instance.web (x1)
ami ami
network_interface network_interface
security_groups security_groups
@ -822,7 +822,7 @@ aws_instance[web] (x1)
vars vars
resource: aws_security_group.firewall.foo resource: aws_security_group.firewall.foo
user: var.foo user: var.foo
aws_security_group[firewall] (x5) aws_security_group.firewall (x5)
` `
const basicVariablesStr = ` const basicVariablesStr = `
@ -854,18 +854,18 @@ do
` `
const dirBasicResourcesStr = ` const dirBasicResourcesStr = `
aws_instance[db] (x1) aws_instance.db (x1)
security_groups security_groups
vars vars
resource: aws_security_group.firewall.*.id resource: aws_security_group.firewall.*.id
aws_instance[web] (x1) aws_instance.web (x1)
ami ami
network_interface network_interface
security_groups security_groups
vars vars
resource: aws_security_group.firewall.foo resource: aws_security_group.firewall.foo
user: var.foo user: var.foo
aws_security_group[firewall] (x5) aws_security_group.firewall (x5)
` `
const dirBasicVariablesStr = ` const dirBasicVariablesStr = `
@ -891,10 +891,10 @@ do
` `
const dirOverrideResourcesStr = ` const dirOverrideResourcesStr = `
aws_instance[db] (x1) aws_instance.db (x1)
ami ami
security_groups security_groups
aws_instance[web] (x1) aws_instance.web (x1)
ami ami
foo foo
network_interface network_interface
@ -902,7 +902,7 @@ aws_instance[web] (x1)
vars vars
resource: aws_security_group.firewall.foo resource: aws_security_group.firewall.foo
user: var.foo user: var.foo
aws_security_group[firewall] (x5) aws_security_group.firewall (x5)
` `
const dirOverrideVariablesStr = ` const dirOverrideVariablesStr = `
@ -918,8 +918,8 @@ aws
` `
const importResourcesStr = ` const importResourcesStr = `
aws_security_group[db] (x1) aws_security_group.db (x1)
aws_security_group[web] (x1) aws_security_group.web (x1)
` `
const importVariablesStr = ` const importVariablesStr = `
@ -938,7 +938,7 @@ bar
` `
const provisionerResourcesStr = ` const provisionerResourcesStr = `
aws_instance[web] (x1) aws_instance.web (x1)
ami ami
security_groups security_groups
provisioners provisioners
@ -950,7 +950,7 @@ aws_instance[web] (x1)
` `
const connectionResourcesStr = ` const connectionResourcesStr = `
aws_instance[web] (x1) aws_instance.web (x1)
ami ami
security_groups security_groups
provisioners provisioners
@ -976,17 +976,17 @@ foo (required)
` `
const createBeforeDestroyResourcesStr = ` const createBeforeDestroyResourcesStr = `
aws_instance[bar] (x1) aws_instance.bar (x1)
ami ami
aws_instance[web] (x1) aws_instance.web (x1)
ami ami
` `
const ignoreChangesResourcesStr = ` const ignoreChangesResourcesStr = `
aws_instance[bar] (x1) aws_instance.bar (x1)
ami ami
aws_instance[baz] (x1) aws_instance.baz (x1)
ami ami
aws_instance[web] (x1) aws_instance.web (x1)
ami ami
` `

9
config/resource_mode.go Normal file
View File

@ -0,0 +1,9 @@
package config
//go:generate stringer -type=ResourceMode -output=resource_mode_string.go resource_mode.go
type ResourceMode int
const (
ManagedResourceMode ResourceMode = iota
DataResourceMode
)

View File

@ -0,0 +1,16 @@
// Code generated by "stringer -type=ResourceMode -output=resource_mode_string.go resource_mode.go"; DO NOT EDIT
package config
import "fmt"
const _ResourceMode_name = "ManagedResourceModeDataResourceMode"
var _ResourceMode_index = [...]uint8{0, 19, 35}
func (i ResourceMode) String() string {
if i < 0 || i >= ResourceMode(len(_ResourceMode_index)-1) {
return fmt.Sprintf("ResourceMode(%d)", i)
}
return _ResourceMode_name[_ResourceMode_index[i]:_ResourceMode_index[i+1]]
}