Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refresh-only plans and forced-replacement #28297

Closed
wants to merge 6 commits into from

Conversation

apparentlymart
Copy link
Contributor

@apparentlymart apparentlymart commented Apr 7, 2021

This draft PR is another incremental step down our road of meeting all of the important use-cases involving externally-visible side-effects through the primary plan/apply workflow, rather than via other secondary commands that often lack the ability for users to preview the effect of the action or combine it with other effects.

This particular PR deals with two use-cases that involve planned actions that aren't typically associated with changes to the configuration:

  • terraform plan -refresh-only is a plannable version of terraform refresh, updating the Terraform state to match what providers report as the state of real remote objects, and updating any root module outputs that are affected by those changes, while skipping any attempt to update the remote objects to match an updated configuration.

    Someone might use this if they know they've intentionally changed something outside of Terraform and so want to update the state to match it, regardless of whether there might be other changes implied by the configuration.

    A later version of this PR will incorporate a version of what I prototyped in backend/local: Show resource instance drift as part of the plan #26921, so that a -refresh-only plan will actually report something useful rather than just saying there's nothing to do.

  • terraform plan -replace=ADDR is a single-operation, plannable version of the current workaround of running terraform taint ADDR followed by terraform plan, avoiding the intermediate step of first marking an object as tainted.

    Someone might use this if they've learned out of band that some object has become degraded in some way, and because they are employing immutable infrastructure patterns the easiest path forward is to just replace the malfunctioning object with a fresh one that has the same configuration.

For the moment this is just a prototype focused on the core behaviors, so that we can discuss tradeoffs of the specific implementation strategies I chose here. If this looks promising then later updates to this PR will build out a better UI implementation for both of these which reports the planning result in a way that actually acknowledges these two options being activated.

It might be easier to review this on a commit-by-commit basis rather than as a full diff, because there's some further commentary in the individual commit messages.


In later changes we're intending to also tackle various other commands that directly modify the state today, but those typically work in conjunction with changes to the configuration and so they'll follow a different strategy involving new language features rather than new command line options. For an example of that sort of thing, see #9048.

@codecov
Copy link

codecov bot commented Apr 7, 2021

Codecov Report

Merging #28297 (aaa22f8) into main (15b6a16) will decrease coverage by 0.05%.
The diff coverage is 46.74%.

Impacted Files Coverage Δ
backend/backend.go 31.25% <ø> (ø)
backend/remote/backend_common.go 49.82% <0.00%> (ø)
command/arguments/plan.go 100.00% <ø> (ø)
plans/mode_string.go 0.00% <0.00%> (ø)
plans/plan.go 40.00% <ø> (ø)
terraform/graphtype_string.go 33.33% <ø> (ø)
command/plan.go 48.38% <15.00%> (-1.20%) ⬇️
terraform/node_resource_abstract_instance.go 72.37% <18.75%> (-0.87%) ⬇️
plans/planfile/tfplan.go 57.29% <26.08%> (-2.07%) ⬇️
backend/local/backend_apply.go 51.16% <33.33%> (ø)
... and 19 more

Previously there were only two planning modes: normal mode and destroy
mode. In that context it made sense for these to be distinguished only by
a boolean flag.

We're now getting ready to add our third mode, "refresh only". This
establishes the idea that planning can be done in one of a number of
mutually-exclusive "modes", which are related to but separate from the
various other options that serve as modifiers for the plan operation.

This commit only introduces the new plans.Mode type and replaces the
existing "destroy" flag with a variable of that type. This doesn't cause
any change in effective behavior because Terraform Core still supports
only NormalMode and DestroyMode, with NewContext rejecting an attempt to
create a RefreshMode context for now.

It is in retrospect a little odd that the "destroy" flag was part of
ContextOpts rather than just an argument to the Plan method, but
refactoring that would be too invasive a change for right now so we'll
leave this as a field of the context for now and save revisiting that for
another day.
So far we've only had "normal mode" and "destroy mode", where the latter
is activated either by "terraform plan -destroy" or "terraform destroy".

In preparation for introducing a third mode "refresh only" this
generalizes how we handle modes so we can potentially deal with an
arbitrary number of modes, although for now we only intend to have three.

Mostly this is just a different implementation of the same old behavior,
but there is one small user-visible difference here: the "terraform apply"
command now accepts a -destroy option, mirroring the option of the same
name on "terraform plan", which in turn makes "terraform destroy"
effectively a shorthand for "terraform apply -destroy".

This is intended to make us consistent that "terraform apply" without a
plan file argument accepts all of the same plan-customization options that
"terraform plan" does, which will in turn avoid us having to add a new
alias of "terraform plan" for each new plan mode we might add. The -help
output is changed in that vein here, although we'll wait for subsequent
commit to make a similar change to the website documentation just so we
can deal with the "refresh only mode" docs at the same time.
This only includes the internal mechanisms to make it work, and not any
of the necessary UI changes to "terraform plan" and "terraform apply" to
make their output make sense in this mode.

At the lowest level of abstraction inside the graph nodes themselves, this
effectively mirrors the existing option to disable refreshing with a new
option to disable change-planning, so that either "half" of the process
can be disabled. As far as the nodes are concerned it would be possible
in principle to disable _both_, but the higher-level representation of
these modes prevents that combination from reaching Terraform Core in
practice, because we block using -refresh-only and -refresh=false at the
same time.
This only includes the internal mechanisms to make it work, and not any
of the necessary UI changes to "terraform plan" and "terraform apply" to
make their output make sense in this mode.

The force-replace options are ultimately handled inside the
NodeAbstractResourceInstance.plan method, at the same place we handle the
similar situation of the provider indicating that replacement is needed,
and so the rest of the changes here are just to propagate the settings
through all of the layers in order to reach that point.

Since the plan renderer has no awareness of this situation right now, it
renders force-replaced instances in the same way as once where the
provider indicates replacement is needed, annotated with "must be
replaced". In a later commit we'll add a special case to show these ones
as "will be replaced as requested", but otherwise this is a complete
implementation of the necessary functionality.
It's been a long time since we gave this page an overhaul, and with our
ongoing efforts to make plan and apply incorporate all of the side-effects
that might need to be done against a configuration it seems like a good
time for some restructuring in that vein.

The starting idea here is to formally split the many "terraform plan"
options into a few different categories:
 - Planning modes
 - Planning options
 - Other options

The planning modes and options are the subset that are also accepted by
"terraform apply" when it's running in its default mode of generating a
plan and then prompting for interactive approval of it. This then allows
us to avoid duplicating all of that information on the "terraform apply"
page, and thus allows us to spend more words discussing each of them.

This also changes the docs for "terraform refresh" and "terraform taint"
to frame them as legacy commands preserved for backward compatibility,
pointing users instead to the relevant options on "terraform plan".

This commit describes some new options that were added in previous commits
but for which we plan some further improvements in subsequent commits.
The new "terraform plan" options described here are therefore not fully
functional as of this commit, but they have been minimally implemented
in the last two commits before this one.
@apparentlymart
Copy link
Contributor Author

This PR was essentially a prototype to see whether these mechanisms would be viable to implement in Terraform's current design without significant refactoring, and it's served that purpose now, but in order to actually land these features I'm intending to work gradually over a series of new pull requests which will ultimately include all of what was here and the rest of what's needed for it to be shippable.

I'm leaving this open here for now just so it's easier to find for folks using it as a prototype to test against, but I will eventually close this out once enough has landed in the main branch via other PRs that it'd be better to use a build from main branch to test instead.

In order to help with testing new automation around -refresh-only, this is
some _very temporary_ UI and correct exit status for situations where
Terraform has detected remote system changes in -refresh-only mode.

This still isn't quite the right behavior because actually applying the
resulting plan doesn't actually commit the refreshed state snapshot, but
it's hopefully at least functional enough for initial testing until we
finish the final implementation.
@github-actions
Copy link
Contributor

github-actions bot commented Jun 6, 2021

I'm going to lock this pull request because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active contributions.
If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 6, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant