-
Notifications
You must be signed in to change notification settings - Fork 155
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
Fix panics in applyTags when tags are unknown #2776
Conversation
Does the PR have any schema changes?Does the PR have any schema changes?Looking good! No breaking changes found. Maintainer note: consult the runbook for dealing with any breaking changes. |
provider/tags.go
Outdated
} | ||
// If there are 0 tags, delete the tags entry rather than sending an empty map. The unknown | ||
// case is quirky though, prefer to send the unknown marker out rather than deleting it. | ||
if !hasTags && !allTags.ContainsUnknowns() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is equally correct, and simpler to comprehend. It allows us to remove hasTags
.
if !hasTags && !allTags.ContainsUnknowns() { | |
if allTags.IsNil() && !allTags.ContainsUnknowns() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah but it also can be an Output{Element: nil, Dependencies}, among other things.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well you're right. IsNil() is the only case we truly want to elide. The condition should just be a nil-check
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
"pgregory.net/rapid" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎉
provider/pvutil.go
Outdated
// Computed, or Output values. All the metadata bits about these is floated to top-level and | ||
// re-applied to the value the user code receives out of composePropertyValue. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is fine. It means that tags: { "foo": "bar", "secret": [secret] }
will come out as [secret]
. I doubt that this will be a problem in practice. I don't want to block merging on it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm you're right. We're losing precision. Unlike what happens in the restricted CheckConfigure, there are true nested secrets here. That's bad especially when merging is not even involved, like empty provider tags + secret resource tags. Hm.. I may do a quick fix to this by keeping a track of secret string hashes and re-secreting matching in the output.
Verifying secret handling for tags at integration level, it's not what you'd expect. First off all if you pass secrets, the provider unwraps the secrets and stores them in tagsAll property. So the protections are defeated. Second if you start from a program that does not have secrets, and you mark a resource input such as tags secret, you don't get an update plan..... I think we need to trace this down as this is more generic than tags. It might be an issue with providers per se. Third. Secrets never reach the code edited here. import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";
import * as awsx from "@pulumi/awsx";
const b2 = new aws.s3.Bucket("my-bucket-40", {}, {});
// Create an AWS provider for the us-east-1 region.
let useast1 = new aws.Provider("useast1", {
region: "us-east-1",
defaultTags: {tags: {"this": b2.arn }},
});
// Create an AWS resource (S3 Bucket)
const bucket = new aws.s3.Bucket(
"my-bucket",
{tags: {"that": pulumi.secret(b2.arn)}},
{provider: useast1});
// Export the name of the bucket
export const bucketName = bucket.id;
// interesting doe adding a <secret> wrapper not update ? Gives
But in the state we get:
Squinting at this, the engine must have applied some secrets handling so the provider did not even get exposed to it. |
OK. I poked around some more. First-class outputs or nested computed values or secrets never reach this code, they never reach Check.. This is all the idiosyncratic behavior of the bridge. The only place is CheckConfig. The repro is: const b2 = new aws.s3.Bucket("my-bucket-40", {}, {});
// Create an AWS provider for the us-east-1 region.
let useast1 = new aws.Provider("useast1", {
region: "us-east-1",
defaultTags: {tags: {"this": b2.id }},
}); IN this case we get the unknown sentinel.
Note that the "this" structure is destroyed already, it's one big unknown. Unfortunately, PreCheckCallback gets this down because it recalls the map from CheckConfig and gets sent it.. So this is where we get the unknown from. This is the only realistic case that can happen given how current bridge + pulumi CLI work. |
Simplified based on the findings, PTAL. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Fixes #2774
We detected an issue in pulumi-eks test suite against the latest v6.0.3 release. This theoretically could affects users that set resource and or provider tags to unknown Outputs, although I tried and failed to reproduce outside of the pulumi-eks test.
The fix for #1655 included reimplementing tag merging (computing effective tags from the set of provider-level tags and resource-level tags) on Pulumi side as a callback to the Check Pulumi gRPC method. This works against resource.PropertyValue model and did not fully account for receiving a Computed when tags are unknown, resulting in a panic in the eks test.
The fix puts some combinators in place to shield resource.PropertyValue transformations from observing Unknowns, Computeds, and first-class Outputs. Note that this is a bit forward looking since this is a bridged provider, and as such on current version of tf-bridge it opts into a simplified version of the wire protocol where for example first-class Output values should not be passed to the bridge code. So it's not strictly the simplest fix, but hopefully this can be useful down the line.
I'd like to consider upstreaming propertyvalue.TransformErr (should it be called Walk in Go?) and similar combinators to pulumi/pulumi at some point if they prove useful.