port cidr functions
This commit is contained in:
parent
ebef145980
commit
b6d3d69d3a
|
@ -0,0 +1,129 @@
|
||||||
|
package funcs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/apparentlymart/go-cidr/cidr"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
"github.com/zclconf/go-cty/cty/function"
|
||||||
|
"github.com/zclconf/go-cty/cty/gocty"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CidrHostFunc contructs a function that calculates a full host IP address
|
||||||
|
// within a given IP network address prefix.
|
||||||
|
var CidrHostFunc = function.New(&function.Spec{
|
||||||
|
Params: []function.Parameter{
|
||||||
|
{
|
||||||
|
Name: "prefix",
|
||||||
|
Type: cty.String,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "hostnum",
|
||||||
|
Type: cty.Number,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: function.StaticReturnType(cty.String),
|
||||||
|
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||||
|
var hostNum int
|
||||||
|
if err := gocty.FromCtyValue(args[1], &hostNum); err != nil {
|
||||||
|
return cty.UnknownVal(cty.String), err
|
||||||
|
}
|
||||||
|
_, network, err := net.ParseCIDR(args[0].AsString())
|
||||||
|
if err != nil {
|
||||||
|
return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
ip, err := cidr.Host(network, hostNum)
|
||||||
|
if err != nil {
|
||||||
|
return cty.UnknownVal(cty.String), err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cty.StringVal(ip.String()), nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// CidrNetmaskFunc contructs a function that converts an IPv4 address prefix given
|
||||||
|
// in CIDR notation into a subnet mask address.
|
||||||
|
var CidrNetmaskFunc = function.New(&function.Spec{
|
||||||
|
Params: []function.Parameter{
|
||||||
|
{
|
||||||
|
Name: "prefix",
|
||||||
|
Type: cty.String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: function.StaticReturnType(cty.String),
|
||||||
|
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||||
|
_, network, err := net.ParseCIDR(args[0].AsString())
|
||||||
|
if err != nil {
|
||||||
|
return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cty.StringVal(net.IP(network.Mask).String()), nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// CidrSubnetFunc contructs a function that calculates a subnet address within
|
||||||
|
// a given IP network address prefix.
|
||||||
|
var CidrSubnetFunc = function.New(&function.Spec{
|
||||||
|
Params: []function.Parameter{
|
||||||
|
{
|
||||||
|
Name: "prefix",
|
||||||
|
Type: cty.String,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "newbits",
|
||||||
|
Type: cty.Number,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "netnum",
|
||||||
|
Type: cty.Number,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: function.StaticReturnType(cty.String),
|
||||||
|
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||||
|
var newbits int
|
||||||
|
if err := gocty.FromCtyValue(args[1], &newbits); err != nil {
|
||||||
|
return cty.UnknownVal(cty.String), err
|
||||||
|
}
|
||||||
|
var netnum int
|
||||||
|
if err := gocty.FromCtyValue(args[2], &netnum); err != nil {
|
||||||
|
return cty.UnknownVal(cty.String), err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, network, err := net.ParseCIDR(args[0].AsString())
|
||||||
|
if err != nil {
|
||||||
|
return cty.UnknownVal(cty.String), fmt.Errorf("invalid CIDR expression: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// For portability with 32-bit systems where the subnet number
|
||||||
|
// will be a 32-bit int, we only allow extension of 32 bits in
|
||||||
|
// one call even if we're running on a 64-bit machine.
|
||||||
|
// (Of course, this is significant only for IPv6.)
|
||||||
|
if newbits > 32 {
|
||||||
|
return cty.UnknownVal(cty.String), fmt.Errorf("may not extend prefix by more than 32 bits")
|
||||||
|
}
|
||||||
|
|
||||||
|
newNetwork, err := cidr.Subnet(network, newbits, netnum)
|
||||||
|
if err != nil {
|
||||||
|
return cty.UnknownVal(cty.String), err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cty.StringVal(newNetwork.String()), nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// CidrHost calculates a full host IP address within a given IP network address prefix.
|
||||||
|
func CidrHost(prefix, hostnum cty.Value) (cty.Value, error) {
|
||||||
|
return CidrHostFunc.Call([]cty.Value{prefix, hostnum})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CidrNetmask converts an IPv4 address prefix given in CIDR notation into a subnet mask address.
|
||||||
|
func CidrNetmask(prefix cty.Value) (cty.Value, error) {
|
||||||
|
return CidrNetmaskFunc.Call([]cty.Value{prefix})
|
||||||
|
}
|
||||||
|
|
||||||
|
// CidrSubnet calculates a subnet address within a given IP network address prefix.
|
||||||
|
func CidrSubnet(prefix, newbits, netnum cty.Value) (cty.Value, error) {
|
||||||
|
return CidrSubnetFunc.Call([]cty.Value{prefix, newbits, netnum})
|
||||||
|
}
|
|
@ -0,0 +1,216 @@
|
||||||
|
package funcs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCidrHost(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
Prefix cty.Value
|
||||||
|
Hostnum cty.Value
|
||||||
|
Want cty.Value
|
||||||
|
Err bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
cty.StringVal("192.168.1.0/24"),
|
||||||
|
cty.NumberIntVal(5),
|
||||||
|
cty.StringVal("192.168.1.5"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.StringVal("192.168.1.0/24"),
|
||||||
|
cty.NumberIntVal(-5),
|
||||||
|
cty.StringVal("192.168.1.251"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.StringVal("192.168.1.0/24"),
|
||||||
|
cty.NumberIntVal(-256),
|
||||||
|
cty.StringVal("192.168.1.0"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.StringVal("192.168.1.0/30"),
|
||||||
|
cty.NumberIntVal(255),
|
||||||
|
cty.UnknownVal(cty.String),
|
||||||
|
true, // 255 doesn't fit in two bits
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.StringVal("192.168.1.0/30"),
|
||||||
|
cty.NumberIntVal(-255),
|
||||||
|
cty.UnknownVal(cty.String),
|
||||||
|
true, // 255 doesn't fit in two bits
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.StringVal("not-a-cidr"),
|
||||||
|
cty.NumberIntVal(6),
|
||||||
|
cty.UnknownVal(cty.String),
|
||||||
|
true, // not a valid CIDR mask
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.StringVal("10.256.0.0/8"),
|
||||||
|
cty.NumberIntVal(6),
|
||||||
|
cty.UnknownVal(cty.String),
|
||||||
|
true, // can't have an octet >255
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(fmt.Sprintf("cidrhost(%#v, %#v)", test.Prefix, test.Hostnum), func(t *testing.T) {
|
||||||
|
got, err := CidrHost(test.Prefix, test.Hostnum)
|
||||||
|
|
||||||
|
if test.Err {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("succeeded; want error")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !got.RawEquals(test.Want) {
|
||||||
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCidrNetmask(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
Prefix cty.Value
|
||||||
|
Want cty.Value
|
||||||
|
Err bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
cty.StringVal("192.168.1.0/24"),
|
||||||
|
cty.StringVal("255.255.255.0"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.StringVal("192.168.1.0/32"),
|
||||||
|
cty.StringVal("255.255.255.255"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.StringVal("0.0.0.0/0"),
|
||||||
|
cty.StringVal("0.0.0.0"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.StringVal("1::/64"),
|
||||||
|
cty.StringVal("ffff:ffff:ffff:ffff::"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.StringVal("not-a-cidr"),
|
||||||
|
cty.UnknownVal(cty.String),
|
||||||
|
true, // not a valid CIDR mask
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.StringVal("110.256.0.0/8"),
|
||||||
|
cty.UnknownVal(cty.String),
|
||||||
|
true, // can't have an octet >255
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(fmt.Sprintf("cidrnetmask(%#v)", test.Prefix), func(t *testing.T) {
|
||||||
|
got, err := CidrNetmask(test.Prefix)
|
||||||
|
|
||||||
|
if test.Err {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("succeeded; want error")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !got.RawEquals(test.Want) {
|
||||||
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCidrSubnet(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
Prefix cty.Value
|
||||||
|
Newbits cty.Value
|
||||||
|
Netnum cty.Value
|
||||||
|
Want cty.Value
|
||||||
|
Err bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
cty.StringVal("192.168.2.0/20"),
|
||||||
|
cty.NumberIntVal(4),
|
||||||
|
cty.NumberIntVal(6),
|
||||||
|
cty.StringVal("192.168.6.0/24"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.StringVal("fe80::/48"),
|
||||||
|
cty.NumberIntVal(16),
|
||||||
|
cty.NumberIntVal(6),
|
||||||
|
cty.StringVal("fe80:0:0:6::/64"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{ // IPv4 address encoded in IPv6 syntax gets normalized
|
||||||
|
cty.StringVal("::ffff:192.168.0.0/112"),
|
||||||
|
cty.NumberIntVal(8),
|
||||||
|
cty.NumberIntVal(6),
|
||||||
|
cty.StringVal("192.168.6.0/24"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{ // not enough bits left
|
||||||
|
cty.StringVal("192.168.0.0/30"),
|
||||||
|
cty.NumberIntVal(4),
|
||||||
|
cty.NumberIntVal(6),
|
||||||
|
cty.UnknownVal(cty.String),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{ // can't encode 16 in 2 bits
|
||||||
|
cty.StringVal("192.168.0.0/168"),
|
||||||
|
cty.NumberIntVal(2),
|
||||||
|
cty.NumberIntVal(16),
|
||||||
|
cty.StringVal("fe80:0:0:6::/64"),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{ // not a valid CIDR mask
|
||||||
|
cty.StringVal("not-a-cidr"),
|
||||||
|
cty.NumberIntVal(4),
|
||||||
|
cty.NumberIntVal(6),
|
||||||
|
cty.StringVal("fe80:0:0:6::/64"),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{ // can't have an octet >255
|
||||||
|
cty.StringVal("10.256.0.0/8"),
|
||||||
|
cty.NumberIntVal(4),
|
||||||
|
cty.NumberIntVal(6),
|
||||||
|
cty.StringVal("fe80:0:0:6::/64"),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(fmt.Sprintf("cidrsubnet(%#v, %#v, %#v)", test.Prefix, test.Newbits, test.Netnum), func(t *testing.T) {
|
||||||
|
got, err := CidrSubnet(test.Prefix, test.Newbits, test.Netnum)
|
||||||
|
|
||||||
|
if test.Err {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("succeeded; want error")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !got.RawEquals(test.Want) {
|
||||||
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,9 +39,9 @@ func (s *Scope) Functions() map[string]function.Function {
|
||||||
"bcrypt": funcs.BcryptFunc,
|
"bcrypt": funcs.BcryptFunc,
|
||||||
"ceil": funcs.CeilFunc,
|
"ceil": funcs.CeilFunc,
|
||||||
"chomp": funcs.ChompFunc,
|
"chomp": funcs.ChompFunc,
|
||||||
"cidrhost": unimplFunc, // TODO
|
"cidrhost": funcs.CidrHostFunc,
|
||||||
"cidrnetmask": unimplFunc, // TODO
|
"cidrnetmask": funcs.CidrNetmaskFunc,
|
||||||
"cidrsubnet": unimplFunc, // TODO
|
"cidrsubnet": funcs.CidrSubnetFunc,
|
||||||
"coalesce": stdlib.CoalesceFunc,
|
"coalesce": stdlib.CoalesceFunc,
|
||||||
"coalescelist": unimplFunc, // TODO
|
"coalescelist": unimplFunc, // TODO
|
||||||
"compact": unimplFunc, // TODO
|
"compact": unimplFunc, // TODO
|
||||||
|
@ -59,8 +59,8 @@ func (s *Scope) Functions() map[string]function.Function {
|
||||||
"floor": funcs.FloorFunc,
|
"floor": funcs.FloorFunc,
|
||||||
"format": stdlib.FormatFunc,
|
"format": stdlib.FormatFunc,
|
||||||
"formatlist": stdlib.FormatListFunc,
|
"formatlist": stdlib.FormatListFunc,
|
||||||
"indent": unimplFunc, // TODO
|
"indent": funcs.IndentFunc,
|
||||||
"index": funcs.IndentFunc,
|
"index": unimplFunc, // TODO
|
||||||
"join": funcs.JoinFunc,
|
"join": funcs.JoinFunc,
|
||||||
"jsondecode": stdlib.JSONDecodeFunc,
|
"jsondecode": stdlib.JSONDecodeFunc,
|
||||||
"jsonencode": stdlib.JSONEncodeFunc,
|
"jsonencode": stdlib.JSONEncodeFunc,
|
||||||
|
@ -82,7 +82,7 @@ func (s *Scope) Functions() map[string]function.Function {
|
||||||
"sha1": funcs.Sha1Func,
|
"sha1": funcs.Sha1Func,
|
||||||
"sha256": funcs.Sha256Func,
|
"sha256": funcs.Sha256Func,
|
||||||
"sha512": funcs.Sha512Func,
|
"sha512": funcs.Sha512Func,
|
||||||
"signum": unimplFunc, // TODO
|
"signum": funcs.SignumFunc,
|
||||||
"slice": unimplFunc, // TODO
|
"slice": unimplFunc, // TODO
|
||||||
"sort": funcs.SortFunc,
|
"sort": funcs.SortFunc,
|
||||||
"split": funcs.SplitFunc,
|
"split": funcs.SplitFunc,
|
||||||
|
@ -91,7 +91,7 @@ func (s *Scope) Functions() map[string]function.Function {
|
||||||
"timeadd": funcs.TimeAddFunc,
|
"timeadd": funcs.TimeAddFunc,
|
||||||
"title": funcs.TitleFunc,
|
"title": funcs.TitleFunc,
|
||||||
"transpose": unimplFunc, // TODO
|
"transpose": unimplFunc, // TODO
|
||||||
"trimspace": funcs.TrimSpace,
|
"trimspace": funcs.TrimSpaceFunc,
|
||||||
"upper": stdlib.UpperFunc,
|
"upper": stdlib.UpperFunc,
|
||||||
"urlencode": funcs.URLEncodeFunc,
|
"urlencode": funcs.URLEncodeFunc,
|
||||||
"uuid": funcs.UUIDFunc,
|
"uuid": funcs.UUIDFunc,
|
||||||
|
|
|
@ -9,7 +9,7 @@ description: |-
|
||||||
|
|
||||||
# `cidrsubnet` Function
|
# `cidrsubnet` Function
|
||||||
|
|
||||||
`cidrhost` calculates a subnet address within given IP network address prefix.
|
`cidrsubnet` calculates a subnet address within given IP network address prefix.
|
||||||
|
|
||||||
```hcl
|
```hcl
|
||||||
cidrsubnet(prefix, newbits, netnum)
|
cidrsubnet(prefix, newbits, netnum)
|
||||||
|
|
Loading…
Reference in New Issue