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

Consider moving planning to be performed in Check #2281

Open
t0yv0 opened this issue Aug 2, 2024 · 1 comment
Open

Consider moving planning to be performed in Check #2281

t0yv0 opened this issue Aug 2, 2024 · 1 comment
Labels
kind/design An engineering design doc, usually part of an Epic

Comments

@t0yv0
Copy link
Member

t0yv0 commented Aug 2, 2024

Consider moving planning to be performed in Check instead of Diff, Create, Update.

Here is a simplified sequence diagram for the calls involved in pulumi up deciding to execute an Update plan on a bridged provider resource as of today.

  sequenceDiagram
      participant Engine
      participant BridgedProvider
      participant TFProvider
      Engine->>BridgedProvider: checkedInputs := Check(oldInputs, config)
      Engine->>BridgedProvider: Diff(oldState, checkedInputs)
      BridgedProvider->>TFProvider: plan := PlanResourceChange(oldState, config=checkedInputs)
      BridgedProvider->>Engine: compare(oldState, plan)
      Engine->>BridgedProvider: Update(oldState, checkedInputs)
      BridgedProvider->>TFProvider: plan := PlanResourceChange(state, config)
      BridgedProvider->>TFProvider: ApplyResourceChange(plan)
Loading

The proposed behavior would instead perform planning during the Check call:

  sequenceDiagram
      participant Engine
      participant BridgedProvider
      participant TFProvider
      Engine->>BridgedProvider: plan := Check(oldState, config)
      BridgedProvider->>TFProvider: plan := PlanResourceChange(oldState, config)
      Engine->>BridgedProvider: Diff(oldState, plan)
      BridgedProvider->>Engine: compare(oldState, plan)
      Engine->>BridgedProvider: Update(oldState, plan)
      BridgedProvider->>TFProvider: ApplyResourceChange(plan)

Loading

Rationale

TF providers do a lot of work in PlanResourceChange (see Lifecycle) that is best treated as a black box by the bridge; this work includes applying defaults and executing PlanModifiers. Explaining the planned state to the engine and making it explicit on the protocol instead of something done implicitly in Diff could be a great simplification. It reduces impedance mismatch and can make debugging and maintaining bridged providers easier.

Necessary Changes

  • Pulumi provider protocol might need to be modified to send the entire old state (inputs and outputs) and not just old inputs to Check
  • Pulumi developer docs around Check need editing explaining that the output of Check represents the planned state and may encompass both inputs proper as well as predicted outputs
  • Modify the SDKv2 bridge
  • Modify the PF bridge

Testing

We would need to double check the coverage of all the flows involving Check.

  • verify creating a resource
  • verify updating a resource
  • verify refreshing a resource (pulumi refresh)
  • verify importing a resource (pulumi import)
  • verify importing a resource through an import option (pulumi up)
  • verify resource getters R.get (pulumi up) # TBD does this involve Check?
  • verify that detailed diff values are resolved against oldState and plan for the purposes of presenting to the user
  • verify ignoreChanges through the above scenarios

Issues Fixed

@pulumi-bot pulumi-bot added the needs-triage Needs attention from the triage team label Aug 2, 2024
@t0yv0 t0yv0 added kind/design An engineering design doc, usually part of an Epic and removed needs-triage Needs attention from the triage team labels Aug 2, 2024
VenelinMartinov added a commit that referenced this issue Oct 30, 2024
This change adds improved TF set handling to the detailed diff v2. The
main challenge here is that pulumi does not have native sets, so set
types are represented as lists.

### Diffing sets using the hash ###

To correctly find the diff of two sets we calculate the hash of each
element ourselves and do the diffs based on that. What makes this
somewhat non-trivial is that due to MaxItemsOne flattening we can't just
hash the pulumi `PropertyValue`s given to us by the engine. Instead we
use `makeSingleTerraformInput` to transform the values using the schema.
We then use the hashes of the elements in the set to calculate the
diffs. This allows us to correctly account for shuffling and duplicates,
matching the terraform behaviour of sets.

When returning the element indices back to the engine, we need to return
them in terms of oldState and newInputs because the engine does not have
access to the plannedState (see
#2281). To do
that we keep the newInputs and match plannedState elements to their
newInputs index by the set hash. Note that this is not possible if the
set element contains any computed properties - these can change during
the planning process, so we do not attempt to match and print a warning
about possibly inaccurate diff results instead.


### Unknowns in sets ###
Note that the terraform planning process does not allow a set to contain
any unknowns, because that breaks the hashing. Because of that plan
should always return an unknown for a set which contains any unknowns.
This accounts for cases where resolving the unknown can result in
duplicate elements.

Unknown elements in sets - the whole set becomes unknown in the plan, so
the algorithm no longer works. Currently we return an update for the
whole set to the engine and it does the diff on its side.

### Testing ###
This PR also includes tests for the set behaviour, both unit tests for
the detailed diff algorithm and integration tests using pulumi programs
for:
- Single element additions, updates and removals
- Shuffling, also with additions, updates and removals
- Multi-element additions, updates and removals
- Unknowns

### Issues ###

Builds on #2405

Stacked on #2515,
#2516,
#2496 and
#2497

fixes #2200
fixes #2300
fixes #1904
fixes #186
@VenelinMartinov
Copy link
Contributor

VenelinMartinov commented Nov 27, 2024

Found some engine behaviour which supports that plan should be run in Check: #2660 (comment)

When a resource is going to be replaced the engine re-runs Check with nil Olds. This would solve #2660 if we were running Plan in Check

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/design An engineering design doc, usually part of an Epic
Projects
None yet
Development

No branches or pull requests

3 participants