Skip to content

Commit

Permalink
make subnet picking more robust
Browse files Browse the repository at this point in the history
  • Loading branch information
BenTheElder committed Apr 30, 2020
1 parent 7be97c1 commit 407d42e
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 19 deletions.
46 changes: 36 additions & 10 deletions pkg/cluster/internal/providers/docker/network.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,11 @@ package docker

import (
"crypto/sha1"
"encoding/binary"
"errors"
"net"
"regexp"
"strings"

"sigs.k8s.io/kind/pkg/exec"
)
Expand All @@ -38,33 +41,56 @@ const fixedNetworkName = "kind"
func ensureNetwork(name string) error {
// TODO: the network might already exist and not have ipv6 ... :|
// discussion: https://github.com/kubernetes-sigs/kind/pull/1508#discussion_r414594198
out, err := exec.Output(exec.Command(
"docker", "network", "ls",
"--filter=name=^"+regexp.QuoteMeta(name)+"$",
"--format={{.Name}}",
))
exists, err := checkIfNetworkExists(name)
if err != nil {
return err
}
// network already exists
if string(out) == name+"\n" {
if exists {
return nil
}

// generate unique subnet per network based on the name
// obtained from the ULA fc00::/8 range
subnet := generateULASubnetFromName(name)
// make N attempts with "probing" in case we happen to collide
for i := int32(0); i < 3; i++ {
subnet := generateULASubnetFromName(name, i)
err = createNetwork(name, subnet)
if err == nil {
return nil
} else if !isPoolOverlapError(err) {
return err
}
}
return errors.New("exhausted attempts trying to find a non-overlapping subnet")
}

func createNetwork(name, subnet string) error {
return exec.Command("docker", "network", "create", "-d=bridge", "--ipv6", "--subnet", subnet, name).Run()
}

// generateULASubnetFromName generate an IPv6 subnet based on the name passed as parameter
func generateULASubnetFromName(name string) string {
func checkIfNetworkExists(name string) (bool, error) {
out, err := exec.Output(exec.Command(
"docker", "network", "ls",
"--filter=name=^"+regexp.QuoteMeta(name)+"$",
"--format={{.Name}}",
))
return strings.HasPrefix(string(out), name), err
}

func isPoolOverlapError(err error) bool {
rerr := exec.RunErrorForError(err)
return rerr != nil && strings.HasPrefix(string(rerr.Output), "Error response from daemon: Pool overlaps with other one on this address space")
}

// generateULASubnetFromName generate an IPv6 subnet based on the
// name and Nth probing attempt
func generateULASubnetFromName(name string, attempt int32) string {
ip := make([]byte, 16)
ip[0] = 0xfc
ip[1] = 0x00
h := sha1.New()
h.Write([]byte(name))
binary.Write(h, binary.LittleEndian, attempt)
bs := h.Sum(nil)
for i := 2; i < 8; i++ {
ip[i] = bs[i]
Expand Down
29 changes: 20 additions & 9 deletions pkg/cluster/internal/providers/docker/network_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,41 +17,52 @@ limitations under the License.
package docker

import (
"fmt"
"testing"
)

func Test_generateULASubnetFromName(t *testing.T) {
t.Parallel()
cases := []struct {
name string
subnet string
name string
attempt int32
subnet string
}{
{
name: "kind",
subnet: "fc00:5ae0:f40:c847::/64",
subnet: "fc00:f853:ccd:e793::/64",
},
{
name: "foo",
attempt: 1,
subnet: "fc00:8edf:7f02:ec8f::/64",
},
{
name: "foo",
attempt: 2,
subnet: "fc00:9968:306b:2c65::/64",
},
{
name: "kind2",
subnet: "fc00:cadf:7baf:98ea::/64",
subnet: "fc00:444c:147a:44ab::/64",
},
{
name: "kin",
subnet: "fc00:f804:3187:7254::/64",
subnet: "fc00:fcd9:c2be:8e23::/64",
},
{
name: "mysupernetwork",
subnet: "fc00:1dd9:697c:314e::/64",
subnet: "fc00:7ae1:1e0d:b4d4::/64",
},
}
for _, tc := range cases {
tc := tc // capture variable
t.Run(tc.name, func(t *testing.T) {
t.Run(fmt.Sprintf("%s,%d", tc.name, tc.attempt), func(t *testing.T) {
t.Parallel()
subnet := generateULASubnetFromName(tc.name)
subnet := generateULASubnetFromName(tc.name, tc.attempt)
if subnet != tc.subnet {
t.Errorf("Wrong subnet from %v: expected %v, received %v", tc.name, tc.subnet, subnet)
}

})
}
}

0 comments on commit 407d42e

Please sign in to comment.