Commit Graph

67 Commits

Author SHA1 Message Date
James Bardin
c9e7346bfd create a new proposed value when replacing
When replacing an instance, calculate a new proposed value from the null
state and the config. This ensures that all unknown values are properly
set.
2018-10-31 13:49:04 -04:00
Martin Atkins
3b2834b8fc core: Re-instate the ignore_changes processing tests 2018-10-16 19:14:11 -07:00
Martin Atkins
311cdbdcab core: Remove unused EvalDiffDestroyModule
This is no longer needed because the state structure self-prunes when
a module becomes empty.
2018-10-16 19:14:11 -07:00
Martin Atkins
49fa2b3f35 core: Always set ProviderAddr on EvalDiffDestroy
If we don't set it, we end up creating an invalid plan where the destroy
changes don't have a provider address set, which then later fails
decoding when round-tripped through a planfile.

This also includes some extra safety checks in EvalDiff and
EvalDiffDestroy so that we can catch this bug sooner in future.

This change is verified by
TestContext2Apply_plannedDestroyInterpolatedCount, which is now passing.
2018-10-16 19:14:11 -07:00
Martin Atkins
9e0f7c10d9 core: Skip ignore_changes handling for create actions
It doesn't make sense to ignore_changes when the prior value is null,
since we have to create something before we can ignore changes to it.

This change is verified by TestContext2Apply_ignoreChangesWildcard.
2018-10-16 19:14:11 -07:00
Martin Atkins
2bab5bf502 core: Allow planned Update change to become NoOp during apply
This can happen if unknown values in the plan actually end up being
identical to the prior values once resolved. In that case, we'll just make
no change at all.

This is verified by TestContext2Apply_ignoreChangesWithDep.
2018-10-16 19:14:11 -07:00
Martin Atkins
a43b7df282 core: Handle forced-create_before_destroy during the plan walk
Previously we used a single plan action "Replace" to represent both the
destroy-before-create and the create-before-destroy variants of replacing.
However, this forces the apply graph builder to jump through a lot of
hoops to figure out which nodes need it forced on and rebuild parts of
the graph to represent that.

If we instead decide between these two cases at plan time, the actual
determination of it is more straightforward because each resource is
represented by only one node in the plan graph, and then we can ensure
we put the right nodes in the graph during DiffTransformer and thus avoid
the logic for dealing with deposed instances being spread across various
different transformers and node types.

As a nice side-effect, this also allows us to show the difference between
destroy-then-create and create-then-destroy in the rendered diff in the
CLI, although this change doesn't fully implement that yet.
2018-10-16 19:14:11 -07:00
Martin Atkins
e9e11955a8 core: EvalDiff must handle Create/Replace as a special case
When we re-run EvalDiff during apply, we may have already completed the
destroy leg of a replace operation, leaving us in a different situation
than we were when we made the original planned change.

Therefore as a special case we will allow a create to turn back into a
replace if there was an earlier diff that requested that.
2018-10-16 19:14:11 -07:00
Martin Atkins
a90d6cc599 core: EvalDiff handling of tainted objects
If the prior object is tainted, we behave as if it doesn't exist at all
for most of our logic here but then at the end turn it into a synthetic
replace operation going from the old object to the new object, similarly
to how we'd behave if given an argument change that "requires
replacement".
2018-10-16 19:14:11 -07:00
Martin Atkins
7f1954e70c core: Don't panic if EvalWriteDiff gets a change in a non-root module 2018-10-16 19:14:11 -07:00
Martin Atkins
70c555cfd3 core: EvalDiff to panic earlier if it gets back nil value from provider
It's not possible for a normal RPC-based provider to get into this
situation because a nil value can't go over the wire, but it's easy to
cause this by not correctly configuring a provider mock during tests.

By panicking early here we produce a more helpful error message and stack
trace than we'd otherwise produce if we let this nil value escape out
into the rest of Terraform.
2018-10-16 19:14:11 -07:00
Martin Atkins
6fd82ef97e core: Split Replace changes into separate Delete/Create changes
Since we do our deletes using a separate graph node from all of the other
actions, and a "Replace" change implies both a delete _and_ a create, we
need to pretend at apply time that a single replace change was actually
two separate changes.

This will also early-exit eval if a destroy node finds a non-Delete change
or if an apply node finds a Delete change. These should not happen in
practice because we leave these nodes out of the graph when they are not
needed for the given action, but we do this here for robustness so as not
to have an invisible dependency between the graph builder and the eval
phase.
2018-10-16 19:14:11 -07:00
Martin Atkins
6365b9ec7f core: EvalCheckPlannedChange to check change action
Previously we were checking only the before and after values, and not
verifying that the change action is unchanged between plan and apply.
2018-10-16 19:14:11 -07:00
Martin Atkins
9af67806fc core: Prune placeholder objects from state after refresh
Prior to our refactoring here, we were relying on a lucky coincidence for
correct behavior of the plan walk following a refresh in the same run:

- The refresh phase created placeholder objects in the state to represent
  any resource instance pending creation, to allow the interpolator to
  read attributes from them when evaluating "provider" and "data" blocks.
  In effect, the refresh walk is creating a partial plan that only covers
  creation actions, but was immediately discarding the actual diff entries
  and storing only the planned new state.

- It happened that objects pending creation showed up in state with an
  empty ID value, since that only gets assigned by the provider during
  apply.

- The Refresh function concluded by calling terraform.State.Prune, which
  deletes from the state any objects that have an empty ID value, which
  therefore prevented these temporary objects from surviving into the
  plan phase.

After refactoring, we no longer have this special ID field on instance
object state, and we instead rely on the Status field for tracking such
things. We also no longer have an explicit "prune" step on state, since
the state mutation methods themselves keep the structure pruned.

To address this, here we introduce a new instance object status "planned",
which is equivalent to having an empty ID value in the old world. We also
introduce a new method on states.SyncState that deletes from the state
any planned objects, which therefore replaces that portion of the old
State.prune operation just for this refresh use-case.

Finally, we are now expecting the expression evaluator to pull pending
objects from the planned changeset rather than from the state directly,
and so for correct results these placeholder resource creation changes
must also be reported in a throwaway changeset during the refresh walk.

The addition of states.ObjectPlanned also permits a previously-missing
safety check in the expression evaluator to prevent us from relying on the
incomplete value stored in state for a pending object, in the event that
some bug prevents the real pending object from being written into the
planned changeset.
2018-10-16 19:14:11 -07:00
Martin Atkins
1ced176fc6 plans: Track RequiredReplace as a cty.PathSet
We were previously tracking this as a []cty.Path, but having it turned
into a pathset on creation makes downstream use of it more convenient and
ensures that it'll obey expected invariants like not containing the same
path twice.
2018-10-16 19:14:11 -07:00
Martin Atkins
d54b52fa01 core: Fix error reporting for malformed provider response values 2018-10-16 19:14:11 -07:00
Martin Atkins
1afdb055ca core: Re-implement ReadDataDiff around our new approach
This is no longer a call into the provider, since all of the data diff
logic is standard for all data sources anyway. Instead, we just compute
the planned new value and construct a planned change from that as-is.

Previously the provider could, in principle, customize the read diff. In
practice there is no real reason to do that and the existing SDK didn't
pass that possibility through to provider code, so we can safely change
this without impacting provider compatibility.
2018-10-16 19:14:11 -07:00
Martin Atkins
7249a73f79 core: Don't panic if "required replace" path doesn't resolve to a value 2018-10-16 19:14:11 -07:00
Martin Atkins
1d672623eb core: Remove changes from the plan after they are applied 2018-10-16 19:14:11 -07:00
Martin Atkins
3d86dc51e0 core: Re-implement EvalReadDiff for the new plan types
This also includes passing in the provider schema to a few more EvalNodes
that were expecting it but not getting it, in order to be able to
successfully test the implementation of EvalReadDiff here.
2018-10-16 19:14:11 -07:00
Martin Atkins
bd299b9a22 core: Re-implement EvalWriteDiff to work with new plan types 2018-10-16 19:14:11 -07:00
Martin Atkins
ec11efc50a core: Tolerate the prior state being nil in EvalDiff
This happens legitimately in situations where we'll be creating an object
for this resource instance for the first time.
2018-10-16 19:14:11 -07:00
Martin Atkins
44bc7519a6 terraform: More wiring in of new provider types
This doesn't actually work yet, but it builds and then panics in a pretty
satisfying way.
2018-10-16 19:12:54 -07:00
James Bardin
16df9c37cf first step in core provider type replacement
Chaange ResourceProvider to providers.Interface starting from the
context, and fix all type errors.

This only replaced some of method calls directly applicable to the
providers themselves. The resource methods will follow.
2018-10-16 19:11:09 -07:00
Martin Atkins
a3403f2766 terraform: Ugly huge change to weave in new State and Plan types
Due to how often the state and plan types are referenced throughout
Terraform, there isn't a great way to switch them out gradually. As a
consequence, this huge commit gets us from the old world to a _compilable_
new world, but still has a large number of known test failures due to
key functionality being stubbed out.

The stubs here are for anything that interacts with providers, since we
now need to do the follow-up work to similarly replace the old
terraform.ResourceProvider interface with its replacement in the new
"providers" package. That work, along with work to fix the remaining
failing tests, will follow in subsequent commits.

The aim here was to replace all references to terraform.State and its
downstream types with states.State, terraform.Plan with plans.Plan,
state.State with statemgr.State, and switch to the new implementations of
the state and plan file formats. However, due to the number of times those
types are used, this also ended up affecting numerous other parts of core
such as terraform.Hook, the backend.Backend interface, and most of the CLI
commands.

Just as with 5861dbf3fc49b19587a31816eb06f511ab861bb4 before, I apologize
in advance to the person who inevitably just found this huge commit while
spelunking through the commit history.
2018-10-16 19:11:09 -07:00
Martin Atkins
fd371d838d core: Handle count.index evaluation more explicitly
Previously we had the evaluate methods accept directly an
addrs.InstanceKey and had our evaluator infer a suitable value for
count.index for it, but that prevents us from setting the index to be
unknown in the validation scenario where we may not be able to predict
the number of instances yet but we still want to be able to check that
the configuration block is type-safe for all possible count values.

To achieve this, we separate the concern of deciding on a value for
count.index from the concern of evaluating it, which then allows for
other implementations of this in future. For the purpose of this commit
there is no change in behavior, with the count.index value being populated
whenever the instance key is a number.

This commit does a little more groundwork for the future implementation
of the for_each feature (which'll support each.key and each.value) but
still doesn't yet implement it, leaving it just stubbed out for the
moment.
2018-10-16 18:50:29 -07:00
Martin Atkins
fb70eaa7d1 core: EvalDiffDestroy only update state if requested
The earlier change 5f07201a made it so that the state is always rewritten
by EvalDiffDestroy, but that was too disruptive to other users of
EvalDiffDestroy.

Now we follow the lead of EvalDiff and have a separate pointer for the
_output_ state, which allows the caller to opt in to having its state
pointer updated to reflect the new (nil) state.

NodePlannableResourceInstanceOrphan is the only caller that currently opts
in to this, since that was the focus of 5f07201a. We may need to make a
similar change to other plannable resource destroy nodes, but we'll wait
to see if that needs to be done in a subsequent commit.
2018-10-16 18:49:20 -07:00
Martin Atkins
26f76dd222 core: When planning to destroy an orphan instance, nil it in state
While we're planning we must always update the state with the proposed new
data resulting from the plan. In this case, we must record that the
orphan instance doesn't exist at all in the proposed new state by storing
its state as nil.

This in turn allows references to the containing resource to evaluate
properly, using the new updated resource count. This fixes
TestContext2Apply_multiVarCountDec.

This also includes a number of changes to the test output of
TestContext2Apply_multiVarCountDec that make it easier to debug failures.
2018-10-16 18:49:20 -07:00
Martin Atkins
1ed56f9903 core: NewInstanceInfo should take ResourceInstance, not Resource
On the initial pass here I reached a faulty conclusion about what from
the new world should shim into a NewInstanceInfo, based on a poor read
of existing code.

It actually _should've_ been based on an absolute instance after all,
as evidenced by the expected result of TestContext2Refresh_targetedCount.
Therefore the signature is changed here, and all of the callers (which,
in retrospect, were all holding a full instance address anyway!) are
updated to that new signature.
2018-10-16 18:49:20 -07:00
Martin Atkins
559f65bf3d core: Fix ignore_changes = all
The initial rework of this function to support traversals didn't correctly
handle the "all" case, due to a logic error where the ignoreAll branch
could be visited only if ignoreChanges were non-empty, but yet the two
are mutually exclusive in practice.

Now we process ignoreAll separately from ignoreChanges, and invert the
two loops so that we will visit all attributes regardless of what is
in the ignoreChanges slice.
2018-10-16 18:49:20 -07:00
Martin Atkins
c82e3ec92f core: even more nil checks to catch missing objects
These are all things that ought to be present in normal use but can end up
being nil in incorrect tests. Test debugging is simpler if these things
return errors gracefully, rather than crashing.
2018-10-16 18:46:46 -07:00
Martin Atkins
d4cfe85361 core: pass InstanceKey to EvaluateBlock
This gives us the value we need to evaluate a "count.index" reference, and
later also the equivalent for the "for_each" argument once implemented.
2018-10-16 18:46:46 -07:00
Martin Atkins
c937c06a03 terraform: ugly huge change to weave in new HCL2-oriented types
Due to how deeply the configuration types go into Terraform Core, there
isn't a great way to switch out to HCL2 gradually. As a consequence, this
huge commit gets us from the old state to a _compilable_ new state, but
does not yet attempt to fix any tests and has a number of known missing
parts and bugs. We will continue to iterate on this in forthcoming
commits, heading back towards passing tests and making Terraform
fully-functional again.

The three main goals here are:
- Use the configuration models from the "configs" package instead of the
  older models in the "config" package, which is now deprecated and
  preserved only to help us write our migration tool.
- Do expression inspection and evaluation using the functionality of the
  new "lang" package, instead of the Interpolator type and related
  functionality in the main "terraform" package.
- Represent addresses of various objects using types in the addrs package,
  rather than hand-constructed strings. This is not critical to support
  the above, but was a big help during the implementation of these other
  points since it made it much more explicit what kind of address is
  expected in each context.

Since our new packages are built to accommodate some future planned
features that are not yet implemented (e.g. the "for_each" argument on
resources, "count"/"for_each" on modules), and since there's still a fair
amount of functionality still using old-style APIs, there is a moderate
amount of shimming here to connect new assumptions with old, hopefully in
a way that makes it easier to find and eliminate these shims later.

I apologize in advance to the person who inevitably just found this huge
commit while spelunking through the commit history.
2018-10-16 18:46:46 -07:00
James Bardin
8d1e479fc7 don't ignore partial containers in diffs
Containers (maps, lists, sets) in an InstanceDiff need to be handled in
their entirety.  Unchanged values cannot be filtered out from diffs, as
providers expect attribute containers to be complete.

If a value in ignore_changes maps to a single key in an attribute
container, and there are other changes present, that ignored value must
be included in the diff as well.
2018-01-17 19:13:32 -05:00
Nathan Evans
45439d0ac3 Add inequality check to ignore redundant diff attributes 2017-11-28 10:11:19 -07:00
James Bardin
36b8be43e8 use the new version package
Update all references to the version values to use the new package.
The VersionString function was left in the terraform package
specifically for the aws provider, which is vendored. We can remove that
last call once the provider is updated.
2017-10-19 21:48:08 -04:00
Chris Marchesi
5654a676d9 core: Skip diff hooks for stubs on eval altogether
Rather than overloading InstanceDiff with a "Stub" attribute that is
going to be largely meaningless, we are just going to skip
pre/post-diff hooks altogether. This is under the notion that we will
eventually not need to "stub" a diff for scale-out, stateless nodes on
refresh at all, so diff behaviour won't be necessary at that point, so
we should not assume that hooks will run at this stage anyway.

Also as part of this removed the CountHook test that is now failing
because CountHook is out of scope of the new behaviour.
2017-06-24 08:01:17 -07:00
Chris Marchesi
45528b2217 core: Instance/EvalDiff.Quiet -> Stub
Changed the language of this field to indicate that this diff is not a
"real" diff, in that it should not be acted on, versus a "quiet" mode,
which would indicate just simply to act silently.
2017-06-21 09:15:08 -07:00
Chris Marchesi
eef933f2a7 core: Don't count scaled-out resources twice in the UI
This fixes a bug with the new refresh graph behaviour where a resource
was being counted twice in the UI on part of being scaled out:

 * We are no longer transforming refresh nodes without state to
   plannable resources (the transformer will be removed shortly)
 * A Quiet flag has been added to EvalDiff and InstanceDiff - this
   allows for the flagging of a diff that should not be treated as real
   diff for purposes of planning
 * When there is no state for a refresh node now, a new path is taken
   that is similar to plan, but flags Quiet, and does nothing with the
   diff afterwards.

Tests pending - light testing has confirmed this should fix the double
count issue, but we should have some tests to actually confirm the bug.
2017-06-20 07:37:32 -07:00
James Bardin
0ae0076e3a Correctly filter flatmapped values in diff
When transforming a diff from DestroyCreate to a simple Update,
ignore_changes can cause keys from flatmapped objects to be filtered
form the diff. We need to filter each flatmapped container as a whole to
ensure that unchanged keys aren't lost in the update.
2017-03-21 09:11:54 -04:00
Mitchell Hashimoto
80457b689c
terraform: do the deposed check within EvalDiff
There is never any reason to separate the two.
2016-11-28 14:34:24 -08:00
Mitchell Hashimoto
b72ef90c2f Revert "[WIP] core: Log diff mismatch using spew instead of %#v" 2016-11-15 11:53:28 -08:00
Mitchell Hashimoto
b20f43834b Merge pull request #9118 from hashicorp/spew-diff-mismatch
[WIP] core: Log diff mismatch using spew instead of %#v
2016-11-15 11:53:19 -08:00
Mitchell Hashimoto
f142978456
terraform: add test to verify tainted resources don't process
ignore_changes

For #7855
2016-10-27 08:44:59 -04:00
Mitchell Hashimoto
984cade39f
Merge branch 'fix-taint-w-ignorechanges' of https://github.com/sl1pm4t/terraform into sl1pm4t-fix-taint-w-ignorechanges 2016-10-27 08:32:37 -04:00
James Nugent
7dab27065f core: Log diff mismatch using spew instead of %#v
This commit improves the error logging for "Diffs do not match" errors
by using the go-spew library to ensure that the structures are presented
fully and in a consistent order. This allows use of the command line
diff tool to analyse what is wrong.
2016-09-29 14:45:46 -05:00
Sander van Harmelen
47dd1ad153 Add wildcard (match all) support to ignore_changes (#8599) 2016-09-02 15:44:35 +02:00
James Bardin
5802f76eaa Make all terraform package tests pass under -race
This isn't a pretty refactor, but fixes the race issues in this package
for now.

Fix race on RawConfig.Config()

fix command package races
2016-07-29 16:12:21 -04:00
Matt Morrison
dbf04721d2 Tainted resource not recreated if ignore_changes used on any attributes. 2016-07-29 09:12:39 +12:00
Paul Hinze
14cea95e86
terraform: another set of ignore_changes fixes
This set of changes addresses two bug scenarios:

(1) When an ignored change canceled a resource replacement, any
downstream resources referencing computer attributes on that resource
would get "diffs didn't match" errors. This happened because the
`EvalDiff` implementation was calling `state.MergeDiff(diff)` on the
unfiltered diff. Generally this is what you want, so that downstream
references catch the "incoming" values. When there's a potential for the
diff to change, thought, this results in problems w/ references.

Here we solve this by doing away with the separate `EvalNode` for
`ignore_changes` processing and integrating it into `EvalDiff`. This
allows us to only call `MergeDiff` with the final, filtered diff.

(2) When a resource had an ignored change but was still being replaced
anyways, the diff was being improperly filtered. This would cause
problems during apply when not all attributes were available to perform
the replacement.

We solve that by deferring actual attribute removal until after we've
decided that we do not have to replace the resource.
2016-07-08 16:48:23 -05:00