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,
|
||||
"ceil": funcs.CeilFunc,
|
||||
"chomp": funcs.ChompFunc,
|
||||
"cidrhost": unimplFunc, // TODO
|
||||
"cidrnetmask": unimplFunc, // TODO
|
||||
"cidrsubnet": unimplFunc, // TODO
|
||||
"cidrhost": funcs.CidrHostFunc,
|
||||
"cidrnetmask": funcs.CidrNetmaskFunc,
|
||||
"cidrsubnet": funcs.CidrSubnetFunc,
|
||||
"coalesce": stdlib.CoalesceFunc,
|
||||
"coalescelist": unimplFunc, // TODO
|
||||
"compact": unimplFunc, // TODO
|
||||
|
@ -59,8 +59,8 @@ func (s *Scope) Functions() map[string]function.Function {
|
|||
"floor": funcs.FloorFunc,
|
||||
"format": stdlib.FormatFunc,
|
||||
"formatlist": stdlib.FormatListFunc,
|
||||
"indent": unimplFunc, // TODO
|
||||
"index": funcs.IndentFunc,
|
||||
"indent": funcs.IndentFunc,
|
||||
"index": unimplFunc, // TODO
|
||||
"join": funcs.JoinFunc,
|
||||
"jsondecode": stdlib.JSONDecodeFunc,
|
||||
"jsonencode": stdlib.JSONEncodeFunc,
|
||||
|
@ -82,7 +82,7 @@ func (s *Scope) Functions() map[string]function.Function {
|
|||
"sha1": funcs.Sha1Func,
|
||||
"sha256": funcs.Sha256Func,
|
||||
"sha512": funcs.Sha512Func,
|
||||
"signum": unimplFunc, // TODO
|
||||
"signum": funcs.SignumFunc,
|
||||
"slice": unimplFunc, // TODO
|
||||
"sort": funcs.SortFunc,
|
||||
"split": funcs.SplitFunc,
|
||||
|
@ -91,7 +91,7 @@ func (s *Scope) Functions() map[string]function.Function {
|
|||
"timeadd": funcs.TimeAddFunc,
|
||||
"title": funcs.TitleFunc,
|
||||
"transpose": unimplFunc, // TODO
|
||||
"trimspace": funcs.TrimSpace,
|
||||
"trimspace": funcs.TrimSpaceFunc,
|
||||
"upper": stdlib.UpperFunc,
|
||||
"urlencode": funcs.URLEncodeFunc,
|
||||
"uuid": funcs.UUIDFunc,
|
||||
|
|
|
@ -9,7 +9,7 @@ description: |-
|
|||
|
||||
# `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
|
||||
cidrsubnet(prefix, newbits, netnum)
|
||||
|
|
Loading…
Reference in New Issue