add unit tests for FreeSubnet & isSubnetPrivate

pull/15394/head
Steven Powell 2022-11-30 14:46:55 -08:00
parent da3d96bbb4
commit 347987be59
2 changed files with 123 additions and 6 deletions

View File

@ -123,7 +123,7 @@ func lookupInInterfaces(ip net.IP) (*Parameters, *net.IPNet, error) {
// inspect initialises IPv4 network parameters struct from given address addr.
// addr can be single address (like "192.168.17.42"), network address (like "192.168.17.0") or in CIDR form (like "192.168.17.42/24 or "192.168.17.0/24").
// If addr belongs to network of local network interface, parameters will also contain info about that network interface.
func inspect(addr string) (*Parameters, error) {
var inspect = func(addr string) (*Parameters, error) {
// extract ip from addr
ip, network, err := net.ParseCIDR(addr)
@ -191,7 +191,7 @@ func inspect(addr string) (*Parameters, error) {
// isSubnetTaken returns if local network subnet exists and any error occurred.
// If will return false in case of an error.
func isSubnetTaken(subnet string) (bool, error) {
var isSubnetTaken = func(subnet string) (bool, error) {
ifAddrs, err := net.InterfaceAddrs()
if err != nil {
return false, fmt.Errorf("failed listing network interface addresses: %w", err)
@ -225,8 +225,9 @@ func IsUser(network string) bool {
// FreeSubnet will try to find free private network beginning with startSubnet, incrementing it in steps up to number of tries.
func FreeSubnet(startSubnet string, step, tries int) (*Parameters, error) {
currSubnet := startSubnet
for try := 0; try < tries; try++ {
n, err := inspect(startSubnet)
n, err := inspect(currSubnet)
if err != nil {
return nil, err
}
@ -249,13 +250,13 @@ func FreeSubnet(startSubnet string, step, tries int) (*Parameters, error) {
klog.Infof("skipping subnet %s that is not private", n.CIDR)
}
prefix, _ := net.ParseIP(n.IP).DefaultMask().Size()
nextSubnet := net.ParseIP(startSubnet).To4()
nextSubnet := net.ParseIP(currSubnet).To4()
if prefix <= 16 {
nextSubnet[1] += byte(step)
} else {
nextSubnet[2] += byte(step)
}
startSubnet = nextSubnet.String()
currSubnet = nextSubnet.String()
}
return nil, fmt.Errorf("no free private network subnets found with given parameters (start: %q, step: %d, tries: %d)", startSubnet, step, tries)
}
@ -265,7 +266,7 @@ func FreeSubnet(startSubnet string, step, tries int) (*Parameters, error) {
// - true, if new reservation was created or expired one renewed
//
// uses sync.Map to manage reservations thread-safe
func reserveSubnet(subnet string, period time.Duration) bool {
var reserveSubnet = func(subnet string, period time.Duration) bool {
// put 'zero' reservation{} Map value for subnet Map key
// to block other processes from concurrently changing this subnet
zero := reservation{}

116
pkg/network/network_test.go Normal file
View File

@ -0,0 +1,116 @@
/*
Copyright 2022 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package network
import (
"strings"
"testing"
"time"
)
func TestIsSubnetPrivate(t *testing.T) {
tests := []struct {
subnet string
expected bool
}{
{"9.255.255.255", false},
{"10.0.0.0", true},
{"10.255.255.255", true},
{"11.0.0.0", false},
{"172.15.255.255", false},
{"172.16.0.0", true},
{"172.31.255.255", true},
{"172.32.0.0", false},
{"192.167.255.255", false},
{"192.168.0.0", true},
{"192.168.255.255", true},
{"192.169.0.0", false},
}
for _, test := range tests {
got := isSubnetPrivate(test.subnet)
if got != test.expected {
t.Errorf("isSubnetPrivate(%q) = %t; expected = %t", test.subnet, got, test.expected)
}
}
}
func TestFreeSubnet(t *testing.T) {
reserveSubnet = func(subnet string, period time.Duration) bool { return true }
t.Run("NoRetriesSuccess", func(t *testing.T) {
startingSubnet := "192.168.0.0"
subnet, err := FreeSubnet(startingSubnet, 0, 1)
if err != nil {
t.Fatal(err)
}
expectedIP := startingSubnet
if subnet.IP != expectedIP {
t.Errorf("expected IP = %q; got = %q", expectedIP, subnet.IP)
}
})
t.Run("FirstSubnetTaken", func(t *testing.T) {
count := 0
isSubnetTaken = func(subnet string) (bool, error) {
count++
return count == 1, nil
}
startingSubnet := "192.168.0.0"
subnet, err := FreeSubnet(startingSubnet, 9, 2)
if err != nil {
t.Fatal(err)
}
expectedIP := "192.168.9.0"
if subnet.IP != expectedIP {
t.Errorf("expected IP = %q; got = %q", expectedIP, subnet.IP)
}
})
t.Run("FirstSubnetIPV6NetworkFound", func(t *testing.T) {
count := 0
inspect = func(addr string) (*Parameters, error) {
count++
p := &Parameters{IP: addr}
if count == 1 {
p.IP = "0.0.0.0"
}
return p, nil
}
startingSubnet := "10.0.0.0"
subnet, err := FreeSubnet(startingSubnet, 9, 2)
if err != nil {
t.Fatal(err)
}
expectedIP := "10.9.0.0"
if subnet.IP != expectedIP {
t.Errorf("expepcted IP = %q; got = %q", expectedIP, subnet.IP)
}
})
t.Run("NonPrivateSubnet", func(t *testing.T) {
startingSubnet := "192.167.0.0"
_, err := FreeSubnet(startingSubnet, 9, 1)
if err == nil {
t.Fatalf("expected to fail since IP non-private but no error thrown")
}
if !strings.Contains(err.Error(), startingSubnet) {
t.Errorf("expected starting subnet of %q to be included in error, but intead got: %v", startingSubnet, err)
}
})
}