diff --git a/stackit/internal/services/iaas/network/resource.go b/stackit/internal/services/iaas/network/resource.go index febf5856..a415bc26 100644 --- a/stackit/internal/services/iaas/network/resource.go +++ b/stackit/internal/services/iaas/network/resource.go @@ -200,7 +200,7 @@ func (r *networkResource) Schema(_ context.Context, _ resource.SchemaRequest, re Optional: true, Computed: true, Validators: []validator.String{ - validate.IP(), + validate.IP(false), }, }, "ipv4_nameservers": schema.ListAttribute{ @@ -252,7 +252,7 @@ func (r *networkResource) Schema(_ context.Context, _ resource.SchemaRequest, re Optional: true, Computed: true, Validators: []validator.String{ - validate.IP(), + validate.IP(false), }, }, "ipv6_nameservers": schema.ListAttribute{ diff --git a/stackit/internal/services/iaas/networkarearoute/resource.go b/stackit/internal/services/iaas/networkarearoute/resource.go index 3525c78e..f854b600 100644 --- a/stackit/internal/services/iaas/networkarearoute/resource.go +++ b/stackit/internal/services/iaas/networkarearoute/resource.go @@ -161,7 +161,7 @@ func (r *networkAreaRouteResource) Schema(_ context.Context, _ resource.SchemaRe stringplanmodifier.RequiresReplace(), }, Validators: []validator.String{ - validate.IP(), + validate.IP(false), }, }, "prefix": schema.StringAttribute{ diff --git a/stackit/internal/services/iaas/networkinterface/resource.go b/stackit/internal/services/iaas/networkinterface/resource.go index 087d67b6..6ecb098b 100644 --- a/stackit/internal/services/iaas/networkinterface/resource.go +++ b/stackit/internal/services/iaas/networkinterface/resource.go @@ -200,7 +200,7 @@ func (r *networkInterfaceResource) Schema(_ context.Context, _ resource.SchemaRe Optional: true, Computed: true, Validators: []validator.String{ - validate.IP(), + validate.IP(false), }, PlanModifiers: []planmodifier.String{ stringplanmodifier.RequiresReplace(), diff --git a/stackit/internal/services/iaas/publicip/resource.go b/stackit/internal/services/iaas/publicip/resource.go index 69bbc169..5cab9d49 100644 --- a/stackit/internal/services/iaas/publicip/resource.go +++ b/stackit/internal/services/iaas/publicip/resource.go @@ -147,7 +147,7 @@ func (r *publicIpResource) Schema(_ context.Context, _ resource.SchemaRequest, r stringplanmodifier.UseStateForUnknown(), }, Validators: []validator.String{ - validate.IP(), + validate.IP(false), }, }, "network_interface_id": schema.StringAttribute{ diff --git a/stackit/internal/services/iaas/publicipassociate/resource.go b/stackit/internal/services/iaas/publicipassociate/resource.go index 84d1872c..7076733d 100644 --- a/stackit/internal/services/iaas/publicipassociate/resource.go +++ b/stackit/internal/services/iaas/publicipassociate/resource.go @@ -155,7 +155,7 @@ func (r *publicIpAssociateResource) Schema(_ context.Context, _ resource.SchemaR stringplanmodifier.UseStateForUnknown(), }, Validators: []validator.String{ - validate.IP(), + validate.IP(false), }, }, "network_interface_id": schema.StringAttribute{ diff --git a/stackit/internal/validate/validate.go b/stackit/internal/validate/validate.go index 7dd1a286..90f4779f 100644 --- a/stackit/internal/validate/validate.go +++ b/stackit/internal/validate/validate.go @@ -67,13 +67,17 @@ func UUID() *Validator { } } -func IP() *Validator { +// IP returns a validator that checks, if the given string is a valid IP address. +// The allowZeroAddress parameter defines, if 0.0.0.0, resp. [::] should be considered valid. +func IP(allowZeroAddress bool) *Validator { description := "value must be an IP address" return &Validator{ description: description, validate: func(_ context.Context, req validator.StringRequest, resp *validator.StringResponse) { - if net.ParseIP(req.ConfigValue.ValueString()) == nil { + ip := net.ParseIP(req.ConfigValue.ValueString()) + invalidZeroAddress := !allowZeroAddress && (net.IPv4zero.Equal(ip) || net.IPv6zero.Equal(ip)) + if ip == nil || invalidZeroAddress { resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic( req.Path, description, diff --git a/stackit/internal/validate/validate_test.go b/stackit/internal/validate/validate_test.go index 428799a4..eba170ca 100644 --- a/stackit/internal/validate/validate_test.go +++ b/stackit/internal/validate/validate_test.go @@ -58,39 +58,87 @@ func TestUUID(t *testing.T) { func TestIP(t *testing.T) { tests := []struct { description string + invalidZero bool input string isValid bool }{ { "ok IP4", + false, "111.222.111.222", true, }, { "ok IP6", + false, "2001:0db8:85a3:08d3::0370:7344", true, }, { "too short", + false, "0.1.2", false, }, { "Empty", + false, "", false, }, { "Not an IP", + false, "for-sure-not-an-IP", false, }, + { + "valid ipv4 zero", + true, + "0.0.0.0", + true, + }, + { + "invalid ipv4 zero", + false, + "0.0.0.0", + false, + }, + { + "valid ipv6 zero", + true, + "::", + true, + }, + { + "valid ipv6 zero short notation", + true, + "::0", + true, + }, + { + "valid ipv6 zero long notation", + true, + "0000:0000:0000:0000:0000:0000:0000:0000", + true, + }, + { + "invalid ipv6 zero short notation", + false, + "::", + false, + }, + { + "invalid ipv6 zero long notation", + false, + "0000:0000:0000:0000:0000:0000:0000:0000", + false, + }, } for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { r := validator.StringResponse{} - IP().ValidateString(context.Background(), validator.StringRequest{ + IP(tt.invalidZero).ValidateString(context.Background(), validator.StringRequest{ ConfigValue: types.StringValue(tt.input), }, &r)