Merge pull request #20094 from hashicorp/jbardin/missing-map-values

handle empty map values
This commit is contained in:
James Bardin 2019-01-23 20:28:34 -05:00 committed by GitHub
commit 1592b4aa67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 278 additions and 23 deletions

View File

@ -29,6 +29,7 @@ func Provider() terraform.ResourceProvider {
"test_resource_deprecated": testResourceDeprecated(),
"test_resource_defaults": testResourceDefaults(),
"test_resource_list": testResourceList(),
"test_resource_map": testResourceMap(),
},
DataSourcesMap: map[string]*schema.Resource{
"test_data_source": testDataSource(),

View File

@ -33,6 +33,89 @@ resource "test_resource_defaults" "foo" {
})
}
func TestResourceDefaults_change(t *testing.T) {
resource.UnitTest(t, resource.TestCase{
Providers: testAccProviders,
CheckDestroy: testAccCheckResourceDestroy,
Steps: []resource.TestStep{
{
Config: strings.TrimSpace(`
resource "test_resource_defaults" "foo" {
}
`),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"test_resource_defaults.foo", "default_string", "default string",
),
resource.TestCheckResourceAttr(
"test_resource_defaults.foo", "default_bool", "1",
),
resource.TestCheckNoResourceAttr(
"test_resource_defaults.foo", "nested.#",
),
),
},
{
Config: strings.TrimSpace(`
resource "test_resource_defaults" "foo" {
default_string = "new"
default_bool = false
nested {
optional = "nested"
}
}
`),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"test_resource_defaults.foo", "default_string", "new",
),
resource.TestCheckResourceAttr(
"test_resource_defaults.foo", "default_bool", "false",
),
resource.TestCheckResourceAttr(
"test_resource_defaults.foo", "nested.#", "1",
),
resource.TestCheckResourceAttr(
"test_resource_defaults.foo", "nested.2950978312.optional", "nested",
),
resource.TestCheckResourceAttr(
"test_resource_defaults.foo", "nested.2950978312.string", "default nested",
),
),
},
{
Config: strings.TrimSpace(`
resource "test_resource_defaults" "foo" {
default_string = "new"
default_bool = false
nested {
optional = "nested"
string = "new"
}
}
`),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"test_resource_defaults.foo", "default_string", "new",
),
resource.TestCheckResourceAttr(
"test_resource_defaults.foo", "default_bool", "false",
),
resource.TestCheckResourceAttr(
"test_resource_defaults.foo", "nested.#", "1",
),
resource.TestCheckResourceAttr(
"test_resource_defaults.foo", "nested.782850362.optional", "nested",
),
resource.TestCheckResourceAttr(
"test_resource_defaults.foo", "nested.782850362.string", "new",
),
),
},
},
})
}
func TestResourceDefaults_inSet(t *testing.T) {
resource.UnitTest(t, resource.TestCase{
Providers: testAccProviders,

View File

@ -0,0 +1,52 @@
package test
import (
"fmt"
"github.com/hashicorp/terraform/helper/schema"
)
func testResourceMap() *schema.Resource {
return &schema.Resource{
Create: testResourceMapCreate,
Read: testResourceMapRead,
Update: testResourceMapUpdate,
Delete: testResourceMapDelete,
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
},
"map_of_three": {
Type: schema.TypeMap,
Optional: true,
Elem: &schema.Schema{Type: schema.TypeString},
},
},
}
}
func testResourceMapCreate(d *schema.ResourceData, meta interface{}) error {
// make sure all elements are passed to the map
m := d.Get("map_of_three").(map[string]interface{})
if len(m) != 3 {
return fmt.Errorf("expected 3 map values, got %#v\n", m)
}
d.SetId("testId")
return nil
}
func testResourceMapRead(d *schema.ResourceData, meta interface{}) error {
return nil
}
func testResourceMapUpdate(d *schema.ResourceData, meta interface{}) error {
return nil
}
func testResourceMapDelete(d *schema.ResourceData, meta interface{}) error {
d.SetId("")
return nil
}

View File

@ -0,0 +1,32 @@
package test
import (
"testing"
"github.com/hashicorp/terraform/helper/resource"
)
func TestResourceMap_basic(t *testing.T) {
resource.UnitTest(t, resource.TestCase{
Providers: testAccProviders,
CheckDestroy: testAccCheckResourceDestroy,
Steps: []resource.TestStep{
{
Config: `
resource "test_resource_map" "foobar" {
name = "test"
map_of_three = {
one = "one"
two = "two"
empty = ""
}
}`,
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"test_resource_map.foobar", "map_of_three.empty", "",
),
),
},
},
})
}

View File

@ -24,9 +24,95 @@ resource "test_resource" "foo" {
}
}
`),
Check: func(s *terraform.State) error {
return nil
},
Check: resource.ComposeTestCheckFunc(
resource.TestCheckNoResourceAttr(
"test_resource.foo", "list.#",
),
),
},
},
})
}
func TestResource_changedList(t *testing.T) {
resource.UnitTest(t, resource.TestCase{
Providers: testAccProviders,
CheckDestroy: testAccCheckResourceDestroy,
Steps: []resource.TestStep{
{
Config: strings.TrimSpace(`
resource "test_resource" "foo" {
required = "yep"
required_map = {
key = "value"
}
}
`),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckNoResourceAttr(
"test_resource.foo", "list.#",
),
),
},
{
Config: strings.TrimSpace(`
resource "test_resource" "foo" {
required = "yep"
required_map = {
key = "value"
}
list = ["a"]
}
`),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"test_resource.foo", "list.#", "1",
),
resource.TestCheckResourceAttr(
"test_resource.foo", "list.0", "a",
),
),
},
{
Config: strings.TrimSpace(`
resource "test_resource" "foo" {
required = "yep"
required_map = {
key = "value"
}
list = ["a", "b"]
}
`),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"test_resource.foo", "list.#", "2",
),
resource.TestCheckResourceAttr(
"test_resource.foo", "list.0", "a",
),
resource.TestCheckResourceAttr(
"test_resource.foo", "list.1", "b",
),
),
},
{
Config: strings.TrimSpace(`
resource "test_resource" "foo" {
required = "yep"
required_map = {
key = "value"
}
list = ["b"]
}
`),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr(
"test_resource.foo", "list.#", "1",
),
resource.TestCheckResourceAttr(
"test_resource.foo", "list.0", "b",
),
),
},
},
})
@ -165,9 +251,6 @@ resource "test_resource" "foo" {
}
}
`),
Check: func(s *terraform.State) error {
return nil
},
},
resource.TestStep{
Config: strings.TrimSpace(`

View File

@ -697,13 +697,6 @@ func (s *GRPCProviderServer) ApplyResourceChange(_ context.Context, req *proto.A
}
}
// strip out non-diffs
for k, v := range diff.Attributes {
if v.New == v.Old && !v.NewComputed && !v.NewRemoved {
delete(diff.Attributes, k)
}
}
// add NewExtra Fields that may have been stored in the private data
if newExtra := private[newExtraKey]; newExtra != nil {
for k, v := range newExtra.(map[string]interface{}) {

View File

@ -454,10 +454,10 @@ func (d *InstanceDiff) Apply(attrs map[string]string, schema *configschema.Block
return result, nil
}
return d.blockDiff(nil, attrs, schema)
return d.applyBlockDiff(nil, attrs, schema)
}
func (d *InstanceDiff) blockDiff(path []string, attrs map[string]string, schema *configschema.Block) (map[string]string, error) {
func (d *InstanceDiff) applyBlockDiff(path []string, attrs map[string]string, schema *configschema.Block) (map[string]string, error) {
result := map[string]string{}
name := ""
@ -554,19 +554,17 @@ func (d *InstanceDiff) blockDiff(path []string, attrs map[string]string, schema
}
// this must be a diff to keep
keep = true
break
}
if !keep {
delete(candidateKeys, k)
}
}
}
for k := range candidateKeys {
newAttrs, err := d.blockDiff(append(path, n, k), attrs, &block.Block)
newAttrs, err := d.applyBlockDiff(append(path, n, k), attrs, &block.Block)
if err != nil {
return result, err
}
@ -737,24 +735,37 @@ func (d *InstanceDiff) applyCollectionDiff(path []string, attrs map[string]strin
}
// collect all the keys from the diff and the old state
noDiff := true
keys := map[string]bool{}
for k := range d.Attributes {
if !strings.HasPrefix(k, currentKey+".") {
continue
}
noDiff = false
keys[k] = true
}
noAttrs := true
for k := range attrs {
if !strings.HasPrefix(k, currentKey+".") {
continue
}
noAttrs = false
keys[k] = true
}
// If there's no diff and no attrs, then there's no value at all.
// This prevents an unexpected zero-count attribute in the attributes.
if noDiff && noAttrs {
return result, nil
}
idx := "#"
if attrSchema.Type.IsMapType() {
idx = "%"
}
for k := range keys {
if !strings.HasPrefix(k, currentKey+".") {
continue
}
// generate an schema placeholder for the values
elSchema := &configschema.Attribute{
Type: attrSchema.Type.ElementType(),
@ -772,7 +783,7 @@ func (d *InstanceDiff) applyCollectionDiff(path []string, attrs map[string]strin
// Don't trust helper/schema to return a valid count, or even have one at
// all.
result[idx] = countFlatmapContainerValues(name+"."+idx, result)
result[name+"."+idx] = countFlatmapContainerValues(name+"."+idx, result)
return result, nil
}