Skip to content

Commit

Permalink
feat: Rework users datasource (#3030)
Browse files Browse the repository at this point in the history
User datasource reworked and aligned with our V1 guidelines,
additionally:
- added a way to reuse show_output and parameters assertions in the
datasource tests
- added init to add Sensitive to certain fields in generated and not
generated output schemas
- added describe output to user (manually like for a few other objects)

References: #2902
  • Loading branch information
sfc-gh-asawicki authored Sep 3, 2024
1 parent ad657eb commit 751239b
Show file tree
Hide file tree
Showing 14 changed files with 1,744 additions and 154 deletions.
13 changes: 13 additions & 0 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,19 @@ Type changes:
Validation changes:
- `default_secondary_roles` - only 1-element lists with `"ALL"` element are now supported. Check [Snowflake docs](https://docs.snowflake.com/en/sql-reference/sql/create-user#optional-object-properties-objectproperties) for more details.

#### *(breaking change)* refactored snowflake_users datasource
Changes:
- account checking logic was entirely removed
- `pattern` renamed to `like`
- `like`, `starts_with`, and `limit` filters added
- `SHOW USERS` output is enclosed in `show_output` field inside `users` (all the previous fields in `users` map were removed)
- Added outputs from **DESC USER** and **SHOW PARAMETERS IN USER** (they can be turned off by declaring `with_describe = false` and `with_parameters = false`, **they're turned on by default**).
The additional parameters call **DESC USER** (with `with_describe` turned on) and **SHOW PARAMETERS IN USER** (with `with_parameters` turned on) **per user** returned by **SHOW USERS**.
The outputs of both commands are held in `users` entry, where **DESC USER** is saved in the `describe_output` field, and **SHOW PARAMETERS IN USER** in the `parameters` field.
It's important to limit the records and calls to Snowflake to the minimum. That's why we recommend assessing which information you need from the data source and then providing strong filters and turning off additional fields for better plan performance.

Connected issues: [#2902](https://github.com/Snowflake-Labs/terraform-provider-snowflake/pull/2902)

## v0.94.0 ➞ v0.94.1
### changes in snowflake_schema

Expand Down
926 changes: 918 additions & 8 deletions docs/data-sources/users.md

Large diffs are not rendered by default.

76 changes: 73 additions & 3 deletions examples/data-sources/snowflake_users/data-source.tf
Original file line number Diff line number Diff line change
@@ -1,3 +1,73 @@
data "snowflake_users" "current" {
pattern = "user1"
}
# Simple usage
data "snowflake_users" "simple" {
}

output "simple_output" {
value = data.snowflake_users.simple.users
}

# Filtering (like)
data "snowflake_users" "like" {
like = "user-name"
}

output "like_output" {
value = data.snowflake_users.like.users
}

# Filtering (starts_with)
data "snowflake_users" "starts_with" {
starts_with = "user-"
}

output "starts_with_output" {
value = data.snowflake_users.starts_with.users
}

# Filtering (limit)
data "snowflake_users" "limit" {
limit {
rows = 10
from = "user-"
}
}

output "limit_output" {
value = data.snowflake_users.limit.users
}

# Without additional data (to limit the number of calls make for every found user)
data "snowflake_users" "only_show" {
# with_describe is turned on by default and it calls DESCRIBE USER for every user found and attaches its output to users.*.describe_output field
with_describe = false

# with_parameters is turned on by default and it calls SHOW PARAMETERS FOR USER for every user found and attaches its output to users.*.parameters field
with_parameters = false
}

output "only_show_output" {
value = data.snowflake_users.only_show.users
}

# Ensure the number of users is equal to at least one element (with the use of postcondition)
data "snowflake_users" "assert_with_postcondition" {
starts_with = "user-name"
lifecycle {
postcondition {
condition = length(self.users) > 0
error_message = "there should be at least one user"
}
}
}

# Ensure the number of users is equal to at exactly one element (with the use of check block)
check "user_check" {
data "snowflake_users" "assert_with_check_block" {
like = "user-name"
}

assert {
condition = length(data.snowflake_users.assert_with_check_block.users) == 1
error_message = "users filtered by '${data.snowflake_users.assert_with_check_block.like}' returned ${length(data.snowflake_users.assert_with_check_block.users)} users where one was expected"
}
}
37 changes: 31 additions & 6 deletions pkg/acceptance/bettertestspoc/assert/resource_assertions.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@ var (
// ResourceAssert is an embeddable struct that should be used to construct new resource assertions (for resource, show output, parameters, etc.).
// It implements both TestCheckFuncProvider and ImportStateCheckFuncProvider which makes it easy to create new resource assertions.
type ResourceAssert struct {
name string
id string
prefix string
assertions []ResourceAssertion
name string
id string
prefix string
assertions []ResourceAssertion
additionalPrefix string
}

// NewResourceAssert creates a ResourceAssert where the resource name should be used as a key for assertions.
Expand All @@ -45,11 +46,22 @@ func NewImportedResourceAssert(id string, prefix string) *ResourceAssert {
}
}

// NewDatasourceAssert creates a ResourceAssert for data sources.
func NewDatasourceAssert(name string, prefix string, additionalPrefix string) *ResourceAssert {
return &ResourceAssert{
name: name,
prefix: prefix,
assertions: make([]ResourceAssertion, 0),
additionalPrefix: additionalPrefix,
}
}

type resourceAssertionType string

const (
resourceAssertionTypeValueSet = "VALUE_SET"
resourceAssertionTypeValueNotSet = "VALUE_NOT_SET"
resourceAssertionTypeValueSet = "VALUE_SET"
resourceAssertionTypeValueNotSet = "VALUE_NOT_SET"
resourceAssertionTypeValuePresent = "VALUE_PRESENT"
)

type ResourceAssertion struct {
Expand All @@ -59,6 +71,7 @@ type ResourceAssertion struct {
}

func (r *ResourceAssert) AddAssertion(assertion ResourceAssertion) {
assertion.fieldName = r.additionalPrefix + assertion.fieldName
r.assertions = append(r.assertions, assertion)
}

Expand Down Expand Up @@ -92,6 +105,11 @@ func ResourceShowOutputValueSet(fieldName string, expected string) ResourceAsser
return ResourceAssertion{fieldName: showOutputPrefix + fieldName, expectedValue: expected, resourceAssertionType: resourceAssertionTypeValueSet}
}

// TODO [SNOW-1501905]: generate assertions with resourceAssertionTypeValuePresent
func ResourceShowOutputValuePresent(fieldName string) ResourceAssertion {
return ResourceAssertion{fieldName: showOutputPrefix + fieldName, resourceAssertionType: resourceAssertionTypeValuePresent}
}

const (
parametersPrefix = "parameters.0."
parametersValueSuffix = ".0.value"
Expand Down Expand Up @@ -137,6 +155,11 @@ func (r *ResourceAssert) ToTerraformTestCheckFunc(t *testing.T) resource.TestChe
errCut, _ := strings.CutPrefix(err.Error(), fmt.Sprintf("%s: ", r.name))
result = append(result, fmt.Errorf("%s %s assertion [%d/%d]: failed with error: %s", r.name, r.prefix, i+1, len(r.assertions), errCut))
}
case resourceAssertionTypeValuePresent:
if err := resource.TestCheckResourceAttrSet(r.name, a.fieldName)(s); err != nil {
errCut, _ := strings.CutPrefix(err.Error(), fmt.Sprintf("%s: ", r.name))
result = append(result, fmt.Errorf("%s %s assertion [%d/%d]: failed with error: %s", r.name, r.prefix, i+1, len(r.assertions), errCut))
}
}
}

Expand All @@ -159,6 +182,8 @@ func (r *ResourceAssert) ToTerraformImportStateCheckFunc(t *testing.T) resource.
}
case resourceAssertionTypeValueNotSet:
panic("implement")
case resourceAssertionTypeValuePresent:
panic("implement")
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package resourceparametersassert

import (
"testing"

"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert"
)

// UsersDatasourceParameters is a temporary workaround to have better parameter assertions in data source acceptance tests.
func UsersDatasourceParameters(t *testing.T, name string) *UserResourceParametersAssert {
t.Helper()

u := UserResourceParametersAssert{
ResourceAssert: assert.NewDatasourceAssert("data."+name, "parameters", "users.0."),
}
u.AddAssertion(assert.ValueSet("parameters.#", "1"))
return &u
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package resourceshowoutputassert

import (
"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert"
)

func (u *UserShowOutputAssert) HasCreatedOnNotEmpty() *UserShowOutputAssert {
u.AddAssertion(assert.ResourceShowOutputValuePresent("created_on"))
return u
}

func (u *UserShowOutputAssert) HasDaysToExpiryNotEmpty() *UserShowOutputAssert {
u.AddAssertion(assert.ResourceShowOutputValuePresent("days_to_expiry"))
return u
}

func (u *UserShowOutputAssert) HasMinsToUnlockNotEmpty() *UserShowOutputAssert {
u.AddAssertion(assert.ResourceShowOutputValuePresent("mins_to_unlock"))
return u
}

func (u *UserShowOutputAssert) HasMinsToBypassMfaNotEmpty() *UserShowOutputAssert {
u.AddAssertion(assert.ResourceShowOutputValuePresent("mins_to_bypass_mfa"))
return u
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package resourceshowoutputassert

import (
"testing"

"github.com/Snowflake-Labs/terraform-provider-snowflake/pkg/acceptance/bettertestspoc/assert"
)

// UsersDatasourceShowOutput is a temporary workaround to have better show output assertions in data source acceptance tests.
func UsersDatasourceShowOutput(t *testing.T, name string) *UserShowOutputAssert {
t.Helper()

u := UserShowOutputAssert{
ResourceAssert: assert.NewDatasourceAssert("data."+name, "show_output", "users.0."),
}
u.AddAssertion(assert.ValueSet("show_output.#", "1"))
return &u
}
10 changes: 10 additions & 0 deletions pkg/datasources/testdata/TestAcc_Users/without_user/test.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
data "snowflake_users" "test" {
like = "non-existing-user"

lifecycle {
postcondition {
condition = length(self.users) > 0
error_message = "there should be at least one user"
}
}
}
Loading

0 comments on commit 751239b

Please sign in to comment.