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

Refactor ToTerraformValue to return a tftypes.Value. #231

Merged
merged 3 commits into from
Dec 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/231.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:breaking-change
The `ToTerraformValue` method of the `attr.Value` interface now returns a `tftypes.Value`, instead of an `interface{}`. Existing types need to be updated to call `tftypes.ValidateValue` and `tftypes.NewValue`, passing the value they were returning before, instead of returning the value directly.
```
6 changes: 4 additions & 2 deletions attr/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package attr

import (
"context"

"github.com/hashicorp/terraform-plugin-go/tftypes"
)

// Value defines an interface for describing data associated with an attribute.
Expand All @@ -12,8 +14,8 @@ type Value interface {
Type(context.Context) Type

// ToTerraformValue returns the data contained in the Value as
// a Go type that tftypes.NewValue will accept.
ToTerraformValue(context.Context) (interface{}, error)
// a tftypes.Value.
ToTerraformValue(context.Context) (tftypes.Value, error)

// Equal must return true if the Value is considered semantically equal
// to the Value passed as an argument.
Expand Down
4 changes: 1 addition & 3 deletions internal/reflect/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,14 +311,12 @@ func FromAttributeValue(ctx context.Context, typ attr.Type, val attr.Value, path
var diags diag.Diagnostics

if typeWithValidate, ok := typ.(attr.TypeWithValidate); ok {
tfType := typ.TerraformType(ctx)
tfVal, err := val.ToTerraformValue(ctx)

if err != nil {
return val, append(diags, toTerraformValueErrorDiag(err, path))
}

diags.Append(typeWithValidate.Validate(ctx, tftypes.NewValue(tfType, tfVal), path)...)
diags.Append(typeWithValidate.Validate(ctx, tfVal, path)...)

if diags.HasError() {
return val, diags
Expand Down
13 changes: 2 additions & 11 deletions internal/reflect/map.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,24 +143,15 @@ func FromMap(ctx context.Context, typ attr.TypeWithElementType, val reflect.Valu
return nil, append(diags, toTerraformValueErrorDiag(err, path))
}

tfElemType := elemType.TerraformType(ctx)
err = tftypes.ValidateValue(tfElemType, tfVal)

if err != nil {
return nil, append(diags, validateValueErrorDiag(err, path))
}

tfElemVal := tftypes.NewValue(tfElemType, tfVal)

if typeWithValidate, ok := typ.(attr.TypeWithValidate); ok {
diags.Append(typeWithValidate.Validate(ctx, tfElemVal, path.WithElementKeyString(key.String()))...)
diags.Append(typeWithValidate.Validate(ctx, tfVal, path.WithElementKeyString(key.String()))...)

if diags.HasError() {
return nil, diags
}
}

tfElems[key.String()] = tfElemVal
tfElems[key.String()] = tfVal
}

err := tftypes.ValidateValue(tfType, tfElems)
Expand Down
16 changes: 3 additions & 13 deletions internal/reflect/slice.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,32 +151,22 @@ func FromSlice(ctx context.Context, typ attr.Type, val reflect.Value, path *tfty
}

tfVal, err := val.ToTerraformValue(ctx)

if err != nil {
return nil, append(diags, toTerraformValueErrorDiag(err, path))
}

err = tftypes.ValidateValue(elemType.TerraformType(ctx), tfVal)

if err != nil {
return nil, append(diags, validateValueErrorDiag(err, path))
}

tfElemVal := tftypes.NewValue(elemType.TerraformType(ctx), tfVal)

if tfType.Is(tftypes.Set{}) {
valPath = path.WithElementKeyValue(tfElemVal)
valPath = path.WithElementKeyValue(tfVal)
}

if typeWithValidate, ok := elemType.(attr.TypeWithValidate); ok {
diags.Append(typeWithValidate.Validate(ctx, tfElemVal, valPath)...)

diags.Append(typeWithValidate.Validate(ctx, tfVal, valPath)...)
if diags.HasError() {
return nil, diags
}
}

tfElems = append(tfElems, tfElemVal)
tfElems = append(tfElems, tfVal)
}

err := tftypes.ValidateValue(tfType, tfElems)
Expand Down
8 changes: 1 addition & 7 deletions internal/reflect/struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,16 +186,10 @@ func FromStruct(ctx context.Context, typ attr.TypeWithAttributeTypes, val reflec

objTypes[name] = attrType.TerraformType(ctx)

tfVal, err := attrVal.ToTerraformValue(ctx)
tfObjVal, err := attrVal.ToTerraformValue(ctx)
if err != nil {
return nil, append(diags, toTerraformValueErrorDiag(err, path))
}
err = tftypes.ValidateValue(objTypes[name], tfVal)
if err != nil {
return nil, append(diags, validateValueErrorDiag(err, path))
}

tfObjVal := tftypes.NewValue(objTypes[name], tfVal)

if typeWithValidate, ok := typ.(attr.TypeWithValidate); ok {
diags.Append(typeWithValidate.Validate(ctx, tfObjVal, path)...)
Expand Down
13 changes: 3 additions & 10 deletions tfsdk/attribute.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,6 @@ func (a Attribute) validate(ctx context.Context, req ValidateAttributeRequest, r

if a.DeprecationMessage != "" && attributeConfig != nil {
tfValue, err := attributeConfig.ToTerraformValue(ctx)

if err != nil {
resp.Diagnostics.AddAttributeError(
req.AttributePath,
Expand All @@ -316,7 +315,7 @@ func (a Attribute) validate(ctx context.Context, req ValidateAttributeRequest, r
return
}

if tfValue != nil {
if !tfValue.IsNull() {
resp.Diagnostics.AddAttributeWarning(
req.AttributePath,
"Attribute Deprecated",
Expand Down Expand Up @@ -378,8 +377,7 @@ func (a Attribute) validateAttributes(ctx context.Context, req ValidateAttribute
}

for _, value := range s.Elems {
tfValueRaw, err := value.ToTerraformValue(ctx)

tfValue, err := value.ToTerraformValue(ctx)
if err != nil {
err := fmt.Errorf("error running ToTerraformValue on element value: %v", value)
resp.Diagnostics.AddAttributeError(
Expand All @@ -391,8 +389,6 @@ func (a Attribute) validateAttributes(ctx context.Context, req ValidateAttribute
return
}

tfValue := tftypes.NewValue(s.ElemType.TerraformType(ctx), tfValueRaw)

for nestedName, nestedAttr := range a.Attributes.GetAttributes() {
nestedAttrReq := ValidateAttributeRequest{
AttributePath: req.AttributePath.WithElementKeyValue(tfValue).WithAttributeName(nestedName),
Expand Down Expand Up @@ -584,8 +580,7 @@ func (a Attribute) modifyPlan(ctx context.Context, req ModifyAttributePlanReques
}

for _, value := range s.Elems {
tfValueRaw, err := value.ToTerraformValue(ctx)

tfValue, err := value.ToTerraformValue(ctx)
if err != nil {
err := fmt.Errorf("error running ToTerraformValue on element value: %v", value)
resp.Diagnostics.AddAttributeError(
Expand All @@ -597,8 +592,6 @@ func (a Attribute) modifyPlan(ctx context.Context, req ModifyAttributePlanReques
return
}

tfValue := tftypes.NewValue(s.ElemType.TerraformType(ctx), tfValueRaw)

for name, attr := range a.Attributes.GetAttributes() {
attrReq := ModifyAttributePlanRequest{
AttributePath: req.AttributePath.WithElementKeyValue(tfValue).WithAttributeName(name),
Expand Down
10 changes: 5 additions & 5 deletions tfsdk/attribute_plan_modification.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func (r RequiresReplaceModifier) Modify(ctx context.Context, req ModifyAttribute
)
return
}
if configRaw == nil && attrSchema.Computed {
if configRaw.IsNull() && attrSchema.Computed {
// if the config is null and the attribute is computed, this
// could be an out of band change, don't require replace
return
Expand Down Expand Up @@ -287,7 +287,7 @@ func (r RequiresReplaceIfModifier) Modify(ctx context.Context, req ModifyAttribu
)
return
}
if configRaw == nil && attrSchema.Computed {
if configRaw.IsNull() && attrSchema.Computed {
// if the config is null and the attribute is computed, this
// could be an out of band change, don't require replace
return
Expand Down Expand Up @@ -354,7 +354,7 @@ func (r UseStateForUnknownModifier) Modify(ctx context.Context, req ModifyAttrib
}

// if we have no state value, there's nothing to preserve
if val == nil {
if val.IsNull() {
return
}

Expand All @@ -369,7 +369,7 @@ func (r UseStateForUnknownModifier) Modify(ctx context.Context, req ModifyAttrib

// if it's not planned to be the unknown value, stick with
// the concrete plan
if val != tftypes.UnknownValue {
if val.IsKnown() {
return
}

Expand All @@ -384,7 +384,7 @@ func (r UseStateForUnknownModifier) Modify(ctx context.Context, req ModifyAttrib

// if the config is the unknown value, use the unknown value
// otherwise, interpolation gets messed up
if val == tftypes.UnknownValue {
if !val.IsKnown() {
return
}

Expand Down
13 changes: 6 additions & 7 deletions tfsdk/attribute_plan_modification_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,31 +95,30 @@ func TestUseStateForUnknownModifier(t *testing.T) {
},
}

var configRaw, planRaw, stateRaw interface{}
configVal := tftypes.NewValue(tftypes.String, nil)
stateVal := tftypes.NewValue(tftypes.String, nil)
planVal := tftypes.NewValue(tftypes.String, nil)
if tc.config != nil {
val, err := tc.config.ToTerraformValue(context.Background())
if err != nil {
t.Fatal(err)
}
configRaw = val
configVal = val
}
if tc.state != nil {
val, err := tc.state.ToTerraformValue(context.Background())
if err != nil {
t.Fatal(err)
}
stateRaw = val
stateVal = val
}
if tc.plan != nil {
val, err := tc.plan.ToTerraformValue(context.Background())
if err != nil {
t.Fatal(err)
}
planRaw = val
planVal = val
}
configVal := tftypes.NewValue(tftypes.String, configRaw)
stateVal := tftypes.NewValue(tftypes.String, stateRaw)
planVal := tftypes.NewValue(tftypes.String, planRaw)

req := ModifyAttributePlanRequest{
AttributePath: tftypes.NewAttributePath(),
Expand Down
12 changes: 3 additions & 9 deletions tfsdk/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,7 @@ func (b Block) modifyPlan(ctx context.Context, req ModifyAttributePlanRequest, r
}

for _, value := range s.Elems {
tfValueRaw, err := value.ToTerraformValue(ctx)

tfValue, err := value.ToTerraformValue(ctx)
if err != nil {
err := fmt.Errorf("error running ToTerraformValue on element value: %v", value)
resp.Diagnostics.AddAttributeError(
Expand All @@ -282,8 +281,6 @@ func (b Block) modifyPlan(ctx context.Context, req ModifyAttributePlanRequest, r
return
}

tfValue := tftypes.NewValue(s.ElemType.TerraformType(ctx), tfValueRaw)

for name, attr := range b.Attributes {
attrReq := ModifyAttributePlanRequest{
AttributePath: req.AttributePath.WithElementKeyValue(tfValue).WithAttributeName(name),
Expand Down Expand Up @@ -482,8 +479,7 @@ func (b Block) validate(ctx context.Context, req ValidateAttributeRequest, resp
}

for _, value := range s.Elems {
tfValueRaw, err := value.ToTerraformValue(ctx)

tfValue, err := value.ToTerraformValue(ctx)
if err != nil {
err := fmt.Errorf("error running ToTerraformValue on element value: %v", value)
resp.Diagnostics.AddAttributeError(
Expand All @@ -495,8 +491,6 @@ func (b Block) validate(ctx context.Context, req ValidateAttributeRequest, resp
return
}

tfValue := tftypes.NewValue(s.ElemType.TerraformType(ctx), tfValueRaw)

for name, attr := range b.Attributes {
nestedAttrReq := ValidateAttributeRequest{
AttributePath: req.AttributePath.WithElementKeyValue(tfValue).WithAttributeName(name),
Expand Down Expand Up @@ -549,7 +543,7 @@ func (b Block) validate(ctx context.Context, req ValidateAttributeRequest, resp
return
}

if tfValue != nil {
if !tfValue.IsNull() {
resp.Diagnostics.AddAttributeWarning(
req.AttributePath,
"Block Deprecated",
Expand Down
11 changes: 1 addition & 10 deletions tfsdk/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,18 @@ import (

"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/diag"
"github.com/hashicorp/terraform-plugin-go/tftypes"
)

// ConvertValue creates a new attr.Value of the attr.Type `typ`, using the data
// in `val`, which can be of any attr.Type so long as its TerraformType method
// returns a tftypes.Type that `typ`'s ValueFromTerraform method can accept.
func ConvertValue(ctx context.Context, val attr.Value, typ attr.Type) (attr.Value, diag.Diagnostics) {
tftype := typ.TerraformType(ctx)
tfval, err := val.ToTerraformValue(ctx)
newVal, err := val.ToTerraformValue(ctx)
if err != nil {
return nil, diag.Diagnostics{diag.NewErrorDiagnostic("Error converting value",
fmt.Sprintf("An unexpected error was encountered converting a %T to a %s. This is always a problem with the provider. Please tell the provider developers that %T ran into the following error during ToTerraformValue: %s", val, typ, val, err),
)}
}
err = tftypes.ValidateValue(tftype, tfval)
if err != nil {
return nil, diag.Diagnostics{diag.NewErrorDiagnostic("Error converting value",
fmt.Sprintf("An unexpected error was encountered converting a %T to a %s. This is always a problem with the provider. Please tell the provider developers that %T is not compatible with %s.", val, typ, val, typ),
)}
}
newVal := tftypes.NewValue(tftype, tfval)
res, err := typ.ValueFromTerraform(ctx, newVal)
if err != nil {
return nil, diag.Diagnostics{diag.NewErrorDiagnostic("Error converting value",
Expand Down
5 changes: 4 additions & 1 deletion tfsdk/convert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func TestConvert(t *testing.T) {
typ: types.NumberType,
expectedDiags: diag.Diagnostics{diag.NewErrorDiagnostic(
"Error converting value",
"An unexpected error was encountered converting a types.String to a types.NumberType. This is always a problem with the provider. Please tell the provider developers that types.String is not compatible with types.NumberType.",
"An unexpected error was encountered converting a types.String to a types.NumberType. This is always a problem with the provider. Please tell the provider developers that types.NumberType returned the following error when calling ValueFromTerraform: can't unmarshal tftypes.String into *big.Float, expected *big.Float",
)},
},
}
Expand All @@ -56,6 +56,9 @@ func TestConvert(t *testing.T) {
got, diags := ConvertValue(context.Background(), tc.val, tc.typ)

if diff := cmp.Diff(diags, tc.expectedDiags); diff != "" {
for _, diag := range diags {
t.Logf("Diag summary: %q, Diag details: %q", diag.Summary(), diag.Detail())
}
t.Fatalf("Unexpected diff in diags (-wanted, +got): %s", diff)
}

Expand Down
8 changes: 2 additions & 6 deletions tfsdk/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ func (p *Plan) Set(ctx context.Context, val interface{}) diag.Diagnostics {
return diags
}

newPlanVal, err := newPlanAttrValue.ToTerraformValue(ctx)
newPlan, err := newPlanAttrValue.ToTerraformValue(ctx)
if err != nil {
err = fmt.Errorf("error running ToTerraformValue on plan: %w", err)
diags.AddError(
Expand All @@ -132,8 +132,6 @@ func (p *Plan) Set(ctx context.Context, val interface{}) diag.Diagnostics {
return diags
}

newPlan := tftypes.NewValue(p.Schema.AttributeType().TerraformType(ctx), newPlanVal)

p.Raw = newPlan
return diags
}
Expand Down Expand Up @@ -167,7 +165,7 @@ func (p *Plan) SetAttribute(ctx context.Context, path *tftypes.AttributePath, va
return diags
}

newTfVal, err := newVal.ToTerraformValue(ctx)
tfVal, err := newVal.ToTerraformValue(ctx)
if err != nil {
err = fmt.Errorf("error running ToTerraformValue on new plan value: %w", err)
diags.AddAttributeError(
Expand All @@ -178,8 +176,6 @@ func (p *Plan) SetAttribute(ctx context.Context, path *tftypes.AttributePath, va
return diags
}

tfVal := tftypes.NewValue(attrType.TerraformType(ctx), newTfVal)

if attrTypeWithValidate, ok := attrType.(attr.TypeWithValidate); ok {
diags.Append(attrTypeWithValidate.Validate(ctx, tfVal, path)...)

Expand Down
Loading