예제 #1
0
func loadConfigOutputs(hclConfig *ast.ObjectList) ([]*tfcfg.Output, error) {
	hclConfig = hclConfig.Children()
	result := make([]*tfcfg.Output, 0, len(hclConfig.Items))

	if len(hclConfig.Items) == 0 {
		return result, nil
	}

	for _, item := range hclConfig.Items {
		n := item.Keys[0].Token.Value().(string)

		if _, ok := item.Val.(*ast.ObjectType); !ok {
			return nil, fmt.Errorf("output '%s': should be a block", n)
		}

		var config map[string]interface{}
		if err := hcl.DecodeObject(&config, item.Val); err != nil {
			return nil, err
		}

		rawConfig, err := tfcfg.NewRawConfig(config)
		if err != nil {
			return nil, fmt.Errorf(
				"error reading output config %s: %s", n, err,
			)
		}

		result = append(result, &tfcfg.Output{
			Name:      n,
			RawConfig: rawConfig,
		})
	}

	return result, nil
}
// GraphNodeFlattenable impl.
func (n *graphNodeModuleExpanded) FlattenGraph() *Graph {
	graph := n.Subgraph()
	input := n.Original.Module.RawConfig

	// Go over each vertex and do some modifications to the graph for
	// flattening. We have to skip some nodes (graphNodeModuleSkippable)
	// as well as setup the variable values.
	for _, v := range graph.Vertices() {
		// If this is a variable, then look it up in the raw configuration.
		// If it exists in the raw configuration, set the value of it.
		if vn, ok := v.(*GraphNodeConfigVariable); ok && input != nil {
			key := vn.VariableName()
			if v, ok := input.Raw[key]; ok {
				config, err := config.NewRawConfig(map[string]interface{}{
					key: v,
				})
				if err != nil {
					// This shouldn't happen because it is already in
					// a RawConfig above meaning it worked once before.
					panic(err)
				}

				// Set the variable value so it is interpolated properly.
				// Also set the module so we set the value on it properly.
				vn.Module = graph.Path[len(graph.Path)-1]
				vn.Value = config
			}
		}
	}

	return graph
}
예제 #3
0
func TestResourceProvider_Configure(t *testing.T) {
	rp := new(ResourceProvider)

	raw := map[string]interface{}{
		"token": "foo",
	}

	rawConfig, err := config.NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	err = rp.Configure(terraform.NewResourceConfig(rawConfig))
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	expected := Config{
		Token: "foo",
	}

	if !reflect.DeepEqual(rp.Config, expected) {
		t.Fatalf("bad: %#v", rp.Config)
	}
}
예제 #4
0
func TestProviderConfigure(t *testing.T) {
	p := Provider().(*schema.Provider)

	raw, err := config.NewRawConfig(map[string]interface{}{
		"account": helpers.TestAccount,
		"key":     helpers.TestKeyFile,
		"key_id":  helpers.TestKeyID,
		"url":     "https://us-east-1.api.joyentcloud.com",
	})
	if err != nil {
		t.Fatal(err)
	}
	providerConfig := terraform.NewResourceConfig(raw)

	assert.Nil(t, p.Meta())
	assert.Nil(t, p.Configure(providerConfig))

	config, ok := p.Meta().(*Config)
	if assert.True(t, ok) {
		assert.NotNil(t, config)
		assert.Equal(t, config.Account, helpers.TestAccount)
		assert.Equal(t, config.Key, helpers.TestKeyFile)
		assert.Equal(t, config.KeyID, helpers.TestKeyID)
		assert.Equal(t, config.URL, "https://us-east-1.api.joyentcloud.com")
	}
}
예제 #5
0
func TestProviderConfigure(t *testing.T) {
	var expectedKey string
	var expectedEmail string

	if v := os.Getenv("HEROKU_EMAIL"); v != "" {
		expectedEmail = v
	} else {
		expectedEmail = "foo"
	}

	if v := os.Getenv("HEROKU_API_KEY"); v != "" {
		expectedKey = v
	} else {
		expectedKey = "foo"
	}

	raw := map[string]interface{}{
		"api_key": expectedKey,
		"email":   expectedEmail,
	}

	rawConfig, err := config.NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	rp := Provider()
	err = rp.Configure(terraform.NewResourceConfig(rawConfig))
	if err != nil {
		t.Fatalf("err: %s", err)
	}
}
예제 #6
0
func TestResourceProvider_Configure(t *testing.T) {
	rp := new(ResourceProvider)

	raw := map[string]interface{}{
		"access_key": "foo",
		"secret_key": "bar",
		"region":     "us-east-1",
	}

	rawConfig, err := config.NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	err = rp.Configure(terraform.NewResourceConfig(rawConfig))
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	expected := Config{
		AccessKey: "foo",
		SecretKey: "bar",
		Region:    "us-east-1",
	}

	if !reflect.DeepEqual(rp.Config, expected) {
		t.Fatalf("bad: %#v", rp.Config)
	}
}
예제 #7
0
func TestProviderValidate(t *testing.T) {
	cases := []struct {
		P      *Provider
		Config map[string]interface{}
		Err    bool
	}{
		{
			P: &Provider{
				Schema: map[string]*Schema{
					"foo": &Schema{},
				},
			},
			Config: nil,
			Err:    true,
		},
	}

	for i, tc := range cases {
		c, err := config.NewRawConfig(tc.Config)
		if err != nil {
			t.Fatalf("err: %s", err)
		}

		_, es := tc.P.Validate(terraform.NewResourceConfig(c))
		if len(es) > 0 != tc.Err {
			t.Fatalf("%d: %#v", i, es)
		}
	}
}
예제 #8
0
func TestProviderConfigure(t *testing.T) {
	var expectedKey string

	if v := os.Getenv("MAILGUN_API_KEY"); v != "" {
		expectedKey = v
	} else {
		expectedKey = "foo"
	}

	raw := map[string]interface{}{
		"api_key": expectedKey,
	}

	rawConfig, err := config.NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	rp := Provider()
	err = rp.Configure(terraform.NewResourceConfig(rawConfig))
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	config := rp.Meta().(*mailgun.Client)
	if config.ApiKey != expectedKey {
		t.Fatalf("bad: %#v", config)
	}
}
예제 #9
0
func TestResourceProvider_Configure(t *testing.T) {
	rp := new(ResourceProvider)

	raw := map[string]interface{}{
		"address":    "demo.consul.io:80",
		"datacenter": "nyc1",
	}

	rawConfig, err := config.NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	err = rp.Configure(terraform.NewResourceConfig(rawConfig))
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	expected := Config{
		Address:    "demo.consul.io:80",
		Datacenter: "nyc1",
	}

	if !reflect.DeepEqual(rp.Config, expected) {
		t.Fatalf("bad: %#v", rp.Config)
	}
}
예제 #10
0
func testConfig(t *testing.T, c map[string]interface{}) *terraform.ResourceConfig {
	r, err := config.NewRawConfig(c)
	if err != nil {
		t.Fatalf("bad: %s", err)
	}

	return terraform.NewResourceConfig(r)
}
예제 #11
0
func testResourceConfig(
	t *testing.T, c map[string]interface{}) *ResourceConfig {
	raw, err := config.NewRawConfig(c)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	return NewResourceConfig(raw)
}
예제 #12
0
func loadConfigProviders(hclConfig *ast.ObjectList) ([]*tfcfg.ProviderConfig, error) {
	hclConfig = hclConfig.Children()
	result := make([]*tfcfg.ProviderConfig, 0, len(hclConfig.Items))

	if len(hclConfig.Items) == 0 {
		return result, nil
	}

	for _, item := range hclConfig.Items {
		n := item.Keys[0].Token.Value().(string)

		var listVal *ast.ObjectList
		if ot, ok := item.Val.(*ast.ObjectType); ok {
			listVal = ot.List
		} else {
			return nil, fmt.Errorf("provider '%s': should be a block", n)
		}

		var config map[string]interface{}
		if err := hcl.DecodeObject(&config, item.Val); err != nil {
			return nil, err
		}

		delete(config, "alias")

		rawConfig, err := tfcfg.NewRawConfig(config)
		if err != nil {
			return nil, fmt.Errorf(
				"error reading provider config %s: %s", n, err,
			)
		}

		// If we have an alias, add it in
		var alias string
		if a := listVal.Filter("alias"); len(a.Items) > 0 {
			err := hcl.DecodeObject(&alias, a.Items[0].Val)
			if err != nil {
				return nil, fmt.Errorf(
					"error reading provider %s alias: %s", n, err,
				)
			}
		}

		result = append(result, &tfcfg.ProviderConfig{
			Name:      n,
			Alias:     alias,
			RawConfig: rawConfig,
		})
	}

	return result, nil
}
예제 #13
0
// interpolateForce is a temporary thing. We want to get rid of interpolate
// above and likewise this, but it can only be done after the f-ast-graph
// refactor is complete.
func (c *ResourceConfig) interpolateForce() {
	if c.raw == nil {
		var err error
		c.raw, err = config.NewRawConfig(make(map[string]interface{}))
		if err != nil {
			panic(err)
		}
	}

	c.ComputedKeys = c.raw.UnknownKeys()
	c.Raw = c.raw.RawMap()
	c.Config = c.raw.Config()
}
예제 #14
0
func loadConfigModules(hclConfig *ast.ObjectList) ([]*tfcfg.Module, error) {
	hclConfig = hclConfig.Children()
	result := make([]*tfcfg.Module, 0, len(hclConfig.Items))

	if len(hclConfig.Items) == 0 {
		return result, nil
	}

	for _, item := range hclConfig.Items {
		n := item.Keys[0].Token.Value().(string)

		var listVal *ast.ObjectList
		if ot, ok := item.Val.(*ast.ObjectType); ok {
			listVal = ot.List
		} else {
			return nil, fmt.Errorf("module '%s': should be a block", n)
		}

		var config map[string]interface{}
		if err := hcl.DecodeObject(&config, item.Val); err != nil {
			return nil, err
		}

		delete(config, "source")

		rawConfig, err := tfcfg.NewRawConfig(config)
		if err != nil {
			return nil, fmt.Errorf(
				"error reading module config %s: %s", n, err,
			)
		}

		var source string
		if a := listVal.Filter("source"); len(a.Items) > 0 {
			err := hcl.DecodeObject(&source, a.Items[0].Val)
			if err != nil {
				return nil, fmt.Errorf(
					"error reading module %s source: %s", n, err,
				)
			}
		}

		result = append(result, &tfcfg.Module{
			Name:      n,
			Source:    source,
			RawConfig: rawConfig,
		})
	}

	return result, nil
}
예제 #15
0
func testConfigInterpolate(
	t *testing.T,
	raw map[string]interface{},
	vs map[string]ast.Variable) *terraform.ResourceConfig {
	rc, err := config.NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	if len(vs) > 0 {
		if err := rc.Interpolate(vs); err != nil {
			t.Fatalf("err: %s", err)
		}
	}

	return terraform.NewResourceConfig(rc)
}
예제 #16
0
func TestAzure_providerConfigure(t *testing.T) {
	home, err := homedir.Dir()
	if err != nil {
		t.Fatalf("Error fetching homedir: %s", err)
	}
	fh, err := ioutil.TempFile(home, "tf-test-home")
	if err != nil {
		t.Fatalf("Error creating homedir-based temporary file: %s", err)
	}
	defer os.Remove(fh.Name())

	_, err = io.WriteString(fh, testAzurePublishSettingsStr)
	if err != nil {
		t.Fatalf("err: %s", err)
	}
	fh.Close()

	r := strings.NewReplacer(home, "~")
	homePath := r.Replace(fh.Name())

	cases := []struct {
		SettingsFile string // String of XML or a path to an XML file
		NilMeta      bool   // whether meta is expected to be nil
	}{
		{testAzurePublishSettingsStr, false},
		{homePath, false},
	}

	for _, tc := range cases {
		rp := Provider()
		raw := map[string]interface{}{
			"settings_file": tc.SettingsFile,
		}

		rawConfig, err := config.NewRawConfig(raw)
		if err != nil {
			t.Fatalf("err: %s", err)
		}

		err = rp.Configure(terraform.NewResourceConfig(rawConfig))
		meta := rp.(*schema.Provider).Meta()
		if (meta == nil) != tc.NilMeta {
			t.Fatalf("expected NilMeta: %t, got meta: %#v, settings_file: %q",
				tc.NilMeta, meta, tc.SettingsFile)
		}
	}
}
예제 #17
0
func TestAzure_providerConfigure(t *testing.T) {
	rp := Provider()
	raw := map[string]interface{}{
		"publish_settings": testAzurePublishSettingsStr,
	}

	rawConfig, err := config.NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	err = rp.Configure(terraform.NewResourceConfig(rawConfig))
	meta := rp.(*schema.Provider).Meta()
	if meta == nil {
		t.Fatalf("Expected metadata, got nil: err: %s", err)
	}
}
예제 #18
0
func TestResourceProvider_Configure(t *testing.T) {
	rp := Provider()

	raw := map[string]interface{}{
		"address":    "demo.consul.io:80",
		"datacenter": "nyc3",
	}

	rawConfig, err := config.NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	err = rp.Configure(terraform.NewResourceConfig(rawConfig))
	if err != nil {
		t.Fatalf("err: %s", err)
	}
}
예제 #19
0
func TestResourceProvider_ConfigureBadRegion(t *testing.T) {
	rp := new(ResourceProvider)

	raw := map[string]interface{}{
		"access_key": "foo",
		"secret_key": "bar",
		"region":     "blah",
	}

	rawConfig, err := config.NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	err = rp.Configure(terraform.NewResourceConfig(rawConfig))
	if err == nil {
		t.Fatalf("should have err: bad region")
	}
}
func TestResourceProvider_Configure(t *testing.T) {
	rp := new(ResourceProvider)
	var expectedToken string
	var expectedEmail string

	if v := os.Getenv("DNSIMPLE_EMAIL"); v != "" {
		expectedEmail = v
	} else {
		expectedEmail = "foo"
	}

	if v := os.Getenv("DNSIMPLE_TOKEN"); v != "" {
		expectedToken = v
	} else {
		expectedToken = "foo"
	}

	raw := map[string]interface{}{
		"token": expectedToken,
		"email": expectedEmail,
	}

	rawConfig, err := config.NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	err = rp.Configure(terraform.NewResourceConfig(rawConfig))
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	expected := Config{
		Token: expectedToken,
		Email: expectedEmail,
	}

	if !reflect.DeepEqual(rp.Config, expected) {
		t.Fatalf("bad: %#v", rp.Config)
	}
}
예제 #21
0
파일: session.go 프로젝트: ryane/terraform
func (s *Session) handleEval(line string) (string, error) {
	// Wrap the line to make it an interpolation.
	line = fmt.Sprintf("${%s}", line)

	// Parse the line
	raw, err := config.NewRawConfig(map[string]interface{}{
		"value": line,
	})
	if err != nil {
		return "", err
	}

	// Set the value
	raw.Key = "value"

	// Get the values
	vars, err := s.Interpolater.Values(&terraform.InterpolationScope{
		Path: []string{"root"},
	}, raw.Variables)
	if err != nil {
		return "", err
	}

	// Interpolate
	if err := raw.Interpolate(vars); err != nil {
		return "", err
	}

	// If we have any unknown keys, let the user know.
	if ks := raw.UnknownKeys(); len(ks) > 0 {
		return "", fmt.Errorf("unknown values referenced, can't compute value")
	}

	// Read the value
	result, err := FormatResult(raw.Value())
	if err != nil {
		return "", err
	}

	return result, nil
}
func TestResourceProvider_Configure(t *testing.T) {
	rp := new(ResourceProvider)
	var expectedKey string
	var expectedEmail string

	if v := os.Getenv("HEROKU_EMAIL"); v != "" {
		expectedEmail = v
	} else {
		expectedEmail = "foo"
	}

	if v := os.Getenv("HEROKU_API_KEY"); v != "" {
		expectedKey = v
	} else {
		expectedKey = "foo"
	}

	raw := map[string]interface{}{
		"api_key": expectedKey,
		"email":   expectedEmail,
	}

	rawConfig, err := config.NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	err = rp.Configure(terraform.NewResourceConfig(rawConfig))
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	expected := Config{
		APIKey: expectedKey,
		Email:  expectedEmail,
	}

	if !reflect.DeepEqual(rp.Config, expected) {
		t.Fatalf("bad: %#v", rp.Config)
	}
}
예제 #23
0
func TestResourceProvider_ConfigureTLS(t *testing.T) {
	rp := Provider()

	raw := map[string]interface{}{
		"address":    "demo.consul.io:80",
		"ca_file":    "test-fixtures/cacert.pem",
		"cert_file":  "test-fixtures/usercert.pem",
		"datacenter": "nyc3",
		"key_file":   "test-fixtures/userkey.pem",
		"scheme":     "https",
	}

	rawConfig, err := config.NewRawConfig(raw)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	err = rp.Configure(terraform.NewResourceConfig(rawConfig))
	if err != nil {
		t.Fatalf("err: %s", err)
	}
}
예제 #24
0
func testConfig(
	t *testing.T,
	c map[string]interface{},
	vs map[string]string) *terraform.ResourceConfig {
	rc, err := config.NewRawConfig(c)
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	if len(vs) > 0 {
		vars := make(map[string]ast.Variable)
		for k, v := range vs {
			vars[k] = ast.Variable{Value: v, Type: ast.TypeString}
		}

		if err := rc.Interpolate(vars); err != nil {
			t.Fatalf("err: %s", err)
		}
	}

	return terraform.NewResourceConfig(rc)
}
예제 #25
0
func TestProviderValidateResource(t *testing.T) {
	cases := []struct {
		P      *Provider
		Type   string
		Config map[string]interface{}
		Err    bool
	}{
		{
			P:      &Provider{},
			Type:   "foo",
			Config: nil,
			Err:    true,
		},

		{
			P: &Provider{
				ResourcesMap: map[string]*Resource{
					"foo": &Resource{},
				},
			},
			Type:   "foo",
			Config: nil,
			Err:    false,
		},
	}

	for i, tc := range cases {
		c, err := config.NewRawConfig(tc.Config)
		if err != nil {
			t.Fatalf("err: %s", err)
		}

		_, es := tc.P.ValidateResource(tc.Type, terraform.NewResourceConfig(c))
		if len(es) > 0 != tc.Err {
			t.Fatalf("%d: %#v", i, es)
		}
	}
}
예제 #26
0
func (n *EvalBuildProviderConfig) Eval(ctx EvalContext) (interface{}, error) {
	cfg := *n.Config

	// If we have a configuration set, then merge that in
	if input := ctx.ProviderInput(n.Provider); input != nil {
		rc, err := config.NewRawConfig(input)
		if err != nil {
			return nil, err
		}

		merged := cfg.raw.Merge(rc)
		cfg = NewResourceConfig(merged)
	}

	// Get the parent configuration if there is one
	if parent := ctx.ParentProviderConfig(n.Provider); parent != nil {
		merged := cfg.raw.Merge(parent.raw)
		cfg = NewResourceConfig(merged)
	}

	*n.Output = cfg
	return nil, nil
}
func TestEvalInterpolate(t *testing.T) {
	config, err := config.NewRawConfig(map[string]interface{}{})
	if err != nil {
		t.Fatalf("err: %s", err)
	}

	var actual *ResourceConfig
	n := &EvalInterpolate{Config: config, Output: &actual}
	result := testResourceConfig(t, map[string]interface{}{})
	ctx := &MockEvalContext{InterpolateConfigResult: result}
	if _, err := n.Eval(ctx); err != nil {
		t.Fatalf("err: %s", err)
	}
	if actual != result {
		t.Fatalf("bad: %#v", actual)
	}

	if !ctx.InterpolateCalled {
		t.Fatal("should be called")
	}
	if !reflect.DeepEqual(ctx.InterpolateConfig, config) {
		t.Fatalf("bad: %#v", ctx.InterpolateConfig)
	}
}
예제 #28
0
func TestResourceConfigGet(t *testing.T) {
	cases := []struct {
		Config map[string]interface{}
		Vars   map[string]string
		Key    string
		Value  interface{}
	}{
		{
			Config: nil,
			Key:    "foo",
			Value:  nil,
		},

		{
			Config: map[string]interface{}{
				"foo": "${var.foo}",
			},
			Key:   "foo",
			Value: "${var.foo}",
		},

		{
			Config: map[string]interface{}{
				"foo": "${var.foo}",
			},
			Vars:  map[string]string{"foo": "bar"},
			Key:   "foo",
			Value: "bar",
		},

		{
			Config: map[string]interface{}{
				"foo": []interface{}{1, 2, 5},
			},
			Key:   "foo.0",
			Value: 1,
		},

		{
			Config: map[string]interface{}{
				"foo": []interface{}{1, 2, 5},
			},
			Key:   "foo.5",
			Value: nil,
		},
	}

	for i, tc := range cases {
		var rawC *config.RawConfig
		if tc.Config != nil {
			var err error
			rawC, err = config.NewRawConfig(tc.Config)
			if err != nil {
				t.Fatalf("err: %s", err)
			}
		}

		rc := NewResourceConfig(rawC)
		if tc.Vars != nil {
			ctx := NewContext(&ContextOpts{Variables: tc.Vars})
			err := rc.interpolate(
				ctx.walkContext(walkInvalid, rootModulePath),
				nil)
			if err != nil {
				t.Fatalf("err: %s", err)
			}
		}

		v, _ := rc.Get(tc.Key)
		if !reflect.DeepEqual(v, tc.Value) {
			t.Fatalf("%d bad: %#v", i, v)
		}
	}
}
예제 #29
0
func TestProviderConfigure(t *testing.T) {
	cases := []struct {
		P      *Provider
		Config map[string]interface{}
		Err    bool
	}{
		{
			P:      &Provider{},
			Config: nil,
			Err:    false,
		},

		{
			P: &Provider{
				Schema: map[string]*Schema{
					"foo": &Schema{
						Type:     TypeInt,
						Optional: true,
					},
				},

				ConfigureFunc: func(d *ResourceData) (interface{}, error) {
					if d.Get("foo").(int) == 42 {
						return nil, nil
					}

					return nil, fmt.Errorf("nope")
				},
			},
			Config: map[string]interface{}{
				"foo": 42,
			},
			Err: false,
		},

		{
			P: &Provider{
				Schema: map[string]*Schema{
					"foo": &Schema{
						Type:     TypeInt,
						Optional: true,
					},
				},

				ConfigureFunc: func(d *ResourceData) (interface{}, error) {
					if d.Get("foo").(int) == 42 {
						return nil, nil
					}

					return nil, fmt.Errorf("nope")
				},
			},
			Config: map[string]interface{}{
				"foo": 52,
			},
			Err: true,
		},
	}

	for i, tc := range cases {
		c, err := config.NewRawConfig(tc.Config)
		if err != nil {
			t.Fatalf("err: %s", err)
		}

		err = tc.P.Configure(terraform.NewResourceConfig(c))
		if err != nil != tc.Err {
			t.Fatalf("%d: %s", i, err)
		}
	}
}
예제 #30
0
func TestResourceConfigGet(t *testing.T) {
	cases := []struct {
		Config map[string]interface{}
		Vars   map[string]string
		Key    string
		Value  interface{}
	}{
		{
			Config: nil,
			Key:    "foo",
			Value:  nil,
		},

		{
			Config: map[string]interface{}{
				"foo": "${var.foo}",
			},
			Key:   "foo",
			Value: "${var.foo}",
		},

		{
			Config: map[string]interface{}{
				"foo": "${var.foo}",
			},
			Vars:  map[string]string{"foo": "bar"},
			Key:   "foo",
			Value: "bar",
		},

		{
			Config: map[string]interface{}{
				"foo": []interface{}{1, 2, 5},
			},
			Key:   "foo.0",
			Value: 1,
		},

		{
			Config: map[string]interface{}{
				"foo": []interface{}{1, 2, 5},
			},
			Key:   "foo.5",
			Value: nil,
		},

		// get from map
		{
			Config: map[string]interface{}{
				"mapname": []map[string]interface{}{
					map[string]interface{}{"key": 1},
				},
			},
			Key:   "mapname.0.key",
			Value: 1,
		},

		// get from map with dot in key
		{
			Config: map[string]interface{}{
				"mapname": []map[string]interface{}{
					map[string]interface{}{"key.name": 1},
				},
			},
			Key:   "mapname.0.key.name",
			Value: 1,
		},

		// get from map with overlapping key names
		{
			Config: map[string]interface{}{
				"mapname": []map[string]interface{}{
					map[string]interface{}{
						"key.name":   1,
						"key.name.2": 2,
					},
				},
			},
			Key:   "mapname.0.key.name.2",
			Value: 2,
		},
		{
			Config: map[string]interface{}{
				"mapname": []map[string]interface{}{
					map[string]interface{}{
						"key.name":     1,
						"key.name.foo": 2,
					},
				},
			},
			Key:   "mapname.0.key.name",
			Value: 1,
		},
		{
			Config: map[string]interface{}{
				"mapname": []map[string]interface{}{
					map[string]interface{}{
						"listkey": []map[string]interface{}{
							{"key": 3},
						},
					},
				},
			},
			Key:   "mapname.0.listkey.0.key",
			Value: 3,
		},
		// FIXME: this is ambiguous, and matches the nested map
		//        leaving here to catch this behaviour if it changes.
		{
			Config: map[string]interface{}{
				"mapname": []map[string]interface{}{
					map[string]interface{}{
						"key.name":   1,
						"key.name.0": 2,
						"key":        map[string]interface{}{"name": 3},
					},
				},
			},
			Key:   "mapname.0.key.name",
			Value: 3,
		},
		/*
			// TODO: can't access this nested list at all.
			// FIXME: key with name matching substring of nested list can panic
			{
				Config: map[string]interface{}{
					"mapname": []map[string]interface{}{
						map[string]interface{}{
							"key.name": []map[string]interface{}{
								{"subkey": 1},
							},
							"key": 3,
						},
					},
				},
				Key:   "mapname.0.key.name.0.subkey",
				Value: 3,
			},
		*/
	}

	for i, tc := range cases {
		var rawC *config.RawConfig
		if tc.Config != nil {
			var err error
			rawC, err = config.NewRawConfig(tc.Config)
			if err != nil {
				t.Fatalf("err: %s", err)
			}
		}

		if tc.Vars != nil {
			vs := make(map[string]ast.Variable)
			for k, v := range tc.Vars {
				vs["var."+k] = ast.Variable{Value: v, Type: ast.TypeString}
			}

			if err := rawC.Interpolate(vs); err != nil {
				t.Fatalf("err: %s", err)
			}
		}

		rc := NewResourceConfig(rawC)
		rc.interpolateForce()

		// Test getting a key
		t.Run(fmt.Sprintf("get-%d", i), func(t *testing.T) {
			v, _ := rc.Get(tc.Key)
			if !reflect.DeepEqual(v, tc.Value) {
				t.Fatalf("%d bad: %#v", i, v)
			}
		})

		// If we have vars, we don't test copying
		if len(tc.Vars) > 0 {
			continue
		}

		// Test copying and equality
		t.Run(fmt.Sprintf("copy-and-equal-%d", i), func(t *testing.T) {
			copy := rc.DeepCopy()
			if !reflect.DeepEqual(copy, rc) {
				t.Fatalf("bad:\n\n%#v\n\n%#v", copy, rc)
			}

			if !copy.Equal(rc) {
				t.Fatalf("copy != rc:\n\n%#v\n\n%#v", copy, rc)
			}
			if !rc.Equal(copy) {
				t.Fatalf("rc != copy:\n\n%#v\n\n%#v", copy, rc)
			}
		})
	}
}