Merge pull request #20158 from hashicorp/jbardin/list-block
Improve list block handling in diff application
This commit is contained in:
commit
8a3bf9a592
|
@ -15,6 +15,27 @@ func TestResourceList_changed(t *testing.T) {
|
||||||
Steps: []resource.TestStep{
|
Steps: []resource.TestStep{
|
||||||
resource.TestStep{
|
resource.TestStep{
|
||||||
Config: strings.TrimSpace(`
|
Config: strings.TrimSpace(`
|
||||||
|
resource "test_resource_list" "foo" {
|
||||||
|
list_block {
|
||||||
|
string = "a"
|
||||||
|
int = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`),
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"test_resource_list.foo", "list_block.#", "1",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"test_resource_list.foo", "list_block.0.string", "a",
|
||||||
|
),
|
||||||
|
resource.TestCheckResourceAttr(
|
||||||
|
"test_resource_list.foo", "list_block.0.int", "1",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
resource.TestStep{
|
||||||
|
Config: strings.TrimSpace(`
|
||||||
resource "test_resource_list" "foo" {
|
resource "test_resource_list" "foo" {
|
||||||
list_block {
|
list_block {
|
||||||
string = "a"
|
string = "a"
|
||||||
|
|
|
@ -59,16 +59,6 @@ resource "test_resource_nested_set" "foo" {
|
||||||
|
|
||||||
// the empty type_list must be passed to the provider with 1 nil element
|
// the empty type_list must be passed to the provider with 1 nil element
|
||||||
func TestResourceNestedSet_emptyBlock(t *testing.T) {
|
func TestResourceNestedSet_emptyBlock(t *testing.T) {
|
||||||
checkFunc := func(s *terraform.State) error {
|
|
||||||
root := s.ModuleByPath(addrs.RootModuleInstance)
|
|
||||||
res := root.Resources["test_resource_nested_set.foo"]
|
|
||||||
for k, v := range res.Primary.Attributes {
|
|
||||||
if strings.HasPrefix(k, "type_list") && v != "1" {
|
|
||||||
return fmt.Errorf("unexpected set value: %s:%s", k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
resource.UnitTest(t, resource.TestCase{
|
resource.UnitTest(t, resource.TestCase{
|
||||||
Providers: testAccProviders,
|
Providers: testAccProviders,
|
||||||
CheckDestroy: testAccCheckResourceDestroy,
|
CheckDestroy: testAccCheckResourceDestroy,
|
||||||
|
@ -80,7 +70,9 @@ resource "test_resource_nested_set" "foo" {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`),
|
`),
|
||||||
Check: checkFunc,
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
resource.TestCheckResourceAttr("test_resource_nested_set.foo", "type_list.#", "1"),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
@ -713,17 +713,27 @@ 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.NewExtra == "" {
|
|
||||||
delete(diff.Attributes, k)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if private != nil {
|
if private != nil {
|
||||||
diff.Meta = private
|
diff.Meta = private
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We need to turn off any RequiresNew. There could be attributes
|
||||||
|
// without changes in here inserted by helper/schema, but if they have
|
||||||
|
// RequiresNew then the state will will be dropped from the ResourceData.
|
||||||
|
for k := range diff.Attributes {
|
||||||
|
diff.Attributes[k].RequiresNew = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that any "removed" attributes actually exist in the prior state, or
|
||||||
|
// helper/schema will confuse itself
|
||||||
|
for k, d := range diff.Attributes {
|
||||||
|
if d.NewRemoved {
|
||||||
|
if _, ok := priorState.Attributes[k]; !ok {
|
||||||
|
delete(diff.Attributes, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
newInstanceState, err := s.provider.Apply(info, priorState, diff)
|
newInstanceState, err := s.provider.Apply(info, priorState, diff)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
resp.Diagnostics = convert.AppendProtoDiag(resp.Diagnostics, err)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
|
@ -494,11 +495,12 @@ func (d *InstanceDiff) applyBlockDiff(path []string, attrs map[string]string, sc
|
||||||
// we need to find the set of all keys that traverse this block
|
// we need to find the set of all keys that traverse this block
|
||||||
candidateKeys := map[string]bool{}
|
candidateKeys := map[string]bool{}
|
||||||
blockKey := blockPrefix + n + "."
|
blockKey := blockPrefix + n + "."
|
||||||
|
localBlockPrefix := localPrefix + n + "."
|
||||||
|
|
||||||
// we can only trust the diff for sets, since the path changes, so don't
|
// we can only trust the diff for sets, since the path changes, so don't
|
||||||
// count existing values as candidate keys. If it turns out we're
|
// count existing values as candidate keys. If it turns out we're
|
||||||
// keeping the attributes, we will check catch it down below with
|
// keeping the attributes, we will catch it down below with "keepBlock"
|
||||||
// "keepBlock" after we check the set count.
|
// after we check the set count.
|
||||||
if block.Nesting != configschema.NestingSet {
|
if block.Nesting != configschema.NestingSet {
|
||||||
for k := range attrs {
|
for k := range attrs {
|
||||||
if strings.HasPrefix(k, blockKey) {
|
if strings.HasPrefix(k, blockKey) {
|
||||||
|
@ -570,7 +572,7 @@ func (d *InstanceDiff) applyBlockDiff(path []string, attrs map[string]string, sc
|
||||||
}
|
}
|
||||||
|
|
||||||
for attr, v := range newAttrs {
|
for attr, v := range newAttrs {
|
||||||
result[localPrefix+n+"."+attr] = v
|
result[localBlockPrefix+attr] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -589,24 +591,51 @@ func (d *InstanceDiff) applyBlockDiff(path []string, attrs map[string]string, sc
|
||||||
if strings.HasPrefix(k, blockKey) {
|
if strings.HasPrefix(k, blockKey) {
|
||||||
// we need the key relative to this block, so remove the
|
// we need the key relative to this block, so remove the
|
||||||
// entire prefix, then re-insert the block name.
|
// entire prefix, then re-insert the block name.
|
||||||
localKey := n + "." + k[len(blockKey):]
|
localKey := localBlockPrefix + k[len(blockKey):]
|
||||||
|
|
||||||
result[localKey] = v
|
result[localKey] = v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if countDiff, ok := d.Attributes[strings.Join(append(path, n, ".#"), ".")]; ok {
|
if countDiff, ok := d.Attributes[strings.Join(append(path, n, "#"), ".")]; ok {
|
||||||
|
|
||||||
if countDiff.NewComputed {
|
if countDiff.NewComputed {
|
||||||
result[localPrefix+n+".#"] = hcl2shim.UnknownVariableValue
|
result[localBlockPrefix+"#"] = hcl2shim.UnknownVariableValue
|
||||||
} else {
|
} else {
|
||||||
result[localPrefix+n+".#"] = countDiff.New
|
result[localBlockPrefix+"#"] = countDiff.New
|
||||||
|
|
||||||
|
// While sets are complete, list are not, and we may not have all the
|
||||||
|
// information to track removals. If the list was truncated, we need to
|
||||||
|
// remove the extra items from the result.
|
||||||
|
if block.Nesting == configschema.NestingList &&
|
||||||
|
countDiff.New != "" && countDiff.New != hcl2shim.UnknownVariableValue {
|
||||||
|
length, _ := strconv.Atoi(countDiff.New)
|
||||||
|
for k := range result {
|
||||||
|
if !strings.HasPrefix(k, localBlockPrefix) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
index := k[len(localBlockPrefix):]
|
||||||
|
nextDot := strings.Index(index, ".")
|
||||||
|
if nextDot < 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
index = index[:nextDot]
|
||||||
|
i, err := strconv.Atoi(index)
|
||||||
|
if err != nil {
|
||||||
|
// this shouldn't happen since we added these
|
||||||
|
// ourself, but make note of it just in case.
|
||||||
|
log.Printf("[ERROR] bas list index in %q: %s", k, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if i >= length {
|
||||||
|
delete(result, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
result[localPrefix+n+".#"] = countFlatmapContainerValues(localPrefix+n+".#", result)
|
result[localBlockPrefix+"#"] = countFlatmapContainerValues(localBlockPrefix+"#", result)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
|
|
Loading…
Reference in New Issue