From 84665131157058a29faddbbdc882526147132189 Mon Sep 17 00:00:00 2001 From: Stefan Majer Date: Tue, 26 Jul 2022 16:06:02 +0200 Subject: [PATCH] Add context (#84) --- etcd.go | 32 +- integration_test.go | 155 +++---- ipam.go | 31 +- ipam_test.go | 34 +- memory.go | 15 +- memory_test.go | 24 +- mongodb.go | 32 +- pkg/service/ipam-service.go | 44 +- .../test/ipam-service_benchmark_test.go | 14 +- prefix.go | 91 ++-- prefix_benchmark_test.go | 24 +- prefix_test.go | 423 ++++++++++-------- redis.go | 16 +- sql.go | 37 +- sql_test.go | 68 +-- storage.go | 16 +- 16 files changed, 558 insertions(+), 498 deletions(-) diff --git a/etcd.go b/etcd.go index e1e98e7..4a26d55 100644 --- a/etcd.go +++ b/etcd.go @@ -56,11 +56,11 @@ func newEtcd(ip, port string, cert, key []byte, insecureskip bool) *etcd { } } -func (e *etcd) CreatePrefix(prefix Prefix) (Prefix, error) { +func (e *etcd) CreatePrefix(ctx context.Context, prefix Prefix) (Prefix, error) { e.lock.Lock() defer e.lock.Unlock() - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) get, err := e.etcdDB.Get(ctx, prefix.Cidr) defer cancel() if err != nil { @@ -75,7 +75,7 @@ func (e *etcd) CreatePrefix(prefix Prefix) (Prefix, error) { if err != nil { return Prefix{}, err } - ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second) + ctx, cancel = context.WithTimeout(ctx, 5*time.Second) _, err = e.etcdDB.Put(ctx, prefix.Cidr, string(pfx)) defer cancel() if err != nil { @@ -85,11 +85,11 @@ func (e *etcd) CreatePrefix(prefix Prefix) (Prefix, error) { return prefix, nil } -func (e *etcd) ReadPrefix(prefix string) (Prefix, error) { +func (e *etcd) ReadPrefix(ctx context.Context, prefix string) (Prefix, error) { e.lock.Lock() defer e.lock.Unlock() - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) get, err := e.etcdDB.Get(ctx, prefix) defer cancel() if err != nil { @@ -103,10 +103,10 @@ func (e *etcd) ReadPrefix(prefix string) (Prefix, error) { return fromJSON(get.Kvs[0].Value) } -func (e *etcd) DeleteAllPrefixes() error { +func (e *etcd) DeleteAllPrefixes(ctx context.Context) error { e.lock.RLock() defer e.lock.RUnlock() - ctx, cancel := context.WithTimeout(context.Background(), 50*time.Minute) + ctx, cancel := context.WithTimeout(ctx, 50*time.Minute) defaultOpts := []clientv3.OpOption{clientv3.WithPrefix(), clientv3.WithKeysOnly(), clientv3.WithSerializable()} pfxs, err := e.etcdDB.Get(ctx, "", defaultOpts...) defer cancel() @@ -123,11 +123,11 @@ func (e *etcd) DeleteAllPrefixes() error { return nil } -func (e *etcd) ReadAllPrefixes() (Prefixes, error) { +func (e *etcd) ReadAllPrefixes(ctx context.Context) (Prefixes, error) { e.lock.Lock() defer e.lock.Unlock() - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) defaultOpts := []clientv3.OpOption{clientv3.WithPrefix(), clientv3.WithKeysOnly(), clientv3.WithSerializable()} pfxs, err := e.etcdDB.Get(ctx, "", defaultOpts...) defer cancel() @@ -149,12 +149,12 @@ func (e *etcd) ReadAllPrefixes() (Prefixes, error) { } return result, nil } -func (e *etcd) ReadAllPrefixCidrs() ([]string, error) { +func (e *etcd) ReadAllPrefixCidrs(ctx context.Context) ([]string, error) { e.lock.Lock() defer e.lock.Unlock() allPrefix := []string{} - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) defaultOpts := []clientv3.OpOption{clientv3.WithPrefix(), clientv3.WithKeysOnly(), clientv3.WithSerializable()} pfxs, err := e.etcdDB.Get(ctx, "", defaultOpts...) defer cancel() @@ -168,7 +168,7 @@ func (e *etcd) ReadAllPrefixCidrs() ([]string, error) { return allPrefix, nil } -func (e *etcd) UpdatePrefix(prefix Prefix) (Prefix, error) { +func (e *etcd) UpdatePrefix(ctx context.Context, prefix Prefix) (Prefix, error) { e.lock.Lock() defer e.lock.Unlock() @@ -179,7 +179,7 @@ func (e *etcd) UpdatePrefix(prefix Prefix) (Prefix, error) { return Prefix{}, err } - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) p, err := e.etcdDB.Get(ctx, prefix.Cidr) defer cancel() if err != nil { @@ -201,7 +201,7 @@ func (e *etcd) UpdatePrefix(prefix Prefix) (Prefix, error) { } // Operation is committed only if the watched keys remain unchanged. - ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second) + ctx, cancel = context.WithTimeout(ctx, 5*time.Second) _, err = e.etcdDB.Put(ctx, prefix.Cidr, string(pn)) defer cancel() if err != nil { @@ -210,11 +210,11 @@ func (e *etcd) UpdatePrefix(prefix Prefix) (Prefix, error) { return prefix, nil } -func (e *etcd) DeletePrefix(prefix Prefix) (Prefix, error) { +func (e *etcd) DeletePrefix(ctx context.Context, prefix Prefix) (Prefix, error) { e.lock.Lock() defer e.lock.Unlock() - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + ctx, cancel := context.WithTimeout(ctx, 5*time.Second) _, err := e.etcdDB.Delete(ctx, prefix.Cidr) defer cancel() if err != nil { diff --git a/integration_test.go b/integration_test.go index 19caa26..d640a8d 100644 --- a/integration_test.go +++ b/integration_test.go @@ -1,6 +1,7 @@ package ipam import ( + "context" "os" "strings" "testing" @@ -10,6 +11,8 @@ import ( ) func TestIntegration(t *testing.T) { + ctx := context.Background() + _, storage, err := startPostgres() require.NoError(t, err) defer storage.db.Close() @@ -20,41 +23,41 @@ func TestIntegration(t *testing.T) { ipam := NewWithStorage(storage) // Public Internet - publicInternet := ipam.PrefixFrom("1.2.3.0/27") + publicInternet := ipam.PrefixFrom(ctx, "1.2.3.0/27") require.NotNil(t, publicInternet) require.Equal(t, 25, int(publicInternet.Usage().AcquiredIPs)) require.Equal(t, 32, int(publicInternet.Usage().AvailableIPs)) require.Equal(t, "", publicInternet.ParentCidr) - _, err = ipam.AcquireChildPrefix(publicInternet.Cidr, 29) + _, err = ipam.AcquireChildPrefix(ctx, publicInternet.Cidr, 29) require.EqualError(t, err, "prefix 1.2.3.0/27 has ips, acquire child prefix not possible") - _, err = ipam.AcquireSpecificChildPrefix(publicInternet.Cidr, "1.2.3.0/29") + _, err = ipam.AcquireSpecificChildPrefix(ctx, publicInternet.Cidr, "1.2.3.0/29") require.EqualError(t, err, "prefix 1.2.3.0/27 has ips, acquire child prefix not possible") - ip, err := ipam.AcquireIP(publicInternet.Cidr) + ip, err := ipam.AcquireIP(ctx, publicInternet.Cidr) require.NoError(t, err) require.NotNil(t, ip) require.True(t, strings.HasPrefix(ip.IP.String(), "1.2.3")) require.Equal(t, "1.2.3.20", ip.IP.String()) // reread prefix - publicInternet = ipam.PrefixFrom("1.2.3.0/27") + publicInternet = ipam.PrefixFrom(ctx, "1.2.3.0/27") require.Equal(t, 26, int(publicInternet.Usage().AcquiredIPs)) - _, err = ipam.ReleaseIP(ip) + _, err = ipam.ReleaseIP(ctx, ip) require.NoError(t, err) // reread prefix - publicInternet = ipam.PrefixFrom("1.2.3.0/27") + publicInternet = ipam.PrefixFrom(ctx, "1.2.3.0/27") require.Equal(t, 25, int(publicInternet.Usage().AcquiredIPs)) // release acquired ip - err = ipam.ReleaseIPFromPrefix("1.2.3.0/27", "1.2.3.1") + err = ipam.ReleaseIPFromPrefix(ctx, "1.2.3.0/27", "1.2.3.1") require.NoError(t, err) // reread prefix - publicInternet = ipam.PrefixFrom("1.2.3.0/27") + publicInternet = ipam.PrefixFrom(ctx, "1.2.3.0/27") require.Equal(t, 24, int(publicInternet.Usage().AcquiredIPs)) // release unacquired ip - err = ipam.ReleaseIPFromPrefix("1.2.3.0/27", "1.2.3.24") + err = ipam.ReleaseIPFromPrefix(ctx, "1.2.3.0/27", "1.2.3.24") require.EqualError(t, err, "NotFound: unable to release ip:1.2.3.24 because it is not allocated in prefix:1.2.3.0/27") // Tenant super network - tenantSuper := ipam.PrefixFrom("10.128.0.0/14") + tenantSuper := ipam.PrefixFrom(ctx, "10.128.0.0/14") require.NotNil(t, tenantSuper) require.Equal(t, uint64(2), tenantSuper.Usage().AcquiredIPs) sum := 0 @@ -70,7 +73,7 @@ func TestIntegration(t *testing.T) { // FIXME This Super Prefix has leaked child prefixes ! require.Equal(t, 18, int(tenantSuper.Usage().AcquiredPrefixes)) - cp, err := ipam.AcquireChildPrefix("10.128.0.0/14", 22) + cp, err := ipam.AcquireChildPrefix(ctx, "10.128.0.0/14", 22) require.NoError(t, err) require.NotNil(t, cp) require.True(t, strings.HasPrefix(cp.Cidr, "10.")) @@ -78,53 +81,53 @@ func TestIntegration(t *testing.T) { require.Equal(t, "10.128.0.0/14", cp.ParentCidr) // reread - tenantSuper = ipam.PrefixFrom("10.128.0.0/14") + tenantSuper = ipam.PrefixFrom(ctx, "10.128.0.0/14") require.NotNil(t, tenantSuper) require.Equal(t, 19, int(tenantSuper.Usage().AcquiredPrefixes)) - err = ipam.ReleaseChildPrefix(cp) + err = ipam.ReleaseChildPrefix(ctx, cp) require.NoError(t, err) // reread - tenantSuper = ipam.PrefixFrom("10.128.0.0/14") + tenantSuper = ipam.PrefixFrom(ctx, "10.128.0.0/14") require.NotNil(t, tenantSuper) require.Equal(t, 18, int(tenantSuper.Usage().AcquiredPrefixes)) - cp, err = ipam.AcquireSpecificChildPrefix("10.128.0.0/14", "10.128.4.0/22") + cp, err = ipam.AcquireSpecificChildPrefix(ctx, "10.128.0.0/14", "10.128.4.0/22") require.NoError(t, err) require.NotNil(t, cp) require.Equal(t, "10.128.4.0/22", cp.String()) require.Equal(t, "10.128.0.0/14", cp.ParentCidr) // reread - tenantSuper = ipam.PrefixFrom("10.128.0.0/14") + tenantSuper = ipam.PrefixFrom(ctx, "10.128.0.0/14") require.NotNil(t, tenantSuper) require.Equal(t, 19, int(tenantSuper.Usage().AcquiredPrefixes)) - err = ipam.ReleaseChildPrefix(cp) + err = ipam.ReleaseChildPrefix(ctx, cp) require.NoError(t, err) // reread - tenantSuper = ipam.PrefixFrom("10.128.0.0/14") + tenantSuper = ipam.PrefixFrom(ctx, "10.128.0.0/14") require.NotNil(t, tenantSuper) - _, err = ipam.AcquireIP("10.128.0.0/14") + _, err = ipam.AcquireIP(ctx, "10.128.0.0/14") require.EqualError(t, err, "prefix 10.128.0.0/14 has childprefixes, acquire ip not possible") // Release existing child prefix - existingChild := ipam.PrefixFrom("10.129.28.0/22") + existingChild := ipam.PrefixFrom(ctx, "10.129.28.0/22") require.NotNil(t, existingChild) - err = ipam.ReleaseChildPrefix(existingChild) + err = ipam.ReleaseChildPrefix(ctx, existingChild) require.NoError(t, err) // reread - tenantSuper = ipam.PrefixFrom("10.128.0.0/14") + tenantSuper = ipam.PrefixFrom(ctx, "10.128.0.0/14") require.NotNil(t, tenantSuper) require.Equal(t, 17, int(tenantSuper.Usage().AcquiredPrefixes)) // Release existing child prefix with ips - existingChildWithIPs := ipam.PrefixFrom("10.130.36.0/22") + existingChildWithIPs := ipam.PrefixFrom(ctx, "10.130.36.0/22") require.NotNil(t, existingChildWithIPs) - err = ipam.ReleaseChildPrefix(existingChildWithIPs) + err = ipam.ReleaseChildPrefix(ctx, existingChildWithIPs) require.EqualError(t, err, "prefix 10.130.36.0/22 has ips, deletion not possible") // Read all child prefixes - pfxs, err := storage.ReadAllPrefixes() + pfxs, err := storage.ReadAllPrefixes(ctx) require.NoError(t, err) childPrefixCount := 0 for _, pfx := range pfxs { @@ -137,6 +140,7 @@ func TestIntegration(t *testing.T) { // require.Equal(t, childPrefixCount, tenantSuper.Usage().AcquiredPrefixes) } func TestIntegrationP(t *testing.T) { + ctx := context.Background() _, storage, err := startPostgres() require.NoError(t, err) defer storage.db.Close() @@ -147,13 +151,13 @@ func TestIntegrationP(t *testing.T) { ipam := NewWithStorage(storage) // Tenant super network 1 - tenantSuper1 := ipam.PrefixFrom("10.64.0.0/14") + tenantSuper1 := ipam.PrefixFrom(ctx, "10.64.0.0/14") require.NotNil(t, tenantSuper1) require.Equal(t, 2, int(tenantSuper1.Usage().AcquiredIPs)) require.Equal(t, 56320, int(tenantSuper1.Usage().AvailableSmallestPrefixes)) require.Equal(t, 36, int(tenantSuper1.Usage().AcquiredPrefixes)) - cp, err := ipam.AcquireChildPrefix("10.64.0.0/14", 22) + cp, err := ipam.AcquireChildPrefix(ctx, "10.64.0.0/14", 22) require.NoError(t, err) require.NotNil(t, cp) require.True(t, strings.HasPrefix(cp.Cidr, "10.")) @@ -161,38 +165,38 @@ func TestIntegrationP(t *testing.T) { require.Equal(t, "10.64.0.0/14", cp.ParentCidr) // reread - tenantSuper1 = ipam.PrefixFrom("10.64.0.0/14") + tenantSuper1 = ipam.PrefixFrom(ctx, "10.64.0.0/14") require.NotNil(t, tenantSuper1) require.Equal(t, 37, int(tenantSuper1.Usage().AcquiredPrefixes)) - err = ipam.ReleaseChildPrefix(cp) + err = ipam.ReleaseChildPrefix(ctx, cp) require.NoError(t, err) // reread - tenantSuper1 = ipam.PrefixFrom("10.64.0.0/14") + tenantSuper1 = ipam.PrefixFrom(ctx, "10.64.0.0/14") require.NotNil(t, tenantSuper1) require.Equal(t, 36, int(tenantSuper1.Usage().AcquiredPrefixes)) - cp, err = ipam.AcquireSpecificChildPrefix("10.64.0.0/14", "10.64.0.0/22") + cp, err = ipam.AcquireSpecificChildPrefix(ctx, "10.64.0.0/14", "10.64.0.0/22") require.NoError(t, err) require.NotNil(t, cp) require.Equal(t, "10.64.0.0/22", cp.String()) require.Equal(t, "10.64.0.0/14", cp.ParentCidr) // reread - tenantSuper1 = ipam.PrefixFrom("10.64.0.0/14") + tenantSuper1 = ipam.PrefixFrom(ctx, "10.64.0.0/14") require.NotNil(t, tenantSuper1) require.Equal(t, 37, int(tenantSuper1.Usage().AcquiredPrefixes)) - err = ipam.ReleaseChildPrefix(cp) + err = ipam.ReleaseChildPrefix(ctx, cp) require.NoError(t, err) // reread - tenantSuper1 = ipam.PrefixFrom("10.64.0.0/14") + tenantSuper1 = ipam.PrefixFrom(ctx, "10.64.0.0/14") require.NotNil(t, tenantSuper1) require.Equal(t, 36, int(tenantSuper1.Usage().AcquiredPrefixes)) - _, err = ipam.AcquireIP("10.64.0.0/14") + _, err = ipam.AcquireIP(ctx, "10.64.0.0/14") require.EqualError(t, err, "prefix 10.64.0.0/14 has childprefixes, acquire ip not possible") // Read all child prefixes - pfxs, err := storage.ReadAllPrefixes() + pfxs, err := storage.ReadAllPrefixes(ctx) require.NoError(t, err) childPrefixesOfTenantSuper := make(map[string]bool) @@ -208,13 +212,13 @@ func TestIntegrationP(t *testing.T) { require.Equal(t, int(tenantSuper1.Usage().AcquiredPrefixes)-2, len(childPrefixesOfTenantSuper)) // Tenant super network 2 - tenantSuper2 := ipam.PrefixFrom("10.76.0.0/14") + tenantSuper2 := ipam.PrefixFrom(ctx, "10.76.0.0/14") require.NotNil(t, tenantSuper2) require.Equal(t, 2, int(tenantSuper2.Usage().AcquiredIPs)) require.Equal(t, 58368, int(tenantSuper2.Usage().AvailableSmallestPrefixes)) require.Equal(t, 28, int(tenantSuper2.Usage().AcquiredPrefixes)) - cp, err = ipam.AcquireChildPrefix("10.76.0.0/14", 22) + cp, err = ipam.AcquireChildPrefix(ctx, "10.76.0.0/14", 22) require.NoError(t, err) require.NotNil(t, cp) require.True(t, strings.HasPrefix(cp.Cidr, "10.")) @@ -222,38 +226,38 @@ func TestIntegrationP(t *testing.T) { require.Equal(t, "10.76.0.0/14", cp.ParentCidr) // reread - tenantSuper2 = ipam.PrefixFrom("10.76.0.0/14") + tenantSuper2 = ipam.PrefixFrom(ctx, "10.76.0.0/14") require.NotNil(t, tenantSuper2) require.Equal(t, 29, int(tenantSuper2.Usage().AcquiredPrefixes)) - err = ipam.ReleaseChildPrefix(cp) + err = ipam.ReleaseChildPrefix(ctx, cp) require.NoError(t, err) // reread - tenantSuper2 = ipam.PrefixFrom("10.76.0.0/14") + tenantSuper2 = ipam.PrefixFrom(ctx, "10.76.0.0/14") require.NotNil(t, tenantSuper2) require.Equal(t, 28, int(tenantSuper2.Usage().AcquiredPrefixes)) - cp, err = ipam.AcquireSpecificChildPrefix("10.76.0.0/14", "10.76.0.0/22") + cp, err = ipam.AcquireSpecificChildPrefix(ctx, "10.76.0.0/14", "10.76.0.0/22") require.NoError(t, err) require.NotNil(t, cp) require.Equal(t, "10.76.0.0/22", cp.String()) require.Equal(t, "10.76.0.0/14", cp.ParentCidr) // reread - tenantSuper2 = ipam.PrefixFrom("10.76.0.0/14") + tenantSuper2 = ipam.PrefixFrom(ctx, "10.76.0.0/14") require.NotNil(t, tenantSuper2) require.Equal(t, 29, int(tenantSuper2.Usage().AcquiredPrefixes)) - err = ipam.ReleaseChildPrefix(cp) + err = ipam.ReleaseChildPrefix(ctx, cp) require.NoError(t, err) // reread - tenantSuper2 = ipam.PrefixFrom("10.76.0.0/14") + tenantSuper2 = ipam.PrefixFrom(ctx, "10.76.0.0/14") require.NotNil(t, tenantSuper2) require.Equal(t, 28, int(tenantSuper2.Usage().AcquiredPrefixes)) - _, err = ipam.AcquireIP("10.76.0.0/14") + _, err = ipam.AcquireIP(ctx, "10.76.0.0/14") require.EqualError(t, err, "prefix 10.76.0.0/14 has childprefixes, acquire ip not possible") // Read all child prefixes - pfxs, err = storage.ReadAllPrefixes() + pfxs, err = storage.ReadAllPrefixes(ctx) require.NoError(t, err) childPrefixesOfTenantSuper = make(map[string]bool) @@ -268,28 +272,29 @@ func TestIntegrationP(t *testing.T) { require.Equal(t, int(tenantSuper2.Usage().AcquiredPrefixes), len(childPrefixesOfTenantSuper)) // Public Internet - publicInternet := ipam.PrefixFrom("1.2.3.0/25") + publicInternet := ipam.PrefixFrom(ctx, "1.2.3.0/25") require.NotNil(t, publicInternet) require.Equal(t, 128, int(publicInternet.Usage().AcquiredIPs)) require.Equal(t, 128, int(publicInternet.Usage().AvailableIPs)) require.Equal(t, "", publicInternet.ParentCidr) - _, err = ipam.AcquireChildPrefix(publicInternet.Cidr, 29) + _, err = ipam.AcquireChildPrefix(ctx, publicInternet.Cidr, 29) require.EqualError(t, err, "prefix 1.2.3.0/25 has ips, acquire child prefix not possible") - _, err = ipam.AcquireSpecificChildPrefix(publicInternet.Cidr, "1.2.3.0/29") + _, err = ipam.AcquireSpecificChildPrefix(ctx, publicInternet.Cidr, "1.2.3.0/29") require.EqualError(t, err, "prefix 1.2.3.0/25 has ips, acquire child prefix not possible") - _, err = ipam.AcquireIP(publicInternet.Cidr) + _, err = ipam.AcquireIP(ctx, publicInternet.Cidr) require.EqualError(t, err, "NoIPAvailableError: no more ips in prefix: 1.2.3.0/25 left, length of prefix.ips: 128") } func TestIntegrationEtcd(t *testing.T) { + ctx := context.Background() _, storage, err := startEtcd() require.NoError(t, err) ipam := NewWithStorage(storage) // Tenant super network 1 - tenantSuper1, err := ipam.NewPrefix("10.64.0.0/14") + tenantSuper1, err := ipam.NewPrefix(ctx, "10.64.0.0/14") require.NoError(t, err) require.NotNil(t, tenantSuper1) @@ -297,7 +302,7 @@ func TestIntegrationEtcd(t *testing.T) { require.Equal(t, 65536, int(tenantSuper1.Usage().AvailableSmallestPrefixes)) require.Equal(t, 0, int(tenantSuper1.Usage().AcquiredPrefixes)) - cp, err := ipam.AcquireChildPrefix("10.64.0.0/14", 22) + cp, err := ipam.AcquireChildPrefix(ctx, "10.64.0.0/14", 22) require.NoError(t, err) require.NotNil(t, cp) require.True(t, strings.HasPrefix(cp.Cidr, "10.")) @@ -305,34 +310,34 @@ func TestIntegrationEtcd(t *testing.T) { require.Equal(t, "10.64.0.0/14", cp.ParentCidr) // reread - tenantSuper1 = ipam.PrefixFrom("10.64.0.0/14") + tenantSuper1 = ipam.PrefixFrom(ctx, "10.64.0.0/14") require.NotNil(t, tenantSuper1) require.Equal(t, 1, int(tenantSuper1.Usage().AcquiredPrefixes)) - err = ipam.ReleaseChildPrefix(cp) + err = ipam.ReleaseChildPrefix(ctx, cp) require.NoError(t, err) // reread - tenantSuper1 = ipam.PrefixFrom("10.64.0.0/14") + tenantSuper1 = ipam.PrefixFrom(ctx, "10.64.0.0/14") require.NotNil(t, tenantSuper1) require.Equal(t, 0, int(tenantSuper1.Usage().AcquiredPrefixes)) - cp, err = ipam.AcquireSpecificChildPrefix("10.64.0.0/14", "10.64.0.0/22") + cp, err = ipam.AcquireSpecificChildPrefix(ctx, "10.64.0.0/14", "10.64.0.0/22") require.NoError(t, err) require.NotNil(t, cp) require.Equal(t, "10.64.0.0/22", cp.String()) require.Equal(t, "10.64.0.0/14", cp.ParentCidr) - _, err = ipam.AcquireIP("10.64.0.0/14") + _, err = ipam.AcquireIP(ctx, "10.64.0.0/14") require.EqualError(t, err, "prefix 10.64.0.0/14 has childprefixes, acquire ip not possible") // Tenant super network 2 - tenantSuper2, err := ipam.NewPrefix("10.76.0.0/14") + tenantSuper2, err := ipam.NewPrefix(ctx, "10.76.0.0/14") require.NoError(t, err) require.NotNil(t, tenantSuper2) require.Equal(t, 2, int(tenantSuper2.Usage().AcquiredIPs)) require.Equal(t, 65536, int(tenantSuper2.Usage().AvailableSmallestPrefixes)) require.Equal(t, 0, int(tenantSuper2.Usage().AcquiredPrefixes)) - cp, err = ipam.AcquireChildPrefix("10.76.0.0/14", 22) + cp, err = ipam.AcquireChildPrefix(ctx, "10.76.0.0/14", 22) require.NoError(t, err) require.NotNil(t, cp) require.True(t, strings.HasPrefix(cp.Cidr, "10.")) @@ -340,38 +345,38 @@ func TestIntegrationEtcd(t *testing.T) { require.Equal(t, "10.76.0.0/14", cp.ParentCidr) // reread - tenantSuper2 = ipam.PrefixFrom("10.76.0.0/14") + tenantSuper2 = ipam.PrefixFrom(ctx, "10.76.0.0/14") require.NotNil(t, tenantSuper2) require.Equal(t, 1, int(tenantSuper2.Usage().AcquiredPrefixes)) - err = ipam.ReleaseChildPrefix(cp) + err = ipam.ReleaseChildPrefix(ctx, cp) require.NoError(t, err) // reread - tenantSuper2 = ipam.PrefixFrom("10.76.0.0/14") + tenantSuper2 = ipam.PrefixFrom(ctx, "10.76.0.0/14") require.NotNil(t, tenantSuper2) require.Equal(t, 0, int(tenantSuper2.Usage().AcquiredPrefixes)) - cp, err = ipam.AcquireSpecificChildPrefix("10.76.0.0/14", "10.76.0.0/22") + cp, err = ipam.AcquireSpecificChildPrefix(ctx, "10.76.0.0/14", "10.76.0.0/22") require.NoError(t, err) require.NotNil(t, cp) require.Equal(t, "10.76.0.0/22", cp.String()) require.Equal(t, "10.76.0.0/14", cp.ParentCidr) // reread - tenantSuper2 = ipam.PrefixFrom("10.76.0.0/14") + tenantSuper2 = ipam.PrefixFrom(ctx, "10.76.0.0/14") require.NotNil(t, tenantSuper2) require.Equal(t, 1, int(tenantSuper2.Usage().AcquiredPrefixes)) - err = ipam.ReleaseChildPrefix(cp) + err = ipam.ReleaseChildPrefix(ctx, cp) require.NoError(t, err) // reread - tenantSuper2 = ipam.PrefixFrom("10.76.0.0/14") + tenantSuper2 = ipam.PrefixFrom(ctx, "10.76.0.0/14") require.NotNil(t, tenantSuper2) require.Equal(t, 0, int(tenantSuper2.Usage().AcquiredPrefixes)) - _, err = ipam.AcquireIP("10.76.0.0/14") + _, err = ipam.AcquireIP(ctx, "10.76.0.0/14") require.EqualError(t, err, "prefix 10.76.0.0/14 has childprefixes, acquire ip not possible") // Read all child prefixes - pfxs, err := storage.ReadAllPrefixes() + pfxs, err := storage.ReadAllPrefixes(ctx) require.NoError(t, err) childPrefixesOfTenantSuper := make(map[string]bool) @@ -386,17 +391,17 @@ func TestIntegrationEtcd(t *testing.T) { require.Equal(t, int(tenantSuper2.Usage().AcquiredPrefixes), len(childPrefixesOfTenantSuper)) // Public Internet - publicInternet, err := ipam.NewPrefix("1.2.3.0/25") + publicInternet, err := ipam.NewPrefix(ctx, "1.2.3.0/25") require.NoError(t, err) require.NotNil(t, publicInternet) require.Equal(t, 2, int(publicInternet.Usage().AcquiredIPs)) require.Equal(t, 128, int(publicInternet.Usage().AvailableIPs)) require.Equal(t, "", publicInternet.ParentCidr) - _, err = ipam.AcquireChildPrefix(publicInternet.Cidr, 29) + _, err = ipam.AcquireChildPrefix(ctx, publicInternet.Cidr, 29) require.NoError(t, err) - _, err = ipam.AcquireSpecificChildPrefix(publicInternet.Cidr, "1.2.3.0/29") + _, err = ipam.AcquireSpecificChildPrefix(ctx, publicInternet.Cidr, "1.2.3.0/29") require.EqualError(t, err, "specific prefix 1.2.3.0/29 is not available in prefix 1.2.3.0/25") - _, err = ipam.AcquireIP(publicInternet.Cidr) + _, err = ipam.AcquireIP(ctx, publicInternet.Cidr) require.EqualError(t, err, "prefix 1.2.3.0/25 has childprefixes, acquire ip not possible") -} \ No newline at end of file +} diff --git a/ipam.go b/ipam.go index 0a38d31..a8e202d 100644 --- a/ipam.go +++ b/ipam.go @@ -1,43 +1,46 @@ package ipam -import "sync" +import ( + "context" + "sync" +) // Ipamer can be used to do IPAM stuff. type Ipamer interface { // NewPrefix create a new Prefix from a string notation. - NewPrefix(cidr string) (*Prefix, error) + NewPrefix(ctx context.Context, cidr string) (*Prefix, error) // DeletePrefix delete a Prefix from a string notation. // If the Prefix is not found an NotFoundError is returned. - DeletePrefix(cidr string) (*Prefix, error) + DeletePrefix(ctx context.Context, cidr string) (*Prefix, error) // AcquireChildPrefix will return a Prefix with a smaller length from the given Prefix. - AcquireChildPrefix(parentCidr string, length uint8) (*Prefix, error) + AcquireChildPrefix(ctx context.Context, parentCidr string, length uint8) (*Prefix, error) // AcquireSpecificChildPrefix will return a Prefix with a smaller length from the given Prefix. - AcquireSpecificChildPrefix(parentCidr, childCidr string) (*Prefix, error) + AcquireSpecificChildPrefix(ctx context.Context, parentCidr, childCidr string) (*Prefix, error) // ReleaseChildPrefix will mark this child Prefix as available again. - ReleaseChildPrefix(child *Prefix) error + ReleaseChildPrefix(ctx context.Context, child *Prefix) error // PrefixFrom will return a known Prefix. - PrefixFrom(cidr string) *Prefix + PrefixFrom(ctx context.Context, cidr string) *Prefix // AcquireSpecificIP will acquire given IP and mark this IP as used, if already in use, return nil. // If specificIP is empty, the next free IP is returned. // If there is no free IP an NoIPAvailableError is returned. - AcquireSpecificIP(prefixCidr, specificIP string) (*IP, error) + AcquireSpecificIP(ctx context.Context, prefixCidr, specificIP string) (*IP, error) // AcquireIP will return the next unused IP from this Prefix. - AcquireIP(prefixCidr string) (*IP, error) + AcquireIP(ctx context.Context, prefixCidr string) (*IP, error) // ReleaseIP will release the given IP for later usage and returns the updated Prefix. // If the IP is not found an NotFoundError is returned. - ReleaseIP(ip *IP) (*Prefix, error) + ReleaseIP(ctx context.Context, ip *IP) (*Prefix, error) // ReleaseIPFromPrefix will release the given IP for later usage. // If the Prefix or the IP is not found an NotFoundError is returned. - ReleaseIPFromPrefix(prefixCidr, ip string) error + ReleaseIPFromPrefix(ctx context.Context, prefixCidr, ip string) error // PrefixesOverlapping will check if one ore more prefix of newPrefixes is overlapping // with one of existingPrefixes PrefixesOverlapping(existingPrefixes []string, newPrefixes []string) error // Dump all stored prefixes as json formatted string - Dump() (string, error) + Dump(ctx context.Context) (string, error) // Load a previously created json formatted dump, deletes all prefixes before loading - Load(dump string) error + Load(ctx context.Context, dump string) error // ReadAllPrefixCidrs retrieves all existing Prefix CIDRs from the underlying storage - ReadAllPrefixCidrs() ([]string, error) + ReadAllPrefixCidrs(ctx context.Context) ([]string, error) } type ipamer struct { diff --git a/ipam_test.go b/ipam_test.go index 01f42c4..d4d9c31 100644 --- a/ipam_test.go +++ b/ipam_test.go @@ -1,21 +1,24 @@ package ipam import ( + "context" "fmt" "strings" ) func ExampleIpamer_NewPrefix() { + ctx := context.Background() + ipamer := New() - prefix, err := ipamer.NewPrefix("192.168.0.0/24") + prefix, err := ipamer.NewPrefix(ctx, "192.168.0.0/24") if err != nil { panic(err) } - ip1, err := ipamer.AcquireIP(prefix.Cidr) + ip1, err := ipamer.AcquireIP(ctx, prefix.Cidr) if err != nil { panic(err) } - ip2, err := ipamer.AcquireIP(prefix.Cidr) + ip2, err := ipamer.AcquireIP(ctx, prefix.Cidr) if err != nil { panic(err) } @@ -32,40 +35,41 @@ func ExampleIpamer_NewPrefix() { // Super Prefix IP2 : 192.168.0.2 // Super Prefix IP2 Parent : 192.168.0.0/24 - _, err = ipamer.ReleaseIP(ip2) + _, err = ipamer.ReleaseIP(ctx, ip2) if err != nil { panic(err) } - _, err = ipamer.ReleaseIP(ip1) + _, err = ipamer.ReleaseIP(ctx, ip1) if err != nil { panic(err) } - _, err = ipamer.DeletePrefix(prefix.Cidr) + _, err = ipamer.DeletePrefix(ctx, prefix.Cidr) if err != nil { panic(err) } } func ExampleIpamer_AcquireChildPrefix() { + ctx := context.Background() ipamer := New() - prefix, err := ipamer.NewPrefix("2001:aabb::/48") + prefix, err := ipamer.NewPrefix(ctx, "2001:aabb::/48") if err != nil { panic(err) } - cp1, err := ipamer.AcquireChildPrefix(prefix.Cidr, 64) + cp1, err := ipamer.AcquireChildPrefix(ctx, prefix.Cidr, 64) if err != nil { panic(err) } - cp2, err := ipamer.AcquireChildPrefix(prefix.Cidr, 72) + cp2, err := ipamer.AcquireChildPrefix(ctx, prefix.Cidr, 72) if err != nil { panic(err) } - ip21, err := ipamer.AcquireIP(cp2.Cidr) + ip21, err := ipamer.AcquireIP(ctx, cp2.Cidr) if err != nil { panic(err) } - prefix = ipamer.PrefixFrom(prefix.Cidr) + prefix = ipamer.PrefixFrom(ctx, prefix.Cidr) fmt.Printf("Super Prefix : %s\n", prefix) fmt.Printf("Child Prefix 1: %s\n", cp1) fmt.Printf("Child Prefix 2: %s\n", cp2) @@ -79,21 +83,21 @@ func ExampleIpamer_AcquireChildPrefix() { // Child Prefix 2 IP1: 2001:aabb:0:1::1 // Super Prefix available child prefixes with 2 bytes: 2147483647 // Super Prefix available child prefixes: 2001:aabb:0:1:100::/72,2001:aabb:0:1:200::/71,2001:aabb:0:1:400::/70,2001:aabb:0:1:800::/69,2001:aabb:0:1:1000::/68,2001:aabb:0:1:2000::/67,2001:aabb:0:1:4000::/66,2001:aabb:0:1:8000::/65,2001:aabb:0:2::/63,2001:aabb:0:4::/62,2001:aabb:0:8::/61,2001:aabb:0:10::/60,2001:aabb:0:20::/59,2001:aabb:0:40::/58,2001:aabb:0:80::/57,2001:aabb:0:100::/56,2001:aabb:0:200::/55,2001:aabb:0:400::/54,2001:aabb:0:800::/53,2001:aabb:0:1000::/52,2001:aabb:0:2000::/51,2001:aabb:0:4000::/50,2001:aabb:0:8000::/49 - err = ipamer.ReleaseChildPrefix(cp1) + err = ipamer.ReleaseChildPrefix(ctx, cp1) if err != nil { panic(err) } - _, err = ipamer.ReleaseIP(ip21) + _, err = ipamer.ReleaseIP(ctx, ip21) if err != nil { panic(err) } - err = ipamer.ReleaseChildPrefix(cp2) + err = ipamer.ReleaseChildPrefix(ctx, cp2) if err != nil { panic(err) } - _, err = ipamer.DeletePrefix(prefix.Cidr) + _, err = ipamer.DeletePrefix(ctx, prefix.Cidr) if err != nil { panic(err) } diff --git a/memory.go b/memory.go index 426c7d4..fb61332 100644 --- a/memory.go +++ b/memory.go @@ -1,6 +1,7 @@ package ipam import ( + "context" "fmt" "sync" ) @@ -21,7 +22,7 @@ func NewMemory() Storage { func (m *memory) Name() string { return "memory" } -func (m *memory) CreatePrefix(prefix Prefix) (Prefix, error) { +func (m *memory) CreatePrefix(_ context.Context, prefix Prefix) (Prefix, error) { m.lock.Lock() defer m.lock.Unlock() @@ -32,7 +33,7 @@ func (m *memory) CreatePrefix(prefix Prefix) (Prefix, error) { m.prefixes[prefix.Cidr] = *prefix.deepCopy() return prefix, nil } -func (m *memory) ReadPrefix(prefix string) (Prefix, error) { +func (m *memory) ReadPrefix(_ context.Context, prefix string) (Prefix, error) { m.lock.RLock() defer m.lock.RUnlock() @@ -42,13 +43,13 @@ func (m *memory) ReadPrefix(prefix string) (Prefix, error) { } return *result.deepCopy(), nil } -func (m *memory) DeleteAllPrefixes() error { +func (m *memory) DeleteAllPrefixes(_ context.Context) error { m.lock.RLock() defer m.lock.RUnlock() m.prefixes = make(map[string]Prefix) return nil } -func (m *memory) ReadAllPrefixes() (Prefixes, error) { +func (m *memory) ReadAllPrefixes(_ context.Context) (Prefixes, error) { m.lock.RLock() defer m.lock.RUnlock() @@ -58,7 +59,7 @@ func (m *memory) ReadAllPrefixes() (Prefixes, error) { } return ps, nil } -func (m *memory) ReadAllPrefixCidrs() ([]string, error) { +func (m *memory) ReadAllPrefixCidrs(_ context.Context) ([]string, error) { m.lock.RLock() defer m.lock.RUnlock() @@ -68,7 +69,7 @@ func (m *memory) ReadAllPrefixCidrs() ([]string, error) { } return ps, nil } -func (m *memory) UpdatePrefix(prefix Prefix) (Prefix, error) { +func (m *memory) UpdatePrefix(_ context.Context, prefix Prefix) (Prefix, error) { m.lock.Lock() defer m.lock.Unlock() @@ -88,7 +89,7 @@ func (m *memory) UpdatePrefix(prefix Prefix) (Prefix, error) { m.prefixes[prefix.Cidr] = *prefix.deepCopy() return prefix, nil } -func (m *memory) DeletePrefix(prefix Prefix) (Prefix, error) { +func (m *memory) DeletePrefix(_ context.Context, prefix Prefix) (Prefix, error) { m.lock.Lock() defer m.lock.Unlock() diff --git a/memory_test.go b/memory_test.go index 70a96ec..66adc7b 100644 --- a/memory_test.go +++ b/memory_test.go @@ -5,39 +5,42 @@ import ( "testing" "github.com/stretchr/testify/require" + "golang.org/x/net/context" ) func Test_ReadPrefix(t *testing.T) { + ctx := context.Background() m := NewMemory() // Prefix - p, err := m.ReadPrefix("12.0.0.0/8") + p, err := m.ReadPrefix(ctx, "12.0.0.0/8") require.NotNil(t, err) require.Equal(t, "prefix 12.0.0.0/8 not found", err.Error()) require.Empty(t, p) prefix := Prefix{Cidr: "12.0.0.0/16"} - p, err = m.CreatePrefix(prefix) + p, err = m.CreatePrefix(ctx, prefix) require.Nil(t, err) require.NotNil(t, p) - p, err = m.ReadPrefix("12.0.0.0/16") + p, err = m.ReadPrefix(ctx, "12.0.0.0/16") require.Nil(t, err) require.NotNil(t, p) require.Equal(t, "12.0.0.0/16", p.Cidr) } func Test_UpdatePrefix(t *testing.T) { + ctx := context.Background() m := NewMemory() prefix := Prefix{} - p, err := m.UpdatePrefix(prefix) + p, err := m.UpdatePrefix(ctx, prefix) require.NotNil(t, err) require.Empty(t, p) require.Equal(t, "prefix not present:{ false map[] 0 map[] 1}", err.Error()) prefix.Cidr = "1.2.3.4/24" - p, err = m.UpdatePrefix(prefix) + p, err = m.UpdatePrefix(ctx, prefix) require.NotNil(t, err) require.Empty(t, p) require.Equal(t, "prefix not found:1.2.3.4/24", err.Error()) @@ -45,6 +48,7 @@ func Test_UpdatePrefix(t *testing.T) { // ensure that locks on memory storage work func Test_UpdatePrefix_Concurrent(t *testing.T) { + ctx := context.Background() m := NewMemory() for i := 0; i < 50000; i++ { @@ -54,23 +58,23 @@ func Test_UpdatePrefix_Concurrent(t *testing.T) { cidr := calcPrefix24(run) + "/24" prefix.Cidr = cidr - p, err := m.CreatePrefix(prefix) + p, err := m.CreatePrefix(ctx, prefix) require.Nil(t, err) require.NotNil(t, p) - p, err = m.ReadPrefix(cidr) + p, err = m.ReadPrefix(ctx, cidr) require.Nil(t, err) require.NotNil(t, p) - p, err = m.UpdatePrefix(p) + p, err = m.UpdatePrefix(ctx, p) require.Nil(t, err) require.NotNil(t, p) - p, err = m.ReadPrefix(cidr) + p, err = m.ReadPrefix(ctx, cidr) require.Nil(t, err) require.NotNil(t, p) - p, err = m.DeletePrefix(p) + p, err = m.DeletePrefix(ctx, p) require.Nil(t, err) require.NotNil(t, p) }(i) diff --git a/mongodb.go b/mongodb.go index 00b322e..d023b65 100644 --- a/mongodb.go +++ b/mongodb.go @@ -50,7 +50,7 @@ func newMongo(ctx context.Context, config MongoConfig) (*mongodb, error) { c := m.Database(config.DatabaseName).Collection(config.CollectionName) - _, err = c.Indexes().CreateMany(context.TODO(), []mongo.IndexModel{{ + _, err = c.Indexes().CreateMany(ctx, []mongo.IndexModel{{ Keys: bson.M{dbIndex: 1}, Options: options.Index().SetUnique(true), }}) @@ -60,12 +60,12 @@ func newMongo(ctx context.Context, config MongoConfig) (*mongodb, error) { return &mongodb{c, sync.RWMutex{}}, nil } -func (m *mongodb) CreatePrefix(prefix Prefix) (Prefix, error) { +func (m *mongodb) CreatePrefix(ctx context.Context, prefix Prefix) (Prefix, error) { m.lock.Lock() defer m.lock.Unlock() f := bson.D{{Key: dbIndex, Value: prefix.Cidr}} - r := m.c.FindOne(context.TODO(), f) + r := m.c.FindOne(ctx, f) // ErrNoDocuments should be returned if the prefix does not exist if r.Err() == nil { @@ -82,12 +82,12 @@ func (m *mongodb) CreatePrefix(prefix Prefix) (Prefix, error) { return prefix, nil } -func (m *mongodb) ReadPrefix(prefix string) (Prefix, error) { +func (m *mongodb) ReadPrefix(ctx context.Context, prefix string) (Prefix, error) { m.lock.Lock() defer m.lock.Unlock() f := bson.D{{Key: dbIndex, Value: prefix}} - r := m.c.FindOne(context.TODO(), f) + r := m.c.FindOne(ctx, f) // ErrNoDocuments should be returned if the prefix does not exist if r.Err() != nil && errors.Is(r.Err(), mongo.ErrNoDocuments) { @@ -104,29 +104,29 @@ func (m *mongodb) ReadPrefix(prefix string) (Prefix, error) { return j.toPrefix(), nil } -func (m *mongodb) DeleteAllPrefixes() error { +func (m *mongodb) DeleteAllPrefixes(ctx context.Context) error { m.lock.Lock() defer m.lock.Unlock() f := bson.D{{}} // match all documents - _, err := m.c.DeleteMany(context.TODO(), f) + _, err := m.c.DeleteMany(ctx, f) if err != nil { return fmt.Errorf(`error deleting all prefixes: %w`, err) } return nil } -func (m *mongodb) ReadAllPrefixes() (Prefixes, error) { +func (m *mongodb) ReadAllPrefixes(ctx context.Context) (Prefixes, error) { m.lock.Lock() defer m.lock.Unlock() f := bson.D{{}} // match all documents - c, err := m.c.Find(context.TODO(), f) + c, err := m.c.Find(ctx, f) if err != nil { return nil, fmt.Errorf(`error reading all prefixes: %w`, err) } var r []prefixJSON - if err := c.All(context.TODO(), &r); err != nil { + if err := c.All(ctx, &r); err != nil { return nil, fmt.Errorf(`error reading all prefixes: %w`, err) } @@ -138,8 +138,8 @@ func (m *mongodb) ReadAllPrefixes() (Prefixes, error) { return s, nil } -func (m *mongodb) ReadAllPrefixCidrs() ([]string, error) { - p, err := m.ReadAllPrefixes() +func (m *mongodb) ReadAllPrefixCidrs(ctx context.Context) ([]string, error) { + p, err := m.ReadAllPrefixes(ctx) if err != nil { return nil, err } @@ -150,7 +150,7 @@ func (m *mongodb) ReadAllPrefixCidrs() ([]string, error) { return s, nil } -func (m *mongodb) UpdatePrefix(prefix Prefix) (Prefix, error) { +func (m *mongodb) UpdatePrefix(ctx context.Context, prefix Prefix) (Prefix, error) { m.lock.Lock() defer m.lock.Unlock() @@ -160,7 +160,7 @@ func (m *mongodb) UpdatePrefix(prefix Prefix) (Prefix, error) { f := bson.D{{Key: dbIndex, Value: prefix.Cidr}, {Key: versionKey, Value: oldVersion}} o := options.Replace().SetUpsert(false) - r, err := m.c.ReplaceOne(context.TODO(), f, prefix.toPrefixJSON(), o) + r, err := m.c.ReplaceOne(ctx, f, prefix.toPrefixJSON(), o) if err != nil { return Prefix{}, fmt.Errorf("unable to update prefix:%s, error: %w", prefix.Cidr, err) } @@ -175,12 +175,12 @@ func (m *mongodb) UpdatePrefix(prefix Prefix) (Prefix, error) { return prefix, nil } -func (m *mongodb) DeletePrefix(prefix Prefix) (Prefix, error) { +func (m *mongodb) DeletePrefix(ctx context.Context, prefix Prefix) (Prefix, error) { m.lock.Lock() defer m.lock.Unlock() f := bson.D{{Key: dbIndex, Value: prefix.Cidr}} - r := m.c.FindOneAndDelete(context.TODO(), f) + r := m.c.FindOneAndDelete(ctx, f) // ErrNoDocuments should be returned if the prefix does not exist if r.Err() != nil && errors.Is(r.Err(), mongo.ErrNoDocuments) { diff --git a/pkg/service/ipam-service.go b/pkg/service/ipam-service.go index 23b62de..fcdb9b7 100644 --- a/pkg/service/ipam-service.go +++ b/pkg/service/ipam-service.go @@ -23,9 +23,9 @@ func New(log *zap.SugaredLogger, ipamer goipam.Ipamer) *IPAMService { } } -func (i *IPAMService) CreatePrefix(_ context.Context, req *connect.Request[v1.CreatePrefixRequest]) (*connect.Response[v1.CreatePrefixResponse], error) { +func (i *IPAMService) CreatePrefix(ctx context.Context, req *connect.Request[v1.CreatePrefixRequest]) (*connect.Response[v1.CreatePrefixResponse], error) { i.log.Debugw("createprefix", "req", req) - resp, err := i.ipamer.NewPrefix(req.Msg.Cidr) + resp, err := i.ipamer.NewPrefix(ctx, req.Msg.Cidr) if err != nil { i.log.Errorw("createprefix", "error", err) return nil, connect.NewError(connect.CodeInvalidArgument, err) @@ -39,9 +39,9 @@ func (i *IPAMService) CreatePrefix(_ context.Context, req *connect.Request[v1.Cr }, }, nil } -func (i *IPAMService) DeletePrefix(_ context.Context, req *connect.Request[v1.DeletePrefixRequest]) (*connect.Response[v1.DeletePrefixResponse], error) { +func (i *IPAMService) DeletePrefix(ctx context.Context, req *connect.Request[v1.DeletePrefixRequest]) (*connect.Response[v1.DeletePrefixResponse], error) { i.log.Debugw("deleteprefix", "req", req) - resp, err := i.ipamer.DeletePrefix(req.Msg.Cidr) + resp, err := i.ipamer.DeletePrefix(ctx, req.Msg.Cidr) if err != nil { i.log.Errorw("deleteprefix", "error", err) return nil, connect.NewError(connect.CodeInvalidArgument, err) @@ -56,10 +56,10 @@ func (i *IPAMService) DeletePrefix(_ context.Context, req *connect.Request[v1.De }, }, nil } -func (i *IPAMService) GetPrefix(_ context.Context, req *connect.Request[v1.GetPrefixRequest]) (*connect.Response[v1.GetPrefixResponse], error) { +func (i *IPAMService) GetPrefix(ctx context.Context, req *connect.Request[v1.GetPrefixRequest]) (*connect.Response[v1.GetPrefixResponse], error) { i.log.Debugw("getprefix", "req", req) - resp := i.ipamer.PrefixFrom(req.Msg.Cidr) + resp := i.ipamer.PrefixFrom(ctx, req.Msg.Cidr) if resp == nil { return &connect.Response[v1.GetPrefixResponse]{}, connect.NewError(connect.CodeNotFound, fmt.Errorf("prefix:%q not found", req.Msg.Cidr)) } @@ -73,10 +73,10 @@ func (i *IPAMService) GetPrefix(_ context.Context, req *connect.Request[v1.GetPr }, }, nil } -func (i *IPAMService) ListPrefixes(_ context.Context, req *connect.Request[v1.ListPrefixesRequest]) (*connect.Response[v1.ListPrefixesResponse], error) { +func (i *IPAMService) ListPrefixes(ctx context.Context, req *connect.Request[v1.ListPrefixesRequest]) (*connect.Response[v1.ListPrefixesResponse], error) { i.log.Debugw("listprefixes", "req", req) - resp, err := i.ipamer.ReadAllPrefixCidrs() + resp, err := i.ipamer.ReadAllPrefixCidrs(ctx) if err != nil { i.log.Errorw("listprefixes", "error", err) return nil, connect.NewError(connect.CodeInvalidArgument, err) @@ -84,7 +84,7 @@ func (i *IPAMService) ListPrefixes(_ context.Context, req *connect.Request[v1.Li var result []*v1.Prefix for _, cidr := range resp { - p := i.ipamer.PrefixFrom(cidr) + p := i.ipamer.PrefixFrom(ctx, cidr) if p == nil { i.log.Warnw("skipping nil prefix of cidr:%q", cidr) continue @@ -98,9 +98,9 @@ func (i *IPAMService) ListPrefixes(_ context.Context, req *connect.Request[v1.Li }, nil } -func (i *IPAMService) AcquireChildPrefix(_ context.Context, req *connect.Request[v1.AcquireChildPrefixRequest]) (*connect.Response[v1.AcquireChildPrefixResponse], error) { +func (i *IPAMService) AcquireChildPrefix(ctx context.Context, req *connect.Request[v1.AcquireChildPrefixRequest]) (*connect.Response[v1.AcquireChildPrefixResponse], error) { i.log.Debugw("acquirechildprefix", "req", req) - resp, err := i.ipamer.AcquireChildPrefix(req.Msg.Cidr, uint8(req.Msg.Length)) + resp, err := i.ipamer.AcquireChildPrefix(ctx, req.Msg.Cidr, uint8(req.Msg.Length)) if err != nil { i.log.Errorw("acquirechildprefix", "error", err) return nil, connect.NewError(connect.CodeInvalidArgument, err) @@ -115,15 +115,15 @@ func (i *IPAMService) AcquireChildPrefix(_ context.Context, req *connect.Request }, nil } -func (i *IPAMService) ReleaseChildPrefix(_ context.Context, req *connect.Request[v1.ReleaseChildPrefixRequest]) (*connect.Response[v1.ReleaseChildPrefixResponse], error) { +func (i *IPAMService) ReleaseChildPrefix(ctx context.Context, req *connect.Request[v1.ReleaseChildPrefixRequest]) (*connect.Response[v1.ReleaseChildPrefixResponse], error) { i.log.Debugw("releasechildprefix", "req", req) - prefix := i.ipamer.PrefixFrom(req.Msg.Cidr) + prefix := i.ipamer.PrefixFrom(ctx, req.Msg.Cidr) if prefix == nil { return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("prefix:%q not found", req.Msg.Cidr)) } - err := i.ipamer.ReleaseChildPrefix(prefix) + err := i.ipamer.ReleaseChildPrefix(ctx, prefix) if err != nil { i.log.Errorw("releasechildprefix", "error", err) return nil, connect.NewError(connect.CodeInvalidArgument, err) @@ -137,10 +137,10 @@ func (i *IPAMService) ReleaseChildPrefix(_ context.Context, req *connect.Request }, }, nil } -func (i *IPAMService) AcquireIP(_ context.Context, req *connect.Request[v1.AcquireIPRequest]) (*connect.Response[v1.AcquireIPResponse], error) { +func (i *IPAMService) AcquireIP(ctx context.Context, req *connect.Request[v1.AcquireIPRequest]) (*connect.Response[v1.AcquireIPResponse], error) { i.log.Debugw("acquireip", "req", req) - resp, err := i.ipamer.AcquireIP(req.Msg.PrefixCidr) + resp, err := i.ipamer.AcquireIP(ctx, req.Msg.PrefixCidr) if err != nil { i.log.Errorw("acquireip", "error", err) return nil, connect.NewError(connect.CodeInvalidArgument, err) @@ -154,7 +154,7 @@ func (i *IPAMService) AcquireIP(_ context.Context, req *connect.Request[v1.Acqui }, }, nil } -func (i *IPAMService) ReleaseIP(_ context.Context, req *connect.Request[v1.ReleaseIPRequest]) (*connect.Response[v1.ReleaseIPResponse], error) { +func (i *IPAMService) ReleaseIP(ctx context.Context, req *connect.Request[v1.ReleaseIPRequest]) (*connect.Response[v1.ReleaseIPResponse], error) { i.log.Debugw("releaseip", "req", req) netip, err := netaddr.ParseIP(req.Msg.Ip) if err != nil { @@ -165,7 +165,7 @@ func (i *IPAMService) ReleaseIP(_ context.Context, req *connect.Request[v1.Relea IP: netip, ParentPrefix: req.Msg.PrefixCidr, } - resp, err := i.ipamer.ReleaseIP(ip) + resp, err := i.ipamer.ReleaseIP(ctx, ip) if err != nil { i.log.Errorw("releaseip", "error", err) return nil, connect.NewError(connect.CodeInvalidArgument, err) @@ -179,9 +179,9 @@ func (i *IPAMService) ReleaseIP(_ context.Context, req *connect.Request[v1.Relea }, }, nil } -func (i *IPAMService) Dump(_ context.Context, req *connect.Request[v1.DumpRequest]) (*connect.Response[v1.DumpResponse], error) { +func (i *IPAMService) Dump(ctx context.Context, req *connect.Request[v1.DumpRequest]) (*connect.Response[v1.DumpResponse], error) { i.log.Debugw("dump", "req", req) - dump, err := i.ipamer.Dump() + dump, err := i.ipamer.Dump(ctx) if err != nil { i.log.Errorw("dump", "error", err) return nil, connect.NewError(connect.CodeInvalidArgument, err) @@ -193,9 +193,9 @@ func (i *IPAMService) Dump(_ context.Context, req *connect.Request[v1.DumpReques }, nil } -func (i *IPAMService) Load(_ context.Context, req *connect.Request[v1.LoadRequest]) (*connect.Response[v1.LoadResponse], error) { +func (i *IPAMService) Load(ctx context.Context, req *connect.Request[v1.LoadRequest]) (*connect.Response[v1.LoadResponse], error) { i.log.Debugw("load", "req", req) - err := i.ipamer.Load(req.Msg.Dump) + err := i.ipamer.Load(ctx, req.Msg.Dump) if err != nil { i.log.Errorw("dump", "error", err) return nil, connect.NewError(connect.CodeInvalidArgument, err) diff --git a/pkg/service/test/ipam-service_benchmark_test.go b/pkg/service/test/ipam-service_benchmark_test.go index 1741339..0524197 100644 --- a/pkg/service/test/ipam-service_benchmark_test.go +++ b/pkg/service/test/ipam-service_benchmark_test.go @@ -17,7 +17,7 @@ import ( // BenchmarkGrpcImpact located in a separate package to prevent import cycles. func BenchmarkGrpcImpact(b *testing.B) { - + ctx := context.Background() ipam := goipam.New() // Get client and server options for all compressors... @@ -52,14 +52,14 @@ func BenchmarkGrpcImpact(b *testing.B) { { name: "library", f: func() { - p, err := ipam.NewPrefix("192.168.0.0/24") + p, err := ipam.NewPrefix(ctx, "192.168.0.0/24") if err != nil { panic(err) } if p == nil { panic("Prefix nil") } - _, err = ipam.DeletePrefix(p.Cidr) + _, err = ipam.DeletePrefix(ctx, p.Cidr) if err != nil { panic(err) } @@ -68,7 +68,7 @@ func BenchmarkGrpcImpact(b *testing.B) { { name: "grpc", f: func() { - p, err := grpc.CreatePrefix(context.Background(), connect.NewRequest(&v1.CreatePrefixRequest{ + p, err := grpc.CreatePrefix(ctx, connect.NewRequest(&v1.CreatePrefixRequest{ Cidr: "192.169.0.0/24", })) if err != nil { @@ -77,7 +77,7 @@ func BenchmarkGrpcImpact(b *testing.B) { if p == nil { panic("Prefix nil") } - _, err = grpc.DeletePrefix(context.Background(), connect.NewRequest(&v1.DeletePrefixRequest{ + _, err = grpc.DeletePrefix(ctx, connect.NewRequest(&v1.DeletePrefixRequest{ Cidr: "192.169.0.0/24", })) if err != nil { @@ -88,7 +88,7 @@ func BenchmarkGrpcImpact(b *testing.B) { { name: "http", f: func() { - p, err := httpclient.CreatePrefix(context.Background(), connect.NewRequest(&v1.CreatePrefixRequest{ + p, err := httpclient.CreatePrefix(ctx, connect.NewRequest(&v1.CreatePrefixRequest{ Cidr: "192.169.0.0/24", })) if err != nil { @@ -97,7 +97,7 @@ func BenchmarkGrpcImpact(b *testing.B) { if p == nil { panic("Prefix nil") } - _, err = httpclient.DeletePrefix(context.Background(), connect.NewRequest(&v1.DeletePrefixRequest{ + _, err = httpclient.DeletePrefix(ctx, connect.NewRequest(&v1.DeletePrefixRequest{ Cidr: "192.169.0.0/24", })) if err != nil { diff --git a/prefix.go b/prefix.go index b86bc03..03e9bd1 100644 --- a/prefix.go +++ b/prefix.go @@ -2,6 +2,7 @@ package ipam import ( "bytes" + "context" "encoding/gob" "errors" "fmt" @@ -141,10 +142,10 @@ type Usage struct { AcquiredPrefixes uint64 } -func (i *ipamer) NewPrefix(cidr string) (*Prefix, error) { +func (i *ipamer) NewPrefix(ctx context.Context, cidr string) (*Prefix, error) { i.mu.Lock() defer i.mu.Unlock() - existingPrefixes, err := i.storage.ReadAllPrefixCidrs() + existingPrefixes, err := i.storage.ReadAllPrefixCidrs(ctx) if err != nil { return nil, err } @@ -156,7 +157,7 @@ func (i *ipamer) NewPrefix(cidr string) (*Prefix, error) { if err != nil { return nil, err } - newPrefix, err := i.storage.CreatePrefix(*p) + newPrefix, err := i.storage.CreatePrefix(ctx, *p) if err != nil { return nil, err } @@ -164,15 +165,15 @@ func (i *ipamer) NewPrefix(cidr string) (*Prefix, error) { return &newPrefix, nil } -func (i *ipamer) DeletePrefix(cidr string) (*Prefix, error) { - p := i.PrefixFrom(cidr) +func (i *ipamer) DeletePrefix(ctx context.Context, cidr string) (*Prefix, error) { + p := i.PrefixFrom(ctx, cidr) if p == nil { return nil, fmt.Errorf("%w: delete prefix:%s", ErrNotFound, cidr) } if p.hasIPs() { return nil, fmt.Errorf("prefix %s has ips, delete prefix not possible", p.Cidr) } - prefix, err := i.storage.DeletePrefix(*p) + prefix, err := i.storage.DeletePrefix(ctx, *p) if err != nil { return nil, fmt.Errorf("delete prefix:%s %w", cidr, err) } @@ -180,29 +181,29 @@ func (i *ipamer) DeletePrefix(cidr string) (*Prefix, error) { return &prefix, nil } -func (i *ipamer) AcquireChildPrefix(parentCidr string, length uint8) (*Prefix, error) { +func (i *ipamer) AcquireChildPrefix(ctx context.Context, parentCidr string, length uint8) (*Prefix, error) { var prefix *Prefix return prefix, retryOnOptimisticLock(func() error { var err error - prefix, err = i.acquireChildPrefixInternal(parentCidr, "", length) + prefix, err = i.acquireChildPrefixInternal(ctx, parentCidr, "", length) return err }) } -func (i *ipamer) AcquireSpecificChildPrefix(parentCidr, childCidr string) (*Prefix, error) { +func (i *ipamer) AcquireSpecificChildPrefix(ctx context.Context, parentCidr, childCidr string) (*Prefix, error) { var prefix *Prefix return prefix, retryOnOptimisticLock(func() error { var err error - prefix, err = i.acquireChildPrefixInternal(parentCidr, childCidr, 0) + prefix, err = i.acquireChildPrefixInternal(ctx, parentCidr, childCidr, 0) return err }) } // acquireChildPrefixInternal will return a Prefix with a smaller length from the given Prefix. -func (i *ipamer) acquireChildPrefixInternal(parentCidr, childCidr string, length uint8) (*Prefix, error) { +func (i *ipamer) acquireChildPrefixInternal(ctx context.Context, parentCidr, childCidr string, length uint8) (*Prefix, error) { specificChildRequest := childCidr != "" var childprefix netaddr.IPPrefix - parent := i.PrefixFrom(parentCidr) + parent := i.PrefixFrom(ctx, parentCidr) if parent == nil { return nil, fmt.Errorf("unable to find prefix for cidr:%s", parentCidr) } @@ -279,7 +280,7 @@ func (i *ipamer) acquireChildPrefixInternal(parentCidr, childCidr string, length parent.availableChildPrefixes[child.Cidr] = false parent.isParent = true - _, err = i.storage.UpdatePrefix(*parent) + _, err = i.storage.UpdatePrefix(ctx, *parent) if err != nil { return nil, fmt.Errorf("unable to update parent prefix:%v error:%w", parent, err) } @@ -287,7 +288,7 @@ func (i *ipamer) acquireChildPrefixInternal(parentCidr, childCidr string, length if err != nil { return nil, fmt.Errorf("unable to persist created child:%w", err) } - _, err = i.storage.CreatePrefix(*child) + _, err = i.storage.CreatePrefix(ctx, *child) if err != nil { return nil, fmt.Errorf("unable to update parent prefix:%v error:%w", child, err) } @@ -295,15 +296,15 @@ func (i *ipamer) acquireChildPrefixInternal(parentCidr, childCidr string, length return child, nil } -func (i *ipamer) ReleaseChildPrefix(child *Prefix) error { +func (i *ipamer) ReleaseChildPrefix(ctx context.Context, child *Prefix) error { return retryOnOptimisticLock(func() error { - return i.releaseChildPrefixInternal(child) + return i.releaseChildPrefixInternal(ctx, child) }) } // releaseChildPrefixInternal will mark this child Prefix as available again. -func (i *ipamer) releaseChildPrefixInternal(child *Prefix) error { - parent := i.PrefixFrom(child.ParentCidr) +func (i *ipamer) releaseChildPrefixInternal(ctx context.Context, child *Prefix) error { + parent := i.PrefixFrom(ctx, child.ParentCidr) if parent == nil { return fmt.Errorf("prefix %s is no child prefix", child.Cidr) @@ -313,34 +314,34 @@ func (i *ipamer) releaseChildPrefixInternal(child *Prefix) error { } parent.availableChildPrefixes[child.Cidr] = true - _, err := i.DeletePrefix(child.Cidr) + _, err := i.DeletePrefix(ctx, child.Cidr) if err != nil { return fmt.Errorf("unable to release prefix %v:%w", child, err) } - _, err = i.storage.UpdatePrefix(*parent) + _, err = i.storage.UpdatePrefix(ctx, *parent) if err != nil { return fmt.Errorf("unable to release prefix %v:%w", child, err) } return nil } -func (i *ipamer) PrefixFrom(cidr string) *Prefix { +func (i *ipamer) PrefixFrom(ctx context.Context, cidr string) *Prefix { ipprefix, err := netaddr.ParseIPPrefix(cidr) if err != nil { return nil } - prefix, err := i.storage.ReadPrefix(ipprefix.Masked().String()) + prefix, err := i.storage.ReadPrefix(ctx, ipprefix.Masked().String()) if err != nil { return nil } return &prefix } -func (i *ipamer) AcquireSpecificIP(prefixCidr, specificIP string) (*IP, error) { +func (i *ipamer) AcquireSpecificIP(ctx context.Context, prefixCidr, specificIP string) (*IP, error) { var ip *IP return ip, retryOnOptimisticLock(func() error { var err error - ip, err = i.acquireSpecificIPInternal(prefixCidr, specificIP) + ip, err = i.acquireSpecificIPInternal(ctx, prefixCidr, specificIP) return err }) } @@ -349,8 +350,8 @@ func (i *ipamer) AcquireSpecificIP(prefixCidr, specificIP string) (*IP, error) { // If specificIP is empty, the next free IP is returned. // If there is no free IP an NoIPAvailableError is returned. // If the Prefix is not found an NotFoundError is returned. -func (i *ipamer) acquireSpecificIPInternal(prefixCidr, specificIP string) (*IP, error) { - prefix := i.PrefixFrom(prefixCidr) +func (i *ipamer) acquireSpecificIPInternal(ctx context.Context, prefixCidr, specificIP string) (*IP, error) { + prefix := i.PrefixFrom(ctx, prefixCidr) if prefix == nil { return nil, fmt.Errorf("%w: unable to find prefix for cidr:%s", ErrNotFound, prefixCidr) } @@ -389,7 +390,7 @@ func (i *ipamer) acquireSpecificIPInternal(prefixCidr, specificIP string) (*IP, ParentPrefix: prefix.Cidr, } prefix.ips[ipstring] = true - _, err := i.storage.UpdatePrefix(*prefix) + _, err := i.storage.UpdatePrefix(ctx, *prefix) if err != nil { return nil, fmt.Errorf("unable to persist acquired ip:%v error:%w", prefix, err) } @@ -400,25 +401,25 @@ func (i *ipamer) acquireSpecificIPInternal(prefixCidr, specificIP string) (*IP, return nil, fmt.Errorf("%w: no more ips in prefix: %s left, length of prefix.ips: %d", ErrNoIPAvailable, prefix.Cidr, len(prefix.ips)) } -func (i *ipamer) AcquireIP(prefixCidr string) (*IP, error) { - return i.AcquireSpecificIP(prefixCidr, "") +func (i *ipamer) AcquireIP(ctx context.Context, prefixCidr string) (*IP, error) { + return i.AcquireSpecificIP(ctx, prefixCidr, "") } -func (i *ipamer) ReleaseIP(ip *IP) (*Prefix, error) { - err := i.ReleaseIPFromPrefix(ip.ParentPrefix, ip.IP.String()) - prefix := i.PrefixFrom(ip.ParentPrefix) +func (i *ipamer) ReleaseIP(ctx context.Context, ip *IP) (*Prefix, error) { + err := i.ReleaseIPFromPrefix(ctx, ip.ParentPrefix, ip.IP.String()) + prefix := i.PrefixFrom(ctx, ip.ParentPrefix) return prefix, err } -func (i *ipamer) ReleaseIPFromPrefix(prefixCidr, ip string) error { +func (i *ipamer) ReleaseIPFromPrefix(ctx context.Context, prefixCidr, ip string) error { return retryOnOptimisticLock(func() error { - return i.releaseIPFromPrefixInternal(prefixCidr, ip) + return i.releaseIPFromPrefixInternal(ctx, prefixCidr, ip) }) } // releaseIPFromPrefixInternal will release the given IP for later usage. -func (i *ipamer) releaseIPFromPrefixInternal(prefixCidr, ip string) error { - prefix := i.PrefixFrom(prefixCidr) +func (i *ipamer) releaseIPFromPrefixInternal(ctx context.Context, prefixCidr, ip string) error { + prefix := i.PrefixFrom(ctx, prefixCidr) if prefix == nil { return fmt.Errorf("%w: unable to find prefix for cidr:%s", ErrNotFound, prefixCidr) } @@ -427,7 +428,7 @@ func (i *ipamer) releaseIPFromPrefixInternal(prefixCidr, ip string) error { return fmt.Errorf("%w: unable to release ip:%s because it is not allocated in prefix:%s", ErrNotFound, ip, prefixCidr) } delete(prefix.ips, ip) - _, err := i.storage.UpdatePrefix(*prefix) + _, err := i.storage.UpdatePrefix(ctx, *prefix) if err != nil { return fmt.Errorf("unable to release ip %v:%w", ip, err) } @@ -486,8 +487,8 @@ func (i *ipamer) newPrefix(cidr, parentCidr string) (*Prefix, error) { return p, nil } -func (i *ipamer) Dump() (string, error) { - pfxs, err := i.storage.ReadAllPrefixes() +func (i *ipamer) Dump(ctx context.Context) (string, error) { + pfxs, err := i.storage.ReadAllPrefixes(ctx) if err != nil { return "", err } @@ -498,8 +499,8 @@ func (i *ipamer) Dump() (string, error) { return string(js), nil } -func (i *ipamer) Load(dump string) error { - existingpfxs, err := i.storage.ReadAllPrefixes() +func (i *ipamer) Load(ctx context.Context, dump string) error { + existingpfxs, err := i.storage.ReadAllPrefixes(ctx) if err != nil { return err } @@ -510,12 +511,12 @@ func (i *ipamer) Load(dump string) error { if err != nil { return err } - err = i.storage.DeleteAllPrefixes() + err = i.storage.DeleteAllPrefixes(ctx) if err != nil { return err } for _, pfx := range pfxs { - _, err = i.storage.CreatePrefix(pfx) + _, err = i.storage.CreatePrefix(ctx, pfx) if err != nil { return err } @@ -524,8 +525,8 @@ func (i *ipamer) Load(dump string) error { } // ReadAllPrefixCidrs retrieves all existing Prefix CIDRs from the underlying storage -func (i *ipamer) ReadAllPrefixCidrs() ([]string, error) { - return i.storage.ReadAllPrefixCidrs() +func (i *ipamer) ReadAllPrefixCidrs(ctx context.Context) ([]string, error) { + return i.storage.ReadAllPrefixCidrs(ctx) } func (p *Prefix) String() string { diff --git a/prefix_benchmark_test.go b/prefix_benchmark_test.go index 5ba8990..f137c58 100644 --- a/prefix_benchmark_test.go +++ b/prefix_benchmark_test.go @@ -1,21 +1,23 @@ package ipam import ( + "context" "fmt" "testing" ) func BenchmarkNewPrefix(b *testing.B) { + ctx := context.Background() benchWithBackends(b, func(b *testing.B, ipam *ipamer) { for n := 0; n < b.N; n++ { - p, err := ipam.NewPrefix("192.168.0.0/24") + p, err := ipam.NewPrefix(ctx, "192.168.0.0/24") if err != nil { panic(err) } if p == nil { panic("Prefix nil") } - _, err = ipam.DeletePrefix(p.Cidr) + _, err = ipam.DeletePrefix(ctx, p.Cidr) if err != nil { panic(err) } @@ -24,26 +26,27 @@ func BenchmarkNewPrefix(b *testing.B) { } func BenchmarkAcquireIP(b *testing.B) { + ctx := context.Background() testCidr := "10.0.0.0/16" benchWithBackends(b, func(b *testing.B, ipam *ipamer) { - p, err := ipam.NewPrefix(testCidr) + p, err := ipam.NewPrefix(ctx, testCidr) if err != nil { panic(err) } for n := 0; n < b.N; n++ { - ip, err := ipam.AcquireIP(p.Cidr) + ip, err := ipam.AcquireIP(ctx, p.Cidr) if err != nil { panic(err) } if ip == nil { panic("IP nil") } - p, err = ipam.ReleaseIP(ip) + p, err = ipam.ReleaseIP(ctx, ip) if err != nil { panic(err) } } - _, err = ipam.DeletePrefix(testCidr) + _, err = ipam.DeletePrefix(ctx, testCidr) if err != nil { b.Fatalf("error deleting prefix:%v", err) } @@ -51,6 +54,7 @@ func BenchmarkAcquireIP(b *testing.B) { } func BenchmarkAcquireChildPrefix(b *testing.B) { + ctx := context.Background() benchmarks := []struct { name string parentLength uint8 @@ -64,21 +68,21 @@ func BenchmarkAcquireChildPrefix(b *testing.B) { for _, bm := range benchmarks { test := bm benchWithBackends(b, func(b *testing.B, ipam *ipamer) { - p, err := ipam.NewPrefix(fmt.Sprintf("192.168.0.0/%d", test.parentLength)) + p, err := ipam.NewPrefix(ctx, fmt.Sprintf("192.168.0.0/%d", test.parentLength)) if err != nil { panic(err) } for n := 0; n < b.N; n++ { - p, err := ipam.AcquireChildPrefix(p.Cidr, test.childLength) + p, err := ipam.AcquireChildPrefix(ctx, p.Cidr, test.childLength) if err != nil { panic(err) } - err = ipam.ReleaseChildPrefix(p) + err = ipam.ReleaseChildPrefix(ctx, p) if err != nil { panic(err) } } - _, err = ipam.DeletePrefix(p.Cidr) + _, err = ipam.DeletePrefix(ctx, p.Cidr) if err != nil { b.Fatalf("error deleting prefix:%v", err) } diff --git a/prefix_test.go b/prefix_test.go index 6804771..337ae45 100644 --- a/prefix_test.go +++ b/prefix_test.go @@ -25,17 +25,17 @@ func mustIP(s string) netaddr.IP { // getHostAddresses will return all possible ipadresses a host can get in the given prefix. // The IPs will be acquired by this method, so that the prefix has no free IPs afterwards. -func (i *ipamer) getHostAddresses(prefix string) ([]string, error) { +func (i *ipamer) getHostAddresses(ctx context.Context, prefix string) ([]string, error) { hostAddresses := []string{} - p, err := i.NewPrefix(prefix) + p, err := i.NewPrefix(ctx, prefix) if err != nil { return hostAddresses, err } // loop till AcquireIP signals that it has no ips left for { - ip, err := i.AcquireIP(p.Cidr) + ip, err := i.AcquireIP(ctx, p.Cidr) if errors.Is(err, ErrNoIPAvailable) { return hostAddresses, nil } @@ -47,18 +47,20 @@ func (i *ipamer) getHostAddresses(prefix string) ([]string, error) { } func TestIPRangeOverlapping(t *testing.T) { + ctx := context.Background() i := New() cidr := "10.10.10.0/24" - _, err := i.NewPrefix(cidr) + _, err := i.NewPrefix(ctx, cidr) require.Nil(t, err) cidr = "10.10.10.1/24" - _, err = i.NewPrefix(cidr) + _, err = i.NewPrefix(ctx, cidr) require.NotNil(t, err) } func TestIpamer_AcquireIP(t *testing.T) { + ctx := context.Background() type fields struct { prefixCIDR string @@ -136,7 +138,7 @@ func TestIpamer_AcquireIP(t *testing.T) { test := tt testWithBackends(t, func(t *testing.T, ipam *ipamer) { - p, err := ipam.NewPrefix(test.fields.prefixCIDR) + p, err := ipam.NewPrefix(ctx, test.fields.prefixCIDR) if err != nil { t.Errorf("Could not create prefix: %v", err) } @@ -146,11 +148,11 @@ func TestIpamer_AcquireIP(t *testing.T) { } var updatedPrefix Prefix - updatedPrefix, err = ipam.storage.UpdatePrefix(*p) + updatedPrefix, err = ipam.storage.UpdatePrefix(ctx, *p) if err != nil { t.Errorf("Could not update prefix: %v", err) } - got, _ := ipam.AcquireIP(updatedPrefix.Cidr) + got, _ := ipam.AcquireIP(ctx, updatedPrefix.Cidr) if test.want == nil || got == nil { if !reflect.DeepEqual(got, test.want) { t.Errorf("Ipamer.AcquireIP() want or got is nil, got %v, want %v", got, test.want) @@ -165,19 +167,20 @@ func TestIpamer_AcquireIP(t *testing.T) { } func TestIpamer_ReleaseIPFromPrefixIPv4(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { - prefix, err := ipam.NewPrefix("192.168.0.0/24") + prefix, err := ipam.NewPrefix(ctx, "192.168.0.0/24") require.Nil(t, err) require.NotNil(t, prefix) - err = ipam.ReleaseIPFromPrefix(prefix.Cidr, "1.2.3.4") + err = ipam.ReleaseIPFromPrefix(ctx, prefix.Cidr, "1.2.3.4") require.NotNil(t, err) require.True(t, errors.As(err, &NotFoundError{}), "error must be of correct type") require.True(t, errors.Is(err, ErrNotFound), "error must be NotFound") require.Equal(t, "NotFound: unable to release ip:1.2.3.4 because it is not allocated in prefix:192.168.0.0/24", err.Error()) - err = ipam.ReleaseIPFromPrefix("4.5.6.7/23", "1.2.3.4") + err = ipam.ReleaseIPFromPrefix(ctx, "4.5.6.7/23", "1.2.3.4") require.NotNil(t, err) require.True(t, errors.As(err, &NotFoundError{}), "error must be of correct type") require.True(t, errors.Is(err, ErrNotFound), "error must be NotFound") @@ -186,25 +189,26 @@ func TestIpamer_ReleaseIPFromPrefixIPv4(t *testing.T) { } func TestIpamer_ReleaseIPFromPrefixIPv6(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { - prefix, err := ipam.NewPrefix("2001:0db8:85a3::/120") + prefix, err := ipam.NewPrefix(ctx, "2001:0db8:85a3::/120") require.Nil(t, err) require.NotNil(t, prefix) - err = ipam.ReleaseIPFromPrefix(prefix.Cidr, "1.2.3.4") + err = ipam.ReleaseIPFromPrefix(ctx, prefix.Cidr, "1.2.3.4") require.NotNil(t, err) require.True(t, errors.As(err, &NotFoundError{}), "error must be of correct type") require.True(t, errors.Is(err, ErrNotFound), "error must be NotFound") require.Equal(t, "NotFound: unable to release ip:1.2.3.4 because it is not allocated in prefix:2001:db8:85a3::/120", err.Error()) - err = ipam.ReleaseIPFromPrefix(prefix.Cidr, "1001:0db8:85a3::1") + err = ipam.ReleaseIPFromPrefix(ctx, prefix.Cidr, "1001:0db8:85a3::1") require.NotNil(t, err) require.True(t, errors.As(err, &NotFoundError{}), "error must be of correct type") require.True(t, errors.Is(err, ErrNotFound), "error must be NotFound") require.Equal(t, "NotFound: unable to release ip:1001:0db8:85a3::1 because it is not allocated in prefix:2001:db8:85a3::/120", err.Error()) - err = ipam.ReleaseIPFromPrefix("1001:0db8:85a3::/120", "1.2.3.4") + err = ipam.ReleaseIPFromPrefix(ctx, "1001:0db8:85a3::/120", "1.2.3.4") require.NotNil(t, err) require.True(t, errors.As(err, &NotFoundError{}), "error must be of correct type") require.True(t, errors.Is(err, ErrNotFound), "error must be NotFound") @@ -212,99 +216,100 @@ func TestIpamer_ReleaseIPFromPrefixIPv6(t *testing.T) { }) } func TestIpamer_AcquireSpecificIP(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { // IPv4 - prefix, err := ipam.NewPrefix("192.168.99.0/24") + prefix, err := ipam.NewPrefix(ctx, "192.168.99.0/24") require.Nil(t, err) require.Equal(t, prefix.availableips(), uint64(256)) // network an broadcast are blocked require.Equal(t, prefix.acquiredips(), uint64(2)) - ip1, err := ipam.AcquireSpecificIP(prefix.Cidr, "192.168.99.1") + ip1, err := ipam.AcquireSpecificIP(ctx, prefix.Cidr, "192.168.99.1") require.Nil(t, err) require.NotNil(t, ip1) - prefix = ipam.PrefixFrom(prefix.Cidr) + prefix = ipam.PrefixFrom(ctx, prefix.Cidr) require.Equal(t, prefix.availableips(), uint64(256)) require.Equal(t, prefix.acquiredips(), uint64(3)) - ip2, err := ipam.AcquireSpecificIP(prefix.Cidr, "192.168.99.2") + ip2, err := ipam.AcquireSpecificIP(ctx, prefix.Cidr, "192.168.99.2") require.Nil(t, err) require.NotEqual(t, ip1, ip2) - prefix = ipam.PrefixFrom(prefix.Cidr) + prefix = ipam.PrefixFrom(ctx, prefix.Cidr) require.Equal(t, prefix.availableips(), uint64(256)) require.Equal(t, prefix.acquiredips(), uint64(4)) require.Equal(t, "192.168.99.1", ip1.IP.String()) require.Equal(t, "192.168.99.2", ip2.IP.String()) // Wish IP out of prefix - ip3, err := ipam.AcquireSpecificIP(prefix.Cidr, "192.168.98.2") + ip3, err := ipam.AcquireSpecificIP(ctx, prefix.Cidr, "192.168.98.2") require.Nil(t, ip3) require.NotNil(t, err) require.Equal(t, "given ip:192.168.98.2 is not in 192.168.99.0/24", err.Error()) // Wish IP is invalid - ip4, err := ipam.AcquireSpecificIP(prefix.Cidr, "192.168.99.1.invalid") + ip4, err := ipam.AcquireSpecificIP(ctx, prefix.Cidr, "192.168.99.1.invalid") require.Nil(t, ip4) require.NotNil(t, err) require.Equal(t, "given ip:192.168.99.1.invalid in not valid", err.Error()) // Cidr is invalid - ip5, err := ipam.AcquireSpecificIP("3.4.5.6/27", "192.168.99.1.invalid") + ip5, err := ipam.AcquireSpecificIP(ctx, "3.4.5.6/27", "192.168.99.1.invalid") require.Nil(t, ip5) require.NotNil(t, err) require.True(t, errors.As(err, &NotFoundError{}), "error must be of correct type") require.True(t, errors.Is(err, ErrNotFound), "error must be NotFound") require.Equal(t, "NotFound: unable to find prefix for cidr:3.4.5.6/27", err.Error()) - prefix, err = ipam.ReleaseIP(ip1) + prefix, err = ipam.ReleaseIP(ctx, ip1) require.Nil(t, err) require.Equal(t, prefix.availableips(), uint64(256)) require.Equal(t, prefix.acquiredips(), uint64(3)) - prefix, err = ipam.ReleaseIP(ip2) + prefix, err = ipam.ReleaseIP(ctx, ip2) require.Nil(t, err) require.Equal(t, prefix.availableips(), uint64(256)) require.Equal(t, prefix.acquiredips(), uint64(2)) // IPv6 - prefix, err = ipam.NewPrefix("2001:0db8:85a3::/120") + prefix, err = ipam.NewPrefix(ctx, "2001:0db8:85a3::/120") require.Nil(t, err) require.Equal(t, prefix.availableips(), uint64(256)) // network is blocked require.Equal(t, prefix.acquiredips(), uint64(1)) - ip1, err = ipam.AcquireSpecificIP(prefix.Cidr, "2001:db8:85a3::1") + ip1, err = ipam.AcquireSpecificIP(ctx, prefix.Cidr, "2001:db8:85a3::1") require.Nil(t, err) require.NotNil(t, ip1) - prefix = ipam.PrefixFrom(prefix.Cidr) + prefix = ipam.PrefixFrom(ctx, prefix.Cidr) require.Equal(t, prefix.availableips(), uint64(256)) require.Equal(t, prefix.acquiredips(), uint64(2)) - ip2, err = ipam.AcquireSpecificIP(prefix.Cidr, "2001:0db8:85a3::2") + ip2, err = ipam.AcquireSpecificIP(ctx, prefix.Cidr, "2001:0db8:85a3::2") require.Nil(t, err) require.NotEqual(t, ip1, ip2) - prefix = ipam.PrefixFrom(prefix.Cidr) + prefix = ipam.PrefixFrom(ctx, prefix.Cidr) require.Equal(t, prefix.availableips(), uint64(256)) require.Equal(t, prefix.acquiredips(), uint64(3)) require.Equal(t, "2001:db8:85a3::1", ip1.IP.String()) require.Equal(t, "2001:db8:85a3::2", ip2.IP.String()) // Wish IP out of prefix - ip3, err = ipam.AcquireSpecificIP(prefix.Cidr, "2001:0db8:85a4::1") + ip3, err = ipam.AcquireSpecificIP(ctx, prefix.Cidr, "2001:0db8:85a4::1") require.Nil(t, ip3) require.NotNil(t, err) require.Equal(t, "given ip:2001:0db8:85a4::1 is not in 2001:db8:85a3::/120", err.Error()) // Cidr is invalid - ip5, err = ipam.AcquireSpecificIP("2001:0db8:95a3::/120", "2001:0db8:95a3::invalid") + ip5, err = ipam.AcquireSpecificIP(ctx, "2001:0db8:95a3::/120", "2001:0db8:95a3::invalid") require.Nil(t, ip5) require.NotNil(t, err) require.True(t, errors.As(err, &NotFoundError{}), "error must be of correct type") require.True(t, errors.Is(err, ErrNotFound), "error must be NotFound") require.Equal(t, "NotFound: unable to find prefix for cidr:2001:0db8:95a3::/120", err.Error()) - prefix, err = ipam.ReleaseIP(ip1) + prefix, err = ipam.ReleaseIP(ctx, ip1) require.Nil(t, err) require.Equal(t, prefix.availableips(), uint64(256)) require.Equal(t, prefix.acquiredips(), uint64(2)) - prefix, err = ipam.ReleaseIP(ip2) + prefix, err = ipam.ReleaseIP(ctx, ip2) require.Nil(t, err) require.Equal(t, prefix.availableips(), uint64(256)) require.Equal(t, prefix.acquiredips(), uint64(1)) @@ -312,34 +317,35 @@ func TestIpamer_AcquireSpecificIP(t *testing.T) { } func TestIpamer_AcquireIPCountsIPv4(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { - prefix, err := ipam.NewPrefix("192.168.0.0/24") + prefix, err := ipam.NewPrefix(ctx, "192.168.0.0/24") require.Nil(t, err) require.Equal(t, prefix.availableips(), uint64(256)) // network and broadcast are blocked require.Equal(t, prefix.acquiredips(), uint64(2)) - ip1, err := ipam.AcquireIP(prefix.Cidr) + ip1, err := ipam.AcquireIP(ctx, prefix.Cidr) require.Nil(t, err) require.NotNil(t, ip1) - prefix = ipam.PrefixFrom(prefix.Cidr) + prefix = ipam.PrefixFrom(ctx, prefix.Cidr) require.Equal(t, prefix.availableips(), uint64(256)) require.Equal(t, prefix.acquiredips(), uint64(3)) - ip2, err := ipam.AcquireIP(prefix.Cidr) + ip2, err := ipam.AcquireIP(ctx, prefix.Cidr) require.Nil(t, err) require.NotEqual(t, ip1, ip2) - prefix = ipam.PrefixFrom(prefix.Cidr) + prefix = ipam.PrefixFrom(ctx, prefix.Cidr) require.Equal(t, prefix.availableips(), uint64(256)) require.Equal(t, prefix.acquiredips(), uint64(4)) require.True(t, strings.HasPrefix(ip1.IP.String(), "192.168.0")) require.True(t, strings.HasPrefix(ip2.IP.String(), "192.168.0")) - prefix, err = ipam.ReleaseIP(ip1) + prefix, err = ipam.ReleaseIP(ctx, ip1) require.Nil(t, err) require.Equal(t, prefix.availableips(), uint64(256)) require.Equal(t, prefix.acquiredips(), uint64(3)) - prefix, err = ipam.ReleaseIP(ip2) + prefix, err = ipam.ReleaseIP(ctx, ip2) require.Nil(t, err) require.Equal(t, prefix.availableips(), uint64(256)) require.Equal(t, prefix.acquiredips(), uint64(2)) @@ -347,34 +353,35 @@ func TestIpamer_AcquireIPCountsIPv4(t *testing.T) { } func TestIpamer_AcquireIPCountsIPv6(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { - prefix, err := ipam.NewPrefix("2001:0db8:85a3::/120") + prefix, err := ipam.NewPrefix(ctx, "2001:0db8:85a3::/120") require.Nil(t, err) require.Equal(t, prefix.availableips(), uint64(256)) // network is blocked require.Equal(t, prefix.acquiredips(), uint64(1)) - ip1, err := ipam.AcquireIP(prefix.Cidr) + ip1, err := ipam.AcquireIP(ctx, prefix.Cidr) require.Nil(t, err) require.NotNil(t, ip1) - prefix = ipam.PrefixFrom(prefix.Cidr) + prefix = ipam.PrefixFrom(ctx, prefix.Cidr) require.Equal(t, prefix.availableips(), uint64(256)) require.Equal(t, prefix.acquiredips(), uint64(2)) - ip2, err := ipam.AcquireIP(prefix.Cidr) + ip2, err := ipam.AcquireIP(ctx, prefix.Cidr) require.Nil(t, err) require.NotEqual(t, ip1, ip2) - prefix = ipam.PrefixFrom(prefix.Cidr) + prefix = ipam.PrefixFrom(ctx, prefix.Cidr) require.Equal(t, prefix.availableips(), uint64(256)) require.Equal(t, prefix.acquiredips(), uint64(3)) require.True(t, strings.HasPrefix(ip1.IP.String(), "2001:db8:85a3::")) require.True(t, strings.HasPrefix(ip2.IP.String(), "2001:db8:85a3::")) - prefix, err = ipam.ReleaseIP(ip1) + prefix, err = ipam.ReleaseIP(ctx, ip1) require.Nil(t, err) require.Equal(t, prefix.availableips(), uint64(256)) require.Equal(t, prefix.acquiredips(), uint64(2)) - prefix, err = ipam.ReleaseIP(ip2) + prefix, err = ipam.ReleaseIP(ctx, ip2) require.Nil(t, err) require.Equal(t, prefix.availableips(), uint64(256)) require.Equal(t, prefix.acquiredips(), uint64(1)) @@ -382,13 +389,14 @@ func TestIpamer_AcquireIPCountsIPv6(t *testing.T) { } func TestIpamer_AcquireChildPrefixFragmented(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { - allPrefixes, err := ipam.storage.ReadAllPrefixes() + allPrefixes, err := ipam.storage.ReadAllPrefixes(ctx) require.NoError(t, err) require.Equal(t, 0, len(allPrefixes)) // Create Prefix with /20 - prefix, err := ipam.NewPrefix("192.168.0.0/20") + prefix, err := ipam.NewPrefix(ctx, "192.168.0.0/20") require.NoError(t, err) s, _ := prefix.availablePrefixes() require.Equal(t, 1024, int(s)) @@ -396,20 +404,20 @@ func TestIpamer_AcquireChildPrefixFragmented(t *testing.T) { require.Equal(t, 0, int(prefix.Usage().AcquiredPrefixes)) // Acquire first half 192.168.0.0/21 = 192.168.0.0 - 192.168.7.254 - c1, err := ipam.AcquireChildPrefix(prefix.Cidr, 21) + c1, err := ipam.AcquireChildPrefix(ctx, prefix.Cidr, 21) require.NoError(t, err) require.NotNil(t, c1) - prefix = ipam.PrefixFrom(prefix.Cidr) + prefix = ipam.PrefixFrom(ctx, prefix.Cidr) s, _ = prefix.availablePrefixes() require.Equal(t, 512, int(s)) require.Equal(t, 1, int(prefix.acquiredPrefixes())) require.Equal(t, 1, int(prefix.Usage().AcquiredPrefixes)) // acquire 1/4the of the rest 192.168.8.0/22 = 192.168.8.0 - 192.168.11.254 - c2, err := ipam.AcquireChildPrefix(prefix.Cidr, 22) + c2, err := ipam.AcquireChildPrefix(ctx, prefix.Cidr, 22) require.NoError(t, err) require.NotNil(t, c2) - prefix = ipam.PrefixFrom(prefix.Cidr) + prefix = ipam.PrefixFrom(ctx, prefix.Cidr) s, a := prefix.availablePrefixes() // Next free must be 192.168.12.0/22 require.Equal(t, []string{"192.168.12.0/22"}, a) @@ -418,18 +426,18 @@ func TestIpamer_AcquireChildPrefixFragmented(t *testing.T) { require.Equal(t, 2, int(prefix.Usage().AcquiredPrefixes)) // acquire impossible size - _, err = ipam.AcquireChildPrefix(prefix.Cidr, 21) + _, err = ipam.AcquireChildPrefix(ctx, prefix.Cidr, 21) require.EqualError(t, err, "no prefix found in 192.168.0.0/20 with length:21, but 192.168.12.0/22 is available") // Release small, first half acquired - err = ipam.ReleaseChildPrefix(c2) + err = ipam.ReleaseChildPrefix(ctx, c2) require.NoError(t, err) // acquire /28 - c3, err := ipam.AcquireChildPrefix(prefix.Cidr, 28) + c3, err := ipam.AcquireChildPrefix(ctx, prefix.Cidr, 28) require.NoError(t, err) require.NotNil(t, c3) - prefix = ipam.PrefixFrom(prefix.Cidr) + prefix = ipam.PrefixFrom(ctx, prefix.Cidr) s, a = prefix.availablePrefixes() require.Equal(t, []string{"192.168.8.16/28", "192.168.8.32/27", "192.168.8.64/26", "192.168.8.128/25", "192.168.9.0/24", "192.168.10.0/23", "192.168.12.0/22"}, a) require.Equal(t, 508, int(s)) @@ -437,11 +445,11 @@ func TestIpamer_AcquireChildPrefixFragmented(t *testing.T) { require.Equal(t, 2, int(prefix.Usage().AcquiredPrefixes)) // acquire impossible size - _, err = ipam.AcquireChildPrefix(prefix.Cidr, 21) + _, err = ipam.AcquireChildPrefix(ctx, prefix.Cidr, 21) require.EqualError(t, err, "no prefix found in 192.168.0.0/20 with length:21, but 192.168.8.16/28,192.168.8.32/27,192.168.8.64/26,192.168.8.128/25,192.168.9.0/24,192.168.10.0/23,192.168.12.0/22 are available") // acquire a /22 which must be possible - c4, err := ipam.AcquireChildPrefix(prefix.Cidr, 22) + c4, err := ipam.AcquireChildPrefix(ctx, prefix.Cidr, 22) require.NoError(t, err) require.NotNil(t, c4) require.Equal(t, c4.String(), "192.168.12.0/22") @@ -450,13 +458,14 @@ func TestIpamer_AcquireChildPrefixFragmented(t *testing.T) { } func TestIpamer_AcquireChildPrefixCounts(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { - allPrefixes, err := ipam.storage.ReadAllPrefixes() + allPrefixes, err := ipam.storage.ReadAllPrefixes(ctx) require.Nil(t, err) require.Equal(t, 0, len(allPrefixes)) - prefix, err := ipam.NewPrefix("192.168.0.0/20") + prefix, err := ipam.NewPrefix(ctx, "192.168.0.0/20") require.Nil(t, err) s, _ := prefix.availablePrefixes() require.Equal(t, s, uint64(1024)) @@ -466,14 +475,14 @@ func TestIpamer_AcquireChildPrefixCounts(t *testing.T) { usage := prefix.Usage() require.Equal(t, "ip:2/4096", usage.String()) - allPrefixes, err = ipam.storage.ReadAllPrefixes() + allPrefixes, err = ipam.storage.ReadAllPrefixes(ctx) require.Nil(t, err) require.Equal(t, 1, len(allPrefixes)) - c1, err := ipam.AcquireChildPrefix(prefix.Cidr, 22) + c1, err := ipam.AcquireChildPrefix(ctx, prefix.Cidr, 22) require.Nil(t, err) require.NotNil(t, c1) - prefix = ipam.PrefixFrom(prefix.Cidr) + prefix = ipam.PrefixFrom(ctx, prefix.Cidr) s, _ = prefix.availablePrefixes() require.Equal(t, uint64(768), s) require.Equal(t, uint64(1), prefix.acquiredPrefixes()) @@ -482,14 +491,14 @@ func TestIpamer_AcquireChildPrefixCounts(t *testing.T) { usage = prefix.Usage() require.Equal(t, "ip:2/4096 prefixes alloc:1 avail:768", usage.String()) - allPrefixes, err = ipam.storage.ReadAllPrefixes() + allPrefixes, err = ipam.storage.ReadAllPrefixes(ctx) require.Nil(t, err) require.Equal(t, 2, len(allPrefixes)) - c2, err := ipam.AcquireChildPrefix(prefix.Cidr, 22) + c2, err := ipam.AcquireChildPrefix(ctx, prefix.Cidr, 22) require.Nil(t, err) require.NotNil(t, c2) - prefix = ipam.PrefixFrom(prefix.Cidr) + prefix = ipam.PrefixFrom(ctx, prefix.Cidr) s, _ = prefix.availablePrefixes() require.Equal(t, uint64(512), s) require.Equal(t, prefix.acquiredPrefixes(), uint64(2)) @@ -498,75 +507,76 @@ func TestIpamer_AcquireChildPrefixCounts(t *testing.T) { require.True(t, strings.HasSuffix(c2.Cidr, "/22")) require.True(t, strings.HasPrefix(c1.Cidr, "192.168.")) require.True(t, strings.HasPrefix(c2.Cidr, "192.168.")) - allPrefixes, err = ipam.storage.ReadAllPrefixes() + allPrefixes, err = ipam.storage.ReadAllPrefixes(ctx) require.Nil(t, err) require.Equal(t, 3, len(allPrefixes)) - err = ipam.ReleaseChildPrefix(c1) - prefix = ipam.PrefixFrom(prefix.Cidr) + err = ipam.ReleaseChildPrefix(ctx, c1) + prefix = ipam.PrefixFrom(ctx, prefix.Cidr) require.Nil(t, err) s, _ = prefix.availablePrefixes() require.Equal(t, uint64(768), s) require.Equal(t, uint64(1), prefix.acquiredPrefixes()) - allPrefixes, err = ipam.storage.ReadAllPrefixes() + allPrefixes, err = ipam.storage.ReadAllPrefixes(ctx) require.Nil(t, err) require.Equal(t, 2, len(allPrefixes)) - err = ipam.ReleaseChildPrefix(c2) - prefix = ipam.PrefixFrom(prefix.Cidr) + err = ipam.ReleaseChildPrefix(ctx, c2) + prefix = ipam.PrefixFrom(ctx, prefix.Cidr) require.Nil(t, err) s, _ = prefix.availablePrefixes() require.Equal(t, uint64(1024), s) require.Equal(t, uint64(0), prefix.acquiredPrefixes()) require.Equal(t, prefix.Usage().AcquiredPrefixes, uint64(0)) - allPrefixes, err = ipam.storage.ReadAllPrefixes() + allPrefixes, err = ipam.storage.ReadAllPrefixes(ctx) require.Nil(t, err) require.Equal(t, 1, len(allPrefixes)) - err = ipam.ReleaseChildPrefix(c1) + err = ipam.ReleaseChildPrefix(ctx, c1) require.Errorf(t, err, "unable to release prefix %s:delete prefix:%s not found", c1.Cidr) - c3, err := ipam.AcquireChildPrefix(prefix.Cidr, 22) + c3, err := ipam.AcquireChildPrefix(ctx, prefix.Cidr, 22) require.Nil(t, err) require.NotNil(t, c2) - ip1, err := ipam.AcquireIP(c3.Cidr) + ip1, err := ipam.AcquireIP(ctx, c3.Cidr) require.Nil(t, err) require.NotNil(t, ip1) - err = ipam.ReleaseChildPrefix(c3) + err = ipam.ReleaseChildPrefix(ctx, c3) require.Errorf(t, err, "prefix %s has ips, deletion not possible", c3.Cidr) - c3, err = ipam.ReleaseIP(ip1) + c3, err = ipam.ReleaseIP(ctx, ip1) require.Nil(t, err) - err = ipam.ReleaseChildPrefix(c3) + err = ipam.ReleaseChildPrefix(ctx, c3) require.Nil(t, err) - allPrefixes, err = ipam.storage.ReadAllPrefixes() + allPrefixes, err = ipam.storage.ReadAllPrefixes(ctx) require.Nil(t, err) require.Equal(t, 1, len(allPrefixes)) }) } func TestIpamer_AcquireChildPrefixIPv4(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { - prefix, err := ipam.NewPrefix("192.168.0.0/20") + prefix, err := ipam.NewPrefix(ctx, "192.168.0.0/20") require.Nil(t, err) s, _ := prefix.availablePrefixes() require.Equal(t, uint64(1024), s) require.Equal(t, prefix.acquiredPrefixes(), uint64(0)) // Same length - cp, err := ipam.AcquireChildPrefix(prefix.Cidr, 20) + cp, err := ipam.AcquireChildPrefix(ctx, prefix.Cidr, 20) require.NotNil(t, err) require.Equal(t, "given length:20 must be greater than prefix length:20", err.Error()) require.Nil(t, cp) // working length - cp, err = ipam.AcquireChildPrefix(prefix.Cidr, 21) + cp, err = ipam.AcquireChildPrefix(ctx, prefix.Cidr, 21) require.Nil(t, err) require.NotNil(t, cp) require.True(t, strings.HasPrefix(cp.Cidr, "192.168.")) @@ -574,67 +584,68 @@ func TestIpamer_AcquireChildPrefixIPv4(t *testing.T) { require.Equal(t, prefix.Cidr, cp.ParentCidr) // No more ChildPrefixes - cp, err = ipam.AcquireChildPrefix(prefix.Cidr, 21) + cp, err = ipam.AcquireChildPrefix(ctx, prefix.Cidr, 21) require.Nil(t, err) require.NotNil(t, cp) - cp, err = ipam.AcquireChildPrefix(prefix.Cidr, 21) + cp, err = ipam.AcquireChildPrefix(ctx, prefix.Cidr, 21) require.NotNil(t, err) require.Equal(t, "no prefix found in 192.168.0.0/20 with length:21", err.Error()) require.Nil(t, cp) // Prefix has ips - p2, err := ipam.NewPrefix("10.0.0.0/24") + p2, err := ipam.NewPrefix(ctx, "10.0.0.0/24") require.Nil(t, err) s, _ = p2.availablePrefixes() require.Equal(t, uint64(64), s) require.Equal(t, p2.acquiredPrefixes(), uint64(0)) - ip, err := ipam.AcquireIP(p2.Cidr) + ip, err := ipam.AcquireIP(ctx, p2.Cidr) require.Nil(t, err) require.NotNil(t, ip) - cp2, err := ipam.AcquireChildPrefix(p2.Cidr, 25) + cp2, err := ipam.AcquireChildPrefix(ctx, p2.Cidr, 25) require.NotNil(t, err) require.Equal(t, "prefix 10.0.0.0/24 has ips, acquire child prefix not possible", err.Error()) require.Nil(t, cp2) // Prefix has Childs, AcquireIP wont work - p3, err := ipam.NewPrefix("172.17.0.0/24") + p3, err := ipam.NewPrefix(ctx, "172.17.0.0/24") require.Nil(t, err) s, _ = p3.availablePrefixes() require.Equal(t, uint64(64), s) require.Equal(t, p3.acquiredPrefixes(), uint64(0)) - cp3, err := ipam.AcquireChildPrefix(p3.Cidr, 25) + cp3, err := ipam.AcquireChildPrefix(ctx, p3.Cidr, 25) require.Nil(t, err) require.NotNil(t, cp3) - p3 = ipam.PrefixFrom(p3.Cidr) - ip, err = ipam.AcquireIP(p3.Cidr) + p3 = ipam.PrefixFrom(ctx, p3.Cidr) + ip, err = ipam.AcquireIP(ctx, p3.Cidr) require.NotNil(t, err) require.Equal(t, "prefix 172.17.0.0/24 has childprefixes, acquire ip not possible", err.Error()) require.Nil(t, ip) // Release Parent Prefix must not work - err = ipam.ReleaseChildPrefix(p3) + err = ipam.ReleaseChildPrefix(ctx, p3) require.NotNil(t, err) require.Equal(t, "prefix 172.17.0.0/24 is no child prefix", err.Error()) }) } func TestIpamer_AcquireChildPrefixIPv6(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { - prefix, err := ipam.NewPrefix("2001:0db8:85a3::/116") + prefix, err := ipam.NewPrefix(ctx, "2001:0db8:85a3::/116") require.Nil(t, err) s, _ := prefix.availablePrefixes() require.Equal(t, uint64(1024), s) require.Equal(t, prefix.acquiredPrefixes(), uint64(0)) // Same length - cp, err := ipam.AcquireChildPrefix(prefix.Cidr, 116) + cp, err := ipam.AcquireChildPrefix(ctx, prefix.Cidr, 116) require.NotNil(t, err) require.Equal(t, "given length:116 must be greater than prefix length:116", err.Error()) require.Nil(t, cp) // working length - cp, err = ipam.AcquireChildPrefix(prefix.Cidr, 117) + cp, err = ipam.AcquireChildPrefix(ctx, prefix.Cidr, 117) require.Nil(t, err) require.NotNil(t, cp) require.True(t, strings.HasPrefix(cp.Cidr, "2001:db8:85a3:")) @@ -642,105 +653,106 @@ func TestIpamer_AcquireChildPrefixIPv6(t *testing.T) { require.Equal(t, prefix.Cidr, cp.ParentCidr) // No more ChildPrefixes - cp, err = ipam.AcquireChildPrefix(prefix.Cidr, 117) + cp, err = ipam.AcquireChildPrefix(ctx, prefix.Cidr, 117) require.Nil(t, err) require.NotNil(t, cp) - cp, err = ipam.AcquireChildPrefix(prefix.Cidr, 117) + cp, err = ipam.AcquireChildPrefix(ctx, prefix.Cidr, 117) require.NotNil(t, err) require.Equal(t, "no prefix found in 2001:db8:85a3::/116 with length:117", err.Error()) require.Nil(t, cp) // Prefix has ips - p2, err := ipam.NewPrefix("2001:0db8:95a3::/120") + p2, err := ipam.NewPrefix(ctx, "2001:0db8:95a3::/120") require.Nil(t, err) s, _ = p2.availablePrefixes() require.Equal(t, uint64(64), s) require.Equal(t, p2.acquiredPrefixes(), uint64(0)) - ip, err := ipam.AcquireIP(p2.Cidr) + ip, err := ipam.AcquireIP(ctx, p2.Cidr) require.Nil(t, err) require.NotNil(t, ip) - cp2, err := ipam.AcquireChildPrefix(p2.Cidr, 121) + cp2, err := ipam.AcquireChildPrefix(ctx, p2.Cidr, 121) require.NotNil(t, err) require.Equal(t, "prefix 2001:db8:95a3::/120 has ips, acquire child prefix not possible", err.Error()) require.Nil(t, cp2) // Prefix has Childs, AcquireIP wont work - p3, err := ipam.NewPrefix("2001:0db8:75a3::/120") + p3, err := ipam.NewPrefix(ctx, "2001:0db8:75a3::/120") require.Nil(t, err) s, _ = p3.availablePrefixes() require.Equal(t, uint64(64), s) require.Equal(t, p3.acquiredPrefixes(), uint64(0)) - cp3, err := ipam.AcquireChildPrefix(p3.Cidr, 121) + cp3, err := ipam.AcquireChildPrefix(ctx, p3.Cidr, 121) require.Nil(t, err) require.NotNil(t, cp3) - p3 = ipam.PrefixFrom(p3.Cidr) - ip, err = ipam.AcquireIP(p3.Cidr) + p3 = ipam.PrefixFrom(ctx, p3.Cidr) + ip, err = ipam.AcquireIP(ctx, p3.Cidr) require.NotNil(t, err) require.Equal(t, "prefix 2001:db8:75a3::/120 has childprefixes, acquire ip not possible", err.Error()) require.Nil(t, ip) // Release Parent Prefix must not work - err = ipam.ReleaseChildPrefix(p3) + err = ipam.ReleaseChildPrefix(ctx, p3) require.NotNil(t, err) require.Equal(t, "prefix 2001:db8:75a3::/120 is no child prefix", err.Error()) }) } func TestIpamer_AcquireSpecificChildPrefixIPv4(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { - prefix, err := ipam.NewPrefix("192.168.0.0/20") + prefix, err := ipam.NewPrefix(ctx, "192.168.0.0/20") require.Nil(t, err) s, _ := prefix.availablePrefixes() require.Equal(t, uint64(1024), s) require.Equal(t, prefix.acquiredPrefixes(), uint64(0)) // Same length - cp, err := ipam.AcquireSpecificChildPrefix(prefix.Cidr, "192.168.0.0/20") + cp, err := ipam.AcquireSpecificChildPrefix(ctx, prefix.Cidr, "192.168.0.0/20") require.NotNil(t, err) require.Equal(t, "given length:20 must be greater than prefix length:20", err.Error()) require.Nil(t, cp) // working length - cp, err = ipam.AcquireSpecificChildPrefix(prefix.Cidr, "192.168.0.0/21") + cp, err = ipam.AcquireSpecificChildPrefix(ctx, prefix.Cidr, "192.168.0.0/21") require.Nil(t, err) require.NotNil(t, cp) require.Equal(t, cp.Cidr, "192.168.0.0/21") require.Equal(t, prefix.Cidr, cp.ParentCidr) // specific prefix not available - cp, err = ipam.AcquireSpecificChildPrefix(prefix.Cidr, "192.168.8.0/21") + cp, err = ipam.AcquireSpecificChildPrefix(ctx, prefix.Cidr, "192.168.8.0/21") require.Nil(t, err) require.NotNil(t, cp) - cp, err = ipam.AcquireSpecificChildPrefix(prefix.Cidr, "192.168.8.0/21") + cp, err = ipam.AcquireSpecificChildPrefix(ctx, prefix.Cidr, "192.168.8.0/21") require.NotNil(t, err) require.Equal(t, "specific prefix 192.168.8.0/21 is not available in prefix 192.168.0.0/20", err.Error()) require.Nil(t, cp) // Prefix has ips - p2, err := ipam.NewPrefix("10.0.0.0/24") + p2, err := ipam.NewPrefix(ctx, "10.0.0.0/24") require.Nil(t, err) s, _ = p2.availablePrefixes() require.Equal(t, uint64(64), s) require.Equal(t, p2.acquiredPrefixes(), uint64(0)) - ip, err := ipam.AcquireIP(p2.Cidr) + ip, err := ipam.AcquireIP(ctx, p2.Cidr) require.Nil(t, err) require.NotNil(t, ip) - cp2, err := ipam.AcquireSpecificChildPrefix(p2.Cidr, "10.0.0.0/25") + cp2, err := ipam.AcquireSpecificChildPrefix(ctx, p2.Cidr, "10.0.0.0/25") require.NotNil(t, err) require.Equal(t, "prefix 10.0.0.0/24 has ips, acquire child prefix not possible", err.Error()) require.Nil(t, cp2) // Prefix has Childs, AcquireIP wont work - p3, err := ipam.NewPrefix("172.17.0.0/24") + p3, err := ipam.NewPrefix(ctx, "172.17.0.0/24") require.Nil(t, err) s, _ = p3.availablePrefixes() require.Equal(t, uint64(64), s) require.Equal(t, p3.acquiredPrefixes(), uint64(0)) - cp3, err := ipam.AcquireSpecificChildPrefix(p3.Cidr, "172.17.0.0/25") + cp3, err := ipam.AcquireSpecificChildPrefix(ctx, p3.Cidr, "172.17.0.0/25") require.Nil(t, err) require.NotNil(t, cp3) - p3 = ipam.PrefixFrom(p3.Cidr) - ip, err = ipam.AcquireIP(p3.Cidr) + p3 = ipam.PrefixFrom(ctx, p3.Cidr) + ip, err = ipam.AcquireIP(ctx, p3.Cidr) require.NotNil(t, err) require.Equal(t, "prefix 172.17.0.0/24 has childprefixes, acquire ip not possible", err.Error()) require.Nil(t, ip) @@ -748,76 +760,78 @@ func TestIpamer_AcquireSpecificChildPrefixIPv4(t *testing.T) { } func TestIpamer_AcquireSpecificChildPrefixIPv6(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { - prefix, err := ipam.NewPrefix("2001:0db8:85a3::/116") + prefix, err := ipam.NewPrefix(ctx, "2001:0db8:85a3::/116") require.Nil(t, err) s, _ := prefix.availablePrefixes() require.Equal(t, uint64(1024), s) require.Equal(t, prefix.acquiredPrefixes(), uint64(0)) // Same length - cp, err := ipam.AcquireSpecificChildPrefix(prefix.Cidr, "2001:0db8:85a3::/116") + cp, err := ipam.AcquireSpecificChildPrefix(ctx, prefix.Cidr, "2001:0db8:85a3::/116") require.NotNil(t, err) require.Equal(t, "given length:116 must be greater than prefix length:116", err.Error()) require.Nil(t, cp) // working length - cp, err = ipam.AcquireSpecificChildPrefix(prefix.Cidr, "2001:0db8:85a3::/117") + cp, err = ipam.AcquireSpecificChildPrefix(ctx, prefix.Cidr, "2001:0db8:85a3::/117") require.Nil(t, err) require.NotNil(t, cp) require.Equal(t, "2001:db8:85a3::/117", cp.Cidr) require.Equal(t, prefix.Cidr, cp.ParentCidr) // specific prefix not available - cp, err = ipam.AcquireSpecificChildPrefix(prefix.Cidr, "2001:0db8:85a3::0800/117") + cp, err = ipam.AcquireSpecificChildPrefix(ctx, prefix.Cidr, "2001:0db8:85a3::0800/117") require.Nil(t, err) require.NotNil(t, cp) require.Equal(t, cp.Cidr, "2001:db8:85a3::800/117") - cp, err = ipam.AcquireSpecificChildPrefix(prefix.Cidr, "2001:0db8:85a3::0800/117") + cp, err = ipam.AcquireSpecificChildPrefix(ctx, prefix.Cidr, "2001:0db8:85a3::0800/117") require.NotNil(t, err) require.Equal(t, "specific prefix 2001:0db8:85a3::0800/117 is not available in prefix 2001:db8:85a3::/116", err.Error()) require.Nil(t, cp) // Prefix has ips - p2, err := ipam.NewPrefix("2001:0db8:95a3::/120") + p2, err := ipam.NewPrefix(ctx, "2001:0db8:95a3::/120") require.Nil(t, err) s, _ = p2.availablePrefixes() require.Equal(t, uint64(64), s) require.Equal(t, p2.acquiredPrefixes(), uint64(0)) - ip, err := ipam.AcquireIP(p2.Cidr) + ip, err := ipam.AcquireIP(ctx, p2.Cidr) require.Nil(t, err) require.NotNil(t, ip) - cp2, err := ipam.AcquireSpecificChildPrefix(p2.Cidr, "2001:0db8:95a3::/121") + cp2, err := ipam.AcquireSpecificChildPrefix(ctx, p2.Cidr, "2001:0db8:95a3::/121") require.NotNil(t, err) require.Equal(t, "prefix 2001:db8:95a3::/120 has ips, acquire child prefix not possible", err.Error()) require.Nil(t, cp2) // Prefix has Childs, AcquireIP wont work - p3, err := ipam.NewPrefix("2001:0db8:75a3::/120") + p3, err := ipam.NewPrefix(ctx, "2001:0db8:75a3::/120") require.Nil(t, err) s, _ = p3.availablePrefixes() require.Equal(t, uint64(64), s) require.Equal(t, p3.acquiredPrefixes(), uint64(0)) - cp3, err := ipam.AcquireSpecificChildPrefix(p3.Cidr, "2001:0db8:75a3::/121") + cp3, err := ipam.AcquireSpecificChildPrefix(ctx, p3.Cidr, "2001:0db8:75a3::/121") require.Nil(t, err) require.NotNil(t, cp3) - p3 = ipam.PrefixFrom(p3.Cidr) - ip, err = ipam.AcquireIP(p3.Cidr) + p3 = ipam.PrefixFrom(ctx, p3.Cidr) + ip, err = ipam.AcquireIP(ctx, p3.Cidr) require.NotNil(t, err) require.Equal(t, "prefix 2001:db8:75a3::/120 has childprefixes, acquire ip not possible", err.Error()) require.Nil(t, ip) // Release Parent Prefix must not work - err = ipam.ReleaseChildPrefix(p3) + err = ipam.ReleaseChildPrefix(ctx, p3) require.NotNil(t, err) require.Equal(t, "prefix 2001:db8:75a3::/120 is no child prefix", err.Error()) }) } func TestIpamer_AcquireChildPrefixNoDuplicatesUntilFullIPv6(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { - prefix, err := ipam.NewPrefix("2001:0db8:85a3::/112") + prefix, err := ipam.NewPrefix(ctx, "2001:0db8:85a3::/112") require.Nil(t, err) s, _ := prefix.availablePrefixes() require.Equal(t, uint64(16384), s) @@ -826,7 +840,7 @@ func TestIpamer_AcquireChildPrefixNoDuplicatesUntilFullIPv6(t *testing.T) { uniquePrefixes := make(map[string]bool) // acquire all /120 prefixes (2^8 = 256) for i := 0; i < 256; i++ { - cp, err := ipam.AcquireChildPrefix(prefix.Cidr, 120) + cp, err := ipam.AcquireChildPrefix(ctx, prefix.Cidr, 120) require.Nil(t, err) require.NotNil(t, cp) require.True(t, strings.HasPrefix(cp.Cidr, "2001:db8:85a3:")) @@ -836,7 +850,7 @@ func TestIpamer_AcquireChildPrefixNoDuplicatesUntilFullIPv6(t *testing.T) { require.False(t, ok) uniquePrefixes[cp.String()] = true } - prefix = ipam.PrefixFrom(prefix.Cidr) + prefix = ipam.PrefixFrom(ctx, prefix.Cidr) require.Equal(t, 256, len(uniquePrefixes)) s, _ = prefix.availablePrefixes() require.Equal(t, uint64(0), s) @@ -845,9 +859,10 @@ func TestIpamer_AcquireChildPrefixNoDuplicatesUntilFullIPv6(t *testing.T) { }) } func TestIpamer_AcquireChildPrefixNoDuplicatesUntilFullIPv4(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { - prefix, err := ipam.NewPrefix("192.168.0.0/16") + prefix, err := ipam.NewPrefix(ctx, "192.168.0.0/16") require.Nil(t, err) s, _ := prefix.availablePrefixes() require.Equal(t, uint64(16384), s) @@ -856,7 +871,7 @@ func TestIpamer_AcquireChildPrefixNoDuplicatesUntilFullIPv4(t *testing.T) { uniquePrefixes := make(map[string]bool) // acquire all /24 prefixes (2^8 = 256) for i := 0; i < 256; i++ { - cp, err := ipam.AcquireChildPrefix(prefix.Cidr, 24) + cp, err := ipam.AcquireChildPrefix(ctx, prefix.Cidr, 24) require.Nil(t, err) require.NotNil(t, cp) require.True(t, strings.HasPrefix(cp.Cidr, "192.168.")) @@ -866,7 +881,7 @@ func TestIpamer_AcquireChildPrefixNoDuplicatesUntilFullIPv4(t *testing.T) { require.False(t, ok) uniquePrefixes[cp.String()] = true } - prefix = ipam.PrefixFrom(prefix.Cidr) + prefix = ipam.PrefixFrom(ctx, prefix.Cidr) require.Equal(t, 256, len(uniquePrefixes)) s, _ = prefix.availablePrefixes() require.Equal(t, uint64(0), s) @@ -927,6 +942,7 @@ func TestPrefix_Availableips(t *testing.T) { } func TestIpamer_PrefixesOverlapping(t *testing.T) { + ctx := context.Background() tests := []struct { name string @@ -989,7 +1005,7 @@ func TestIpamer_PrefixesOverlapping(t *testing.T) { test := tt testWithBackends(t, func(t *testing.T, ipam *ipamer) { for _, ep := range test.existingPrefixes { - p, err := ipam.NewPrefix(ep) + p, err := ipam.NewPrefix(ctx, ep) if err != nil { t.Errorf("Newprefix on ExistingPrefix failed:%v", err) } @@ -1012,6 +1028,7 @@ func TestIpamer_PrefixesOverlapping(t *testing.T) { } func TestIpamer_NewPrefix(t *testing.T) { + ctx := context.Background() tests := []struct { name string @@ -1072,14 +1089,14 @@ func TestIpamer_NewPrefix(t *testing.T) { for _, tt := range tests { test := tt testWithBackends(t, func(t *testing.T, ipam *ipamer) { - got, err := ipam.NewPrefix(test.cidr) + got, err := ipam.NewPrefix(ctx, test.cidr) if (err != nil) != test.wantErr { - t.Errorf("Ipamer.NewPrefix() error = %v, wantErr %v", err, test.wantErr) + t.Errorf("Ipamer.NewPrefix(ctx, ) error = %v, wantErr %v", err, test.wantErr) return } if (err != nil) && test.errorString != err.Error() { - t.Errorf("Ipamer.NewPrefix() error = %v, errorString %v", err, test.errorString) + t.Errorf("Ipamer.NewPrefix(ctx, ) error = %v, errorString %v", err, test.errorString) return } @@ -1087,171 +1104,178 @@ func TestIpamer_NewPrefix(t *testing.T) { return } if got.Cidr != test.wantcidr { - t.Errorf("Ipamer.NewPrefix() cidr = %v, want %v", got.Cidr, test.wantcidr) + t.Errorf("Ipamer.NewPrefix(ctx, ) cidr = %v, want %v", got.Cidr, test.wantcidr) } }) } } func TestIpamer_DeletePrefix(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { // IPv4 - prefix, err := ipam.NewPrefix("192.168.0.0/20") + prefix, err := ipam.NewPrefix(ctx, "192.168.0.0/20") require.Nil(t, err) s, _ := prefix.availablePrefixes() require.Equal(t, uint64(1024), s) require.Equal(t, prefix.acquiredPrefixes(), uint64(0)) require.Equal(t, prefix.Usage().AcquiredPrefixes, uint64(0)) - ip, err := ipam.AcquireIP(prefix.Cidr) + ip, err := ipam.AcquireIP(ctx, prefix.Cidr) require.Nil(t, err) require.NotNil(t, ip) - _, err = ipam.DeletePrefix(prefix.Cidr) + _, err = ipam.DeletePrefix(ctx, prefix.Cidr) require.NotNil(t, err) require.Equal(t, "prefix 192.168.0.0/20 has ips, delete prefix not possible", err.Error()) // IPv6 - prefix, err = ipam.NewPrefix("2001:0db8:85a3::/120") + prefix, err = ipam.NewPrefix(ctx, "2001:0db8:85a3::/120") require.Nil(t, err) s, _ = prefix.availablePrefixes() require.Equal(t, uint64(64), s) require.Equal(t, prefix.acquiredPrefixes(), uint64(0)) require.Equal(t, prefix.Usage().AcquiredPrefixes, uint64(0)) - ip, err = ipam.AcquireIP(prefix.Cidr) + ip, err = ipam.AcquireIP(ctx, prefix.Cidr) require.Nil(t, err) require.NotNil(t, ip) - _, err = ipam.DeletePrefix(prefix.Cidr) + _, err = ipam.DeletePrefix(ctx, prefix.Cidr) require.NotNil(t, err) require.Equal(t, "prefix 2001:db8:85a3::/120 has ips, delete prefix not possible", err.Error()) - _, err = ipam.ReleaseIP(ip) + _, err = ipam.ReleaseIP(ctx, ip) require.Nil(t, err) - _, err = ipam.DeletePrefix(prefix.Cidr) + _, err = ipam.DeletePrefix(ctx, prefix.Cidr) require.Nil(t, err) }) } func TestIpamer_PrefixFrom(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { - prefix := ipam.PrefixFrom("192.168.0.0/20") + prefix := ipam.PrefixFrom(ctx, "192.168.0.0/20") require.Nil(t, prefix) - prefix, err := ipam.NewPrefix("192.168.0.0/20") + prefix, err := ipam.NewPrefix(ctx, "192.168.0.0/20") require.Nil(t, err) require.NotNil(t, prefix) - prefix = ipam.PrefixFrom("192.168.0.0/20") + prefix = ipam.PrefixFrom(ctx, "192.168.0.0/20") require.NotNil(t, prefix) // non canonical form still returns the same prefix - prefix2 := ipam.PrefixFrom("10.0.5.0/8") + prefix2 := ipam.PrefixFrom(ctx, "10.0.5.0/8") require.Nil(t, prefix2) - prefix2a, err := ipam.NewPrefix("10.8.0.0/8") + prefix2a, err := ipam.NewPrefix(ctx, "10.8.0.0/8") require.Nil(t, err) require.NotNil(t, prefix2a) - prefix2b := ipam.PrefixFrom("10.2.0.0/8") + prefix2b := ipam.PrefixFrom(ctx, "10.2.0.0/8") require.NotNil(t, prefix2b) require.Equal(t, prefix2a.Cidr, prefix2b.Cidr) }) } func TestIpamerAcquireIP(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { cidr := "10.0.0.0/16" - p, err := ipam.NewPrefix(cidr) + p, err := ipam.NewPrefix(ctx, cidr) require.NoError(t, err) for n := 0; n < 10; n++ { if len(p.ips) != 2 { t.Fatalf("expected 2 ips in prefix, got %d", len(p.ips)) } - ip, err := ipam.AcquireIP(p.Cidr) + ip, err := ipam.AcquireIP(ctx, p.Cidr) require.NoError(t, err) require.NotNil(t, ip, "IP is nil") - p, err = ipam.ReleaseIP(ip) + p, err = ipam.ReleaseIP(ctx, ip) require.NoError(t, err) } - _, err = ipam.DeletePrefix(cidr) + _, err = ipam.DeletePrefix(ctx, cidr) require.NoError(t, err, "error deleting prefix:%v", err) }) } func TestIpamerAcquireIPv6(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { cidr := "2001:0db8:85a3::/120" - p, err := ipam.NewPrefix(cidr) + p, err := ipam.NewPrefix(ctx, cidr) require.NoError(t, err) for n := 0; n < 10; n++ { if len(p.ips) != 1 { t.Fatalf("expected 1 ips in prefix, got %d", len(p.ips)) } - ip, err := ipam.AcquireIP(p.Cidr) + ip, err := ipam.AcquireIP(ctx, p.Cidr) require.NoError(t, err) require.NotNil(t, ip, "IP is nil") - p, err = ipam.ReleaseIP(ip) + p, err = ipam.ReleaseIP(ctx, ip) require.NoError(t, err) } - _, err = ipam.DeletePrefix(cidr) + _, err = ipam.DeletePrefix(ctx, cidr) require.NoError(t, err, "error deleting prefix:%v", err) }) } func TestIpamerAcquireAlreadyAquiredIPv4(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { cidr := "192.168.0.0/16" - p, err := ipam.NewPrefix(cidr) + p, err := ipam.NewPrefix(ctx, cidr) require.NoError(t, err) - ip, err := ipam.AcquireSpecificIP(p.Cidr, "192.168.2.4") + ip, err := ipam.AcquireSpecificIP(ctx, p.Cidr, "192.168.2.4") require.NoError(t, err) require.NotNil(t, ip, "IP is nil") require.Equal(t, ip.IP.String(), "192.168.2.4") - _, err = ipam.AcquireSpecificIP(p.Cidr, "192.168.2.4") + _, err = ipam.AcquireSpecificIP(ctx, p.Cidr, "192.168.2.4") require.ErrorIs(t, err, ErrAlreadyAllocated) require.EqualError(t, err, "AlreadyAllocatedError: given ip:192.168.2.4 is already allocated") - _, err = ipam.ReleaseIP(ip) + _, err = ipam.ReleaseIP(ctx, ip) require.NoError(t, err) - _, err = ipam.DeletePrefix(cidr) + _, err = ipam.DeletePrefix(ctx, cidr) require.NoError(t, err) }) } func TestIpamerAcquireAlreadyAquiredIPv6(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { cidr := "2001:0db8:85a3::/64" - p, err := ipam.NewPrefix(cidr) + p, err := ipam.NewPrefix(ctx, cidr) require.NoError(t, err) - ip, err := ipam.AcquireSpecificIP(p.Cidr, "2001:0db8:85a3::1") + ip, err := ipam.AcquireSpecificIP(ctx, p.Cidr, "2001:0db8:85a3::1") require.NoError(t, err) require.NotNil(t, ip, "IP is nil") require.Equal(t, ip.IP.String(), "2001:db8:85a3::1") - _, err = ipam.AcquireSpecificIP(p.Cidr, "2001:0db8:85a3::1") + _, err = ipam.AcquireSpecificIP(ctx, p.Cidr, "2001:0db8:85a3::1") require.ErrorIs(t, err, ErrAlreadyAllocated) require.EqualError(t, err, "AlreadyAllocatedError: given ip:2001:db8:85a3::1 is already allocated") - _, err = ipam.ReleaseIP(ip) + _, err = ipam.ReleaseIP(ctx, ip) require.NoError(t, err) - _, err = ipam.DeletePrefix(cidr) + _, err = ipam.DeletePrefix(ctx, cidr) require.NoError(t, err) }) } func TestGetHostAddresses(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { cidr := "4.1.0.0/24" - ips, err := ipam.getHostAddresses(cidr) + ips, err := ipam.getHostAddresses(ctx, cidr) require.NoError(t, err) require.NotNil(t, ips) require.Equal(t, 254, len(ips)) - ip, err := ipam.AcquireIP(cidr) + ip, err := ipam.AcquireIP(ctx, cidr) require.Error(t, err) require.True(t, errors.As(err, &NoIPAvailableError{}), "error must be of correct type") require.True(t, errors.Is(err, ErrNoIPAvailable), "error must be NoIPAvailable") @@ -1259,12 +1283,12 @@ func TestGetHostAddresses(t *testing.T) { require.Nil(t, ip) cidr = "3.1.0.0/26" - ips, err = ipam.getHostAddresses(cidr) + ips, err = ipam.getHostAddresses(ctx, cidr) require.NoError(t, err) require.NotNil(t, ips) require.Equal(t, 62, len(ips)) - ip, err = ipam.AcquireIP(cidr) + ip, err = ipam.AcquireIP(ctx, cidr) require.Error(t, err) require.True(t, errors.As(err, &NoIPAvailableError{}), "error must be of correct type") require.True(t, errors.Is(err, ErrNoIPAvailable), "error must be NoIPAvailable") @@ -1274,14 +1298,15 @@ func TestGetHostAddresses(t *testing.T) { } func TestGetHostAddressesIPv6(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { cidr := "2001:0db8:85a3::/120" - ips, err := ipam.getHostAddresses(cidr) + ips, err := ipam.getHostAddresses(ctx, cidr) require.NoError(t, err) require.NotNil(t, ips) require.Equal(t, 255, len(ips)) - ip, err := ipam.AcquireIP(cidr) + ip, err := ipam.AcquireIP(ctx, cidr) require.Error(t, err) require.True(t, errors.As(err, &NoIPAvailableError{}), "error must be of correct type") require.True(t, errors.Is(err, ErrNoIPAvailable), "error must be NoIPAvailable") @@ -1289,12 +1314,12 @@ func TestGetHostAddressesIPv6(t *testing.T) { require.Nil(t, ip) cidr = "2001:0db8:95a3::/122" - ips, err = ipam.getHostAddresses(cidr) + ips, err = ipam.getHostAddresses(ctx, cidr) require.NoError(t, err) require.NotNil(t, ips) require.Equal(t, 63, len(ips)) - ip, err = ipam.AcquireIP(cidr) + ip, err = ipam.AcquireIP(ctx, cidr) require.Error(t, err) require.True(t, errors.As(err, &NoIPAvailableError{}), "error must be of correct type") require.True(t, errors.Is(err, ErrNoIPAvailable), "error must be NoIPAvailable") @@ -1326,8 +1351,9 @@ func TestPrefixDeepCopy(t *testing.T) { } func TestGob(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { - prefix, err := ipam.NewPrefix("192.168.0.0/24") + prefix, err := ipam.NewPrefix(ctx, "192.168.0.0/24") require.Nil(t, err) require.NotNil(t, prefix) @@ -1407,19 +1433,20 @@ func TestPrefix_availablePrefixes(t *testing.T) { } func TestAcquireIPParallel(t *testing.T) { + ctx := context.Background() ipsCount := 50 g, _ := errgroup.WithContext(context.Background()) mu := sync.Mutex{} testWithBackends(t, func(t *testing.T, ipam *ipamer) { - p, err := ipam.NewPrefix("192.169.0.0/20") + p, err := ipam.NewPrefix(ctx, "192.169.0.0/20") if err != nil { panic(err) } ips := make(map[string]bool) for n := 0; n < ipsCount; n++ { g.Go(func() error { - ip, err := ipam.AcquireIP(p.Cidr) + ip, err := ipam.AcquireIP(ctx, p.Cidr) if err != nil { return err } @@ -1444,12 +1471,12 @@ func TestAcquireIPParallel(t *testing.T) { t.Fatal(err) } for ip := range ips { - err := ipam.ReleaseIPFromPrefix(p.Cidr, ip) + err := ipam.ReleaseIPFromPrefix(ctx, p.Cidr, ip) if err != nil { t.Error(err) } } - _, err = ipam.DeletePrefix(p.Cidr) + _, err = ipam.DeletePrefix(ctx, p.Cidr) if err != nil { t.Error(err) } @@ -1457,42 +1484,44 @@ func TestAcquireIPParallel(t *testing.T) { } func Test_ipamer_DumpAndLoad(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { - prefix, err := ipam.NewPrefix("192.168.0.0/24") + prefix, err := ipam.NewPrefix(ctx, "192.168.0.0/24") require.Nil(t, err) require.NotNil(t, prefix) - data, err := ipam.Dump() + data, err := ipam.Dump(ctx) require.Nil(t, err) require.NotEmpty(t, data) t.Log(data) - err = ipam.Load(data) + err = ipam.Load(ctx, data) require.Error(t, err) require.Equal(t, "prefixes exist, please drop existing data before loading", err.Error()) - err = ipam.storage.DeleteAllPrefixes() + err = ipam.storage.DeleteAllPrefixes(ctx) require.NoError(t, err) - err = ipam.Load(data) + err = ipam.Load(ctx, data) require.NoError(t, err) - newPrefix := ipam.PrefixFrom(prefix.Cidr) + newPrefix := ipam.PrefixFrom(ctx, prefix.Cidr) require.Equal(t, prefix, newPrefix) }) } func TestIpamer_ReadAllPrefixCidrs(t *testing.T) { + ctx := context.Background() testWithBackends(t, func(t *testing.T, ipam *ipamer) { const cidr = "192.168.0.0/20" - prefix, err := ipam.NewPrefix(cidr) + prefix, err := ipam.NewPrefix(ctx, cidr) require.Nil(t, err) require.NotNil(t, prefix) - cidrs, err := ipam.ReadAllPrefixCidrs() + cidrs, err := ipam.ReadAllPrefixCidrs(ctx) require.Nil(t, err) require.NotNil(t, cidrs) require.Equal(t, 1, len(cidrs)) diff --git a/redis.go b/redis.go index 824b75d..dade160 100644 --- a/redis.go +++ b/redis.go @@ -9,8 +9,6 @@ import ( redigo "github.com/go-redis/redis/v8" ) -var ctx = context.Background() - type redis struct { rdb *redigo.Client lock sync.RWMutex @@ -37,7 +35,7 @@ func newRedis(ip, port string) *redis { } } -func (r *redis) CreatePrefix(prefix Prefix) (Prefix, error) { +func (r *redis) CreatePrefix(ctx context.Context, prefix Prefix) (Prefix, error) { r.lock.Lock() defer r.lock.Unlock() @@ -55,7 +53,7 @@ func (r *redis) CreatePrefix(prefix Prefix) (Prefix, error) { err = r.rdb.Set(ctx, prefix.Cidr, pfx, 0).Err() return prefix, err } -func (r *redis) ReadPrefix(prefix string) (Prefix, error) { +func (r *redis) ReadPrefix(ctx context.Context, prefix string) (Prefix, error) { r.lock.RLock() defer r.lock.RUnlock() @@ -65,13 +63,13 @@ func (r *redis) ReadPrefix(prefix string) (Prefix, error) { } return fromJSON([]byte(result)) } -func (r *redis) DeleteAllPrefixes() error { +func (r *redis) DeleteAllPrefixes(ctx context.Context) error { r.lock.RLock() defer r.lock.RUnlock() _, err := r.rdb.FlushAll(ctx).Result() return err } -func (r *redis) ReadAllPrefixes() (Prefixes, error) { +func (r *redis) ReadAllPrefixes(ctx context.Context) (Prefixes, error) { r.lock.RLock() defer r.lock.RUnlock() @@ -94,7 +92,7 @@ func (r *redis) ReadAllPrefixes() (Prefixes, error) { } return result, nil } -func (r *redis) ReadAllPrefixCidrs() ([]string, error) { +func (r *redis) ReadAllPrefixCidrs(ctx context.Context) ([]string, error) { r.lock.RLock() defer r.lock.RUnlock() pfxs, err := r.rdb.Keys(ctx, "*").Result() @@ -103,7 +101,7 @@ func (r *redis) ReadAllPrefixCidrs() ([]string, error) { } return pfxs, nil } -func (r *redis) UpdatePrefix(prefix Prefix) (Prefix, error) { +func (r *redis) UpdatePrefix(ctx context.Context, prefix Prefix) (Prefix, error) { r.lock.Lock() defer r.lock.Unlock() @@ -143,7 +141,7 @@ func (r *redis) UpdatePrefix(prefix Prefix) (Prefix, error) { return prefix, nil } -func (r *redis) DeletePrefix(prefix Prefix) (Prefix, error) { +func (r *redis) DeletePrefix(ctx context.Context, prefix Prefix) (Prefix, error) { r.lock.Lock() defer r.lock.Unlock() diff --git a/sql.go b/sql.go index 50a3faa..2008be6 100644 --- a/sql.go +++ b/sql.go @@ -1,6 +1,7 @@ package ipam import ( + "context" "fmt" "github.com/jmoiron/sqlx" @@ -10,16 +11,16 @@ type sql struct { db *sqlx.DB } -func (s *sql) prefixExists(prefix Prefix) (*Prefix, bool) { - p, err := s.ReadPrefix(prefix.Cidr) +func (s *sql) prefixExists(ctx context.Context, prefix Prefix) (*Prefix, bool) { + p, err := s.ReadPrefix(ctx, prefix.Cidr) if err != nil { return nil, false } return &p, true } -func (s *sql) CreatePrefix(prefix Prefix) (Prefix, error) { - existingPrefix, exists := s.prefixExists(prefix) +func (s *sql) CreatePrefix(ctx context.Context, prefix Prefix) (Prefix, error) { + existingPrefix, exists := s.prefixExists(ctx, prefix) if exists { return *existingPrefix, nil } @@ -32,31 +33,31 @@ func (s *sql) CreatePrefix(prefix Prefix) (Prefix, error) { if err != nil { return Prefix{}, fmt.Errorf("unable to start transaction:%w", err) } - _, err = tx.Exec("INSERT INTO prefixes (cidr, prefix) VALUES ($1, $2)", prefix.Cidr, pj) + _, err = tx.ExecContext(ctx, "INSERT INTO prefixes (cidr, prefix) VALUES ($1, $2)", prefix.Cidr, pj) if err != nil { return Prefix{}, fmt.Errorf("unable to insert prefix:%w", err) } return prefix, tx.Commit() } -func (s *sql) ReadPrefix(prefix string) (Prefix, error) { +func (s *sql) ReadPrefix(ctx context.Context, prefix string) (Prefix, error) { var result []byte - err := s.db.Get(&result, "SELECT prefix FROM prefixes WHERE cidr=$1", prefix) + err := s.db.GetContext(ctx, &result, "SELECT prefix FROM prefixes WHERE cidr=$1", prefix) if err != nil { return Prefix{}, fmt.Errorf("unable to read prefix:%w", err) } return fromJSON(result) } -func (s *sql) DeleteAllPrefixes() error { - _, err := s.db.Exec("DELETE FROM prefixes") +func (s *sql) DeleteAllPrefixes(ctx context.Context) error { + _, err := s.db.ExecContext(ctx, "DELETE FROM prefixes") return err } // ReadAllPrefixes returns all known prefixes. -func (s *sql) ReadAllPrefixes() (Prefixes, error) { +func (s *sql) ReadAllPrefixes(ctx context.Context) (Prefixes, error) { var prefixes [][]byte - err := s.db.Select(&prefixes, "SELECT prefix FROM prefixes") + err := s.db.SelectContext(ctx, &prefixes, "SELECT prefix FROM prefixes") if err != nil { return nil, fmt.Errorf("unable to read prefixes:%w", err) } @@ -73,9 +74,9 @@ func (s *sql) ReadAllPrefixes() (Prefixes, error) { } // ReadAllPrefixCidrs is cheaper that ReadAllPrefixes because it only returns the Cidrs. -func (s *sql) ReadAllPrefixCidrs() ([]string, error) { +func (s *sql) ReadAllPrefixCidrs(ctx context.Context) ([]string, error) { cidrs := []string{} - err := s.db.Select(&cidrs, "SELECT cidr FROM prefixes") + err := s.db.SelectContext(ctx, &cidrs, "SELECT cidr FROM prefixes") if err != nil { return nil, fmt.Errorf("unable to read prefixes:%w", err) } @@ -84,7 +85,7 @@ func (s *sql) ReadAllPrefixCidrs() ([]string, error) { // UpdatePrefix tries to update the prefix. // Returns OptimisticLockError if it does not succeed due to a concurrent update. -func (s *sql) UpdatePrefix(prefix Prefix) (Prefix, error) { +func (s *sql) UpdatePrefix(ctx context.Context, prefix Prefix) (Prefix, error) { oldVersion := prefix.version prefix.version = oldVersion + 1 pn, err := prefix.toJSON() @@ -95,7 +96,7 @@ func (s *sql) UpdatePrefix(prefix Prefix) (Prefix, error) { if err != nil { return Prefix{}, fmt.Errorf("unable to start transaction:%w", err) } - result, err := tx.Exec("SELECT prefix FROM prefixes WHERE cidr=$1 AND prefix->>'Version'=$2 FOR UPDATE", prefix.Cidr, oldVersion) + result, err := tx.ExecContext(ctx, "SELECT prefix FROM prefixes WHERE cidr=$1 AND prefix->>'Version'=$2 FOR UPDATE", prefix.Cidr, oldVersion) if err != nil { return Prefix{}, fmt.Errorf("%w: unable to select for update prefix:%s", ErrOptimisticLockError, prefix.Cidr) } @@ -108,7 +109,7 @@ func (s *sql) UpdatePrefix(prefix Prefix) (Prefix, error) { _ = tx.Rollback() return Prefix{}, fmt.Errorf("%w: select for update did not effect any row", ErrOptimisticLockError) } - result, err = tx.Exec("UPDATE prefixes SET prefix=$1 WHERE cidr=$2 AND prefix->>'Version'=$3", pn, prefix.Cidr, oldVersion) + result, err = tx.ExecContext(ctx, "UPDATE prefixes SET prefix=$1 WHERE cidr=$2 AND prefix->>'Version'=$3", pn, prefix.Cidr, oldVersion) if err != nil { return Prefix{}, fmt.Errorf("%w: unable to update prefix:%s", ErrOptimisticLockError, prefix.Cidr) } @@ -124,12 +125,12 @@ func (s *sql) UpdatePrefix(prefix Prefix) (Prefix, error) { return prefix, tx.Commit() } -func (s *sql) DeletePrefix(prefix Prefix) (Prefix, error) { +func (s *sql) DeletePrefix(ctx context.Context, prefix Prefix) (Prefix, error) { tx, err := s.db.Beginx() if err != nil { return Prefix{}, fmt.Errorf("unable to start transaction:%w", err) } - _, err = tx.Exec("DELETE from prefixes WHERE cidr=$1", prefix.Cidr) + _, err = tx.ExecContext(ctx, "DELETE from prefixes WHERE cidr=$1", prefix.Cidr) if err != nil { return Prefix{}, fmt.Errorf("unable delete prefix:%w", err) } diff --git a/sql_test.go b/sql_test.go index c066a4b..4f81d7c 100644 --- a/sql_test.go +++ b/sql_test.go @@ -1,6 +1,7 @@ package ipam import ( + "context" "testing" "time" @@ -9,58 +10,60 @@ import ( ) func Test_sql_prefixExists(t *testing.T) { + ctx := context.Background() testWithSQLBackends(t, func(t *testing.T, db *sql) { require.NotNil(t, db) // Existing Prefix prefix := Prefix{Cidr: "10.0.0.0/16"} - p, err := db.CreatePrefix(prefix) + p, err := db.CreatePrefix(ctx, prefix) require.Nil(t, err) require.NotNil(t, p) require.Equal(t, prefix.Cidr, p.Cidr) - got, exists := db.prefixExists(prefix) + got, exists := db.prefixExists(ctx, prefix) require.True(t, exists) require.Equal(t, got.Cidr, prefix.Cidr) // NonExisting Prefix notExistingPrefix := Prefix{Cidr: "10.0.0.0/8"} - got, exists = db.prefixExists(notExistingPrefix) + got, exists = db.prefixExists(ctx, notExistingPrefix) require.False(t, exists) require.Nil(t, got) // Delete Existing Prefix - _, err = db.DeletePrefix(prefix) + _, err = db.DeletePrefix(ctx, prefix) require.Nil(t, err) - got, exists = db.prefixExists(prefix) + got, exists = db.prefixExists(ctx, prefix) require.False(t, exists) require.Nil(t, got) }) } func Test_sql_CreatePrefix(t *testing.T) { + ctx := context.Background() testWithSQLBackends(t, func(t *testing.T, db *sql) { require.NotNil(t, db) // Existing Prefix prefix := Prefix{Cidr: "11.0.0.0/16"} - got, exists := db.prefixExists(prefix) + got, exists := db.prefixExists(ctx, prefix) require.False(t, exists) require.Nil(t, got) - p, err := db.CreatePrefix(prefix) + p, err := db.CreatePrefix(ctx, prefix) require.Nil(t, err) require.NotNil(t, p) require.Equal(t, prefix.Cidr, p.Cidr) - got, exists = db.prefixExists(prefix) + got, exists = db.prefixExists(ctx, prefix) require.True(t, exists) require.Equal(t, got.Cidr, prefix.Cidr) // Duplicate Prefix - p, err = db.CreatePrefix(prefix) + p, err = db.CreatePrefix(ctx, prefix) require.Nil(t, err) require.NotNil(t, p) require.Equal(t, prefix.Cidr, p.Cidr) - ps, err := db.ReadAllPrefixCidrs() + ps, err := db.ReadAllPrefixCidrs(ctx) require.Nil(t, err) require.NotNil(t, ps) require.Equal(t, 1, len(ps)) @@ -68,21 +71,22 @@ func Test_sql_CreatePrefix(t *testing.T) { } func Test_sql_ReadPrefix(t *testing.T) { + ctx := context.Background() testWithSQLBackends(t, func(t *testing.T, db *sql) { require.NotNil(t, db) // Prefix - p, err := db.ReadPrefix("12.0.0.0/8") + p, err := db.ReadPrefix(ctx, "12.0.0.0/8") require.NotNil(t, err) require.Equal(t, "unable to read prefix:sql: no rows in result set", err.Error()) require.Empty(t, p) prefix := Prefix{Cidr: "12.0.0.0/16"} - p, err = db.CreatePrefix(prefix) + p, err = db.CreatePrefix(ctx, prefix) require.Nil(t, err) require.NotNil(t, p) - p, err = db.ReadPrefix("12.0.0.0/16") + p, err = db.ReadPrefix(ctx, "12.0.0.0/16") require.Nil(t, err) require.NotNil(t, p) require.Equal(t, "12.0.0.0/16", p.Cidr) @@ -90,29 +94,30 @@ func Test_sql_ReadPrefix(t *testing.T) { } func Test_sql_ReadAllPrefix(t *testing.T) { + ctx := context.Background() testWithSQLBackends(t, func(t *testing.T, db *sql) { require.NotNil(t, db) // no Prefixes - ps, err := db.ReadAllPrefixCidrs() + ps, err := db.ReadAllPrefixCidrs(ctx) require.Nil(t, err) require.NotNil(t, ps) require.Equal(t, 0, len(ps)) // One Prefix prefix := Prefix{Cidr: "12.0.0.0/16"} - p, err := db.CreatePrefix(prefix) + p, err := db.CreatePrefix(ctx, prefix) require.Nil(t, err) require.NotNil(t, p) - ps, err = db.ReadAllPrefixCidrs() + ps, err = db.ReadAllPrefixCidrs(ctx) require.Nil(t, err) require.NotNil(t, ps) require.Equal(t, 1, len(ps)) // no Prefixes again - _, err = db.DeletePrefix(prefix) + _, err = db.DeletePrefix(ctx, prefix) require.Nil(t, err) - ps, err = db.ReadAllPrefixCidrs() + ps, err = db.ReadAllPrefixCidrs(ctx) require.Nil(t, err) require.NotNil(t, ps) require.Equal(t, 0, len(ps)) @@ -120,17 +125,18 @@ func Test_sql_ReadAllPrefix(t *testing.T) { } func Test_sql_UpdatePrefix(t *testing.T) { + ctx := context.Background() testWithSQLBackends(t, func(t *testing.T, db *sql) { require.NotNil(t, db) // Prefix prefix := Prefix{Cidr: "13.0.0.0/16", ParentCidr: "13.0.0.0/8"} - p, err := db.CreatePrefix(prefix) + p, err := db.CreatePrefix(ctx, prefix) require.Nil(t, err) require.NotNil(t, p) // Check if present - p, err = db.ReadPrefix("13.0.0.0/16") + p, err = db.ReadPrefix(ctx, "13.0.0.0/16") require.Nil(t, err) require.NotNil(t, p) require.Equal(t, "13.0.0.0/16", p.Cidr) @@ -138,10 +144,10 @@ func Test_sql_UpdatePrefix(t *testing.T) { // Modify prefix.ParentCidr = "13.0.0.0/12" - p, err = db.UpdatePrefix(prefix) + p, err = db.UpdatePrefix(ctx, prefix) require.Nil(t, err) require.NotNil(t, p) - p, err = db.ReadPrefix("13.0.0.0/16") + p, err = db.ReadPrefix(ctx, "13.0.0.0/16") require.Nil(t, err) require.NotNil(t, p) require.Equal(t, "13.0.0.0/16", p.Cidr) @@ -150,19 +156,20 @@ func Test_sql_UpdatePrefix(t *testing.T) { } func Test_ConcurrentAcquirePrefix(t *testing.T) { + ctx := context.Background() testWithSQLBackends(t, func(t *testing.T, db *sql) { require.NotNil(t, db) ipamer := NewWithStorage(db) const parentCidr = "1.0.0.0/16" - _, err := ipamer.NewPrefix(parentCidr) + _, err := ipamer.NewPrefix(ctx, parentCidr) require.Nil(t, err) count := 20 prefixes := make(chan string) for i := 0; i < count; i++ { - go acquirePrefix(t, db, parentCidr, prefixes) + go acquirePrefix(t, ctx, db, parentCidr, prefixes) } prefixMap := make(map[string]bool) @@ -177,14 +184,14 @@ func Test_ConcurrentAcquirePrefix(t *testing.T) { }) } -func acquirePrefix(t *testing.T, db *sql, cidr string, prefixes chan string) { +func acquirePrefix(t *testing.T, ctx context.Context, db *sql, cidr string, prefixes chan string) { require.NotNil(t, db) ipamer := NewWithStorage(db) var cp *Prefix var err error for cp == nil { - cp, err = ipamer.AcquireChildPrefix(cidr, 26) + cp, err = ipamer.AcquireChildPrefix(ctx, cidr, 26) if err != nil { t.Error(err) } @@ -194,19 +201,20 @@ func acquirePrefix(t *testing.T, db *sql, cidr string, prefixes chan string) { } func Test_ConcurrentAcquireIP(t *testing.T) { + ctx := context.Background() testWithSQLBackends(t, func(t *testing.T, db *sql) { require.NotNil(t, db) ipamer := NewWithStorage(db) const parentCidr = "2.7.0.0/16" - _, err := ipamer.NewPrefix(parentCidr) + _, err := ipamer.NewPrefix(ctx, parentCidr) require.Nil(t, err) count := 30 ips := make(chan string) for i := 0; i < count; i++ { - go acquireIP(t, db, parentCidr, ips) + go acquireIP(t, ctx, db, parentCidr, ips) } ipMap := make(map[string]bool) @@ -221,14 +229,14 @@ func Test_ConcurrentAcquireIP(t *testing.T) { }) } -func acquireIP(t *testing.T, db *sql, prefix string, ips chan string) { +func acquireIP(t *testing.T, ctx context.Context, db *sql, prefix string, ips chan string) { require.NotNil(t, db) ipamer := NewWithStorage(db) var ip *IP var err error for ip == nil { - ip, err = ipamer.AcquireIP(prefix) + ip, err = ipamer.AcquireIP(ctx, prefix) if err != nil { t.Error(err) } diff --git a/storage.go b/storage.go index 4d6f166..4ce2d8f 100644 --- a/storage.go +++ b/storage.go @@ -1,13 +1,15 @@ package ipam +import "context" + // Storage is a interface to store ipam objects. type Storage interface { Name() string - CreatePrefix(prefix Prefix) (Prefix, error) - ReadPrefix(prefix string) (Prefix, error) - DeleteAllPrefixes() error - ReadAllPrefixes() (Prefixes, error) - ReadAllPrefixCidrs() ([]string, error) - UpdatePrefix(prefix Prefix) (Prefix, error) - DeletePrefix(prefix Prefix) (Prefix, error) + CreatePrefix(ctx context.Context, prefix Prefix) (Prefix, error) + ReadPrefix(ctx context.Context, prefix string) (Prefix, error) + DeleteAllPrefixes(ctx context.Context) error + ReadAllPrefixes(ctx context.Context) (Prefixes, error) + ReadAllPrefixCidrs(ctx context.Context) ([]string, error) + UpdatePrefix(ctx context.Context, prefix Prefix) (Prefix, error) + DeletePrefix(ctx context.Context, prefix Prefix) (Prefix, error) }