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

(bugfix) Fix cognito user pool client orphaned resources #1021

Merged
merged 3 commits into from
Jan 3, 2024

Conversation

mbbush
Copy link
Collaborator

@mbbush mbbush commented Dec 13, 2023

Description of your changes

The cognito idp user pool client is one of the handful of resources in terraform-provider-aws v4.67.0 that use the terraform plugin framework, and therefore that we still reconcile by forking a CLI terraform process.

There are three problems with the cognito user pool client:

  1. Creation fails if the spec.forProvider.name doesn't match the regex for the clientId
  2. Whenever the provider pod restarts, crossplane will either create a duplicate resource, orphaning the existing one, or get into a Synced: False state, depending on whether spec.forProvider.name matches the clientId regex.
  3. Observe-only resources fail, because terraform import expects an argument that is not the same as the terraform id.

This PR fixes the first two issues, and makes the third issue no worse.

Context

This resource has two idiosyncrasies that make it difficult.

  1. Calling terraform apply -refresh-only with an id of "" returns an error, because the terraform provider passes through the empty-string id to the aws sdk, which (correctly) complains that trying to find a user pool client with id "" is invalid. I suspect this is because the resource uses the terraform plugin framework, which introduced the concept of a null value as distinct from a zero value. I suspect that if we had some way to specify a null value for the id, it would work fine, but the type in upjet is string not *string so I don't see how to do that without major changes. Maybe the upjet developers will need to add a transformation layer from zero values back to null when integrating with the terraform provider framework, or perhaps they'll come up with a better idea.

  2. The argument needed to terraform import the resource is different than the terraform id, but the terraform id is needed to run terraform apply -refresh-only. The provider uses terraform import for resources with management policy Observe and terraform apply -refresh-only for other management policies. Unless someone has a clever way to identify which case we're in, in the context of an external name configuration, we can only support one of these use cases.

#762 was an attempt to work around the "id":"" issue, but it introduced new problems.

My solution for the first issue is to simply use a static client id of "invalidnonemptystring", which satisfies the client id regex [\w+]+, so doesn't cause an error when we run terraform apply -refresh-only, but also doesn't match any actual existing client id (the aws-generated ids that I've seen are all 26 lowercase alphanumeric characters long). This feels like getting the right result by doing the wrong thing, but I can't think of any actual problems it would cause, nor can I come up with a better idea.

For the second issue, I've chosen to write an external name config that supports assuming full control of existing resources, and does not support observing existing resources only, as that seems like the lesser of two evils (especially since it's necessary for recovering from a provider pod crash restart). It also has the advantage of keeping the terraform id set to the value of the terraform id (except when we don't have an id, so set it to "invalidnonemptystring" to get the necessary NotFound response).

Ultimately I hope that when we reconcile this through the terraform plugin framework, we'll be able to ignore the format expected by terraform import and just use the id and user pool id parameters. It looks like that's exactly the implementation used in the Read method on the terraform provider.

Fixes #828
Fixes #807
Fixes #1049

I have:

  • Run make reviewable test to ensure this PR is ready for review.

How has this code been tested

Uptest passes. It did not pass on main with the previous config, because the import step resulted in creating a second user pool client.

Creating an observe-only resource fails. This is not a regression, as it fails in the same way with the current version of the provider, and I don't believe it is fixable without breaking other, more important functionality.

The external name format is remaining the same: the alphanumeric client id.

When I ran uptest locally for the existing examples/cognitoidp/userpooluicustomization.yaml, I got an error about the domain already being registered, so I updated the example to use a random domain name and now it passes consistently.

@mbbush
Copy link
Collaborator Author

mbbush commented Dec 13, 2023

/test-examples="examples/cognitoidp/userpoolclient.yaml"

@mbbush
Copy link
Collaborator Author

mbbush commented Dec 13, 2023

/test-examples="examples/cognitoidp/userpooluicustomization.yaml"

@mbbush mbbush changed the title Update external name config for cognito user pool client (bugfix) Allow import of cognito user pool client Dec 22, 2023
@mbbush mbbush changed the title (bugfix) Allow import of cognito user pool client (bugfix) Fix cognito user pool client external-name config Dec 22, 2023
func cognitoUserPoolClient() config.ExternalName {
e := config.IdentifierFromProvider
// TODO: Uncomment when it's acceptable to remove fields from spec.initProvider (major release)
//e.IdentifierFields = []string{"user_pool_id"}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Before I rebased and included #1046, setting this was a non-breaking change, since reference fields and identifier fields were both omitted from spec.initProvider. Now, if I set it here, then upjet removes userPoolId, userPoolIdRef and userPoolIdSelector from spec.initProvider.

For this resource, not having those fields in initProvider seems totally fine (if they change, terraform will require destroying and recreating the resource anyways), and it is necessary to know the user pool id in some form in order to read the state of the resource from AWS, so it seems to conceptually fit as an identifier field.

This is a bugfix, so I think the best compromise is to leave it commented out, with a TODO to add it back when we do a major release.

@mbbush
Copy link
Collaborator Author

mbbush commented Dec 30, 2023

/test-examples="examples/cognitoidp/userpoolclient-with-dashes.yaml, examples/cognitoidp/userpoolclient-observe.yaml"

@mbbush mbbush changed the title (bugfix) Fix cognito user pool client external-name config (bugfix) Fix cognito user pool client orphaned resources Dec 30, 2023
@mbbush
Copy link
Collaborator Author

mbbush commented Dec 30, 2023

/test-examples="examples/cognitoidp/userpoolclient.yaml, examples/cognitoidp/userpoolclient-with-dashes.yaml"

@mbbush
Copy link
Collaborator Author

mbbush commented Jan 3, 2024

@ulucinar @turkenf @sergenyalcin I'd really like to get this merged and (ideally) released promptly. This resource is basically unusable without this fix.

@turkenf
Copy link
Collaborator

turkenf commented Jan 3, 2024

/test-examples="examples/cognitoidp/userpoolclient.yaml"

@turkenf
Copy link
Collaborator

turkenf commented Jan 3, 2024

/test-examples="examples/cognitoidp/userpoolclient-with-dashes.yaml"

Copy link
Collaborator

@turkenf turkenf left a comment

Choose a reason for hiding this comment

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

Thank you for this PR @mbbush, I left a few comments to you for consider.

Comment on lines +1 to +32
---
apiVersion: cognitoidp.aws.upbound.io/v1beta1
kind: UserPool
metadata:
annotations:
uptest.upbound.io/timeout: "900"
labels:
testing.upbound.io/example-name: example-with-dashes
name: example-with-dashes
spec:
forProvider:
name: example
region: us-west-1

---

apiVersion: cognitoidp.aws.upbound.io/v1beta1
kind: UserPoolClient
metadata:
annotations:
uptest.upbound.io/timeout: "900"
labels:
testing.upbound.io/example-name: example-with-dashes
name: example-with-dashes
spec:
forProvider:
name: name-that-doesnt-match-id-regex
region: us-west-1
userPoolIdSelector:
matchLabels:
testing.upbound.io/example-name: example-with-dashes

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think there is no need to add another example here that only contains dashes in the name, instead we can give the name in the main example(examples/cognitoidp/userpoolclient.yaml) with dashes and remove this example, what do you think?

@@ -0,0 +1,28 @@
---
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you please remove this separator?

---
apiVersion: cognitoidp.aws.upbound.io/v1beta1
kind: UserPool
metadata:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you please add the example-id?

Suggested change
metadata:
metadata:
annotations:
meta.upbound.io/example-id: cognitoidp/v1beta1/userpoolclient


apiVersion: cognitoidp.aws.upbound.io/v1beta1
kind: UserPoolClient
metadata:
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
metadata:
metadata:
annotations:
meta.upbound.io/example-id: cognitoidp/v1beta1/userpoolclient

Copy link
Collaborator

@turkenf turkenf left a comment

Choose a reason for hiding this comment

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

Thank you for your work on this PR @mbbush, the improvements noted in the comments above are not prohibitive in moving this PR forward and can be fixed later.

@turkenf turkenf merged commit 38d693f into crossplane-contrib:main Jan 3, 2024
13 of 14 checks passed
Copy link
Collaborator

@ulucinar ulucinar left a comment

Choose a reason for hiding this comment

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

Thank you @mbbush, lgtm.

Copy link

github-actions bot commented Jan 3, 2024

Backport failed for release-0.46, because it was unable to cherry-pick the commit(s).

Please cherry-pick the changes locally.

git fetch origin release-0.46
git worktree add -d .worktree/backport-1021-to-release-0.46 origin/release-0.46
cd .worktree/backport-1021-to-release-0.46
git checkout -b backport-1021-to-release-0.46
ancref=$(git merge-base 8f8e547a8fbba3cac6e3b1ffe137478818b4b06e dc6a1aa2ccc11eccddef0ce18e21cc6b1b875c87)
git cherry-pick -x $ancref..dc6a1aa2ccc11eccddef0ce18e21cc6b1b875c87

Copy link

github-actions bot commented Jan 3, 2024

Successfully created backport PR #1059 for release-0.47.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants