Skip to content

Commit 0f3a8c0

Browse files
authored
fix(dns): add validator to block zone creation with trailing dot (#1225)
relates to #1083
1 parent 530dbbe commit 0f3a8c0

5 files changed

Lines changed: 75 additions & 2 deletions

File tree

docs/data-sources/dns_zone.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ data "stackit_dns_zone" "example" {
2828

2929
### Optional
3030

31-
- `dns_name` (String) The zone name. E.g. `example.com`
31+
- `dns_name` (String) The zone name. E.g. `example.com` (must not end with a trailing dot).
3232
- `zone_id` (String) The zone ID.
3333

3434
### Read-Only

stackit/internal/services/dns/dns_acc_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,20 @@ func TestAccDnsMinResource(t *testing.T) {
9393
ConfigVariables: configVarsInvalid(testConfigVarsMin),
9494
ExpectError: regexp.MustCompile(`not a valid dns name. Need at least two levels`),
9595
},
96+
// Creation fail: trailing dot is rejected on purpose
97+
{
98+
Config: resourceMinConfig,
99+
ConfigVariables: func() config.Variables {
100+
vars := maps.Clone(testConfigVarsMin)
101+
102+
// Ensure it ends with a dot (even if the random value already had one, be explicit)
103+
base := testutil.ConvertConfigVariable(vars["dns_name"])
104+
vars["dns_name"] = config.StringVariable(base + ".")
105+
106+
return vars
107+
}(),
108+
ExpectError: regexp.MustCompile(`dns_name must not end with a trailing dot`),
109+
},
96110
// creation
97111
{
98112
Config: resourceMinConfig,

stackit/internal/services/dns/zone/datasource.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"net/http"
77

88
"github.com/hashicorp/terraform-plugin-framework-validators/datasourcevalidator"
9+
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
910
"github.com/hashicorp/terraform-plugin-framework/path"
1011
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
1112
dnsUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/dns/utils"
@@ -95,8 +96,16 @@ func (d *zoneDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, r
9596
Computed: true,
9697
},
9798
"dns_name": schema.StringAttribute{
98-
Description: "The zone name. E.g. `example.com`",
99+
Description: "The zone name. E.g. `example.com` (must not end with a trailing dot).",
99100
Optional: true,
101+
Validators: []validator.String{
102+
stringvalidator.LengthAtLeast(1),
103+
stringvalidator.LengthAtMost(253),
104+
stringvalidator.RegexMatches(
105+
dnsNameNoTrailingDotRegex,
106+
"dns_name must not end with a trailing dot",
107+
),
108+
},
100109
},
101110
"description": schema.StringAttribute{
102111
Description: "Description of the zone.",

stackit/internal/services/dns/zone/resource.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"math"
7+
"regexp"
78
"strings"
89

910
dnsUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/dns/utils"
@@ -36,6 +37,9 @@ var (
3637
_ resource.ResourceWithImportState = &zoneResource{}
3738
)
3839

40+
// dnsNameNoTrailingDotRegex defines the zone dns_name without trailing dot
41+
var dnsNameNoTrailingDotRegex = regexp.MustCompile(`^.*[^.]$`)
42+
3943
type Model struct {
4044
Id types.String `tfsdk:"id"` // needed by TF
4145
ZoneId types.String `tfsdk:"zone_id"`
@@ -144,6 +148,10 @@ func (r *zoneResource) Schema(_ context.Context, _ resource.SchemaRequest, resp
144148
Validators: []validator.String{
145149
stringvalidator.LengthAtLeast(1),
146150
stringvalidator.LengthAtMost(253),
151+
stringvalidator.RegexMatches(
152+
dnsNameNoTrailingDotRegex,
153+
"dns_name must not end with a trailing dot",
154+
),
147155
},
148156
},
149157
"description": schema.StringAttribute{

stackit/internal/services/dns/zone/resource_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -435,3 +435,45 @@ func TestToPayloadUpdate(t *testing.T) {
435435
})
436436
}
437437
}
438+
439+
func TestDnsNameNoTrailingDot(t *testing.T) {
440+
tests := []struct {
441+
description string
442+
input string
443+
match bool
444+
}{
445+
{
446+
"normal domain without trailing dot",
447+
"example.com",
448+
true,
449+
},
450+
{
451+
"single layer without trailing dot",
452+
"example",
453+
true,
454+
},
455+
{
456+
"domain with trailing dot",
457+
"example.com.",
458+
false,
459+
},
460+
{
461+
"only trailing dot",
462+
".",
463+
false,
464+
},
465+
{
466+
"single layer with trailing dot",
467+
"example.",
468+
false,
469+
},
470+
}
471+
for _, tt := range tests {
472+
t.Run(tt.description, func(t *testing.T) {
473+
got := dnsNameNoTrailingDotRegex.MatchString(tt.input)
474+
if got != tt.match {
475+
t.Fatalf("dnsNameNoTrailingDotRegex.MatchString(%q) = %v, want %v", tt.input, got, tt.match)
476+
}
477+
})
478+
}
479+
}

0 commit comments

Comments
 (0)