Skip to content

Commit

Permalink
add nonsensitive_values field to tfe_outputs
Browse files Browse the repository at this point in the history
  • Loading branch information
Uk1288 committed Dec 7, 2022
1 parent 7473c11 commit 60ff514
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 25 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## Unreleased

FEATURES:
* d/tfe_outputs: Add `nonsensitive_values` attribute to expose current non-sensitive outputs of a given workspace ([#711](https://github.com/hashicorp/terraform-provider-tfe/pull/711))

## v0.40.0 (December 6, 2022)

Expand Down
64 changes: 39 additions & 25 deletions tfe/data_source_outputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (d dataSourceOutputs) ReadDataSource(ctx context.Context, req *tfprotov5.Re
return resp, nil
}

tftypesValues, stateTypes, err := parseStateOutput(remoteStateOutput)
tftypesValues, stateTypes, tftypesNonsensitiveValues, nonsensitiveStateTypes, err := parseStateOutput(remoteStateOutput)
if err != nil {
resp.Diagnostics = append(resp.Diagnostics, &tfprotov5.Diagnostic{
Severity: tfprotov5.DiagnosticSeverityError,
Expand All @@ -70,23 +70,26 @@ func (d dataSourceOutputs) ReadDataSource(ctx context.Context, req *tfprotov5.Re
id := fmt.Sprintf("%s-%s", orgName, wsName)
state, err := tfprotov5.NewDynamicValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"workspace": tftypes.String,
"organization": tftypes.String,
"values": tftypes.DynamicPseudoType,
"id": tftypes.String,
"workspace": tftypes.String,
"organization": tftypes.String,
"values": tftypes.DynamicPseudoType,
"nonsensitive_values": tftypes.DynamicPseudoType,
"id": tftypes.String,
},
}, tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"workspace": tftypes.String,
"organization": tftypes.String,
"values": tftypes.Object{AttributeTypes: stateTypes},
"id": tftypes.String,
"workspace": tftypes.String,
"organization": tftypes.String,
"values": tftypes.Object{AttributeTypes: stateTypes},
"nonsensitive_values": tftypes.Object{AttributeTypes: nonsensitiveStateTypes},
"id": tftypes.String,
},
}, map[string]tftypes.Value{
"workspace": tftypes.NewValue(tftypes.String, wsName),
"organization": tftypes.NewValue(tftypes.String, orgName),
"values": tftypes.NewValue(tftypes.Object{AttributeTypes: stateTypes}, tftypesValues),
"id": tftypes.NewValue(tftypes.String, id),
"workspace": tftypes.NewValue(tftypes.String, wsName),
"organization": tftypes.NewValue(tftypes.String, orgName),
"values": tftypes.NewValue(tftypes.Object{AttributeTypes: stateTypes}, tftypesValues),
"nonsensitive_values": tftypes.NewValue(tftypes.Object{AttributeTypes: nonsensitiveStateTypes}, tftypesNonsensitiveValues),
"id": tftypes.NewValue(tftypes.String, id),
}))

if err != nil {
Expand Down Expand Up @@ -117,10 +120,11 @@ func (d dataSourceOutputs) readConfigValues(req *tfprotov5.ReadDataSourceRequest
config := req.Config
val, err := config.Unmarshal(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"workspace": tftypes.String,
"organization": tftypes.String,
"values": tftypes.DynamicPseudoType,
"id": tftypes.String,
"workspace": tftypes.String,
"organization": tftypes.String,
"values": tftypes.DynamicPseudoType,
"nonsensitive_values": tftypes.DynamicPseudoType,
"id": tftypes.String,
}})
if err != nil {
return orgName, wsName, fmt.Errorf("Error unmarshalling config: %w", err)
Expand Down Expand Up @@ -154,7 +158,8 @@ type stateData struct {
}

type outputData struct {
Value cty.Value
Value cty.Value
Sensitive cty.Value
}

func (d dataSourceOutputs) readStateOutput(ctx context.Context, tfeClient *tfe.Client, orgName, wsName string) (*stateData, error) {
Expand Down Expand Up @@ -191,40 +196,49 @@ func (d dataSourceOutputs) readStateOutput(ctx context.Context, tfeClient *tfe.C
return nil, fmt.Errorf("Could not unmarshal output value: %w", err)
}
sd.outputs[op.Name] = &outputData{
Value: v.Value,
Value: v.Value,
Sensitive: cty.BoolVal(op.Sensitive),
}
}

return sd, nil
}

func parseStateOutput(stateOutput *stateData) (map[string]tftypes.Value, map[string]tftypes.Type, error) {
func parseStateOutput(stateOutput *stateData) (map[string]tftypes.Value, map[string]tftypes.Type, map[string]tftypes.Value, map[string]tftypes.Type, error) {
tftypesValues := map[string]tftypes.Value{}
stateTypes := map[string]tftypes.Type{}

tftypesNonsensitiveValues := map[string]tftypes.Value{}
nonsensitiveStateTypes := map[string]tftypes.Type{}

for name, output := range stateOutput.outputs {
marshData, err := output.Value.Type().MarshalJSON()
if err != nil {
return nil, nil, fmt.Errorf("Could not marshal output type: %w", err)
return nil, nil, nil, nil, fmt.Errorf("Could not marshal output type: %w", err)
}
tfType, err := tftypes.ParseJSONType(marshData)
if err != nil {
return nil, nil, fmt.Errorf("Could not parse json type data: %w", err)
return nil, nil, nil, nil, fmt.Errorf("Could not parse json type data: %w", err)
}
mByte, err := ctyjson.Marshal(output.Value, output.Value.Type())
if err != nil {
return nil, nil, fmt.Errorf("Could not marshal output value and output type: %w", err)
return nil, nil, nil, nil, fmt.Errorf("Could not marshal output value and output type: %w", err)
}
tfRawState := tfprotov5.RawState{
JSON: mByte,
}
newVal, err := tfRawState.Unmarshal(tfType)
if err != nil {
return nil, nil, fmt.Errorf("Could not unmarshal tftype into value: %w", err)
return nil, nil, nil, nil, fmt.Errorf("Could not unmarshal tftype into value: %w", err)
}
if output.Sensitive.False() {
tftypesNonsensitiveValues[name] = newVal
nonsensitiveStateTypes[name] = tfType
}

tftypesValues[name] = newVal
stateTypes[name] = tfType
}

return tftypesValues, stateTypes, nil
return tftypesValues, stateTypes, tftypesNonsensitiveValues, nonsensitiveStateTypes, nil
}
85 changes: 85 additions & 0 deletions tfe/data_source_outputs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,51 @@ func TestAccTFEOutputs(t *testing.T) {
})
}

func TestAccTFEOutputs_ReadAllNonSensitiveValues(t *testing.T) {
skipIfUnitTest(t)

client, err := getClientUsingEnv()
if err != nil {
t.Fatalf("error getting client %v", err)
}

rInt := rand.New(rand.NewSource(time.Now().UnixNano())).Int()
fileName := "test-fixtures/state-versions/terraform.tfstate"
orgName, wsName, orgCleanup := createStateVersion(t, client, rInt, fileName)
t.Cleanup(orgCleanup)

waitForOutputs(t, client, orgName, wsName)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
ProtoV5ProviderFactories: testAccMuxedProviders,
Steps: []resource.TestStep{
{
Config: testAccTFEOutputs_dataSourceReadNonsensitiveValues(rInt, orgName, wsName),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(
"tfe_organization.foobar", "name", fmt.Sprintf("tst-%d", rInt)),
resource.TestCheckResourceAttr(
"tfe_workspace.foobar", "name", fmt.Sprintf("workspace-test-%d", rInt)),
resource.TestCheckResourceAttr(
"data.tfe_outputs.foobar", "organization", orgName),
resource.TestCheckResourceAttr(
"data.tfe_outputs.foobar", "workspace", wsName),
// nonsensitive_values does not set sensitive values
resource.TestCheckNoResourceAttr("data.tfe_outputs.foobar", "nonsensitive_values.test_output_string"),
// These outputs rely on the values in test-fixtures/state-versions/terraform.tfstate
testCheckOutputState("test_output_list_string", &terraform.OutputState{Value: []interface{}{"us-west-1a"}}),
testCheckOutputState("test_output_tuple_number", &terraform.OutputState{Value: []interface{}{"1", "2"}}),
testCheckOutputState("test_output_tuple_string", &terraform.OutputState{Value: []interface{}{"one", "two"}}),
testCheckOutputState("test_output_object", &terraform.OutputState{Value: map[string]interface{}{"foo": "bar"}}),
testCheckOutputState("test_output_number", &terraform.OutputState{Value: "5"}),
testCheckOutputState("test_output_bool", &terraform.OutputState{Value: "true"}),
),
},
},
})
}

func TestAccTFEOutputs_emptyOutputs(t *testing.T) {
skipIfUnitTest(t)

Expand Down Expand Up @@ -249,6 +294,46 @@ output "test_output_bool" {
`, rInt, rInt, org, workspace)
}

func testAccTFEOutputs_dataSourceReadNonsensitiveValues(rInt int, org, workspace string) string {
return fmt.Sprintf(`
resource "tfe_organization" "foobar" {
name = "tst-%d"
email = "admin@company.com"
}
resource "tfe_workspace" "foobar" {
name = "workspace-test-%d"
organization = tfe_organization.foobar.name
}
data "tfe_outputs" "foobar" {
organization = "%s"
workspace = "%s"
}
// All of these values reference the outputs in the file
// 'test-fixtures/state-versions/terraform.tfstate except the sensitive attr test_output_string
output "test_output_list_string" {
value = data.tfe_outputs.foobar.nonsensitive_values.test_output_list_string
}
output "test_output_tuple_number" {
value = data.tfe_outputs.foobar.nonsensitive_values.test_output_tuple_number
}
output "test_output_tuple_string" {
value = data.tfe_outputs.foobar.nonsensitive_values.test_output_tuple_string
}
output "test_output_object" {
value = data.tfe_outputs.foobar.nonsensitive_values.test_output_object
}
output "test_output_number" {
value = data.tfe_outputs.foobar.nonsensitive_values.test_output_number
}
output "test_output_bool" {
value = data.tfe_outputs.foobar.nonsensitive_values.test_output_bool
}
`, rInt, rInt, org, workspace)
}

func testAccTFEOutputs_dataSource_emptyOutputs(rInt int, org, workspace string) string {
return fmt.Sprintf(`
resource "tfe_organization" "foobar" {
Expand Down
6 changes: 6 additions & 0 deletions tfe/plugin_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,12 @@ func PluginProviderServer() tfprotov5.ProviderServer {
Computed: true,
Sensitive: true,
},
{
Name: "nonsensitive_values",
Type: tftypes.DynamicPseudoType,
Computed: true,
Sensitive: false,
},
},
},
},
Expand Down
1 change: 1 addition & 0 deletions website/docs/d/outputs.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,4 @@ The following arguments are supported:
The following attributes are exported:

* `values` - The current output values for the specified workspace.
* `nonsensitive_values` - The current non-sensitive output values for the specified workspace.

0 comments on commit 60ff514

Please sign in to comment.