Merge branch 'upstream'

This commit is contained in:
2015-11-09 08:15:44 +00:00
100 changed files with 16891 additions and 347 deletions

View File

@@ -15,7 +15,6 @@
package network
import (
"encoding/json"
"fmt"
"log"
"net"
@@ -23,26 +22,18 @@ import (
"github.com/vtolstov/cloudinit/datasource/metadata/digitalocean"
)
func ProcessDigitalOceanNetconf(config []byte) ([]InterfaceGenerator, error) {
func ProcessDigitalOceanNetconf(config digitalocean.Metadata) ([]InterfaceGenerator, error) {
log.Println("Processing DigitalOcean network config")
if len(config) == 0 {
return nil, nil
}
var cfg digitalocean.Metadata
if err := json.Unmarshal(config, &cfg); err != nil {
return nil, err
}
log.Println("Parsing nameservers")
nameservers, err := parseNameservers(cfg.DNS)
nameservers, err := parseNameservers(config.DNS)
if err != nil {
return nil, err
}
log.Printf("Parsed %d nameservers\n", len(nameservers))
log.Println("Parsing interfaces")
generators, err := parseInterfaces(cfg.Interfaces, nameservers)
generators, err := parseInterfaces(config.Interfaces, nameservers)
if err != nil {
return nil, err
}
@@ -52,9 +43,9 @@ func ProcessDigitalOceanNetconf(config []byte) ([]InterfaceGenerator, error) {
return generators, nil
}
func parseNameservers(cfg digitalocean.DNS) ([]net.IP, error) {
nameservers := make([]net.IP, 0, len(cfg.Nameservers))
for _, ns := range cfg.Nameservers {
func parseNameservers(config digitalocean.DNS) ([]net.IP, error) {
nameservers := make([]net.IP, 0, len(config.Nameservers))
for _, ns := range config.Nameservers {
if ip := net.ParseIP(ns); ip == nil {
return nil, fmt.Errorf("could not parse %q as nameserver IP address", ns)
} else {
@@ -64,16 +55,16 @@ func parseNameservers(cfg digitalocean.DNS) ([]net.IP, error) {
return nameservers, nil
}
func parseInterfaces(cfg digitalocean.Interfaces, nameservers []net.IP) ([]InterfaceGenerator, error) {
generators := make([]InterfaceGenerator, 0, len(cfg.Public)+len(cfg.Private))
for _, iface := range cfg.Public {
func parseInterfaces(config digitalocean.Interfaces, nameservers []net.IP) ([]InterfaceGenerator, error) {
generators := make([]InterfaceGenerator, 0, len(config.Public)+len(config.Private))
for _, iface := range config.Public {
if generator, err := parseInterface(iface, nameservers, true); err == nil {
generators = append(generators, &physicalInterface{*generator})
} else {
return nil, err
}
}
for _, iface := range cfg.Private {
for _, iface := range config.Private {
if generator, err := parseInterface(iface, []net.IP{}, false); err == nil {
generators = append(generators, &physicalInterface{*generator})
} else {
@@ -135,6 +126,28 @@ func parseInterface(iface digitalocean.Interface, nameservers []net.IP, useRoute
})
}
}
if iface.AnchorIPv4 != nil {
var ip, mask net.IP
if ip = net.ParseIP(iface.AnchorIPv4.IPAddress); ip == nil {
return nil, fmt.Errorf("could not parse %q as anchor IPv4 address", iface.AnchorIPv4.IPAddress)
}
if mask = net.ParseIP(iface.AnchorIPv4.Netmask); mask == nil {
return nil, fmt.Errorf("could not parse %q as anchor IPv4 mask", iface.AnchorIPv4.Netmask)
}
addresses = append(addresses, net.IPNet{
IP: ip,
Mask: net.IPMask(mask),
})
if useRoute {
routes = append(routes, route{
destination: net.IPNet{
IP: net.IPv4zero,
Mask: net.IPMask(net.IPv4zero),
},
})
}
}
hwaddr, err := net.ParseMAC(iface.MAC)
if err != nil {

View File

@@ -52,6 +52,14 @@ func TestParseNameservers(t *testing.T) {
}
}
func mkInvalidMAC() error {
if isGo15 {
return &net.AddrError{Err: "invalid MAC address", Addr: "bad"}
} else {
return errors.New("invalid MAC address: bad")
}
}
func TestParseInterface(t *testing.T) {
for _, tt := range []struct {
cfg digitalocean.Interface
@@ -64,7 +72,7 @@ func TestParseInterface(t *testing.T) {
cfg: digitalocean.Interface{
MAC: "bad",
},
err: errors.New("invalid MAC address: bad"),
err: mkInvalidMAC(),
},
{
cfg: digitalocean.Interface{
@@ -250,6 +258,70 @@ func TestParseInterface(t *testing.T) {
},
},
},
{
cfg: digitalocean.Interface{
MAC: "01:23:45:67:89:AB",
AnchorIPv4: &digitalocean.Address{
IPAddress: "bad",
Netmask: "255.255.0.0",
},
},
nss: []net.IP{},
err: errors.New("could not parse \"bad\" as anchor IPv4 address"),
},
{
cfg: digitalocean.Interface{
MAC: "01:23:45:67:89:AB",
AnchorIPv4: &digitalocean.Address{
IPAddress: "1.2.3.4",
Netmask: "bad",
},
},
nss: []net.IP{},
err: errors.New("could not parse \"bad\" as anchor IPv4 mask"),
},
{
cfg: digitalocean.Interface{
MAC: "01:23:45:67:89:AB",
IPv4: &digitalocean.Address{
IPAddress: "1.2.3.4",
Netmask: "255.255.0.0",
Gateway: "5.6.7.8",
},
AnchorIPv4: &digitalocean.Address{
IPAddress: "7.8.9.10",
Netmask: "255.255.0.0",
},
},
useRoute: true,
nss: []net.IP{},
iface: &logicalInterface{
hwaddr: net.HardwareAddr([]byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab}),
config: configMethodStatic{
addresses: []net.IPNet{
{
IP: net.ParseIP("1.2.3.4"),
Mask: net.IPMask(net.ParseIP("255.255.0.0")),
},
{
IP: net.ParseIP("7.8.9.10"),
Mask: net.IPMask(net.ParseIP("255.255.0.0")),
},
},
nameservers: []net.IP{},
routes: []route{
{
destination: net.IPNet{IP: net.IPv4zero, Mask: net.IPMask(net.IPv4zero)},
gateway: net.ParseIP("5.6.7.8"),
},
{
destination: net.IPNet{IP: net.IPv4zero, Mask: net.IPMask(net.IPv4zero)},
},
},
},
},
},
} {
iface, err := parseInterface(tt.cfg, tt.nss, tt.useRoute)
if !errorsEqual(tt.err, err) {
@@ -337,13 +409,13 @@ func TestParseInterfaces(t *testing.T) {
cfg: digitalocean.Interfaces{
Public: []digitalocean.Interface{{MAC: "bad"}},
},
err: errors.New("invalid MAC address: bad"),
err: mkInvalidMAC(),
},
{
cfg: digitalocean.Interfaces{
Private: []digitalocean.Interface{{MAC: "bad"}},
},
err: errors.New("invalid MAC address: bad"),
err: mkInvalidMAC(),
},
} {
ifaces, err := parseInterfaces(tt.cfg, tt.nss)
@@ -358,27 +430,37 @@ func TestParseInterfaces(t *testing.T) {
func TestProcessDigitalOceanNetconf(t *testing.T) {
for _, tt := range []struct {
cfg string
cfg digitalocean.Metadata
ifaces []InterfaceGenerator
err error
}{
{
cfg: ``,
},
{
cfg: `{"dns":{"nameservers":["bad"]}}`,
cfg: digitalocean.Metadata{
DNS: digitalocean.DNS{
Nameservers: []string{"bad"},
},
},
err: errors.New("could not parse \"bad\" as nameserver IP address"),
},
{
cfg: `{"interfaces":{"public":[{"ipv4":{"ip_address":"bad"}}]}}`,
cfg: digitalocean.Metadata{
Interfaces: digitalocean.Interfaces{
Public: []digitalocean.Interface{
digitalocean.Interface{
IPv4: &digitalocean.Address{
IPAddress: "bad",
},
},
},
},
},
err: errors.New("could not parse \"bad\" as IPv4 address"),
},
{
cfg: `{}`,
ifaces: []InterfaceGenerator{},
},
} {
ifaces, err := ProcessDigitalOceanNetconf([]byte(tt.cfg))
ifaces, err := ProcessDigitalOceanNetconf(tt.cfg)
if !errorsEqual(tt.err, err) {
t.Fatalf("bad error (%q): want %q, got %q", tt.cfg, tt.err, err)
}

View File

@@ -130,7 +130,17 @@ type bondInterface struct {
}
func (b *bondInterface) Netdev() string {
return fmt.Sprintf("[NetDev]\nKind=bond\nName=%s\n", b.name)
config := fmt.Sprintf("[NetDev]\nKind=bond\nName=%s\n", b.name)
if b.hwaddr != nil {
config += fmt.Sprintf("MACAddress=%s\n", b.hwaddr.String())
}
config += fmt.Sprintf("\n[Bond]\n")
for _, name := range sortedKeys(b.options) {
config += fmt.Sprintf("%s=%s\n", name, b.options[name])
}
return config
}
func (b *bondInterface) Type() string {

View File

@@ -52,7 +52,7 @@ func TestInterfaceGenerators(t *testing.T) {
},
{
name: "testname",
netdev: "[NetDev]\nKind=bond\nName=testname\n",
netdev: "[NetDev]\nKind=bond\nName=testname\n\n[Bond]\n",
network: "[Match]\nName=testname\n\n[Network]\nBond=testbond1\nVLAN=testvlan1\nVLAN=testvlan2\nDHCP=true\n",
kind: "bond",
iface: &bondInterface{logicalInterface: logicalInterface{

View File

@@ -0,0 +1,5 @@
// +build !go1.5
package network
const isGo15 = false

View File

@@ -0,0 +1,5 @@
// +build go1.5
package network
const isGo15 = true

127
network/packet.go Normal file
View File

@@ -0,0 +1,127 @@
// Copyright 2015 CoreOS, Inc.
//
// 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 (
"net"
"github.com/coreos/coreos-cloudinit/datasource/metadata/packet"
)
func ProcessPacketNetconf(netdata packet.NetworkData) ([]InterfaceGenerator, error) {
var nameservers []net.IP
if netdata.DNS != nil {
nameservers = netdata.DNS
} else {
nameservers = append(nameservers, net.ParseIP("8.8.8.8"), net.ParseIP("8.8.4.4"))
}
generators, err := parseNetwork(netdata, nameservers)
if err != nil {
return nil, err
}
return generators, nil
}
func parseNetwork(netdata packet.NetworkData, nameservers []net.IP) ([]InterfaceGenerator, error) {
var interfaces []InterfaceGenerator
var addresses []net.IPNet
var routes []route
for _, netblock := range netdata.Netblocks {
addresses = append(addresses, net.IPNet{
IP: netblock.Address,
Mask: net.IPMask(netblock.Netmask),
})
if netblock.Public == false {
routes = append(routes, route{
destination: net.IPNet{
IP: net.IPv4(10, 0, 0, 0),
Mask: net.IPv4Mask(255, 0, 0, 0),
},
gateway: netblock.Gateway,
})
} else {
if netblock.AddressFamily == 4 {
routes = append(routes, route{
destination: net.IPNet{
IP: net.IPv4zero,
Mask: net.IPMask(net.IPv4zero),
},
gateway: netblock.Gateway,
})
} else {
routes = append(routes, route{
destination: net.IPNet{
IP: net.IPv6zero,
Mask: net.IPMask(net.IPv6zero),
},
gateway: netblock.Gateway,
})
}
}
}
bond := bondInterface{
logicalInterface: logicalInterface{
name: "bond0",
config: configMethodStatic{
addresses: addresses,
nameservers: nameservers,
routes: routes,
},
},
options: map[string]string{
"Mode": "802.3ad",
"LACPTransmitRate": "fast",
"MIIMonitorSec": ".2",
"UpDelaySec": ".2",
"DownDelaySec": ".2",
},
}
for _, iface := range netdata.Interfaces {
if iface.Name != "chassis0" && iface.Name != "ipmi0" {
bond.slaves = append(bond.slaves, iface.Name)
if iface.Name == "enp1s0f0" {
bond.hwaddr, _ = net.ParseMAC(iface.Mac)
}
}
}
for _, iface := range netdata.Interfaces {
if iface.Name != "chassis0" && iface.Name != "ipmi0" {
p := physicalInterface{
logicalInterface: logicalInterface{
name: iface.Name,
config: configMethodStatic{
nameservers: nameservers,
},
children: []networkInterface{&bond},
},
}
if iface.Name == "enp1s0f0" {
p.configDepth = 20
}
interfaces = append(interfaces, &p)
}
}
interfaces = append(interfaces, &bond)
return interfaces, nil
}

174
network/vmware.go Normal file
View File

@@ -0,0 +1,174 @@
// Copyright 2015 CoreOS, Inc.
//
// 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 (
"fmt"
"log"
"net"
)
func ProcessVMwareNetconf(config map[string]string) ([]InterfaceGenerator, error) {
log.Println("Processing VMware network config")
log.Println("Parsing nameservers")
var nameservers []net.IP
for i := 0; ; i++ {
if ipStr, ok := config[fmt.Sprintf("dns.server.%d", i)]; ok {
if ip := net.ParseIP(ipStr); ip != nil {
nameservers = append(nameservers, ip)
} else {
return nil, fmt.Errorf("invalid nameserver: %q", ipStr)
}
} else {
break
}
}
log.Printf("Parsed %d nameservers", len(nameservers))
var interfaces []InterfaceGenerator
for i := 0; ; i++ {
var addresses []net.IPNet
var routes []route
var err error
var dhcp bool
iface := &physicalInterface{}
log.Printf("Proccessing interface %d", i)
log.Println("Processing DHCP")
if dhcp, err = processDHCPConfig(config, fmt.Sprintf("interface.%d.", i)); err != nil {
return nil, err
}
log.Println("Processing addresses")
if as, err := processAddressConfig(config, fmt.Sprintf("interface.%d.", i)); err == nil {
addresses = append(addresses, as...)
} else {
return nil, err
}
log.Println("Processing routes")
if rs, err := processRouteConfig(config, fmt.Sprintf("interface.%d.", i)); err == nil {
routes = append(routes, rs...)
} else {
return nil, err
}
if mac, ok := config[fmt.Sprintf("interface.%d.mac", i)]; ok {
log.Printf("Parsing interface %d MAC address: %q", i, mac)
if hwaddr, err := net.ParseMAC(mac); err == nil {
iface.hwaddr = hwaddr
} else {
return nil, fmt.Errorf("error while parsing MAC address: %v", err)
}
}
if name, ok := config[fmt.Sprintf("interface.%d.name", i)]; ok {
log.Printf("Parsing interface %d name: %q", i, name)
iface.name = name
}
if len(addresses) > 0 || len(routes) > 0 {
iface.config = configMethodStatic{
hwaddress: iface.hwaddr,
addresses: addresses,
nameservers: nameservers,
routes: routes,
}
} else if dhcp {
iface.config = configMethodDHCP{
hwaddress: iface.hwaddr,
}
} else {
break
}
interfaces = append(interfaces, iface)
}
return interfaces, nil
}
func processAddressConfig(config map[string]string, prefix string) (addresses []net.IPNet, err error) {
for a := 0; ; a++ {
prefix := fmt.Sprintf("%sip.%d.", prefix, a)
addressStr, ok := config[prefix+"address"]
if !ok {
break
}
ip, network, err := net.ParseCIDR(addressStr)
if err != nil {
return nil, fmt.Errorf("invalid address: %q", addressStr)
}
addresses = append(addresses, net.IPNet{
IP: ip,
Mask: network.Mask,
})
}
return
}
func processRouteConfig(config map[string]string, prefix string) (routes []route, err error) {
for r := 0; ; r++ {
prefix := fmt.Sprintf("%sroute.%d.", prefix, r)
gatewayStr, gok := config[prefix+"gateway"]
destinationStr, dok := config[prefix+"destination"]
if gok && !dok {
return nil, fmt.Errorf("missing destination key")
} else if !gok && dok {
return nil, fmt.Errorf("missing gateway key")
} else if !gok && !dok {
break
}
gateway := net.ParseIP(gatewayStr)
if gateway == nil {
return nil, fmt.Errorf("invalid gateway: %q", gatewayStr)
}
_, destination, err := net.ParseCIDR(destinationStr)
if err != nil {
return nil, err
}
routes = append(routes, route{
destination: *destination,
gateway: gateway,
})
}
return
}
func processDHCPConfig(config map[string]string, prefix string) (dhcp bool, err error) {
dhcpStr, ok := config[prefix+"dhcp"]
if !ok {
return false, nil
}
switch dhcpStr {
case "yes":
return true, nil
case "no":
return false, nil
default:
return false, fmt.Errorf("invalid DHCP option: %q", dhcpStr)
}
}

361
network/vmware_test.go Normal file
View File

@@ -0,0 +1,361 @@
// Copyright 2015 CoreOS, Inc.
//
// 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 (
"errors"
"net"
"reflect"
"testing"
)
func mustParseMac(mac net.HardwareAddr, err error) net.HardwareAddr {
if err != nil {
panic(err)
}
return mac
}
func TestProcessVMwareNetconf(t *testing.T) {
tests := []struct {
config map[string]string
interfaces []InterfaceGenerator
err error
}{
{},
{
config: map[string]string{
"interface.0.dhcp": "yes",
},
interfaces: []InterfaceGenerator{
&physicalInterface{logicalInterface{
config: configMethodDHCP{},
}},
},
},
{
config: map[string]string{
"interface.0.mac": "00:11:22:33:44:55",
"interface.0.dhcp": "yes",
},
interfaces: []InterfaceGenerator{
&physicalInterface{logicalInterface{
hwaddr: mustParseMac(net.ParseMAC("00:11:22:33:44:55")),
config: configMethodDHCP{hwaddress: mustParseMac(net.ParseMAC("00:11:22:33:44:55"))},
}},
},
},
{
config: map[string]string{
"interface.0.name": "eth0",
"interface.0.dhcp": "yes",
},
interfaces: []InterfaceGenerator{
&physicalInterface{logicalInterface{
name: "eth0",
config: configMethodDHCP{},
}},
},
},
{
config: map[string]string{
"interface.0.mac": "00:11:22:33:44:55",
"interface.0.ip.0.address": "10.0.0.100/24",
"interface.0.route.0.gateway": "10.0.0.1",
"interface.0.route.0.destination": "0.0.0.0/0",
},
interfaces: []InterfaceGenerator{
&physicalInterface{logicalInterface{
hwaddr: mustParseMac(net.ParseMAC("00:11:22:33:44:55")),
config: configMethodStatic{
hwaddress: mustParseMac(net.ParseMAC("00:11:22:33:44:55")),
addresses: []net.IPNet{net.IPNet{IP: net.ParseIP("10.0.0.100"), Mask: net.CIDRMask(24, net.IPv4len*8)}},
// I realize how upset you must be that I am shoving an IPMask into an IP. This is because net.IPv4zero is
// actually a magic IPv6 address which ruins our equality check. What's that? Just use IP::Equal()? I'd rather
// DeepEqual just handle that for me, but until Go gets operator overloading, we are stuck with this.
routes: []route{route{
destination: net.IPNet{IP: net.IP(net.CIDRMask(0, net.IPv4len*8)), Mask: net.CIDRMask(0, net.IPv4len*8)},
gateway: net.ParseIP("10.0.0.1")},
},
},
}},
},
},
{
config: map[string]string{
"dns.server.0": "1.2.3.4",
"dns.server.1": "5.6.7.8",
"interface.0.mac": "00:11:22:33:44:55",
"interface.0.ip.0.address": "10.0.0.100/24",
"interface.0.ip.1.address": "10.0.0.101/24",
"interface.0.route.0.gateway": "10.0.0.1",
"interface.0.route.0.destination": "0.0.0.0/0",
"interface.1.name": "eth0",
"interface.1.ip.0.address": "10.0.1.100/24",
"interface.1.route.0.gateway": "10.0.1.1",
"interface.1.route.0.destination": "0.0.0.0/0",
"interface.2.dhcp": "yes",
"interface.2.mac": "00:11:22:33:44:77",
},
interfaces: []InterfaceGenerator{
&physicalInterface{logicalInterface{
hwaddr: mustParseMac(net.ParseMAC("00:11:22:33:44:55")),
config: configMethodStatic{
hwaddress: mustParseMac(net.ParseMAC("00:11:22:33:44:55")),
addresses: []net.IPNet{
net.IPNet{IP: net.ParseIP("10.0.0.100"), Mask: net.CIDRMask(24, net.IPv4len*8)},
net.IPNet{IP: net.ParseIP("10.0.0.101"), Mask: net.CIDRMask(24, net.IPv4len*8)},
},
routes: []route{route{
destination: net.IPNet{IP: net.IP(net.CIDRMask(0, net.IPv4len*8)), Mask: net.CIDRMask(0, net.IPv4len*8)},
gateway: net.ParseIP("10.0.0.1")},
},
nameservers: []net.IP{net.ParseIP("1.2.3.4"), net.ParseIP("5.6.7.8")},
},
}},
&physicalInterface{logicalInterface{
name: "eth0",
config: configMethodStatic{
addresses: []net.IPNet{net.IPNet{IP: net.ParseIP("10.0.1.100"), Mask: net.CIDRMask(24, net.IPv4len*8)}},
routes: []route{route{
destination: net.IPNet{IP: net.IP(net.CIDRMask(0, net.IPv4len*8)), Mask: net.CIDRMask(0, net.IPv4len*8)},
gateway: net.ParseIP("10.0.1.1")},
},
nameservers: []net.IP{net.ParseIP("1.2.3.4"), net.ParseIP("5.6.7.8")},
},
}},
&physicalInterface{logicalInterface{
hwaddr: mustParseMac(net.ParseMAC("00:11:22:33:44:77")),
config: configMethodDHCP{hwaddress: mustParseMac(net.ParseMAC("00:11:22:33:44:77"))},
}},
},
},
{
config: map[string]string{"dns.server.0": "test dns"},
err: errors.New(`invalid nameserver: "test dns"`),
},
}
for i, tt := range tests {
interfaces, err := ProcessVMwareNetconf(tt.config)
if !reflect.DeepEqual(tt.err, err) {
t.Errorf("bad error (#%d): want %v, got %v", i, tt.err, err)
}
if !reflect.DeepEqual(tt.interfaces, interfaces) {
t.Errorf("bad interfaces (#%d): want %#v, got %#v", i, tt.interfaces, interfaces)
for _, iface := range tt.interfaces {
t.Logf(" want: %#v", iface)
}
for _, iface := range interfaces {
t.Logf(" got: %#v", iface)
}
}
}
}
func TestProcessAddressConfig(t *testing.T) {
tests := []struct {
config map[string]string
prefix string
addresses []net.IPNet
err error
}{
{},
// static - ipv4
{
config: map[string]string{
"ip.0.address": "10.0.0.100/24",
},
addresses: []net.IPNet{{IP: net.ParseIP("10.0.0.100"), Mask: net.CIDRMask(24, net.IPv4len*8)}},
},
{
config: map[string]string{
"this.is.a.prefix.ip.0.address": "10.0.0.100/24",
},
prefix: "this.is.a.prefix.",
addresses: []net.IPNet{{IP: net.ParseIP("10.0.0.100"), Mask: net.CIDRMask(24, net.IPv4len*8)}},
},
{
config: map[string]string{
"ip.0.address": "10.0.0.100/24",
"ip.1.address": "10.0.0.101/24",
"ip.2.address": "10.0.0.102/24",
},
addresses: []net.IPNet{
{IP: net.ParseIP("10.0.0.100"), Mask: net.CIDRMask(24, net.IPv4len*8)},
{IP: net.ParseIP("10.0.0.101"), Mask: net.CIDRMask(24, net.IPv4len*8)},
{IP: net.ParseIP("10.0.0.102"), Mask: net.CIDRMask(24, net.IPv4len*8)},
},
},
// static - ipv6
{
config: map[string]string{
"ip.0.address": "fe00::100/64",
},
addresses: []net.IPNet{{IP: net.ParseIP("fe00::100"), Mask: net.IPMask(net.CIDRMask(64, net.IPv6len*8))}},
},
{
config: map[string]string{
"ip.0.address": "fe00::100/64",
"ip.1.address": "fe00::101/64",
"ip.2.address": "fe00::102/64",
},
addresses: []net.IPNet{
{IP: net.ParseIP("fe00::100"), Mask: net.CIDRMask(64, net.IPv6len*8)},
{IP: net.ParseIP("fe00::101"), Mask: net.CIDRMask(64, net.IPv6len*8)},
{IP: net.ParseIP("fe00::102"), Mask: net.CIDRMask(64, net.IPv6len*8)},
},
},
// invalid
{
config: map[string]string{
"ip.0.address": "test address",
},
err: errors.New(`invalid address: "test address"`),
},
}
for i, tt := range tests {
addresses, err := processAddressConfig(tt.config, tt.prefix)
if !reflect.DeepEqual(tt.err, err) {
t.Errorf("bad error (#%d): want %v, got %v", i, tt.err, err)
}
if err != nil {
continue
}
if !reflect.DeepEqual(tt.addresses, addresses) {
t.Errorf("bad addresses (#%d): want %#v, got %#v", i, tt.addresses, addresses)
}
}
}
func TestProcessRouteConfig(t *testing.T) {
tests := []struct {
config map[string]string
prefix string
routes []route
err error
}{
{},
{
config: map[string]string{
"route.0.gateway": "10.0.0.1",
"route.0.destination": "0.0.0.0/0",
},
routes: []route{{destination: net.IPNet{IP: net.IP(net.CIDRMask(0, net.IPv4len*8)), Mask: net.CIDRMask(0, net.IPv4len*8)}, gateway: net.ParseIP("10.0.0.1")}},
},
{
config: map[string]string{
"this.is.a.prefix.route.0.gateway": "10.0.0.1",
"this.is.a.prefix.route.0.destination": "0.0.0.0/0",
},
prefix: "this.is.a.prefix.",
routes: []route{{destination: net.IPNet{IP: net.IP(net.CIDRMask(0, net.IPv4len*8)), Mask: net.CIDRMask(0, net.IPv4len*8)}, gateway: net.ParseIP("10.0.0.1")}},
},
{
config: map[string]string{
"route.0.gateway": "fe00::1",
"route.0.destination": "::/0",
},
routes: []route{{destination: net.IPNet{IP: net.IPv6zero, Mask: net.IPMask(net.IPv6zero)}, gateway: net.ParseIP("fe00::1")}},
},
// invalid
{
config: map[string]string{
"route.0.gateway": "test gateway",
"route.0.destination": "0.0.0.0/0",
},
err: errors.New(`invalid gateway: "test gateway"`),
},
{
config: map[string]string{
"route.0.gateway": "10.0.0.1",
"route.0.destination": "test destination",
},
err: &net.ParseError{Type: "CIDR address", Text: "test destination"},
},
}
for i, tt := range tests {
routes, err := processRouteConfig(tt.config, tt.prefix)
if !reflect.DeepEqual(tt.err, err) {
t.Errorf("bad error (#%d): want %v, got %v", i, tt.err, err)
}
if err != nil {
continue
}
if !reflect.DeepEqual(tt.routes, routes) {
t.Errorf("bad routes (#%d): want %#v, got %#v", i, tt.routes, routes)
}
}
}
func TestProcessDHCPConfig(t *testing.T) {
tests := []struct {
config map[string]string
prefix string
dhcp bool
err error
}{
{},
// prefix
{config: map[string]string{"this.is.a.prefix.mac": ""}, prefix: "this.is.a.prefix.", dhcp: false},
{config: map[string]string{"this.is.a.prefix.dhcp": "yes"}, prefix: "this.is.a.prefix.", dhcp: true},
// dhcp
{config: map[string]string{"dhcp": "yes"}, dhcp: true},
{config: map[string]string{"dhcp": "no"}, dhcp: false},
// invalid
{config: map[string]string{"dhcp": "blah"}, err: errors.New(`invalid DHCP option: "blah"`)},
}
for i, tt := range tests {
dhcp, err := processDHCPConfig(tt.config, tt.prefix)
if !reflect.DeepEqual(tt.err, err) {
t.Errorf("bad error (#%d): want %v, got %v", i, tt.err, err)
}
if err != nil {
continue
}
if tt.dhcp != dhcp {
t.Errorf("bad dhcp (#%d): want %v, got %v", i, tt.dhcp, dhcp)
}
}
}