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

feat: Secret resource #3110

Open
wants to merge 60 commits into
base: main
Choose a base branch
from
Open

feat: Secret resource #3110

wants to merge 60 commits into from

Conversation

sfc-gh-fbudzynski
Copy link
Collaborator

@sfc-gh-fbudzynski sfc-gh-fbudzynski commented Oct 2, 2024

Changes

  • add snowflake_secret_with_client_credentials resource
  • add snowflake_secret_with_authorization_code_grant resource
  • add snowflake_secret_with_basic_authentication resource
  • add snowflake_secret_with_generic_string resource
  • fix parsing oauth_scopes list with comma separated elements

Test Plan

  • acceptance tests

References

https://docs.snowflake.com/en/sql-reference/sql/create-secret

…ource

# Conflicts:
#	pkg/acceptance/check_destroy.go
#	pkg/acceptance/helpers/secret_client.go
#	pkg/provider/provider.go
#	pkg/provider/resources/resources.go
#	pkg/resources/secret_common.go
… dateTime input formats for refresh_token_expiry_time
# Conflicts:
#	pkg/acceptance/bettertestspoc/assert/objectassert/gen/sdk_object_def.go
#	pkg/acceptance/helpers/secret_client.go
#	pkg/sdk/poc/README.md
#	pkg/sdk/secrets_gen_test.go
#	pkg/sdk/secrets_impl_gen.go
#	pkg/sdk/secrets_validations_gen.go
#	pkg/sdk/testint/secrets_gen_integration_test.go
@sfc-gh-fbudzynski sfc-gh-fbudzynski changed the title Secret resource feat: Secret resource Oct 2, 2024
Copy link

github-actions bot commented Oct 2, 2024

Integration tests failure for 07dc9e29726c465f1635293ac6b23b7e13fdaf9b

Copy link

github-actions bot commented Oct 3, 2024

Integration tests failure for 5c6d9ee8b438ea658b934cbd02c5f6bccbc83dcd

Copy link

github-actions bot commented Oct 3, 2024

Integration tests failure for 805c61e39315df81ff6f61aa9098a2b699ce26bd

Copy link

github-actions bot commented Oct 3, 2024

Integration tests failure for 044c4846d411d361ab761f43443b18bdaa26af4f

Copy link

github-actions bot commented Oct 3, 2024

Integration tests failure for 4ae36a482dc1a7721e769128cc8a2ad23dd79efe

@sfc-gh-fbudzynski sfc-gh-fbudzynski marked this pull request as ready for review October 4, 2024 06:51
Copy link

github-actions bot commented Oct 4, 2024

Integration tests failure for 4ac851a10ed73c3b48e20d37e3d20f124c75991e

},
"username": {
Type: schema.TypeString,
Optional: true,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why optional? In other fields too.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Describe for secret in Snowflake leaves some of those fields as "null" since secret can be one of four different types. Thought it needs to be included here as well, but it works as it should after removing the Optional from those fields.

}
if details.OauthRefreshTokenExpiryTime != nil {
s["oauth_refresh_token_expiry_time"] = details.OauthRefreshTokenExpiryTime.String()
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

created_on is missing.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

added created_on to SecretDescriptionToSchema()

@@ -71,7 +71,7 @@ func (c *SecretClient) CreateWithBasicAuthenticationFlow(t *testing.T, id sdk.Sc
func (c *SecretClient) CreateWithGenericString(t *testing.T, id sdk.SchemaObjectIdentifier, secretString string) (*sdk.Secret, func()) {
t.Helper()
ctx := context.Background()
request := sdk.NewCreateWithGenericStringSecretRequest(id, secretString)
request := sdk.NewCreateWithGenericStringSecretRequest(id, secretString).WithOrReplace(true)
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should not use OrReplace by default. It should be either an argument, or the function should be called CreateOrReplace... or Recreate...

ShowOutputAttributeName: {
Type: schema.TypeList,
Computed: true,
Description: "Outputs the result of `SHOW SECRET` for the given secret.",
Copy link
Collaborator

Choose a reason for hiding this comment

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

should be SHOW SECRETS

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

has been changed

if err := d.Set("database", secret.DatabaseName); err != nil {
return err
}
if err := d.Set("schema", secret.SchemaName); err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

name, database, and schema should be only in the import function.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

removed

return diag.FromErr(err)
}

if err := client.Secrets.Drop(ctx, sdk.NewDropSecretRequest(id).WithIfExists(*sdk.Bool(true))); err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

It could be simply WithIfExists(true). This applies to other resources.

})
}

func TestAcc_SecretWithAuthorizationCodeGrant_DifferentTimeFormats(t *testing.T) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

👍

name = "EXAMPLE_SECRET"
database = "EXAMPLE_DB"
schema = "EXAMPLE_SCHEMA"
api_authentication = "EXAMPLE_SECURITY_INTEGRATION_NAME"
Copy link
Collaborator

Choose a reason for hiding this comment

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

We have the fully_qualified_name field in most of the resources, and we prefer using this instead. Security integrations have simple IDs, but schema-level objects and resources with arguments have complex IDs that are error-prone. Just for the future :)

HasDatabaseString(id.DatabaseName()).
HasSchemaString(id.SchemaName()).
HasApiAuthenticationString(integrationId.Name()),
assert.Check(resource.TestCheckResourceAttr(secretModel.ResourceReference(), "oauth_scopes.#", "1")),
Copy link
Collaborator

Choose a reason for hiding this comment

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

You can add a custom method in the resourceassert package - check _ext files. This way we can assert slices, because generator doesn't handle them properly. There, you can compare slices by length, and then check if they have the same elements.

},
// set oauth_scopes and comment in config
{
Config: config.FromModel(t, secretModel.
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please also add a test case that checks for external changes - see how we do it in other resources. Add them to all of the secret resources.

@sfc-gh-jmichalak
Copy link
Collaborator

I forgot to add, but there are missing entries in MIGRATION GUIDE and custom templates in templates/resources/ - please copy one of them and adjust accordingly for each of the resources. We need them to add some custom notes - in this case, we want to note that these resources are v1 release candidates.

description: |-
Secret with OAuth authorization code grant where Secrets Type attribute is set to OAUTH2.
---

Copy link
Collaborator

Choose a reason for hiding this comment

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

The documentation for new resources should contain note at the top that it's v1 candidate. You have to create custom template that's under template/resources/* directory (ask if you need guidance on that; same for other newly added resources).

@@ -0,0 +1,10 @@
# basic resource
Copy link
Collaborator

Choose a reason for hiding this comment

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

We usually give a basic example that contains only a minimal field resources to create it (e.g., comment is not needed; it's optional) and a complete example with all options set. Of course every resource is different, but this one is pretty straight forward and basic + complete examples would be sufficient. (same applies to other resources).

@@ -71,7 +71,7 @@ func (c *SecretClient) CreateWithBasicAuthenticationFlow(t *testing.T, id sdk.Sc
func (c *SecretClient) CreateWithGenericString(t *testing.T, id sdk.SchemaObjectIdentifier, secretString string) (*sdk.Secret, func()) {
t.Helper()
ctx := context.Background()
request := sdk.NewCreateWithGenericStringSecretRequest(id, secretString)
request := sdk.NewCreateWithGenericStringSecretRequest(id, secretString).WithOrReplace(true)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this needed? I'm guessing it was needed because in some of the tests you're calling Create too many times?

if err := d.Set("database", secret.DatabaseName); err != nil {
return err
}
if err := d.Set("schema", secret.SchemaName); err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

We don't set "name", "database", "schema" in read, we only do it in the import, remove them from here

return nil
}

func handleSecretUpdate(id sdk.SchemaObjectIdentifier, d *schema.ResourceData) *sdk.AlterSecretRequest {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would refactor this function to take in *sdk.AlterSecretRequest rather than create it here and either return it or nil. I can imagine there's a bug (or could be) that we would like to use that request and it's nil, because there were no changes done to "comment" field where the request is created.

return nil, err
}

if err := d.Set("oauth_refresh_token_expiry_time", secretDescription.OauthRefreshTokenExpiryTime.String()); err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why this is not read in the Read operation?

if err := d.Set("oauth_refresh_token_expiry_time", secretDescription.OauthRefreshTokenExpiryTime.String()); err != nil {
return nil, err
}
if err := d.Set("api_authentication", secretDescription.IntegrationName); err != nil {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Import is constructed in a way that always after Import there's Read, so if you read this value in Read operation you can safely remove it.

return diag.FromErr(err)
}

if err = setStateToValuesFromConfig(d, secretAuthorizationCodeGrantSchema, []string{"oauth_refresh_token_expiry_time"}); err != nil {
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 normal d.Set would be sufficient here (it would change the behaviour, but I think it's better suited for this case.

WithComment(newComment),
),
ConfigPlanChecks: resource.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
Copy link
Collaborator

Choose a reason for hiding this comment

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

you can also check action for the whole resource with:

plancheck.ExpectResourceAction(secretModel.ResourceReference(), plancheck.ResourceActionUpdate),

return diag.Diagnostics{
diag.Diagnostic{
Severity: diag.Warning,
Summary: "Failed to retrieve secret. Target object not found. Marking the resource as removed.",
Copy link
Collaborator

Choose a reason for hiding this comment

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

It's also to give details here like in the diag below (it's easier to detect for which id this error happened; for us and the user).

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.

3 participants