Skip to content

Commit

Permalink
Update netaddr which made all structs opaque, which is good (#61)
Browse files Browse the repository at this point in the history
  • Loading branch information
majst01 authored Jul 22, 2021
1 parent 45aa14d commit 2f3212f
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 116 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ CGO_ENABLED := $(or ${CGO_ENABLED},0)
GO := go
GO111MODULE := on
PG_VERSION := $(or ${PG_VERSION},13-alpine)
COCKROACH_VERSION := $(or ${COCKROACH_VERSION},v20.2.9)
COCKROACH_VERSION := $(or ${COCKROACH_VERSION},v21.1.3)

.EXPORT_ALL_VARIABLES:

Expand Down
12 changes: 6 additions & 6 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ module github.com/metal-stack/go-ipam
go 1.16

require (
github.com/avast/retry-go v3.0.0+incompatible
github.com/jmoiron/sqlx v1.3.3
github.com/lib/pq v1.10.1
github.com/avast/retry-go/v3 v3.1.1
github.com/jmoiron/sqlx v1.3.4
github.com/lib/pq v1.10.2
github.com/stretchr/testify v1.7.0
github.com/testcontainers/testcontainers-go v0.10.0
golang.org/x/net v0.0.0-20210510120150-4163338589ed // indirect
github.com/testcontainers/testcontainers-go v0.11.1
golang.org/x/net v0.0.0-20210716203947-853a461950ff // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
inet.af/netaddr v0.0.0-20210511181906-37180328850c
inet.af/netaddr v0.0.0-20210718074554-06ca8145d722
)
85 changes: 61 additions & 24 deletions go.sum

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ func TestIntegration(t *testing.T) {
// Only logs if fails
ipprefix, err := netaddr.ParseIPPrefix(pfx)
require.NoError(t, err)
smallest := 1 << (ipprefix.IP.BitLen() - 2 - ipprefix.Bits)
smallest := 1 << (ipprefix.IP().BitLen() - 2 - ipprefix.Bits())
sum += smallest
t.Logf("available prefix:%s smallest left:%d sum:%d", pfx, smallest, sum)
}
Expand Down
65 changes: 65 additions & 0 deletions json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package ipam

import (
"encoding/json"
"fmt"
)

type prefixJSON struct {
Prefix
AvailableChildPrefixes map[string]bool // available child prefixes of this prefix
// TODO remove this in the next release
ChildPrefixLength int // the length of the child prefixes. Legacy to migrate existing prefixes stored in the db to set the IsParent on reads.
IsParent bool // set to true if there are child prefixes
IPs map[string]bool // The ips contained in this prefix
Version int64 // Version is used for optimistic locking
}

func (p prefixJSON) toPrefix() Prefix {
// Legacy support only on reading from database, convert to isParent.
// TODO remove this in the next release
if p.ChildPrefixLength > 0 {
p.IsParent = true
}
return Prefix{
Cidr: p.Cidr,
ParentCidr: p.ParentCidr,
availableChildPrefixes: p.AvailableChildPrefixes,
childPrefixLength: p.ChildPrefixLength,
isParent: p.IsParent,
ips: p.IPs,
version: p.Version,
}
}

func (p Prefix) toPrefixJSON() prefixJSON {
return prefixJSON{
Prefix: Prefix{
Cidr: p.Cidr,
ParentCidr: p.ParentCidr,
},
AvailableChildPrefixes: p.availableChildPrefixes,
IsParent: p.isParent,
// TODO remove this in the next release
ChildPrefixLength: p.childPrefixLength,
IPs: p.ips,
Version: p.version,
}
}

func (p Prefix) toJSON() ([]byte, error) {
pj, err := json.Marshal(p.toPrefixJSON())
if err != nil {
return nil, fmt.Errorf("unable to marshal prefix:%w", err)
}
return pj, nil
}

func fromJSON(js []byte) (Prefix, error) {
var pre prefixJSON
err := json.Unmarshal(js, &pre)
if err != nil {
return Prefix{}, fmt.Errorf("unable to unmarshal prefix:%w", err)
}
return pre.toPrefix(), nil
}
60 changes: 35 additions & 25 deletions prefix.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"net"
"strings"

"github.com/avast/retry-go"
"github.com/avast/retry-go/v3"
"inet.af/netaddr"
)

Expand Down Expand Up @@ -213,17 +213,17 @@ func (i *ipamer) acquireChildPrefixInternal(parentCidr, childCidr string, length
if err != nil {
return nil, err
}
length = childprefix.Bits
length = childprefix.Bits()
}
if ipprefix.Bits >= length {
return nil, fmt.Errorf("given length:%d must be greater than prefix length:%d", length, ipprefix.Bits)
if ipprefix.Bits() >= length {
return nil, fmt.Errorf("given length:%d must be greater than prefix length:%d", length, ipprefix.Bits())
}
if parent.hasIPs() {
return nil, fmt.Errorf("prefix %s has ips, acquire child prefix not possible", parent.Cidr)
}

var ipset netaddr.IPSetBuilder
ipset.AddPrefix(ipprefix)
var ipsetBuilder netaddr.IPSetBuilder
ipsetBuilder.AddPrefix(ipprefix)
for cp, available := range parent.availableChildPrefixes {
if available {
continue
Expand All @@ -232,16 +232,20 @@ func (i *ipamer) acquireChildPrefixInternal(parentCidr, childCidr string, length
if err != nil {
return nil, err
}
ipset.RemovePrefix(cpipprefix)
ipsetBuilder.RemovePrefix(cpipprefix)
}

var cp netaddr.IPPrefix
ipset, err := ipsetBuilder.IPSet()
if err != nil {
return nil, fmt.Errorf("error constructing ipset:%w", err)
}

var cp netaddr.IPPrefix
if !specificChildRequest {
var ok bool
cp, _, ok = ipset.IPSet().RemoveFreePrefix(length)
cp, _, ok = ipset.RemoveFreePrefix(length)
if !ok {
pfxs := ipset.IPSet().Prefixes()
pfxs := ipset.Prefixes()
if len(pfxs) == 0 {
return nil, fmt.Errorf("no prefix found in %s with length:%d", parentCidr, length)
}
Expand All @@ -258,7 +262,7 @@ func (i *ipamer) acquireChildPrefixInternal(parentCidr, childCidr string, length
return nil, fmt.Errorf("no prefix found in %s with length:%d, but %s %s available", parentCidr, length, strings.Join(availablePrefixes, ","), adj)
}
} else {
if ok := ipset.IPSet().ContainsPrefix(childprefix); !ok {
if ok := ipset.ContainsPrefix(childprefix); !ok {
// Parent prefix does not contain specific child prefix
return nil, fmt.Errorf("specific prefix %s is not available in prefix %s", childCidr, parentCidr)
}
Expand Down Expand Up @@ -371,7 +375,7 @@ func (i *ipamer) acquireSpecificIPInternal(prefixCidr, specificIP string) (*IP,
}
}

for ip := ipnet.Range().From; ipnet.Contains(ip); ip = ip.Next() {
for ip := ipnet.Range().From(); ipnet.Contains(ip); ip = ip.Next() {
ipstring := ip.String()
_, ok := prefix.ips[ipstring]
if ok {
Expand Down Expand Up @@ -471,10 +475,10 @@ func (i *ipamer) newPrefix(cidr, parentCidr string) (*Prefix, error) {

// FIXME: should this be done by the user ?
// First ip in the prefix and broadcast is blocked.
p.ips[ipnet.Range().From.String()] = true
if ipnet.IP.Is4() {
p.ips[ipnet.Range().From().String()] = true
if ipnet.IP().Is4() {
// broadcast is ipv4 only
p.ips[ipnet.Range().To.String()] = true
p.ips[ipnet.Range().To().String()] = true
}

return p, nil
Expand Down Expand Up @@ -506,10 +510,10 @@ func (p *Prefix) hasIPs() bool {
if err != nil {
return false
}
if ipprefix.IP.Is4() && len(p.ips) > 2 {
if ipprefix.IP().Is4() && len(p.ips) > 2 {
return true
}
if ipprefix.IP.Is6() && len(p.ips) > 1 {
if ipprefix.IP().Is6() && len(p.ips) > 1 {
return true
}
return false
Expand All @@ -522,10 +526,10 @@ func (p *Prefix) availableips() uint64 {
return 0
}
// We don't report more than 2^31 available IPs by design
if (ipprefix.IP.BitLen() - ipprefix.Bits) > 31 {
if (ipprefix.IP().BitLen() - ipprefix.Bits()) > 31 {
return math.MaxInt32
}
return 1 << (ipprefix.IP.BitLen() - ipprefix.Bits)
return 1 << (ipprefix.IP().BitLen() - ipprefix.Bits())
}

// acquiredips return the number of ips acquired in this Prefix
Expand All @@ -539,8 +543,8 @@ func (p *Prefix) availablePrefixes() (uint64, []string) {
if err != nil {
return 0, nil
}
var ipset netaddr.IPSetBuilder
ipset.AddPrefix(prefix)
var ipsetBuilder netaddr.IPSetBuilder
ipsetBuilder.AddPrefix(prefix)
for cp, available := range p.availableChildPrefixes {
if available {
continue
Expand All @@ -549,16 +553,22 @@ func (p *Prefix) availablePrefixes() (uint64, []string) {
if err != nil {
continue
}
ipset.RemovePrefix(ipprefix)
ipsetBuilder.RemovePrefix(ipprefix)
}

ipset, err := ipsetBuilder.IPSet()
if err != nil {
return 0, []string{}
}

// Only 2 Bit Prefixes are usable, set max bits available 2 less than max in family
maxBits := prefix.IP.BitLen() - 2
pfxs := ipset.IPSet().Prefixes()
maxBits := prefix.IP().BitLen() - 2
pfxs := ipset.Prefixes()
totalAvailable := uint64(0)
availablePrefixes := []string{}
for _, pfx := range pfxs {
// same as: totalAvailable += uint64(math.Pow(float64(2), float64(maxBits-pfx.Bits)))
totalAvailable += 1 << (maxBits - pfx.Bits)
totalAvailable += 1 << (maxBits - pfx.Bits())
availablePrefixes = append(availablePrefixes, pfx.String())
}
// we are not reporting more that 2^31 available prefixes
Expand Down
2 changes: 1 addition & 1 deletion prefix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1390,7 +1390,7 @@ func TestPrefix_availablePrefixes(t *testing.T) {
// Only logs if fails
ipprefix, err := netaddr.ParseIPPrefix(pfx)
require.NoError(t, err)
smallest := 1 << (ipprefix.IP.BitLen() - 2 - ipprefix.Bits)
smallest := 1 << (ipprefix.IP().BitLen() - 2 - ipprefix.Bits())
t.Logf("available prefix:%s smallest left:%d", pfx, smallest)
}

Expand Down
65 changes: 8 additions & 57 deletions sql.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package ipam

import (
"encoding/json"
"fmt"

"github.com/jmoiron/sqlx"
Expand All @@ -11,48 +10,6 @@ type sql struct {
db *sqlx.DB
}

type prefixJSON struct {
Prefix
AvailableChildPrefixes map[string]bool // available child prefixes of this prefix
// TODO remove this in the next release
ChildPrefixLength int // the length of the child prefixes. Legacy to migrate existing prefixes stored in the db to set the IsParent on reads.
IsParent bool // set to true if there are child prefixes
IPs map[string]bool // The ips contained in this prefix
Version int64 // Version is used for optimistic locking
}

func (p prefixJSON) toPrefix() Prefix {
// Legacy support only on reading from database, convert to isParent.
// TODO remove this in the next release
if p.ChildPrefixLength > 0 {
p.IsParent = true
}
return Prefix{
Cidr: p.Cidr,
ParentCidr: p.ParentCidr,
availableChildPrefixes: p.AvailableChildPrefixes,
childPrefixLength: p.ChildPrefixLength,
isParent: p.IsParent,
ips: p.IPs,
version: p.Version,
}
}

func (p Prefix) toPrefixJSON() prefixJSON {
return prefixJSON{
Prefix: Prefix{
Cidr: p.Cidr,
ParentCidr: p.ParentCidr,
},
AvailableChildPrefixes: p.availableChildPrefixes,
IsParent: p.isParent,
// TODO remove this in the next release
ChildPrefixLength: p.childPrefixLength,
IPs: p.ips,
Version: p.version,
}
}

func (s *sql) prefixExists(prefix Prefix) (*Prefix, bool) {
p, err := s.ReadPrefix(prefix.Cidr)
if err != nil {
Expand All @@ -67,9 +24,9 @@ func (s *sql) CreatePrefix(prefix Prefix) (Prefix, error) {
return *existingPrefix, nil
}
prefix.version = int64(0)
pj, err := json.Marshal(prefix.toPrefixJSON())
pj, err := prefix.toJSON()
if err != nil {
return Prefix{}, fmt.Errorf("unable to marshal prefix:%w", err)
return Prefix{}, err
}
tx, err := s.db.Beginx()
if err != nil {
Expand All @@ -88,12 +45,7 @@ func (s *sql) ReadPrefix(prefix string) (Prefix, error) {
if err != nil {
return Prefix{}, fmt.Errorf("unable to read prefix:%w", err)
}
var pre prefixJSON
err = json.Unmarshal(result, &pre)
if err != nil {
return Prefix{}, fmt.Errorf("unable to unmarshal prefix:%w", err)
}
return pre.toPrefix(), nil
return fromJSON(result)
}

// ReadAllPrefixes returns all known prefixes.
Expand All @@ -106,12 +58,11 @@ func (s *sql) ReadAllPrefixes() ([]Prefix, error) {

result := []Prefix{}
for _, v := range prefixes {
var pre prefixJSON
err = json.Unmarshal(v, &pre)
pfx, err := fromJSON(v)
if err != nil {
return nil, fmt.Errorf("unable to unmarshal prefix:%w", err)
return nil, err
}
result = append(result, pre.toPrefix())
result = append(result, pfx)
}
return result, nil
}
Expand All @@ -131,9 +82,9 @@ func (s *sql) ReadAllPrefixCidrs() ([]string, error) {
func (s *sql) UpdatePrefix(prefix Prefix) (Prefix, error) {
oldVersion := prefix.version
prefix.version = oldVersion + 1
pn, err := json.Marshal(prefix.toPrefixJSON())
pn, err := prefix.toJSON()
if err != nil {
return Prefix{}, fmt.Errorf("unable to marshal prefix:%w", err)
return Prefix{}, err
}
tx, err := s.db.Beginx()
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion testing_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ func init() {
}
cockroachVersion = os.Getenv("COCKROACH_VERSION")
if cockroachVersion == "" {
cockroachVersion = "v20.2.7"
cockroachVersion = "v21.1.3"
}
fmt.Printf("Using postgres:%s cockroach:%s\n", pgVersion, cockroachVersion)
// prevent testcontainer logging mangle test and benchmark output
Expand Down

0 comments on commit 2f3212f

Please sign in to comment.