fix: validates the record conent based on record type (#267)

* fix:  validates the record conent based on record type

Signed-off-by: Jorge Turrado <jorge.turrado@scrm.lidl>

* fix typo

Signed-off-by: Jorge Turrado <jorge.turrado@scrm.lidl>

* apply feedback

Signed-off-by: Jorge Turrado <jorge.turrado@scrm.lidl>

* apply feedback

Signed-off-by: Jorge Turrado <jorge.turrado@scrm.lidl>

---------

Signed-off-by: Jorge Turrado <jorge.turrado@scrm.lidl>
This commit is contained in:
Jorge Turrado Ferrero 2024-02-20 16:17:27 +01:00 committed by GitHub
parent a88688ce93
commit 32d176ee86
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 196 additions and 1 deletions

View file

@ -158,7 +158,7 @@ func (r *recordSetResource) Schema(_ context.Context, _ resource.SchemaRequest,
Validators: []validator.List{
listvalidator.SizeAtLeast(1),
listvalidator.UniqueValues(),
listvalidator.ValueStringsAre(validate.IP()),
listvalidator.ValueStringsAre(validate.RecordSet()),
},
},
"ttl": schema.Int64Attribute{

View file

@ -10,7 +10,9 @@ import (
"github.com/google/uuid"
"github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
)
@ -73,6 +75,45 @@ func IP() *Validator {
}
}
func RecordSet() *Validator {
const typePath = "type"
return &Validator{
description: "value must be a valid record set",
validate: func(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) {
recordType := basetypes.StringValue{}
req.Config.GetAttribute(ctx, path.Root(typePath), &recordType)
switch recordType.ValueString() {
case "A":
ip := net.ParseIP(req.ConfigValue.ValueString())
if ip == nil || ip.To4() == nil {
resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic(
req.Path,
"value must be an IPv4 address",
req.ConfigValue.ValueString(),
))
}
case "AAAA":
ip := net.ParseIP(req.ConfigValue.ValueString())
if ip == nil || ip.To4() != nil || ip.To16() == nil {
resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic(
req.Path,
"value must be an IPv6 address",
req.ConfigValue.ValueString(),
))
}
case "CNAME":
case "NS":
case "MX":
case "TXT":
case "ALIAS":
case "DNAME":
case "CAA":
default:
}
},
}
}
func NoSeparator() *Validator {
description := fmt.Sprintf("value must not contain identifier separator '%s'", core.Separator)

View file

@ -4,8 +4,11 @@ import (
"context"
"testing"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-go/tftypes"
)
func TestUUID(t *testing.T) {
@ -101,6 +104,157 @@ func TestIP(t *testing.T) {
}
}
func TestRecordSet(t *testing.T) {
tests := []struct {
description string
record string
recordType string
isValid bool
}{
{
"A record ok IP4",
"111.222.111.222",
"A",
true,
},
{
"A record fail IP6",
"2001:0db8:85a3:08d3::0370:7344",
"A",
false,
},
{
"A record too short",
"0.1.2",
"A",
false,
},
{
"A record Empty",
"",
"A",
false,
},
{
"A record Not an IP",
"for-sure-not-an-IP",
"A",
false,
},
{
"AAAA record fail IP4",
"111.222.111.222",
"AAAA",
false,
},
{
"AAAA record ok IP6",
"2001:0db8:85a3:08d3::0370:7344",
"AAAA",
true,
},
{
"AAAA record too short",
"0.1.2",
"AAAA",
false,
},
{
"AAAA record Empty",
"",
"AAAA",
false,
},
{
"AAAA record Not an IP",
"for-sure-not-an-IP",
"AAAA",
false,
},
{
"CNAME record",
"some-record",
"CNAME",
true,
},
{
"NS record",
"some-record",
"NS",
true,
},
{
"MX record",
"some-record",
"MX",
true,
},
{
"TXT record",
"some-record",
"TXT",
true,
},
{
"ALIAS record",
"some-record",
"ALIAS",
true,
},
{
"DNAME record",
"some-record",
"DNAME",
true,
},
{
"CAA record",
"some-record",
"CAA",
true,
},
{
"random record",
"some-record",
"random",
true,
},
}
for _, tt := range tests {
t.Run(tt.description, func(t *testing.T) {
r := validator.StringResponse{}
scheme := tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"type": tftypes.String,
},
}
value := map[string]tftypes.Value{
"type": tftypes.NewValue(tftypes.String, tt.recordType),
}
record := tftypes.NewValue(scheme, value)
RecordSet().ValidateString(context.Background(), validator.StringRequest{
Config: tfsdk.Config{
Schema: schema.Schema{
Attributes: map[string]schema.Attribute{
"type": schema.StringAttribute{},
},
},
Raw: record,
},
ConfigValue: types.StringValue(tt.record),
}, &r)
if !tt.isValid && !r.Diagnostics.HasError() {
t.Fatalf("Should have failed")
}
if tt.isValid && r.Diagnostics.HasError() {
t.Fatalf("Should not have failed: %v", r.Diagnostics.Errors())
}
})
}
}
func TestNoSeparator(t *testing.T) {
tests := []struct {
description string