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

Remove custom code for computing planned state #1276

Closed
wants to merge 15 commits into from

Conversation

t0yv0
Copy link
Member

@t0yv0 t0yv0 commented Jul 6, 2023

Factors out ProposedNew into its own package so both Plugin Framework providers and SDKv2-based providers that opt into DiffStrategy=PlanState call the exact same logical implementation that is linked from TF CLI sources (objchange.ProposedNew).

  graph LR
      A[pfutils.ProposedNew] --> B[tfplan.ProposedNew]
      C[sdkv2.ProposedNew]   --> B[tfplan.ProposedNew]
      B --> D[objchange.ProposedNew]
Loading

Previously, PF-based providers used a homegrown implementation that was likely diverging from TF implementation in some of the edge cases. The PR trades off maintaining that implementation (which is no longer necessary) to maintaining some adapter boilerplate code.

@github-actions
Copy link

github-actions bot commented Jul 6, 2023

Diff for pulumi-random with merge commit 5cb3634

@github-actions
Copy link

github-actions bot commented Jul 6, 2023

Diff for pulumi-azuread with merge commit 5cb3634

@@ -38,7 +38,7 @@ outputs:
testOptionalString__expect: |-
input2
testSetOptionals__actual: ${set.setOptionals}
testSetOptionals__expect: [ "${r.requiredInputStringCopy}", a, b ]
testSetOptionals__expect: [ a, b, "${r.requiredInputStringCopy}" ]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change caused set elements to be reordered in the list projection to Pulumi. While this should theoretically be alright, as the subsequent Diff recognizes that sets are sets and should not be sensitive to element order, perhaps there are some unintended consequences?

Should the bridge define some guarantees on the canonical set ordering when projecting elements to Pulumi array property values?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the bridge needs to define a constant ordering since we expose this data as a list. We shouldn't need to guarantee anything about the ordering, except consistency.

Its not unreasonable to assume that new Resource("one", opt=set[0]); new Resource("two", opt=set[1]) is safe between updates, where a re-order would break this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I grok the example, could you expand.

Since bridged providers customize Diff method sending re-ordered versions of the set should result in a no-changes diff. But I have a hunch somewhere we run into trouble anyway. Would be good to file an issue figuring out where.

Perhaps with ignoreChanges paths is one place.

FWIW this PR seems to move us from "user-defined" ordering to "TF-defined" consistent ordering, indirectly, based on the example changes. There's some subtleties with this. It probably is affected by TF hashing functions and when they change upstream this is likely to reorder sets.

There are also some serious issues with sets on the SDKV2 based providers. I was recently looking at this one:

#1255

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#1276 (review) has a better example. Basically, external users can rely on the ordering of pulumi lists (and tf sets) such that re-ordering will always cause problems, no matter what the bridge does.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we believe that TF-defined sets are better (more consistent, easier to maintain) then I'm ok moving to them as long as we (1) recognize it is a breaking change and (2) put tests and documentation locking that ordering in.

@t0yv0 t0yv0 marked this pull request as ready for review July 6, 2023 22:29
@t0yv0 t0yv0 requested a review from iwahbe July 6, 2023 22:29
@github-actions
Copy link

github-actions bot commented Jul 6, 2023

Diff for pulumi-random with merge commit 8124203

@github-actions
Copy link

github-actions bot commented Jul 6, 2023

Diff for pulumi-azuread with merge commit 8124203

@t0yv0 t0yv0 removed the request for review from iwahbe July 6, 2023 22:37
@t0yv0
Copy link
Member Author

t0yv0 commented Jul 6, 2023

OK not ready for review quite yet, there's some more edge cases caught by the integration test suite.

@github-actions
Copy link

github-actions bot commented Jul 7, 2023

Diff for pulumi-random with merge commit 2bfc532

@github-actions
Copy link

github-actions bot commented Jul 7, 2023

Diff for pulumi-azuread with merge commit 2bfc532

Copy link
Member

@iwahbe iwahbe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general this looks good. I'm scared of the set re-ordering though. That seams like a meaningful breaking change that could cause replaces. The obvious example is:

const resource = new Resource("parent")
resource.setProperty.apply(set => {
  // Create resources inside an apply, 
  // each resource is name associated with its ordering
  set.map((i, v) => new Child(`child-${i}`, {
    needsReplaceOnChanges: v,
  }))
}

Here a change in order would cause all children to be replaced.

@@ -45,32 +48,41 @@ type Schema interface {
//
// [State Upgrade]: https://developer.hashicorp.com/terraform/plugin/framework/resources/state-upgrade
ResourceSchemaVersion() int64

Proto() *tfprotov6.Schema
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Proto() *tfprotov6.Schema
// The tfprotov6.Schema associated with `Schema`. If nil, then ...
Proto() *tfprotov6.Schema

}

func FromProviderSchema(x pschema.Schema) Schema {
attrs := convertMap(FromProviderAttribute, x.Attributes)
blocks := convertMap(FromProviderBlock, x.Blocks)
// Provider schemas cannot be versioned, see also x.GetVersion() always returning 0.
version := int64(0)
return newSchemaAdapter(x, x.Type(), x.DeprecationMessage, attrs, blocks, x.AttributeAtPath, version)
proto, err := schemav6.ProviderSchema(x)
contract.AssertNoError(err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why this is safe?

Suggested change
contract.AssertNoError(err)
contract.AssertNoErrorf(err, "Will not error because...")

Comment on lines +160 to +170
func (r *schemaExtractingResource) Create(context.Context, resource.CreateRequest, *resource.CreateResponse) {
}

func (r *schemaExtractingResource) Read(context.Context, resource.ReadRequest, *resource.ReadResponse) {
}

func (r *schemaExtractingResource) Update(context.Context, resource.UpdateRequest, *resource.UpdateResponse) {
}

func (r *schemaExtractingResource) Delete(context.Context, resource.DeleteRequest, *resource.DeleteResponse) {
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we assert that these methods will not be called (and then panic)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this would reduce branch coverage :)

@@ -0,0 +1,81 @@
// Copyright 2016-2023, Pulumi Corporation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is called testbridge_resource_compopt.go If compopt is "component options", can we just say that? Otherwise, can we say what it is?

@@ -38,7 +38,7 @@ outputs:
testOptionalString__expect: |-
input2
testSetOptionals__actual: ${set.setOptionals}
testSetOptionals__expect: [ "${r.requiredInputStringCopy}", a, b ]
testSetOptionals__expect: [ a, b, "${r.requiredInputStringCopy}" ]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the bridge needs to define a constant ordering since we expose this data as a list. We shouldn't need to guarantee anything about the ordering, except consistency.

Its not unreasonable to assume that new Resource("one", opt=set[0]); new Resource("two", opt=set[1]) is safe between updates, where a re-order would break this.


prov := &schema.Provider{
ResourcesMap: map[string]*schema.Resource{
"xres": res,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"xres": res,
resourceName: res,

ctypack "github.com/zclconf/go-cty/cty/msgpack"
)

func ProposedNew(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the public entry point for this package. Can we add a comment explaining why it exists and what it does.

If we don't believe another repository could consume this function, we should mark it as unstable for consumption. We should be clear on when we adopt a backwards compatibility requirement.

@t0yv0
Copy link
Member Author

t0yv0 commented May 21, 2024

Obsolete now, the objective is met without this PR.

@t0yv0 t0yv0 closed this May 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants