instances: Non-existing module instance has no resource instances

Previously we were treating it as a programming error to ask for the
instances of a resource inside an instance of a module that is declared
but whose declaration doesn't include the given instance key.

However, that's actually a valid situation which can arise if, for
example, the user has changed the repetition/expansion mode for an
existing module call and so now all of the resource instances addresses it
previously contained are "orphaned".

To represent that, we'll instead say that an invalid instance key of a
declared module behaves as if it contains no resource instances at all,
regardless of the configurations of any resources nested inside. This
then gives the result needed to successfully detect all of the former
resource instances as "orphaned" and plan to destroy them.

However, this then introduces a new case for
NodePlannableResourceInstanceOrphan.deleteActionReason to deal with: the
resource configuration still exists (because configuration isn't aware of
individual module/resource instances) but the module instance does not.
This actually allows us to resolve, at least partially, a previous missing
piece of explaining to the user why the resource instances are planned
for deletion in that case, finally allowing us to be explicit to the user
that it's because of the module instance being removed, which
internally we call plans.ResourceInstanceDeleteBecauseNoModule.

Co-authored-by: Alisdair McDiarmid <alisdair@users.noreply.github.com>
This commit is contained in:
Martin Atkins
2021-12-08 15:40:32 -05:00
committed by Alisdair McDiarmid
parent 6530055d18
commit ec6fe93fa8
4 changed files with 246 additions and 8 deletions

View File

@@ -199,14 +199,47 @@ func TestExpander(t *testing.T) {
}
})
t.Run("module single resource count2", func(t *testing.T) {
got := ex.ExpandModuleResource(
// Two different ways of asking the same question, which should
// both produce the same result.
// First: nested expansion of all instances of the resource across
// all instances of the module, but it's a single-instance module
// so the first level is a singleton.
got1 := ex.ExpandModuleResource(
mustModuleAddr(`single`),
count2ResourceAddr,
)
// Second: expansion of only instances belonging to a specific
// instance of the module, but again it's a single-instance module
// so there's only one to ask about.
got2 := ex.ExpandResource(
count2ResourceAddr.Absolute(
addrs.RootModuleInstance.Child("single", addrs.NoKey),
),
)
want := []addrs.AbsResourceInstance{
mustAbsResourceInstanceAddr(`module.single.test.count2[0]`),
mustAbsResourceInstanceAddr(`module.single.test.count2[1]`),
}
if diff := cmp.Diff(want, got1); diff != "" {
t.Errorf("wrong ExpandModuleResource result\n%s", diff)
}
if diff := cmp.Diff(want, got2); diff != "" {
t.Errorf("wrong ExpandResource result\n%s", diff)
}
})
t.Run("module single resource count2 with non-existing module instance", func(t *testing.T) {
got := ex.ExpandResource(
count2ResourceAddr.Absolute(
// Note: This is intentionally an invalid instance key,
// so we're asking about module.single[1].test.count2
// even though module.single doesn't have count set and
// therefore there is no module.single[1].
addrs.RootModuleInstance.Child("single", addrs.IntKey(1)),
),
)
// If the containing module instance doesn't exist then it can't
// possibly have any resource instances inside it.
want := ([]addrs.AbsResourceInstance)(nil)
if diff := cmp.Diff(want, got); diff != "" {
t.Errorf("wrong result\n%s", diff)
}
@@ -261,6 +294,36 @@ func TestExpander(t *testing.T) {
t.Errorf("wrong result\n%s", diff)
}
})
t.Run("module count2 module count2 GetDeepestExistingModuleInstance", func(t *testing.T) {
t.Run("first step invalid", func(t *testing.T) {
got := ex.GetDeepestExistingModuleInstance(mustModuleInstanceAddr(`module.count2["nope"].module.count2[0]`))
want := addrs.RootModuleInstance
if !want.Equal(got) {
t.Errorf("wrong result\ngot: %s\nwant: %s", got, want)
}
})
t.Run("second step invalid", func(t *testing.T) {
got := ex.GetDeepestExistingModuleInstance(mustModuleInstanceAddr(`module.count2[1].module.count2`))
want := mustModuleInstanceAddr(`module.count2[1]`)
if !want.Equal(got) {
t.Errorf("wrong result\ngot: %s\nwant: %s", got, want)
}
})
t.Run("neither step valid", func(t *testing.T) {
got := ex.GetDeepestExistingModuleInstance(mustModuleInstanceAddr(`module.count2.module.count2["nope"]`))
want := addrs.RootModuleInstance
if !want.Equal(got) {
t.Errorf("wrong result\ngot: %s\nwant: %s", got, want)
}
})
t.Run("both steps valid", func(t *testing.T) {
got := ex.GetDeepestExistingModuleInstance(mustModuleInstanceAddr(`module.count2[1].module.count2[0]`))
want := mustModuleInstanceAddr(`module.count2[1].module.count2[0]`)
if !want.Equal(got) {
t.Errorf("wrong result\ngot: %s\nwant: %s", got, want)
}
})
})
t.Run("module count2 resource count2 resource count2", func(t *testing.T) {
got := ex.ExpandModuleResource(
mustModuleAddr(`count2.count2`),