Commit Graph

164 Commits

Author SHA1 Message Date
Mitchell Hashimoto cfb440ea60
terraform: don't prune state on init()
Init should only _add_ values, not remove them.

During graph execution, there are steps that expect that a state isn't
being actively pruned out from under it. Namely: writing deposed states.

Writing deposed states has no way to handle if a state changes
underneath it because the only way to uniquely identify a deposed state
is its index in the deposed array. When destroying deposed resources, we
set the value to `<nil>`. If the array is pruned before the next deposed
destroy, then the indexes have changed, and this can cause a crash.

This PR does the following (with more details below):

  * `init()` no longer prunes.

  * `ReadState()` always prunes before returning. I can't think of a
    scenario where this is unsafe since generally we can always START
    from a pruned state, its just causing problems to prune
    mid-execution.

  * Exported State APIs updated to be robust against nil ModuleStates.

Instead, I think we should adopt the following semantics for init/prune
in our structures that support it (Diff, for example). By having
consistent semantics around these functions, we can avoid this in the
future and have set expectations working with them.

  * `init()` (in anything) will only ever be additive, and won't change
    ordering or existing values. It won't remove values.

  * `prune()` is destructive, expectedly.

  * Functions on a structure must not assume a pruned structure 100% of
    the time. They must be robust to handle nils. This is especially
    important because in many cases values such as `Modules` in state
    are exported so end users can simply modify them outside of the
    exported APIs.

This PR may expose us to unknown crashes but I've tried to cover our
cases in exposed APIs by checking for nil.
2016-12-02 11:48:34 -05:00
James Bardin a5cb530571 Move the state module cleanup from init to prune
It makes for sense for this to happen in State.prune(). Also move a
redundant pruning from ResourceState.init, and make sure
ResourceState.prune is called from the parent's prune method.
2016-11-20 11:33:41 -05:00
James Bardin def55e52ca An empty module in state can panic
An empty module, or a module with an empty path can panic during graph
traversal. Make sure we clear these out when reading and writing the
state.
2016-11-18 17:59:07 -05:00
James Nugent afe2d7b65b Merge pull request #7320 from dtolnay/conflict
core: Allow refresh of local state with no resources
2016-10-14 11:00:46 -05:00
James Bardin 816c04309c Filter nil Deposed values during State init
The Deposed slice wasn't being normalized and nil values could be read
in from a state file. Filter out the nils during init. There is
still a bug in copystructure, but that will be addressed separately.
2016-10-13 13:46:40 -04:00
James Bardin e5ff4d5bd4 create some unlocked methods for State
State.Init() needs to be called externally, so make sure it only calls
unlocked internal methods once a lock is acquired.
2016-09-16 09:59:04 -04:00
James Bardin 90aab0105d Add locks to InstanceState
Rename receivers for consistency
2016-09-16 09:59:04 -04:00
James Bardin 8d5080fe72 Lock all ResourceState methods
Also rename receivers so they are consistent.
2016-09-16 09:59:04 -04:00
James Bardin 469e71a488 add locks to all exported ModuleState methods 2016-09-16 09:59:04 -04:00
James Bardin f1b44e36b4 add lock to OutputState.Equal 2016-09-16 09:59:04 -04:00
James Bardin 1235623fad Add locks to exported RemoteState methods 2016-09-16 09:59:04 -04:00
James Bardin 0be90c8994 Add locks to all exported State methods
move implementation into private methods when needed
2016-09-16 09:59:04 -04:00
David Tolnay 61185f083c Merge branch hashicorp/master into dtolnay/conflict 2016-09-07 19:17:22 -07:00
Trevor Pounds 38784cd38f Cleanup inaccurate state structure comments. 2016-09-06 16:24:51 -04:00
Mitchell Hashimoto 3c2db3d572 Merge pull request #8463 from hashicorp/b-read-state-validate
Validate the state on load
2016-08-26 13:27:21 -07:00
James Bardin 4559eec8be Remove the proposed LockedCopy function
We didn't add the LockedCopy function in copystructure, in favor of
using the Config struct directly.
2016-08-25 16:55:28 -04:00
James Bardin 0e6e206465 Add an InstanceState.Set method to set all fields
We con no longer copy an InstanceState via a simple
dereference+assignment because of the mutex which can't be copied. This
adds a set method to properly set all field from another InstanceState,
and take the appropriate locks while doing so.
2016-08-25 14:41:51 -04:00
James Bardin 7a579c271d Remove GoString methods from state
These method set the GoString format behavior, but they only output the
default GoString format. Remove them to prevent copying mutex values.
2016-08-25 14:41:39 -04:00
Mitchell Hashimoto 73f7c04159
terraform: clear docs for devs 2016-08-24 18:44:54 -07:00
Mitchell Hashimoto 3376611ad0
terraform: Validate validates single module states 2016-08-24 18:42:13 -07:00
Mitchell Hashimoto c65bbc4f0c
terraform: minor edits to reading state to prep for validation 2016-08-24 18:20:47 -07:00
James Bardin 056fd70da4 Add locks to state structs for copying
Add locks to the state structs to handle concurrency during the graph
walks. We can't embed the mutexes due to serialization constraints when
communicating with providers, so expose the Lock/Unlock methods
manually.

Use copystructure.LockedCopy to ensure locks are honored.
2016-08-24 11:15:52 -04:00
Mitchell Hashimoto a44c8b8760
terraform: state mv tests 2016-08-18 15:05:42 -04:00
James Bardin cdb80f68a8 Ensure better state normalization
Fix checksum issue with remote state

If we read a state file with "null" objects in a module and they become
initialized to an empty map the state file may be written out with empty
objects rather than "null", changing the checksum. If we can detect
this, increment the serial number to prevent a conflict in atlas.

Our fakeAtlas test server now needs to decode the state directly rather
than using the ReadState function, so as to be able to read the state
unaltered.

The terraform.State data structures have initialization spread out
throughout the package. More thoroughly initialize State during
ReadState, and add a call to init() during WriteState as another
normalization safeguard.

Expose State.init through an exported Init() method, so that a new State
can be completely realized outside of the terraform package.
Additionally, the internal init now completely walks all internal state
structures ensuring that all maps and slices are initialized.  While it
was mentioned before that the `init()` methods are problematic with too
many call sites, expanding this out better exposes the entry points that
will need to be refactored later for improved concurrency handling.

The State structures had a mix of `omitempty` fields. Remove omitempty
for all maps and slices as part of this normalization process. Make
Lineage mandatory, which is now explicitly set in some tests.
2016-08-12 11:09:50 -04:00
James Bardin 2747d964cb Merge pull request #7877 from hashicorp/jbardin/races
core: Fix race conditions, mostly around diffs
2016-07-29 18:42:31 -04:00
James Nugent 0e4e94a86f core: Fix -module for terraform output command
The behaviour whereby outputs for a particular nested module can be
output was broken by the changes for lists and maps. This commit
restores the previous behaviour by passing the module path into the
outputsAsString function.

We also add a new test of this since the code path for indivdual output
vs all outputs for a module has diverged.
2016-07-29 16:39:59 -05: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
Paul Hinze 40fbb8d2e8 Merge pull request #7370 from tpounds/show-tf-ver-on-state-mismatch
Show Terraform version on state version mismatch.
2016-06-29 10:48:13 -05:00
James Bardin 94f1899f4e increment the state serial whenever we upgrade
Always increment the state serial whenever we upgrade the state version.
This prevents possible version conflicts between local and remote state
when one has been upgraded, but the serial numbers match.
2016-06-28 16:32:36 -04:00
Trevor Pounds 5932214f0d Show Terraform version on state version mismatch. 2016-06-27 11:52:43 -07:00
David Tolnay ff80e7b245
Allow refresh of local state with no resources 2016-06-24 21:54:43 -07:00
James Nugent 17f4777039 core: Fix Stringer on OutputState for types
The implementation of Stringer on OutputState previously assumed outputs
may only be strings - we now no longer cast to string, instead using the
built in formatting directives.
2016-06-23 21:15:33 +01:00
James Nugent d60365af02 core: Correctly ensure that State() is a copy
The previous mechanism for testing state threw away the mutation made on
the state by calling State() twice - this commit corrects the test to
match the comment.

In addition, we replace the custom copying logic with the copystructure
library to simplify the code.
2016-06-22 17:21:27 +03:00
Martin Atkins 985fa371dc core: State "Lineage" concept
The lineage of a state is an identifier shared by a set of states whose
serials are meaningfully comparable because they are produced by
progressive Refresh/Apply operations from the same initial empty state.

This is initialized as a type-4 (random) UUID when a new state is
initialized and then preserved on all other changes.

Since states before this change will not have lineage but users may wish
to set a lineage for an existing state in order to get the safety
benefits it will grow to imply, an empty lineage is considered to be
compatible with all lineages.
2016-06-10 07:31:30 -07:00
James Nugent 706ccb7dfe core: Introduce state v3 and upgrade process
This commit makes the current Terraform state version 3 (previously 2),
and a migration process as part of reading v2 state. For the most part
this is unnecessary: helper/schema will deal with upgrading state for
providers written with that framework. However, for providers which
implemented the resource model directly, this gives a best-efforts
attempt at lossless upgrade.

The heuristics used to change the count of a map from the .# key to the
.% key are as follows:

    - if the flat map contains any non-numeric keys, we treat it as a
      map
    - if the map is empty it must be computed or optional, so we remove
      it from state

There is a known edge condition: maps with all-numeric keys are
indistinguishable from sets without access to the schema. They will need
manual conversion or may result in spurious diffs.
2016-06-09 10:49:49 +01:00
James Nugent 49995428fd core: Remove support for V0 state
This removes support for the V0 binary state format which was present in
Terraform prior to 0.3. We still check for the file type and present an
error message explaining to the user that they can upgrade it using a
prior version of Terraform.
2016-06-08 18:38:41 +01:00
Sander van Harmelen d97b24e3c1
Add tests and fix last issues 2016-05-26 19:56:03 -05:00
Sander van Harmelen 8560f50cbc
Change taint behaviour to act as a normal resource
This means it’s shown correctly in a plan and takes into account any
actions that are dependant on the tainted resource and, vice verse, any
actions that the tainted resource depends on.

So this changes the behaviour from saying this resource is tainted so
just forget about it and make sure it gets deleted in the background,
to saying I want that resource to be recreated (taking into account the
existing resource and it’s place in the graph).
2016-05-26 19:55:26 -05:00
James Nugent 3ea3c657b5 core: Use OutputState in JSON instead of map
This commit forward ports the changes made for 0.6.17, in order to store
the type and sensitive flag against outputs.

It also refactors the logic of the import for V0 to V1 state, and
fixes up the call sites of the new format for outputs in V2 state.

Finally we fix up tests which did not previously set a state version
where one is required.
2016-05-18 13:25:20 -05:00
Martin Atkins 844e1abdd3 core: ResourceStateKey understands resource modes
Once a data resource gets into the state, the state system needs to be
able to parse its id to match it with resources in the configuration.
Since data resources live in a separate namespace than managed resources,
the extra "mode" discriminator is required to specify which namespace
we're talking about, just like we do in the resource configuration.
2016-05-14 08:26:36 -07:00
Mitchell Hashimoto 1d3f11f0ba
terraform: working on fixes for imports in modules 2016-05-11 13:02:32 -07:00
Mitchell Hashimoto 45c3fd58d8
terraform: InstanceState.DeepCopy is exported 2016-05-11 13:02:31 -07:00
Mitchell Hashimoto 1040cb7cdb
terraform: add type to Ephemeral 2016-05-11 13:02:31 -07:00
Mitchell Hashimoto af3c3e4c60
terraform: Module copy copies outputs and dependencies 2016-05-10 13:25:02 -07:00
Mitchell Hashimoto 25098f20c9
terraform: State.Add works for module to module (new) 2016-05-10 13:25:02 -07:00
James Nugent e57a399d71 core: Use native HIL maps instead of flatmaps
This changes the representation of maps in the interpolator from the
dotted flatmap form of a string variable named "var.variablename.key"
per map element to use native HIL maps instead.

This involves porting some of the interpolation functions in order to
keep the tests green, and adding support for map outputs.

There is one backwards incompatibility: as a result of an implementation
detail of maps, one could access an indexed map variable using the
syntax "${var.variablename.key}".

This is no longer possible - instead HIL native syntax -
"${var.variablename["key"]}" must be used. This was previously
documented, (though not heavily used) so it must be noted as a backward
compatibility issue for Terraform 0.7.
2016-05-10 14:49:13 -04:00
James Nugent 6aac79e194 state: Add support for outputs of multiple types
This commit adds the groundwork for supporting module outputs of types
other than string. In order to do so, the state version is increased
from 1 to 2 (though the "public-facing" state version is actually as the
first state file was binary).

Tests are added to ensure that V2 (1) state is upgraded to V3 (2) state,
though no separate read path is required since the V2 JSON will
unmarshal correctly into the V3 structure.

Outputs in a ModuleState are now of type map[string]interface{}, and a
test covers round-tripping string, []string and map[string]string, which
should cover all of the types in question.

Type switches have been added where necessary to deal with the
interface{} value, but they currently default to panicking when the input
is not a string.
2016-05-10 14:40:12 -04:00
James Nugent 3393492033 Renumber original binary state as V0
This commit rectifies the fact that the original binary state is
referred to as V1 in the source code, but the first version of the JSON
state uses StateVersion: 1. We instead make the code refer to V0 as the
binary state, and V1 as the first version of JSON state.
2016-05-10 14:40:12 -04:00
Mitchell Hashimoto 35c87836b4 core: Add terraform_version to state
This adds a field terraform_version to the state that represents the
Terraform version that wrote that state. If Terraform encounters a state
written by a future version, it will error. You must use at least the
version that wrote that state.

Internally we have fields to override this behavior (StateFutureAllowed),
but I chose not to expose them as CLI flags, since the user can just
modify the state directly. This is tricky, but should be tricky to
represent the horrible disaster that can happen by enabling it.

We didn't have to bump the state format version since the absense of the
field means it was written by version "0.0.0" which will always be
older. In effect though this change will always apply to version 2 of
the state since it appears in 0.7 which bumped the version for other
purposes.
2016-05-10 14:40:11 -04:00
Mitchell Hashimoto a94b9fdc92 terraform: Internals for `state rm` command
I decided to split this up from the terraform state rm command to make the diff easier to see. These changes will also be used for terraform state mv.

This adds a `Remove` method to the `*terraform.State` struct. It takes a list of addresses and removes the items matching that list. This leverages the `StateFilter` committed last week to make the view of the world consistent across address lookups.

There is a lot of test duplication here with StateFilter, but in Terraform style: we like it that way.
2016-05-10 14:14:48 -04:00