/
falls_within_cidr.go
139 lines (123 loc) · 4.3 KB
/
falls_within_cidr.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package apstravalidator
import (
"context"
"fmt"
"github.com/IBM/netaddr"
"github.com/hashicorp/terraform-plugin-framework-validators/helpers/validatordiag"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"net"
)
var _ validator.String = fallsWithinCidrValidator{}
type fallsWithinCidrValidator struct {
expression path.Expression
allZerosOk bool
allOnesOk bool
}
func (o fallsWithinCidrValidator) Description(_ context.Context) string {
switch {
case o.allZerosOk && o.allOnesOk:
return fmt.Sprintf("Ensures that the supplied IP address falls "+
"within the CIDR block specified at attribute %q.",
o.expression.Resolve().String())
case o.allZerosOk:
return fmt.Sprintf("Ensures that the supplied IP address falls "+
"within the CIDR block specified at attribute %q. and is not the "+
"\"all ones\" address.", o.expression.Resolve().String())
case o.allOnesOk:
return fmt.Sprintf("Ensures that the supplied IP address falls "+
"within the CIDR block specified at attribute %q. and is not the "+
"\"all zeros\" address.", o.expression.Resolve().String())
default:
return fmt.Sprintf("Ensures that the supplied IP address falls "+
"within the CIDR block specified at attribute %q. and is neither "+
" the \"all zeros\" address nor the \"all ones\" address.",
o.expression.Resolve().String())
}
}
func (o fallsWithinCidrValidator) MarkdownDescription(ctx context.Context) string {
return o.Description(ctx)
}
func (o fallsWithinCidrValidator) ValidateString(ctx context.Context, req validator.StringRequest, resp *validator.StringResponse) {
if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() {
return
}
expressions := req.PathExpression.MergeExpressions(o.expression)
for i := range expressions {
expressions[i] = expressions[i].Resolve()
}
for _, expression := range expressions {
matchedPaths, diags := req.Config.PathMatches(ctx, expression)
resp.Diagnostics.Append(diags...)
if resp.Diagnostics.HasError() {
return
}
for _, mp := range matchedPaths {
var mpVal attr.Value
diags = req.Config.GetAttribute(ctx, mp, &mpVal)
resp.Diagnostics.Append(diags...)
// Collect all errors
if diags.HasError() {
continue
}
// Delay validation until all involved attribute have a known value
if mpVal.IsUnknown() {
return
}
var allZeros net.IP
var subnet *net.IPNet
var err error
if mpString, ok := mpVal.(types.String); ok {
allZeros, subnet, err = net.ParseCIDR(mpString.ValueString())
if err != nil {
resp.Diagnostics.AddAttributeError(
mp, fmt.Sprintf("error parsing CIDR block %q",
mpString.ValueString()), err.Error(),
)
return
}
} else {
resp.Diagnostics.Append(validatordiag.BugInProviderDiagnostic(
fmt.Sprintf("attribute at %q must be types.String got %t",
o.expression, mpVal)))
return
}
ip := net.ParseIP(req.ConfigValue.ValueString())
if !subnet.Contains(ip) {
resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic(
req.Path,
fmt.Sprintf("value must fall within %s", subnet.String()),
req.ConfigValue.ValueString()))
return
}
if ip.Equal(allZeros) && !o.allZerosOk {
resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic(
req.Path,
"value must not be the all-zeros address %s",
req.ConfigValue.ValueString()))
return
}
allOnes := netaddr.BroadcastAddr(subnet)
if ip.Equal(allOnes) && !o.allOnesOk {
resp.Diagnostics.Append(validatordiag.InvalidAttributeValueDiagnostic(
req.Path,
"value must not be the all-ones address %s",
req.ConfigValue.ValueString()))
return
}
}
}
}
// FallsWithinCidr determines whether this attribute's value falls within the
// CIDR block specified by the attribute at expression. Arguments allZerosOk and
// allOnesOk modify the notion of "within" to include (true) or exclude (false)
// the first (all zeros) and last (all ones) addresses in the block.
func FallsWithinCidr(e path.Expression, allZerosOk bool, allOnesOk bool) validator.String {
return fallsWithinCidrValidator{
expression: e,
allZerosOk: allZerosOk,
allOnesOk: allOnesOk,
}
}