Skip to content

Commit

Permalink
Merge branch 'main' into enhancement/issue-85
Browse files Browse the repository at this point in the history
  • Loading branch information
vmanilo authored Jul 25, 2023
2 parents 8ab6d9b + c5dfd5e commit 967b396
Show file tree
Hide file tree
Showing 13 changed files with 1,905 additions and 8 deletions.
8 changes: 8 additions & 0 deletions .changes/1.4.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
## 1.4.0 (July 24, 2023)

FEATURES:

* tfjsonpath: Introduced new `tfjsonpath` package which contains methods that allow traversal of Terraform JSON data ([#154](https://github.com/hashicorp/terraform-plugin-testing/issues/154))
* plancheck: Added `ExpectUnknownValue` built-in plan check, which asserts that a given attribute has an unknown value ([#154](https://github.com/hashicorp/terraform-plugin-testing/issues/154))
* plancheck: Added `ExpectSensitiveValue` built-in plan check, which asserts that a given attribute has a sensitive value ([#154](https://github.com/hashicorp/terraform-plugin-testing/issues/154))

8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## 1.4.0 (July 24, 2023)

FEATURES:

* tfjsonpath: Introduced new `tfjsonpath` package which contains methods that allow traversal of Terraform JSON data ([#154](https://github.com/hashicorp/terraform-plugin-testing/issues/154))
* plancheck: Added `ExpectUnknownValue` built-in plan check, which asserts that a given attribute has an unknown value ([#154](https://github.com/hashicorp/terraform-plugin-testing/issues/154))
* plancheck: Added `ExpectSensitiveValue` built-in plan check, which asserts that a given attribute has a sensitive value ([#154](https://github.com/hashicorp/terraform-plugin-testing/issues/154))

## 1.3.0 (June 13, 2023)

FEATURES:
Expand Down
61 changes: 61 additions & 0 deletions plancheck/expect_sensitive_value.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package plancheck

import (
"context"
"fmt"

"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
)

var _ PlanCheck = expectSensitiveValue{}

type expectSensitiveValue struct {
resourceAddress string
attributePath tfjsonpath.Path
}

// CheckPlan implements the plan check logic.
func (e expectSensitiveValue) CheckPlan(ctx context.Context, req CheckPlanRequest, resp *CheckPlanResponse) {

for _, rc := range req.Plan.ResourceChanges {
if e.resourceAddress != rc.Address {
continue
}

result, err := tfjsonpath.Traverse(rc.Change.AfterSensitive, e.attributePath)
if err != nil {
resp.Error = err
return
}

isSensitive, ok := result.(bool)
if !ok {
resp.Error = fmt.Errorf("invalid path: the path value cannot be asserted as bool")
return
}

if !isSensitive {
resp.Error = fmt.Errorf("attribute at path is not sensitive")
return
}

return
}

resp.Error = fmt.Errorf("%s - Resource not found in plan ResourceChanges", e.resourceAddress)
}

// ExpectSensitiveValue returns a plan check that asserts that the specified attribute at the given resource has a sensitive value.
//
// Due to implementation differences between the terraform-plugin-sdk and the terraform-plugin-framework, representation of sensitive
// values may differ. For example, terraform-plugin-sdk based providers may have less precise representations of sensitive values, such
// as marking whole maps as sensitive rather than individual element values.
func ExpectSensitiveValue(resourceAddress string, attributePath tfjsonpath.Path) PlanCheck {
return expectSensitiveValue{
resourceAddress: resourceAddress,
attributePath: attributePath,
}
}
300 changes: 300 additions & 0 deletions plancheck/expect_sensitive_value_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,300 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package plancheck_test

import (
"context"
"regexp"
"testing"

"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"

r "github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/plancheck"
"github.com/hashicorp/terraform-plugin-testing/tfjsonpath"
)

func Test_ExpectSensitiveValue_SensitiveStringAttribute(t *testing.T) {
t.Parallel()

r.UnitTest(t, r.TestCase{
ProviderFactories: map[string]func() (*schema.Provider, error){
"test": func() (*schema.Provider, error) { //nolint:unparam // required signature
return testProviderSensitive(), nil
},
},
Steps: []r.TestStep{
{
Config: `
resource "test_resource" "one" {
sensitive_string_attribute = "test"
}
`,
ConfigPlanChecks: r.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectSensitiveValue("test_resource.one",
tfjsonpath.New("sensitive_string_attribute")),
},
},
},
},
})
}

func Test_ExpectSensitiveValue_SensitiveListAttribute(t *testing.T) {
t.Parallel()

r.UnitTest(t, r.TestCase{
ProviderFactories: map[string]func() (*schema.Provider, error){
"test": func() (*schema.Provider, error) { //nolint:unparam // required signature
return testProviderSensitive(), nil
},
},
Steps: []r.TestStep{
{
Config: `
resource "test_resource" "one" {
sensitive_list_attribute = ["value1"]
}
`,
ConfigPlanChecks: r.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectSensitiveValue("test_resource.one",
tfjsonpath.New("sensitive_list_attribute")),
},
},
},
},
})
}

func Test_ExpectSensitiveValue_SensitiveSetAttribute(t *testing.T) {
t.Parallel()

r.UnitTest(t, r.TestCase{
ProviderFactories: map[string]func() (*schema.Provider, error){
"test": func() (*schema.Provider, error) { //nolint:unparam // required signature
return testProviderSensitive(), nil
},
},
Steps: []r.TestStep{
{
Config: `
resource "test_resource" "one" {
sensitive_set_attribute = ["value1"]
}
`,
ConfigPlanChecks: r.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectSensitiveValue("test_resource.one",
tfjsonpath.New("sensitive_set_attribute")),
},
},
},
},
})
}

func Test_ExpectSensitiveValue_SensitiveMapAttribute(t *testing.T) {
t.Parallel()

r.UnitTest(t, r.TestCase{
ProviderFactories: map[string]func() (*schema.Provider, error){
"test": func() (*schema.Provider, error) { //nolint:unparam // required signature
return testProviderSensitive(), nil
},
},
Steps: []r.TestStep{
{
Config: `
resource "test_resource" "one" {
sensitive_map_attribute = {
key1 = "value1",
}
}
`,
ConfigPlanChecks: r.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectSensitiveValue("test_resource.one",
tfjsonpath.New("sensitive_map_attribute")),
},
},
},
},
})
}

func Test_ExpectSensitiveValue_ListNestedBlock_SensitiveAttribute(t *testing.T) {
t.Parallel()

r.UnitTest(t, r.TestCase{
ProviderFactories: map[string]func() (*schema.Provider, error){
"test": func() (*schema.Provider, error) { //nolint:unparam // required signature
return testProviderSensitive(), nil
},
},
Steps: []r.TestStep{
{
Config: `
resource "test_resource" "one" {
list_nested_block_sensitive_attribute {
sensitive_list_nested_block_attribute = "sensitive-test"
list_nested_block_attribute = "test"
}
}
`,
ConfigPlanChecks: r.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectSensitiveValue("test_resource.one",
tfjsonpath.New("list_nested_block_sensitive_attribute").AtSliceIndex(0).
AtMapKey("sensitive_list_nested_block_attribute")),
},
},
},
},
})
}

func Test_ExpectSensitiveValue_SetNestedBlock_SensitiveAttribute(t *testing.T) {
t.Parallel()

r.UnitTest(t, r.TestCase{
ProviderFactories: map[string]func() (*schema.Provider, error){
"test": func() (*schema.Provider, error) { //nolint:unparam // required signature
return testProviderSensitive(), nil
},
},
Steps: []r.TestStep{
{
Config: `
resource "test_resource" "one" {
set_nested_block_sensitive_attribute {
sensitive_set_nested_block_attribute = "sensitive-test"
set_nested_block_attribute = "test"
}
}
`,
ConfigPlanChecks: r.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectSensitiveValue("test_resource.one",
tfjsonpath.New("set_nested_block_sensitive_attribute")),
},
},
},
},
})
}

func Test_ExpectSensitiveValue_ExpectError_ResourceNotFound(t *testing.T) {
t.Parallel()

r.UnitTest(t, r.TestCase{
ProviderFactories: map[string]func() (*schema.Provider, error){
"test": func() (*schema.Provider, error) { //nolint:unparam // required signature
return testProviderSensitive(), nil
},
},
Steps: []r.TestStep{
{
Config: `
resource "test_resource" "one" {}
`,
ConfigPlanChecks: r.ConfigPlanChecks{
PreApply: []plancheck.PlanCheck{
plancheck.ExpectSensitiveValue("test_resource.two", tfjsonpath.New("set_attribute")),
},
},
ExpectError: regexp.MustCompile(`test_resource.two - Resource not found in plan ResourceChanges`),
},
},
})
}

func testProviderSensitive() *schema.Provider {
return &schema.Provider{
ResourcesMap: map[string]*schema.Resource{
"test_resource": {
CreateContext: func(_ context.Context, d *schema.ResourceData, _ interface{}) diag.Diagnostics {
d.SetId("test")
return nil
},
UpdateContext: func(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics {
return nil
},
DeleteContext: func(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics {
return nil
},
ReadContext: func(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics {
return nil
},
Schema: map[string]*schema.Schema{
"sensitive_string_attribute": {
Sensitive: true,
Optional: true,
Type: schema.TypeString,
},
"sensitive_list_attribute": {
Sensitive: true,
Type: schema.TypeList,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Optional: true,
},
"sensitive_set_attribute": {
Sensitive: true,
Type: schema.TypeSet,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Optional: true,
},
"sensitive_map_attribute": {
Sensitive: true,
Type: schema.TypeMap,
Elem: &schema.Schema{
Type: schema.TypeString,
},
Optional: true,
},
"list_nested_block_sensitive_attribute": {
Type: schema.TypeList,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"list_nested_block_attribute": {
Type: schema.TypeString,
Optional: true,
},
"sensitive_list_nested_block_attribute": {
Sensitive: true,
Type: schema.TypeString,
Optional: true,
},
},
},
},
"set_nested_block_sensitive_attribute": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"set_nested_block_attribute": {
Type: schema.TypeString,
Optional: true,
},
"sensitive_set_nested_block_attribute": {
Sensitive: true,
Type: schema.TypeString,
Optional: true,
},
},
},
},
},
},
},
}
}
Loading

0 comments on commit 967b396

Please sign in to comment.