-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WIP: implement IntOrString as DynamicType
Signed-off-by: Sebastian Hoß <[email protected]>
- Loading branch information
Showing
5 changed files
with
384 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 2 additions & 0 deletions
2
examples/data-sources/k8s_monitoring_coreos_com_pod_monitor_v1_manifest/outputs.tf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
output "manifests" { | ||
value = { | ||
"example" = data.k8s_monitoring_coreos_com_pod_monitor_v1_manifest.example.yaml | ||
"int_target_port" = data.k8s_monitoring_coreos_com_pod_monitor_v1_manifest.int_target_port.yaml | ||
"string_target_port" = data.k8s_monitoring_coreos_com_pod_monitor_v1_manifest.string_target_port.yaml | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
/* | ||
* SPDX-FileCopyrightText: The terraform-provider-k8s Authors | ||
* SPDX-License-Identifier: 0BSD | ||
*/ | ||
|
||
package customtypes | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"github.com/hashicorp/terraform-plugin-framework/attr" | ||
"github.com/hashicorp/terraform-plugin-framework/attr/xattr" | ||
"github.com/hashicorp/terraform-plugin-framework/diag" | ||
"github.com/hashicorp/terraform-plugin-framework/path" | ||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes" | ||
"github.com/hashicorp/terraform-plugin-go/tftypes" | ||
"math/big" | ||
yaml "sigs.k8s.io/yaml/goyaml.v3" | ||
"strconv" | ||
) | ||
|
||
var ( | ||
_ basetypes.DynamicTypable = IntOrStringType{} | ||
_ xattr.TypeWithValidate = IntOrStringType{} | ||
_ basetypes.DynamicValuable = IntOrStringValue{} | ||
_ yaml.Marshaler = IntOrStringValue{} | ||
) | ||
|
||
type IntOrStringType struct { | ||
basetypes.DynamicType | ||
// ... potentially other fields ... | ||
} | ||
|
||
type IntOrStringValue struct { | ||
basetypes.DynamicValue | ||
// ... potentially other fields ... | ||
} | ||
|
||
func (t IntOrStringType) Equal(o attr.Type) bool { | ||
other, ok := o.(IntOrStringType) | ||
|
||
if !ok { | ||
return false | ||
} | ||
|
||
return t.DynamicType.Equal(other.DynamicType) | ||
} | ||
|
||
func (t IntOrStringType) String() string { | ||
return "IntOrStringType" | ||
} | ||
|
||
func (t IntOrStringType) ValueFromDynamic(ctx context.Context, in basetypes.DynamicValue) (basetypes.DynamicValuable, diag.Diagnostics) { | ||
value := IntOrStringValue{ | ||
DynamicValue: in, | ||
} | ||
|
||
return value, nil | ||
} | ||
|
||
func (t IntOrStringType) ValueFromTerraform(ctx context.Context, in tftypes.Value) (attr.Value, error) { | ||
attrValue, err := t.DynamicType.ValueFromTerraform(ctx, in) | ||
|
||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
dynamicValue, ok := attrValue.(basetypes.DynamicValue) | ||
|
||
if !ok { | ||
return nil, fmt.Errorf("unexpected value type of %T", attrValue) | ||
} | ||
|
||
dynamicValuable, diags := t.ValueFromDynamic(ctx, dynamicValue) | ||
|
||
if diags.HasError() { | ||
return nil, fmt.Errorf("unexpected error converting DynamicValue to DynamicValuable: %v", diags) | ||
} | ||
|
||
return dynamicValuable, nil | ||
} | ||
|
||
func (t IntOrStringType) ValueType(ctx context.Context) attr.Value { | ||
return IntOrStringValue{} | ||
} | ||
|
||
func (t IntOrStringType) Validate(ctx context.Context, value tftypes.Value, path path.Path) diag.Diagnostics { | ||
if value.IsNull() || !value.IsKnown() { | ||
return nil | ||
} | ||
|
||
var diags diag.Diagnostics | ||
|
||
valueType := value.Type() | ||
|
||
if !valueType.Is(tftypes.String) && !valueType.Is(tftypes.Number) { | ||
diags.AddAttributeError( | ||
path, | ||
"Invalid Terraform Value", | ||
"IntOrString type only accepts values of type tftypes.String or tftypes.Number but got "+valueType.String()+". "+ | ||
"Make sure your Terraform configuration matches this expectation.\n\n"+ | ||
"Path: "+path.String(), | ||
) | ||
return diags | ||
} | ||
|
||
return diags | ||
} | ||
|
||
func (v IntOrStringValue) Equal(o attr.Value) bool { | ||
other, ok := o.(IntOrStringValue) | ||
|
||
if !ok { | ||
return false | ||
} | ||
|
||
return v.DynamicValue.Equal(other.DynamicValue) | ||
} | ||
|
||
func (v IntOrStringValue) Type(ctx context.Context) attr.Type { | ||
return IntOrStringType{} | ||
} | ||
|
||
func (v IntOrStringValue) MarshalYAML() (interface{}, error) { | ||
if v.IsUnknown() || v.IsNull() { | ||
return nil, nil | ||
} | ||
|
||
tfValue, err := v.UnderlyingValue().ToTerraformValue(context.Background()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
switch { | ||
case tfValue.Type().Is(tftypes.String): | ||
var yamlValue string | ||
err = tfValue.As(&yamlValue) | ||
if err != nil { | ||
return nil, err | ||
} | ||
i, err := strconv.Atoi(yamlValue) | ||
if err == nil { | ||
return i, nil | ||
} | ||
return yamlValue, nil | ||
case tfValue.Type().Is(tftypes.Number): | ||
var yamlValue big.Float | ||
err = tfValue.As(&yamlValue) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if yamlValue.IsInt() { | ||
inv, acc := yamlValue.Int64() | ||
if acc != big.Exact { | ||
return nil, fmt.Errorf("%s inexact integer approximation when converting number value", v.String()) | ||
} | ||
return inv, nil | ||
} else { | ||
return nil, fmt.Errorf("%s is not an integer", v.String()) | ||
} | ||
default: | ||
return nil, nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,170 @@ | ||
/* | ||
* SPDX-FileCopyrightText: The terraform-provider-k8s Authors | ||
* SPDX-License-Identifier: 0BSD | ||
*/ | ||
|
||
package customtypes | ||
|
||
import ( | ||
"context" | ||
"github.com/google/go-cmp/cmp" | ||
"github.com/hashicorp/terraform-plugin-framework/attr" | ||
"github.com/hashicorp/terraform-plugin-framework/types/basetypes" | ||
"github.com/hashicorp/terraform-plugin-go/tftypes" | ||
"math/big" | ||
yaml "sigs.k8s.io/yaml/goyaml.v3" | ||
"testing" | ||
) | ||
|
||
func TestIntOrStringTypeTerraformType(t *testing.T) { | ||
result := IntOrStringType{}.TerraformType(context.Background()) | ||
if diff := cmp.Diff(result, tftypes.DynamicPseudoType); diff != "" { | ||
t.Errorf("unexpected result (+expected, -got): %s", diff) | ||
} | ||
} | ||
|
||
func TestIntOrStringTypeValueFromTerraform(t *testing.T) { | ||
type testCase struct { | ||
receiver IntOrStringType | ||
input tftypes.Value | ||
expected attr.Value | ||
} | ||
tests := map[string]testCase{ | ||
"string": { | ||
receiver: IntOrStringType{}, | ||
input: tftypes.NewValue(tftypes.String, "hello"), | ||
expected: IntOrStringValue{ | ||
DynamicValue: basetypes.NewDynamicValue(basetypes.NewStringValue("hello")), | ||
}, | ||
}, | ||
"number": { | ||
receiver: IntOrStringType{}, | ||
input: tftypes.NewValue(tftypes.Number, 123), | ||
expected: IntOrStringValue{ | ||
DynamicValue: basetypes.NewDynamicValue(basetypes.NewNumberValue(big.NewFloat(123))), | ||
}, | ||
}, | ||
"nil": { | ||
receiver: IntOrStringType{}, | ||
input: tftypes.NewValue(nil, nil), | ||
expected: IntOrStringValue{ | ||
DynamicValue: basetypes.NewDynamicNull(), | ||
}, | ||
}, | ||
"unknown": { | ||
receiver: IntOrStringType{}, | ||
input: tftypes.NewValue(tftypes.DynamicPseudoType, tftypes.UnknownValue), | ||
expected: IntOrStringValue{ | ||
DynamicValue: basetypes.NewDynamicUnknown(), | ||
}, | ||
}, | ||
} | ||
for name, test := range tests { | ||
t.Run(name, func(t *testing.T) { | ||
got, err := test.receiver.ValueFromTerraform(context.Background(), test.input) | ||
if err != nil { | ||
t.Errorf("Unexpected error: %s", err.Error()) | ||
return | ||
} | ||
if diff := cmp.Diff(test.expected, got); diff != "" { | ||
t.Errorf("unexpected result (-expected, +got): %s", diff) | ||
} | ||
if test.expected != nil && test.expected.IsNull() != test.input.IsNull() { | ||
t.Errorf("Expected null-ness match: expected %t, got %t", test.expected.IsNull(), test.input.IsNull()) | ||
} | ||
if test.expected != nil && test.expected.IsUnknown() != !test.input.IsKnown() { | ||
t.Errorf("Expected unknown-ness match: expected %t, got %t", test.expected.IsUnknown(), !test.input.IsKnown()) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestIntOrStringValueToTerraformValue(t *testing.T) { | ||
type testCase struct { | ||
receiver IntOrStringValue | ||
expected tftypes.Value | ||
} | ||
tests := map[string]testCase{ | ||
"string": { | ||
receiver: IntOrStringValue{ | ||
DynamicValue: basetypes.NewDynamicValue(basetypes.NewStringValue("hello")), | ||
}, | ||
expected: tftypes.NewValue(tftypes.String, "hello"), | ||
}, | ||
"number": { | ||
receiver: IntOrStringValue{ | ||
DynamicValue: basetypes.NewDynamicValue(basetypes.NewNumberValue(big.NewFloat(123))), | ||
}, | ||
expected: tftypes.NewValue(tftypes.Number, 123), | ||
}, | ||
"unknown": { | ||
receiver: IntOrStringValue{ | ||
DynamicValue: basetypes.NewDynamicUnknown(), | ||
}, | ||
expected: tftypes.NewValue(tftypes.DynamicPseudoType, tftypes.UnknownValue), | ||
}, | ||
"null": { | ||
receiver: IntOrStringValue{ | ||
DynamicValue: basetypes.NewDynamicNull(), | ||
}, | ||
expected: tftypes.NewValue(tftypes.DynamicPseudoType, nil), | ||
}, | ||
} | ||
for name, test := range tests { | ||
t.Run(name, func(t *testing.T) { | ||
got, err := test.receiver.ToTerraformValue(context.Background()) | ||
if err != nil { | ||
t.Errorf("Unexpected error: %s", err) | ||
return | ||
} | ||
if diff := cmp.Diff(test.expected, got); diff != "" { | ||
t.Errorf("Unexpected diff (+wanted, -got): %s", diff) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestIntOrStringValueYamlMarshaller(t *testing.T) { | ||
type testCase struct { | ||
value attr.Value | ||
expected string | ||
} | ||
tests := map[string]testCase{ | ||
"int": { | ||
value: IntOrStringValue{ | ||
DynamicValue: basetypes.NewDynamicValue(basetypes.NewNumberValue(big.NewFloat(123))), | ||
}, | ||
expected: "123", | ||
}, | ||
"string": { | ||
value: IntOrStringValue{ | ||
DynamicValue: basetypes.NewDynamicValue(basetypes.NewStringValue("hello")), | ||
}, | ||
expected: "hello", | ||
}, | ||
"null": { | ||
value: IntOrStringValue{ | ||
DynamicValue: basetypes.NewDynamicNull(), | ||
}, | ||
expected: "null", | ||
}, | ||
"unknown": { | ||
value: IntOrStringValue{ | ||
DynamicValue: basetypes.NewDynamicUnknown(), | ||
}, | ||
expected: "null", | ||
}, | ||
} | ||
for name, test := range tests { | ||
t.Run(name, func(t *testing.T) { | ||
marshal, err := yaml.Marshal(test.value) | ||
if err != nil { | ||
t.Errorf("Unexpected error: %s", err) | ||
return | ||
} | ||
if diff := cmp.Diff(test.expected+"\n", string(marshal)); diff != "" { | ||
t.Errorf("unexpected result (-expected, +got): %s", diff) | ||
} | ||
}) | ||
} | ||
} |
Oops, something went wrong.