diff --git a/go.mod b/go.mod index d5a8dc26..67a25cc9 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/Microsoft/hcsshim v0.8.6 github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44 - github.com/containernetworking/cni v0.8.1 + github.com/containernetworking/cni v0.8.1-0.20201216164644-62e54113f44a github.com/coreos/go-iptables v0.5.0 github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c @@ -17,7 +17,7 @@ require ( github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56 github.com/mattn/go-shellwords v1.0.3 - github.com/onsi/ginkgo v1.12.1 + github.com/onsi/ginkgo v1.13.0 github.com/onsi/gomega v1.10.3 github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 github.com/sirupsen/logrus v1.0.6 // indirect diff --git a/go.sum b/go.sum index 4ab479a3..f7d78e1f 100644 --- a/go.sum +++ b/go.sum @@ -6,8 +6,8 @@ github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae h1:AMzIhMUq github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44 h1:y853v6rXx+zefEcjET3JuKAqvhj+FKflQijjeaSv2iA= github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= -github.com/containernetworking/cni v0.8.1 h1:7zpDnQ3T3s4ucOuJ/ZCLrYBxzkg0AELFfII3Epo9TmI= -github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.1-0.20201216164644-62e54113f44a h1:EDko/CXJ2CYF5pFdlTO/qHwkD0IxpSQY+FcTll6A/F4= +github.com/containernetworking/cni v0.8.1-0.20201216164644-62e54113f44a/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtrBI6QcRV0NiNt15Y= github.com/coreos/go-iptables v0.5.0 h1:mw6SAibtHKZcNzAsOxjoHIG0gy5YFHhypWSSNc6EjbQ= github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM= @@ -24,6 +24,8 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c h1:RBUpb2b14UnmRHNd2uHz20ZHLDK+SW5Us/vWF5IHRaY= github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -48,13 +50,17 @@ github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.13.0 h1:M76yO2HkZASFjXL0HSoZJ1AYEmQxNJmY41Jx1zNUq1Y= +github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 h1:2c1EFnZHIPCW8qKWgHMH/fX2PkSabFc5mrVzfUNdg5U= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= +github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/sirupsen/logrus v1.0.6 h1:hcP1GmhGigz/O7h1WVUM5KklBp1JoNS9FggWKdj/j3s= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -69,6 +75,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnk golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARVd/2vfGcmI45D2gj45M= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -76,13 +83,17 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637 h1:O5hKNaGxIT4A8OTMnuh6UpmBdI3SAPxlZ3g0olDrJVM= golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/pkg/hns/endpoint_windows.go b/pkg/hns/endpoint_windows.go index 777f07b4..9dfa41d9 100644 --- a/pkg/hns/endpoint_windows.go +++ b/pkg/hns/endpoint_windows.go @@ -23,7 +23,7 @@ import ( "github.com/Microsoft/hcsshim/hcn" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/errors" ) @@ -286,28 +286,20 @@ func ConstructResult(hnsNetwork *hcsshim.HNSNetwork, hnsEndpoint *hcsshim.HNSEnd return nil, errors.Annotatef(err, "failed to parse CIDR from %s", hnsNetwork.Subnets[0].AddressPrefix) } - var ipVersion string - if ipv4 := hnsEndpoint.IPAddress.To4(); ipv4 != nil { - ipVersion = "4" - } else if ipv6 := hnsEndpoint.IPAddress.To16(); ipv6 != nil { - ipVersion = "6" - } else { - return nil, fmt.Errorf("IPAddress of HNSEndpoint %s isn't a valid ipv4 or ipv6 Address", hnsEndpoint.Name) - } - resultIPConfig := ¤t.IPConfig{ - Version: ipVersion, Address: net.IPNet{ IP: hnsEndpoint.IPAddress, Mask: ipSubnet.Mask}, Gateway: net.ParseIP(hnsEndpoint.GatewayAddress), } - result := ¤t.Result{} - result.Interfaces = []*current.Interface{resultInterface} - result.IPs = []*current.IPConfig{resultIPConfig} - result.DNS = types.DNS{ - Search: strings.Split(hnsEndpoint.DNSSuffix, ","), - Nameservers: strings.Split(hnsEndpoint.DNSServerList, ","), + result := ¤t.Result{ + CNIVersion: current.ImplementedSpecVersion, + Interfaces: []*current.Interface{resultInterface}, + IPs: []*current.IPConfig{resultIPConfig}, + DNS: types.DNS{ + Search: strings.Split(hnsEndpoint.DNSSuffix, ","), + Nameservers: strings.Split(hnsEndpoint.DNSServerList, ","), + }, } return result, nil @@ -341,29 +333,21 @@ func ConstructHcnResult(hcnNetwork *hcn.HostComputeNetwork, hcnEndpoint *hcn.Hos return nil, err } - var ipVersion string ipAddress := net.ParseIP(hcnEndpoint.IpConfigurations[0].IpAddress) - if ipv4 := ipAddress.To4(); ipv4 != nil { - ipVersion = "4" - } else if ipv6 := ipAddress.To16(); ipv6 != nil { - ipVersion = "6" - } else { - return nil, fmt.Errorf("[win-cni] The IPAddress of hnsEndpoint isn't a valid ipv4 or ipv6 Address.") - } - resultIPConfig := ¤t.IPConfig{ - Version: ipVersion, Address: net.IPNet{ IP: ipAddress, Mask: ipSubnet.Mask}, Gateway: net.ParseIP(hcnEndpoint.Routes[0].NextHop), } - result := ¤t.Result{} - result.Interfaces = []*current.Interface{resultInterface} - result.IPs = []*current.IPConfig{resultIPConfig} - result.DNS = types.DNS{ - Search: hcnEndpoint.Dns.Search, - Nameservers: hcnEndpoint.Dns.ServerList, + result := ¤t.Result{ + CNIVersion: current.ImplementedSpecVersion, + Interfaces: []*current.Interface{resultInterface}, + IPs: []*current.IPConfig{resultIPConfig}, + DNS: types.DNS{ + Search: hcnEndpoint.Dns.Search, + Nameservers: hcnEndpoint.Dns.ServerList, + }, } return result, nil diff --git a/pkg/ip/ipforward_linux.go b/pkg/ip/ipforward_linux.go index 8216a2c3..e52a45ba 100644 --- a/pkg/ip/ipforward_linux.go +++ b/pkg/ip/ipforward_linux.go @@ -18,7 +18,7 @@ import ( "bytes" "io/ioutil" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" ) func EnableIP4Forward() error { @@ -36,12 +36,13 @@ func EnableForward(ips []*current.IPConfig) error { v6 := false for _, ip := range ips { - if ip.Version == "4" && !v4 { + isV4 := ip.Address.IP.To4() != nil + if isV4 && !v4 { if err := EnableIP4Forward(); err != nil { return err } v4 = true - } else if ip.Version == "6" && !v6 { + } else if !isV4 && !v6 { if err := EnableIP6Forward(); err != nil { return err } diff --git a/pkg/ip/utils_linux.go b/pkg/ip/utils_linux.go index 7623c5e1..0cba4891 100644 --- a/pkg/ip/utils_linux.go +++ b/pkg/ip/utils_linux.go @@ -21,7 +21,7 @@ import ( "net" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/vishvananda/netlink" ) @@ -57,15 +57,10 @@ func ValidateExpectedInterfaceIPs(ifName string, resultIPs []*current.IPConfig) findGwy := &netlink.Route{Dst: ourPrefix} routeFilter := netlink.RT_FILTER_DST - var family int - switch { - case ips.Version == "4": + family := netlink.FAMILY_V6 + if ips.Address.IP.To4() != nil { family = netlink.FAMILY_V4 - case ips.Version == "6": - family = netlink.FAMILY_V6 - default: - return fmt.Errorf("Invalid IP Version %v for interface %v", ips.Version, ifName) } gwy, err := netlink.RouteListFiltered(family, findGwy, routeFilter) diff --git a/pkg/ipam/ipam_linux.go b/pkg/ipam/ipam_linux.go index 86052d4d..bf16cb0f 100644 --- a/pkg/ipam/ipam_linux.go +++ b/pkg/ipam/ipam_linux.go @@ -19,7 +19,7 @@ import ( "net" "os" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/ip" "github.com/containernetworking/plugins/pkg/utils/sysctl" @@ -61,7 +61,7 @@ func ConfigureIface(ifName string, res *current.Result) error { // Make sure sysctl "disable_ipv6" is 0 if we are about to add // an IPv6 address to the interface - if !has_enabled_ipv6 && ipc.Version == "6" { + if !has_enabled_ipv6 && ipc.Address.IP.To4() == nil { // Enabled IPv6 for loopback "lo" and the interface // being configured for _, iface := range [2]string{"lo", ifName} { diff --git a/pkg/ipam/ipam_linux_test.go b/pkg/ipam/ipam_linux_test.go index a2e974ab..1921cb88 100644 --- a/pkg/ipam/ipam_linux_test.go +++ b/pkg/ipam/ipam_linux_test.go @@ -19,7 +19,7 @@ import ( "syscall" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" @@ -109,13 +109,11 @@ var _ = Describe("ConfigureIface", func() { }, IPs: []*current.IPConfig{ { - Version: "4", Interface: current.Int(0), Address: *ipv4, Gateway: ipgw4, }, { - Version: "6", Interface: current.Int(0), Address: *ipv6, Gateway: ipgw6, @@ -281,12 +279,10 @@ var _ = Describe("ConfigureIface", func() { }, IPs: []*current.IPConfig{ { - Version: "4", Address: *ipv4, Gateway: ipgw4, }, { - Version: "6", Address: *ipv6, Gateway: ipgw6, }, diff --git a/pkg/testutils/ping.go b/pkg/testutils/ping.go index 5ee9db1c..8c47c3d7 100644 --- a/pkg/testutils/ping.go +++ b/pkg/testutils/ping.go @@ -17,13 +17,24 @@ package testutils import ( "bytes" "fmt" + "net" "os/exec" "strconv" "syscall" ) // Ping shells out to the `ping` command. Returns nil if successful. -func Ping(saddr, daddr string, isV6 bool, timeoutSec int) error { +func Ping(saddr, daddr string, timeoutSec int) error { + ip := net.ParseIP(saddr) + if ip == nil { + return fmt.Errorf("failed to parse IP %q", saddr) + } + + bin := "ping6" + if ip.To4() != nil { + bin = "ping" + } + args := []string{ "-c", "1", "-W", strconv.Itoa(timeoutSec), @@ -31,11 +42,6 @@ func Ping(saddr, daddr string, isV6 bool, timeoutSec int) error { daddr, } - bin := "ping" - if isV6 { - bin = "ping6" - } - cmd := exec.Command(bin, args...) var stderr bytes.Buffer cmd.Stderr = &stderr diff --git a/pkg/testutils/testing.go b/pkg/testutils/testing.go new file mode 100644 index 00000000..9444a8b2 --- /dev/null +++ b/pkg/testutils/testing.go @@ -0,0 +1,54 @@ +// Copyright 2016 CNI authors +// +// 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 testutils + +import ( + "github.com/containernetworking/cni/pkg/version" +) + +// AllSpecVersions contains all CNI spec version numbers +var AllSpecVersions = [...]string{"0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0", "1.0.0"} + +// SpecVersionHasIPVersion returns true if the given CNI specification version +// includes the "version" field in the IP address elements +func SpecVersionHasIPVersion(ver string) bool { + for _, i := range []string{"0.3.0", "0.3.1", "0.4.0"} { + if ver == i { + return true + } + } + return false +} + +// SpecVersionHasCHECK returns true if the given CNI specification version +// supports the CHECK command +func SpecVersionHasCHECK(ver string) bool { + ok, _ := version.GreaterThanOrEqualTo(ver, "0.4.0") + return ok +} + +// SpecVersionHasChaining returns true if the given CNI specification version +// supports plugin chaining +func SpecVersionHasChaining(ver string) bool { + ok, _ := version.GreaterThanOrEqualTo(ver, "0.3.0") + return ok +} + +// SpecVersionHasMultipleIPs returns true if the given CNI specification version +// supports more than one IP address of each family +func SpecVersionHasMultipleIPs(ver string) bool { + ok, _ := version.GreaterThanOrEqualTo(ver, "0.3.0") + return ok +} diff --git a/plugins/ipam/dhcp/daemon.go b/plugins/ipam/dhcp/daemon.go index 83d475d0..73606d0d 100644 --- a/plugins/ipam/dhcp/daemon.go +++ b/plugins/ipam/dhcp/daemon.go @@ -30,7 +30,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/coreos/go-systemd/activation" ) @@ -43,13 +43,15 @@ type DHCP struct { leases map[string]*DHCPLease hostNetnsPrefix string clientTimeout time.Duration + clientResendMax time.Duration broadcast bool } -func newDHCP(clientTimeout time.Duration) *DHCP { +func newDHCP(clientTimeout, clientResendMax time.Duration) *DHCP { return &DHCP{ - leases: make(map[string]*DHCPLease), - clientTimeout: clientTimeout, + leases: make(map[string]*DHCPLease), + clientTimeout: clientTimeout, + clientResendMax: clientResendMax, } } @@ -67,7 +69,7 @@ func (d *DHCP) Allocate(args *skel.CmdArgs, result *current.Result) error { clientID := generateClientID(args.ContainerID, conf.Name, args.IfName) hostNetns := d.hostNetnsPrefix + args.Netns - l, err := AcquireLease(clientID, hostNetns, args.IfName, d.clientTimeout, d.broadcast) + l, err := AcquireLease(clientID, hostNetns, args.IfName, d.clientTimeout, d.clientResendMax, d.broadcast) if err != nil { return err } @@ -81,7 +83,6 @@ func (d *DHCP) Allocate(args *skel.CmdArgs, result *current.Result) error { d.setLease(clientID, l) result.IPs = []*current.IPConfig{{ - Version: "4", Address: *ipn, Gateway: l.Gateway(), }} @@ -162,7 +163,7 @@ func getListener(socketPath string) (net.Listener, error) { func runDaemon( pidfilePath, hostPrefix, socketPath string, - dhcpClientTimeout time.Duration, broadcast bool, + dhcpClientTimeout time.Duration, resendMax time.Duration, broadcast bool, ) error { // since other goroutines (on separate threads) will change namespaces, // ensure the RPC server does not get scheduled onto those @@ -183,7 +184,7 @@ func runDaemon( return fmt.Errorf("Error getting listener: %v", err) } - dhcp := newDHCP(dhcpClientTimeout) + dhcp := newDHCP(dhcpClientTimeout, resendMax) dhcp.hostNetnsPrefix = hostPrefix dhcp.broadcast = broadcast rpc.Register(dhcp) diff --git a/plugins/ipam/dhcp/dhcp2_test.go b/plugins/ipam/dhcp/dhcp2_test.go index fecd601e..f5a4c320 100644 --- a/plugins/ipam/dhcp/dhcp2_test.go +++ b/plugins/ipam/dhcp/dhcp2_test.go @@ -23,7 +23,7 @@ import ( "time" "github.com/containernetworking/cni/pkg/skel" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" diff --git a/plugins/ipam/dhcp/dhcp_test.go b/plugins/ipam/dhcp/dhcp_test.go index 560745ca..2a1de5c6 100644 --- a/plugins/ipam/dhcp/dhcp_test.go +++ b/plugins/ipam/dhcp/dhcp_test.go @@ -15,7 +15,9 @@ package main import ( + "bytes" "fmt" + "io" "io/ioutil" "net" "os" @@ -25,7 +27,7 @@ import ( "time" "github.com/containernetworking/cni/pkg/skel" - "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" @@ -208,6 +210,11 @@ var _ = Describe("DHCP Operations", func() { dhcpPluginPath, err := exec.LookPath("dhcp") Expect(err).NotTo(HaveOccurred()) clientCmd = exec.Command(dhcpPluginPath, "daemon", "-socketpath", socketPath) + + // copy dhcp client's stdout/stderr to test stdout + clientCmd.Stdout = os.Stdout + clientCmd.Stderr = os.Stderr + err = clientCmd.Start() Expect(err).NotTo(HaveOccurred()) Expect(clientCmd.Process).NotTo(BeNil()) @@ -226,118 +233,127 @@ var _ = Describe("DHCP Operations", func() { clientCmd.Wait() Expect(originalNS.Close()).To(Succeed()) + Expect(testutils.UnmountNS(originalNS)).To(Succeed()) Expect(targetNS.Close()).To(Succeed()) - defer os.RemoveAll(tmpDir) + Expect(testutils.UnmountNS(targetNS)).To(Succeed()) + + Expect(os.RemoveAll(tmpDir)).To(Succeed()) }) - It("configures and deconfigures a link with ADD/DEL", func() { - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ipvlan", - "ipam": { - "type": "dhcp", - "daemonSocketPath": "%s" - } -}`, socketPath) + for _, ver := range testutils.AllSpecVersions { + // Redefine ver inside for scope so real value is picked up by each dynamically defined It() + // See Gingkgo's "Patterns for dynamically generating tests" documentation. + ver := ver - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: contVethName, - StdinData: []byte(conf), - } + It(fmt.Sprintf("[%s] configures and deconfigures a link with ADD/DEL", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "ipam": { + "type": "dhcp", + "daemonSocketPath": "%s" + } + }`, ver, socketPath) - var addResult *current.Result - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: contVethName, + StdinData: []byte(conf), + } - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - - addResult, err = current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - Expect(len(addResult.IPs)).To(Equal(1)) - Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24")) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - err = originalNS.Do(func(ns.NetNS) error { - return testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("correctly handles multiple DELs for the same container", func() { - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ipvlan", - "ipam": { - "type": "dhcp", - "daemonSocketPath": "%s" - } -}`, socketPath) - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: contVethName, - StdinData: []byte(conf), - } - - var addResult *current.Result - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - - addResult, err = current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - Expect(len(addResult.IPs)).To(Equal(1)) - Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24")) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - wg := sync.WaitGroup{} - wg.Add(3) - started := sync.WaitGroup{} - started.Add(3) - for i := 0; i < 3; i++ { - go func() { + var addResult *types100.Result + err := originalNS.Do(func(ns.NetNS) error { defer GinkgoRecover() - // Wait until all goroutines are running - started.Done() - started.Wait() - - err = originalNS.Do(func(ns.NetNS) error { - return testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) }) Expect(err).NotTo(HaveOccurred()) - wg.Done() - }() - } - wg.Wait() - err = originalNS.Do(func(ns.NetNS) error { - return testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + addResult, err = types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + Expect(len(addResult.IPs)).To(Equal(1)) + Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24")) + return nil }) + Expect(err).NotTo(HaveOccurred()) + + err = originalNS.Do(func(ns.NetNS) error { + return testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + }) + Expect(err).NotTo(HaveOccurred()) }) - Expect(err).NotTo(HaveOccurred()) - }) + + It(fmt.Sprintf("[%s] correctly handles multiple DELs for the same container", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "ipam": { + "type": "dhcp", + "daemonSocketPath": "%s" + } + }`, ver, socketPath) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: contVethName, + StdinData: []byte(conf), + } + + var addResult *types100.Result + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + + addResult, err = types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + Expect(len(addResult.IPs)).To(Equal(1)) + Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24")) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + wg := sync.WaitGroup{} + wg.Add(3) + started := sync.WaitGroup{} + started.Add(3) + for i := 0; i < 3; i++ { + go func() { + defer GinkgoRecover() + + // Wait until all goroutines are running + started.Done() + started.Wait() + + err = originalNS.Do(func(ns.NetNS) error { + return testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + }) + Expect(err).NotTo(HaveOccurred()) + wg.Done() + }() + } + wg.Wait() + + err = originalNS.Do(func(ns.NetNS) error { + return testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + }) + Expect(err).NotTo(HaveOccurred()) + }) + } }) const ( @@ -508,7 +524,19 @@ var _ = Describe("DHCP Lease Unavailable Operations", func() { // Start the DHCP client daemon dhcpPluginPath, err := exec.LookPath("dhcp") Expect(err).NotTo(HaveOccurred()) - clientCmd = exec.Command(dhcpPluginPath, "daemon", "-socketpath", socketPath) + // Use very short timeouts for lease-unavailable operations because + // the same test is run many times, and the delays will exceed the + // `go test` timeout with default delays. Since our DHCP server + // and client daemon are local processes anyway, we can depend on + // them to respond very quickly. + clientCmd = exec.Command(dhcpPluginPath, "daemon", "-socketpath", socketPath, "-timeout", "2s", "-resendmax", "8s") + + // copy dhcp client's stdout/stderr to test stdout + var b bytes.Buffer + mw := io.MultiWriter(os.Stdout, &b) + clientCmd.Stdout = mw + clientCmd.Stderr = mw + err = clientCmd.Start() Expect(err).NotTo(HaveOccurred()) Expect(clientCmd.Process).NotTo(BeNil()) @@ -527,92 +555,101 @@ var _ = Describe("DHCP Lease Unavailable Operations", func() { clientCmd.Wait() Expect(originalNS.Close()).To(Succeed()) + Expect(testutils.UnmountNS(originalNS)).To(Succeed()) Expect(targetNS.Close()).To(Succeed()) - defer os.RemoveAll(tmpDir) + Expect(testutils.UnmountNS(targetNS)).To(Succeed()) + + Expect(os.RemoveAll(tmpDir)).To(Succeed()) }) - It("Configures multiple links with multiple ADD with second lease unavailable", func() { - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "bridge", - "bridge": "%s", - "ipam": { - "type": "dhcp", - "daemonSocketPath": "%s" - } - }`, hostBridgeName, socketPath) + for _, ver := range testutils.AllSpecVersions { + // Redefine ver inside for scope so real value is picked up by each dynamically defined It() + // See Gingkgo's "Patterns for dynamically generating tests" documentation. + ver := ver - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: contVethName0, - StdinData: []byte(conf), - } + It(fmt.Sprintf("[%s] configures multiple links with multiple ADD with second lease unavailable", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "bridge", + "bridge": "%s", + "ipam": { + "type": "dhcp", + "daemonSocketPath": "%s" + } + }`, ver, hostBridgeName, socketPath) - var addResult *current.Result - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: contVethName0, + StdinData: []byte(conf), + } - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + var addResult *types100.Result + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + + addResult, err = types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + Expect(len(addResult.IPs)).To(Equal(1)) + Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24")) + return nil }) Expect(err).NotTo(HaveOccurred()) - addResult, err = current.GetResult(r) + args = &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: contVethName1, + StdinData: []byte(conf), + } + + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + _, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).To(HaveOccurred()) + println(err.Error()) + Expect(err.Error()).To(Equal("error calling DHCP.Allocate: no more tries")) + return nil + }) Expect(err).NotTo(HaveOccurred()) - Expect(len(addResult.IPs)).To(Equal(1)) - Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24")) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - args = &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: contVethName1, - StdinData: []byte(conf), - } + args = &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: contVethName1, + StdinData: []byte(conf), + } - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - _, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + err = originalNS.Do(func(ns.NetNS) error { + return testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) }) - Expect(err).To(HaveOccurred()) - println(err.Error()) - Expect(err.Error()).To(Equal("error calling DHCP.Allocate: no more tries")) - return nil - }) - Expect(err).NotTo(HaveOccurred()) + Expect(err).NotTo(HaveOccurred()) - args = &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: contVethName1, - StdinData: []byte(conf), - } + args = &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: contVethName0, + StdinData: []byte(conf), + } - err = originalNS.Do(func(ns.NetNS) error { - return testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + err = originalNS.Do(func(ns.NetNS) error { + return testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) }) + Expect(err).NotTo(HaveOccurred()) }) - Expect(err).NotTo(HaveOccurred()) - - args = &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: contVethName0, - StdinData: []byte(conf), - } - - err = originalNS.Do(func(ns.NetNS) error { - return testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - }) - Expect(err).NotTo(HaveOccurred()) - }) + } }) diff --git a/plugins/ipam/dhcp/lease.go b/plugins/ipam/dhcp/lease.go index 855dd28c..ef68931d 100644 --- a/plugins/ipam/dhcp/lease.go +++ b/plugins/ipam/dhcp/lease.go @@ -57,6 +57,7 @@ type DHCPLease struct { rebindingTime time.Time expireTime time.Time timeout time.Duration + resendMax time.Duration broadcast bool stopping uint32 stop chan struct{} @@ -68,13 +69,14 @@ type DHCPLease struct { // calling DHCPLease.Stop() func AcquireLease( clientID, netns, ifName string, - timeout time.Duration, broadcast bool, + timeout, resendMax time.Duration, broadcast bool, ) (*DHCPLease, error) { errCh := make(chan error, 1) l := &DHCPLease{ clientID: clientID, stop: make(chan struct{}), timeout: timeout, + resendMax: resendMax, broadcast: broadcast, } @@ -139,7 +141,7 @@ func (l *DHCPLease) acquire() error { opts[dhcp4.OptionClientIdentifier] = []byte(l.clientID) opts[dhcp4.OptionParameterRequestList] = []byte{byte(dhcp4.OptionRouter), byte(dhcp4.OptionSubnetMask)} - pkt, err := backoffRetry(func() (*dhcp4.Packet, error) { + pkt, err := backoffRetry(l.resendMax, func() (*dhcp4.Packet, error) { ok, ack, err := DhcpRequest(c, opts) switch { case err != nil: @@ -258,7 +260,7 @@ func (l *DHCPLease) renew() error { opts := make(dhcp4.Options) opts[dhcp4.OptionClientIdentifier] = []byte(l.clientID) - pkt, err := backoffRetry(func() (*dhcp4.Packet, error) { + pkt, err := backoffRetry(l.resendMax, func() (*dhcp4.Packet, error) { ok, ack, err := DhcpRenew(c, *l.ack, opts) switch { case err != nil: @@ -340,7 +342,7 @@ func jitter(span time.Duration) time.Duration { return time.Duration(float64(span) * (2.0*rand.Float64() - 1.0)) } -func backoffRetry(f func() (*dhcp4.Packet, error)) (*dhcp4.Packet, error) { +func backoffRetry(resendMax time.Duration, f func() (*dhcp4.Packet, error)) (*dhcp4.Packet, error) { var baseDelay time.Duration = resendDelay0 var sleepTime time.Duration @@ -358,7 +360,7 @@ func backoffRetry(f func() (*dhcp4.Packet, error)) (*dhcp4.Packet, error) { time.Sleep(sleepTime) - if baseDelay < resendDelayMax { + if baseDelay < resendMax { baseDelay *= 2 } else { break diff --git a/plugins/ipam/dhcp/main.go b/plugins/ipam/dhcp/main.go index 1435f762..72680607 100644 --- a/plugins/ipam/dhcp/main.go +++ b/plugins/ipam/dhcp/main.go @@ -26,7 +26,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" bv "github.com/containernetworking/plugins/pkg/utils/buildversion" ) @@ -40,19 +40,21 @@ func main() { var socketPath string var broadcast bool var timeout time.Duration + var resendMax time.Duration daemonFlags := flag.NewFlagSet("daemon", flag.ExitOnError) daemonFlags.StringVar(&pidfilePath, "pidfile", "", "optional path to write daemon PID to") daemonFlags.StringVar(&hostPrefix, "hostprefix", "", "optional prefix to host root") daemonFlags.StringVar(&socketPath, "socketpath", "", "optional dhcp server socketpath") daemonFlags.BoolVar(&broadcast, "broadcast", false, "broadcast DHCP leases") daemonFlags.DurationVar(&timeout, "timeout", 10*time.Second, "optional dhcp client timeout duration") + daemonFlags.DurationVar(&resendMax, "resendmax", resendDelayMax, "optional dhcp client resend max duration") daemonFlags.Parse(os.Args[2:]) if socketPath == "" { socketPath = defaultSocketPath } - if err := runDaemon(pidfilePath, hostPrefix, socketPath, timeout, broadcast); err != nil { + if err := runDaemon(pidfilePath, hostPrefix, socketPath, timeout, resendMax, broadcast); err != nil { log.Printf(err.Error()) os.Exit(1) } @@ -69,7 +71,7 @@ func cmdAdd(args *skel.CmdArgs) error { return err } - result := ¤t.Result{} + result := ¤t.Result{CNIVersion: current.ImplementedSpecVersion} if err := rpcCall("DHCP.Allocate", args, result); err != nil { return err } @@ -96,7 +98,7 @@ func cmdCheck(args *skel.CmdArgs) error { return err } - result := ¤t.Result{} + result := ¤t.Result{CNIVersion: current.ImplementedSpecVersion} if err := rpcCall("DHCP.Allocate", args, result); err != nil { return err } diff --git a/plugins/ipam/host-local/backend/allocator/allocator.go b/plugins/ipam/host-local/backend/allocator/allocator.go index 4cec1a74..8469b6d2 100644 --- a/plugins/ipam/host-local/backend/allocator/allocator.go +++ b/plugins/ipam/host-local/backend/allocator/allocator.go @@ -21,7 +21,7 @@ import ( "os" "strconv" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/ip" "github.com/containernetworking/plugins/plugins/ipam/host-local/backend" ) @@ -108,13 +108,8 @@ func (a *IPAllocator) Get(id string, ifname string, requestedIP net.IP) (*curren if reservedIP == nil { return nil, fmt.Errorf("no IP addresses available in range set: %s", a.rangeset.String()) } - version := "4" - if reservedIP.IP.To4() == nil { - version = "6" - } return ¤t.IPConfig{ - Version: version, Address: *reservedIP, Gateway: gw, }, nil diff --git a/plugins/ipam/host-local/backend/allocator/allocator_test.go b/plugins/ipam/host-local/backend/allocator/allocator_test.go index a7027c15..d06fe5b9 100644 --- a/plugins/ipam/host-local/backend/allocator/allocator_test.go +++ b/plugins/ipam/host-local/backend/allocator/allocator_test.go @@ -19,7 +19,7 @@ import ( "net" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" fakestore "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/testing" . "github.com/onsi/ginkgo" diff --git a/plugins/ipam/host-local/backend/allocator/config.go b/plugins/ipam/host-local/backend/allocator/config.go index c8cb2a74..936b0979 100644 --- a/plugins/ipam/host-local/backend/allocator/config.go +++ b/plugins/ipam/host-local/backend/allocator/config.go @@ -20,7 +20,7 @@ import ( "net" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/020" + "github.com/containernetworking/cni/pkg/version" ) // The top-level network config - IPAM plugins are passed the full configuration @@ -136,10 +136,8 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) { // CNI spec 0.2.0 and below supported only one v4 and v6 address if numV4 > 1 || numV6 > 1 { - for _, v := range types020.SupportedVersions { - if n.CNIVersion == v { - return nil, "", fmt.Errorf("CNI version %v does not support more than 1 address per family", n.CNIVersion) - } + if ok, _ := version.GreaterThanOrEqualTo(n.CNIVersion, "0.3.0"); !ok { + return nil, "", fmt.Errorf("CNI version %v does not support more than 1 address per family", n.CNIVersion) } } diff --git a/plugins/ipam/host-local/host_local_test.go b/plugins/ipam/host-local/host_local_test.go index 6f32c0a3..5cba3d40 100644 --- a/plugins/ipam/host-local/host_local_test.go +++ b/plugins/ipam/host-local/host_local_test.go @@ -24,8 +24,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/020" - "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/testutils" "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/disk" @@ -36,753 +35,651 @@ import ( const LineBreak = "\r\n" var _ = Describe("host-local Operations", func() { - It("allocates and releases addresses with ADD/DEL", func() { - const ifname string = "eth0" - const nspath string = "/some/where" + var tmpDir string + const ( + ifname string = "eth0" + nspath string = "/some/where" + ) - tmpDir, err := getTmpDir() + BeforeEach(func() { + var err error + tmpDir, err = ioutil.TempDir("", "host-local_test") Expect(err).NotTo(HaveOccurred()) - defer os.RemoveAll(tmpDir) - - err = ioutil.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0644) - Expect(err).NotTo(HaveOccurred()) - - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ipvlan", - "master": "foo0", - "ipam": { - "type": "host-local", - "dataDir": "%s", - "resolvConf": "%s/resolv.conf", - "ranges": [ - [{ "subnet": "10.1.2.0/24" }, {"subnet": "10.2.2.0/24"}], - [{ "subnet": "2001:db8:1::0/64" }] - ], - "routes": [ - {"dst": "0.0.0.0/0"}, - {"dst": "::/0"}, - {"dst": "192.168.0.0/16", "gw": "1.1.1.1"}, - {"dst": "2001:db8:2::0/64", "gw": "2001:db8:3::1"} - ] - } - }`, tmpDir, tmpDir) - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: nspath, - IfName: ifname, - StdinData: []byte(conf), - } - - // Allocate the IP - r, raw, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - // Gomega is cranky about slices with different caps - Expect(*result.IPs[0]).To(Equal( - current.IPConfig{ - Version: "4", - Address: mustCIDR("10.1.2.2/24"), - Gateway: net.ParseIP("10.1.2.1"), - })) - - Expect(*result.IPs[1]).To(Equal( - current.IPConfig{ - Version: "6", - Address: mustCIDR("2001:db8:1::2/64"), - Gateway: net.ParseIP("2001:db8:1::1"), - }, - )) - Expect(len(result.IPs)).To(Equal(2)) - - Expect(result.Routes).To(Equal([]*types.Route{ - {Dst: mustCIDR("0.0.0.0/0"), GW: nil}, - {Dst: mustCIDR("::/0"), GW: nil}, - {Dst: mustCIDR("192.168.0.0/16"), GW: net.ParseIP("1.1.1.1")}, - {Dst: mustCIDR("2001:db8:2::0/64"), GW: net.ParseIP("2001:db8:3::1")}, - })) - - ipFilePath1 := filepath.Join(tmpDir, "mynet", "10.1.2.2") - contents, err := ioutil.ReadFile(ipFilePath1) - Expect(err).NotTo(HaveOccurred()) - Expect(string(contents)).To(Equal(args.ContainerID + LineBreak + ifname)) - - ipFilePath2 := filepath.Join(tmpDir, disk.GetEscapedPath("mynet", "2001:db8:1::2")) - contents, err = ioutil.ReadFile(ipFilePath2) - Expect(err).NotTo(HaveOccurred()) - Expect(string(contents)).To(Equal(args.ContainerID + LineBreak + ifname)) - - lastFilePath1 := filepath.Join(tmpDir, "mynet", "last_reserved_ip.0") - contents, err = ioutil.ReadFile(lastFilePath1) - Expect(err).NotTo(HaveOccurred()) - Expect(string(contents)).To(Equal("10.1.2.2")) - - lastFilePath2 := filepath.Join(tmpDir, "mynet", "last_reserved_ip.1") - contents, err = ioutil.ReadFile(lastFilePath2) - Expect(err).NotTo(HaveOccurred()) - Expect(string(contents)).To(Equal("2001:db8:1::2")) - // Release the IP - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - - _, err = os.Stat(ipFilePath1) - Expect(err).To(HaveOccurred()) - _, err = os.Stat(ipFilePath2) - Expect(err).To(HaveOccurred()) - }) - - It("allocates and releases addresses on specific interface with ADD/DEL", func() { - const ifname0 string = "eth0" - const ifname1 string = "eth1" - const nspath string = "/some/where" - - tmpDir, err := getTmpDir() - Expect(err).NotTo(HaveOccurred()) - defer os.RemoveAll(tmpDir) - - err = ioutil.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0644) - Expect(err).NotTo(HaveOccurred()) - - conf0 := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet0", - "type": "ipvlan", - "master": "foo0", - "ipam": { - "type": "host-local", - "dataDir": "%s", - "resolvConf": "%s/resolv.conf", - "ranges": [ - [{ "subnet": "10.1.2.0/24" }] - ] - } - }`, tmpDir, tmpDir) - - conf1 := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet1", - "type": "ipvlan", - "master": "foo1", - "ipam": { - "type": "host-local", - "dataDir": "%s", - "resolvConf": "%s/resolv.conf", - "ranges": [ - [{ "subnet": "10.2.2.0/24" }] - ] - } - }`, tmpDir, tmpDir) - - args0 := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: nspath, - IfName: ifname0, - StdinData: []byte(conf0), - } - - // Allocate the IP - r0, raw, err := testutils.CmdAddWithArgs(args0, func() error { - return cmdAdd(args0) - }) - Expect(err).NotTo(HaveOccurred()) - Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) - - _, err = current.GetResult(r0) - Expect(err).NotTo(HaveOccurred()) - - args1 := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: nspath, - IfName: ifname1, - StdinData: []byte(conf1), - } - - // Allocate the IP - r1, raw, err := testutils.CmdAddWithArgs(args1, func() error { - return cmdAdd(args1) - }) - Expect(err).NotTo(HaveOccurred()) - Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) - - _, err = current.GetResult(r1) - Expect(err).NotTo(HaveOccurred()) - - ipFilePath0 := filepath.Join(tmpDir, "mynet0", "10.1.2.2") - contents, err := ioutil.ReadFile(ipFilePath0) - Expect(err).NotTo(HaveOccurred()) - Expect(string(contents)).To(Equal(args0.ContainerID + LineBreak + ifname0)) - - ipFilePath1 := filepath.Join(tmpDir, "mynet1", "10.2.2.2") - contents, err = ioutil.ReadFile(ipFilePath1) - Expect(err).NotTo(HaveOccurred()) - Expect(string(contents)).To(Equal(args1.ContainerID + LineBreak + ifname1)) - - // Release the IP on ifname0 - err = testutils.CmdDelWithArgs(args0, func() error { - return cmdDel(args0) - }) - Expect(err).NotTo(HaveOccurred()) - _, err = os.Stat(ipFilePath0) - Expect(err).To(HaveOccurred()) - - // reread ipFilePath1, ensure that ifname1 didn't get deleted - contents, err = ioutil.ReadFile(ipFilePath1) - Expect(err).NotTo(HaveOccurred()) - Expect(string(contents)).To(Equal(args1.ContainerID + LineBreak + ifname1)) - - // Release the IP on ifname1 - err = testutils.CmdDelWithArgs(args1, func() error { - return cmdDel(args1) - }) - Expect(err).NotTo(HaveOccurred()) - - _, err = os.Stat(ipFilePath1) - Expect(err).To(HaveOccurred()) - }) - - It("repeat allocating addresses on specific interface for same container ID with ADD", func() { - const ifname string = "eth0" - const nspath string = "/some/where" - - tmpDir, err := getTmpDir() - Expect(err).NotTo(HaveOccurred()) - defer os.RemoveAll(tmpDir) - - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet0", - "type": "ipvlan", - "master": "foo0", - "ipam": { - "type": "host-local", - "dataDir": "%s", - "ranges": [ - [{ "subnet": "10.1.2.0/24" }] - ] - } - }`, tmpDir) - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: nspath, - IfName: ifname, - StdinData: []byte(conf), - } - - args1 := &skel.CmdArgs{ - ContainerID: "dummy1", - Netns: nspath, - IfName: ifname, - StdinData: []byte(conf), - } - - // Allocate the IP - r0, raw, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) - - result0, err := current.GetResult(r0) - Expect(err).NotTo(HaveOccurred()) - Expect(len(result0.IPs)).Should(Equal(1)) - Expect(result0.IPs[0].Address.String()).Should(Equal("10.1.2.2/24")) - - // Allocate the IP with the same container ID - _, _, err = testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).To(HaveOccurred()) - - // Allocate the IP with the another container ID - r1, raw, err := testutils.CmdAddWithArgs(args1, func() error { - return cmdAdd(args1) - }) - Expect(err).NotTo(HaveOccurred()) - Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) - - result1, err := current.GetResult(r1) - Expect(err).NotTo(HaveOccurred()) - Expect(len(result1.IPs)).Should(Equal(1)) - Expect(result1.IPs[0].Address.String()).Should(Equal("10.1.2.3/24")) - - // Allocate the IP with the same container ID again - _, _, err = testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).To(HaveOccurred()) - - ipFilePath := filepath.Join(tmpDir, "mynet0", "10.1.2.2") - - // Release the IPs - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - _, err = os.Stat(ipFilePath) - Expect(err).To(HaveOccurred()) - - err = testutils.CmdDelWithArgs(args1, func() error { - return cmdDel(args1) - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("Verify DEL works on backwards compatible allocate", func() { - const nspath string = "/some/where" - const ifname string = "eth0" - - tmpDir, err := getTmpDir() - Expect(err).NotTo(HaveOccurred()) - defer os.RemoveAll(tmpDir) - - err = ioutil.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0644) - Expect(err).NotTo(HaveOccurred()) - - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ipvlan", - "master": "foo", - "ipam": { - "type": "host-local", - "dataDir": "%s", - "resolvConf": "%s/resolv.conf", - "ranges": [ - [{ "subnet": "10.1.2.0/24" }] - ] - } - }`, tmpDir, tmpDir) - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: nspath, - IfName: ifname, - StdinData: []byte(conf), - } - - // Allocate the IP - r, raw, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) - - _, err = current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - ipFilePath := filepath.Join(tmpDir, "mynet", "10.1.2.2") - contents, err := ioutil.ReadFile(ipFilePath) - Expect(err).NotTo(HaveOccurred()) - Expect(string(contents)).To(Equal(args.ContainerID + LineBreak + ifname)) - err = ioutil.WriteFile(ipFilePath, []byte(strings.TrimSpace(args.ContainerID)), 0644) - Expect(err).NotTo(HaveOccurred()) - - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - _, err = os.Stat(ipFilePath) - Expect(err).To(HaveOccurred()) - }) - - It("doesn't error when passed an unknown ID on DEL", func() { - const ifname string = "eth0" - const nspath string = "/some/where" - - tmpDir, err := getTmpDir() - Expect(err).NotTo(HaveOccurred()) - defer os.RemoveAll(tmpDir) - - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.0", - "name": "mynet", - "type": "ipvlan", - "master": "foo0", - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24", - "dataDir": "%s" - } - }`, tmpDir) - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: nspath, - IfName: ifname, - StdinData: []byte(conf), - } - - // Release the IP - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("allocates and releases an address with ADD/DEL and 0.1.0 config", func() { - const ifname string = "eth0" - const nspath string = "/some/where" - - tmpDir, err := getTmpDir() - Expect(err).NotTo(HaveOccurred()) - defer os.RemoveAll(tmpDir) - - err = ioutil.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0644) - Expect(err).NotTo(HaveOccurred()) - - conf := fmt.Sprintf(`{ - "cniVersion": "0.1.0", - "name": "mynet", - "type": "ipvlan", - "master": "foo0", - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24", - "dataDir": "%s", - "resolvConf": "%s/resolv.conf" - } - }`, tmpDir, tmpDir) - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: nspath, - IfName: ifname, - StdinData: []byte(conf), - } - - // Allocate the IP - r, raw, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - Expect(strings.Index(string(raw), "\"ip4\":")).Should(BeNumerically(">", 0)) - - result, err := types020.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - expectedAddress, err := types.ParseCIDR("10.1.2.2/24") - Expect(err).NotTo(HaveOccurred()) - expectedAddress.IP = expectedAddress.IP.To16() - Expect(result.IP4.IP).To(Equal(*expectedAddress)) - Expect(result.IP4.Gateway).To(Equal(net.ParseIP("10.1.2.1"))) - - ipFilePath := filepath.Join(tmpDir, "mynet", "10.1.2.2") - contents, err := ioutil.ReadFile(ipFilePath) - Expect(err).NotTo(HaveOccurred()) - Expect(string(contents)).To(Equal(args.ContainerID + LineBreak + ifname)) - - lastFilePath := filepath.Join(tmpDir, "mynet", "last_reserved_ip.0") - contents, err = ioutil.ReadFile(lastFilePath) - Expect(err).NotTo(HaveOccurred()) - Expect(string(contents)).To(Equal("10.1.2.2")) - - Expect(result.DNS).To(Equal(types.DNS{Nameservers: []string{"192.0.2.3"}})) - - // Release the IP - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - - _, err = os.Stat(ipFilePath) - Expect(err).To(HaveOccurred()) - }) - - It("ignores whitespace in disk files", func() { - const ifname string = "eth0" - const nspath string = "/some/where" - - tmpDir, err := getTmpDir() - Expect(err).NotTo(HaveOccurred()) - defer os.RemoveAll(tmpDir) - - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ipvlan", - "master": "foo0", - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24", - "dataDir": "%s" - } - }`, tmpDir) - - args := &skel.CmdArgs{ - ContainerID: " dummy\n ", - Netns: nspath, - IfName: ifname, - StdinData: []byte(conf), - } - - // Allocate the IP - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - ipFilePath := filepath.Join(tmpDir, "mynet", result.IPs[0].Address.IP.String()) - contents, err := ioutil.ReadFile(ipFilePath) - Expect(err).NotTo(HaveOccurred()) - Expect(string(contents)).To(Equal("dummy" + LineBreak + ifname)) - - // Release the IP - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - - _, err = os.Stat(ipFilePath) - Expect(err).To(HaveOccurred()) - }) - - It("does not output an error message upon initial subnet creation", func() { - const ifname string = "eth0" - const nspath string = "/some/where" - - tmpDir, err := getTmpDir() - Expect(err).NotTo(HaveOccurred()) - defer os.RemoveAll(tmpDir) - - conf := fmt.Sprintf(`{ - "cniVersion": "0.2.0", - "name": "mynet", - "type": "ipvlan", - "master": "foo0", - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24", - "dataDir": "%s" - } - }`, tmpDir) - - args := &skel.CmdArgs{ - ContainerID: "testing", - Netns: nspath, - IfName: ifname, - StdinData: []byte(conf), - } - - // Allocate the IP - _, out, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - Expect(strings.Index(string(out), "Error retriving last reserved ip")).To(Equal(-1)) - }) - - It("allocates a custom IP when requested by config args", func() { - const ifname string = "eth0" - const nspath string = "/some/where" - - tmpDir, err := getTmpDir() - Expect(err).NotTo(HaveOccurred()) - defer os.RemoveAll(tmpDir) - - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ipvlan", - "master": "foo0", - "ipam": { - "type": "host-local", - "dataDir": "%s", - "ranges": [ - [{ "subnet": "10.1.2.0/24" }] - ] - }, - "args": { - "cni": { - "ips": ["10.1.2.88"] - } - } - }`, tmpDir) - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: nspath, - IfName: ifname, - StdinData: []byte(conf), - } - - // Allocate the IP - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - Expect(result.IPs).To(HaveLen(1)) - Expect(result.IPs[0].Address.IP).To(Equal(net.ParseIP("10.1.2.88"))) - }) - - It("allocates custom IPs from multiple ranges", func() { - const ifname string = "eth0" - const nspath string = "/some/where" - - tmpDir, err := getTmpDir() - Expect(err).NotTo(HaveOccurred()) - defer os.RemoveAll(tmpDir) - - err = ioutil.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0644) - Expect(err).NotTo(HaveOccurred()) - - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ipvlan", - "master": "foo0", - "ipam": { - "type": "host-local", - "dataDir": "%s", - "ranges": [ - [{ "subnet": "10.1.2.0/24" }], - [{ "subnet": "10.1.3.0/24" }] - ] - }, - "args": { - "cni": { - "ips": ["10.1.2.88", "10.1.3.77"] - } - } - }`, tmpDir) - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: nspath, - IfName: ifname, - StdinData: []byte(conf), - } - - // Allocate the IP - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - Expect(result.IPs).To(HaveLen(2)) - Expect(result.IPs[0].Address.IP).To(Equal(net.ParseIP("10.1.2.88"))) - Expect(result.IPs[1].Address.IP).To(Equal(net.ParseIP("10.1.3.77"))) - }) - - It("allocates custom IPs from multiple protocols", func() { - const ifname string = "eth0" - const nspath string = "/some/where" - - tmpDir, err := getTmpDir() - Expect(err).NotTo(HaveOccurred()) - defer os.RemoveAll(tmpDir) - - err = ioutil.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0644) - Expect(err).NotTo(HaveOccurred()) - - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ipvlan", - "master": "foo0", - "ipam": { - "type": "host-local", - "dataDir": "%s", - "ranges": [ - [{"subnet":"172.16.1.0/24"}, { "subnet": "10.1.2.0/24" }], - [{ "subnet": "2001:db8:1::/48" }] - ] - }, - "args": { - "cni": { - "ips": ["10.1.2.88", "2001:db8:1::999"] - } - } - }`, tmpDir) - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: nspath, - IfName: ifname, - StdinData: []byte(conf), - } - - // Allocate the IP - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - Expect(result.IPs).To(HaveLen(2)) - Expect(result.IPs[0].Address.IP).To(Equal(net.ParseIP("10.1.2.88"))) - Expect(result.IPs[1].Address.IP).To(Equal(net.ParseIP("2001:db8:1::999"))) - }) - - It("fails if a requested custom IP is not used", func() { - const ifname string = "eth0" - const nspath string = "/some/where" - - tmpDir, err := getTmpDir() - Expect(err).NotTo(HaveOccurred()) - defer os.RemoveAll(tmpDir) - - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ipvlan", - "master": "foo0", - "ipam": { - "type": "host-local", - "dataDir": "%s", - "ranges": [ - [{ "subnet": "10.1.2.0/24" }], - [{ "subnet": "10.1.3.0/24" }] - ] - }, - "args": { - "cni": { - "ips": ["10.1.2.88", "10.1.2.77"] - } - } - }`, tmpDir) - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: nspath, - IfName: ifname, - StdinData: []byte(conf), - } - - // Allocate the IP - _, _, err = testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).To(HaveOccurred()) - // Need to match prefix, because ordering is not guaranteed - Expect(err.Error()).To(HavePrefix("failed to allocate all requested IPs: 10.1.2.")) - }) -}) - -func getTmpDir() (string, error) { - tmpDir, err := ioutil.TempDir("", "host_local_artifacts") - if err == nil { tmpDir = filepath.ToSlash(tmpDir) - } + }) - return tmpDir, err -} + AfterEach(func() { + os.RemoveAll(tmpDir) + }) + + for _, ver := range testutils.AllSpecVersions { + // Redefine ver inside for scope so real value is picked up by each dynamically defined It() + // See Gingkgo's "Patterns for dynamically generating tests" documentation. + ver := ver + + It(fmt.Sprintf("[%s] allocates and releases addresses with ADD/DEL", ver), func() { + err := ioutil.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0644) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "master": "foo0", + "ipam": { + "type": "host-local", + "dataDir": "%s", + "resolvConf": "%s/resolv.conf", + "ranges": [ + [{ "subnet": "10.1.2.0/24" }, {"subnet": "10.2.2.0/24"}], + [{ "subnet": "2001:db8:1::0/64" }] + ], + "routes": [ + {"dst": "0.0.0.0/0"}, + {"dst": "::/0"}, + {"dst": "192.168.0.0/16", "gw": "1.1.1.1"}, + {"dst": "2001:db8:2::0/64", "gw": "2001:db8:3::1"} + ] + } + }`, ver, tmpDir, tmpDir) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: nspath, + IfName: ifname, + StdinData: []byte(conf), + } + + // Allocate the IP + r, raw, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + if testutils.SpecVersionHasIPVersion(ver) { + Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) + } + + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + // Gomega is cranky about slices with different caps + Expect(*result.IPs[0]).To(Equal( + types100.IPConfig{ + Address: mustCIDR("10.1.2.2/24"), + Gateway: net.ParseIP("10.1.2.1"), + })) + + Expect(*result.IPs[1]).To(Equal( + types100.IPConfig{ + Address: mustCIDR("2001:db8:1::2/64"), + Gateway: net.ParseIP("2001:db8:1::1"), + }, + )) + Expect(len(result.IPs)).To(Equal(2)) + + for _, expectedRoute := range []*types.Route{ + {Dst: mustCIDR("0.0.0.0/0"), GW: nil}, + {Dst: mustCIDR("::/0"), GW: nil}, + {Dst: mustCIDR("192.168.0.0/16"), GW: net.ParseIP("1.1.1.1")}, + {Dst: mustCIDR("2001:db8:2::0/64"), GW: net.ParseIP("2001:db8:3::1")}, + } { + found := false + for _, foundRoute := range result.Routes { + if foundRoute.String() == expectedRoute.String() { + found = true + break + } + } + Expect(found).To(BeTrue()) + } + + ipFilePath1 := filepath.Join(tmpDir, "mynet", "10.1.2.2") + contents, err := ioutil.ReadFile(ipFilePath1) + Expect(err).NotTo(HaveOccurred()) + Expect(string(contents)).To(Equal(args.ContainerID + LineBreak + ifname)) + + ipFilePath2 := filepath.Join(tmpDir, disk.GetEscapedPath("mynet", "2001:db8:1::2")) + contents, err = ioutil.ReadFile(ipFilePath2) + Expect(err).NotTo(HaveOccurred()) + Expect(string(contents)).To(Equal(args.ContainerID + LineBreak + ifname)) + + lastFilePath1 := filepath.Join(tmpDir, "mynet", "last_reserved_ip.0") + contents, err = ioutil.ReadFile(lastFilePath1) + Expect(err).NotTo(HaveOccurred()) + Expect(string(contents)).To(Equal("10.1.2.2")) + + lastFilePath2 := filepath.Join(tmpDir, "mynet", "last_reserved_ip.1") + contents, err = ioutil.ReadFile(lastFilePath2) + Expect(err).NotTo(HaveOccurred()) + Expect(string(contents)).To(Equal("2001:db8:1::2")) + // Release the IP + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + + _, err = os.Stat(ipFilePath1) + Expect(err).To(HaveOccurred()) + _, err = os.Stat(ipFilePath2) + Expect(err).To(HaveOccurred()) + }) + + It(fmt.Sprintf("[%s] allocates and releases addresses on specific interface with ADD/DEL", ver), func() { + const ifname1 string = "eth1" + + err := ioutil.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0644) + Expect(err).NotTo(HaveOccurred()) + + conf0 := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet0", + "type": "ipvlan", + "master": "foo0", + "ipam": { + "type": "host-local", + "dataDir": "%s", + "resolvConf": "%s/resolv.conf", + "ranges": [ + [{ "subnet": "10.1.2.0/24" }] + ] + } + }`, ver, tmpDir, tmpDir) + + conf1 := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet1", + "type": "ipvlan", + "master": "foo1", + "ipam": { + "type": "host-local", + "dataDir": "%s", + "resolvConf": "%s/resolv.conf", + "ranges": [ + [{ "subnet": "10.2.2.0/24" }] + ] + } + }`, ver, tmpDir, tmpDir) + + args0 := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: nspath, + IfName: ifname, + StdinData: []byte(conf0), + } + + // Allocate the IP + r0, raw, err := testutils.CmdAddWithArgs(args0, func() error { + return cmdAdd(args0) + }) + Expect(err).NotTo(HaveOccurred()) + if testutils.SpecVersionHasIPVersion(ver) { + Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) + } + + _, err = types100.GetResult(r0) + Expect(err).NotTo(HaveOccurred()) + + args1 := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: nspath, + IfName: ifname1, + StdinData: []byte(conf1), + } + + // Allocate the IP + r1, raw, err := testutils.CmdAddWithArgs(args1, func() error { + return cmdAdd(args1) + }) + Expect(err).NotTo(HaveOccurred()) + if testutils.SpecVersionHasIPVersion(ver) { + Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) + } + + _, err = types100.GetResult(r1) + Expect(err).NotTo(HaveOccurred()) + + ipFilePath0 := filepath.Join(tmpDir, "mynet0", "10.1.2.2") + contents, err := ioutil.ReadFile(ipFilePath0) + Expect(err).NotTo(HaveOccurred()) + Expect(string(contents)).To(Equal(args0.ContainerID + LineBreak + ifname)) + + ipFilePath1 := filepath.Join(tmpDir, "mynet1", "10.2.2.2") + contents, err = ioutil.ReadFile(ipFilePath1) + Expect(err).NotTo(HaveOccurred()) + Expect(string(contents)).To(Equal(args1.ContainerID + LineBreak + ifname1)) + + // Release the IP on ifname + err = testutils.CmdDelWithArgs(args0, func() error { + return cmdDel(args0) + }) + Expect(err).NotTo(HaveOccurred()) + _, err = os.Stat(ipFilePath0) + Expect(err).To(HaveOccurred()) + + // reread ipFilePath1, ensure that ifname1 didn't get deleted + contents, err = ioutil.ReadFile(ipFilePath1) + Expect(err).NotTo(HaveOccurred()) + Expect(string(contents)).To(Equal(args1.ContainerID + LineBreak + ifname1)) + + // Release the IP on ifname1 + err = testutils.CmdDelWithArgs(args1, func() error { + return cmdDel(args1) + }) + Expect(err).NotTo(HaveOccurred()) + + _, err = os.Stat(ipFilePath1) + Expect(err).To(HaveOccurred()) + }) + + It(fmt.Sprintf("[%s] repeat allocating addresses on specific interface for same container ID with ADD", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet0", + "type": "ipvlan", + "master": "foo0", + "ipam": { + "type": "host-local", + "dataDir": "%s", + "ranges": [ + [{ "subnet": "10.1.2.0/24" }] + ] + } + }`, ver, tmpDir) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: nspath, + IfName: ifname, + StdinData: []byte(conf), + } + + args1 := &skel.CmdArgs{ + ContainerID: "dummy1", + Netns: nspath, + IfName: ifname, + StdinData: []byte(conf), + } + + // Allocate the IP + r0, raw, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + if testutils.SpecVersionHasIPVersion(ver) { + Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) + } + + result0, err := types100.GetResult(r0) + Expect(err).NotTo(HaveOccurred()) + Expect(len(result0.IPs)).Should(Equal(1)) + Expect(result0.IPs[0].Address.String()).Should(Equal("10.1.2.2/24")) + + // Allocate the IP with the same container ID + _, _, err = testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).To(HaveOccurred()) + + // Allocate the IP with the another container ID + r1, raw, err := testutils.CmdAddWithArgs(args1, func() error { + return cmdAdd(args1) + }) + Expect(err).NotTo(HaveOccurred()) + if testutils.SpecVersionHasIPVersion(ver) { + Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) + } + + result1, err := types100.GetResult(r1) + Expect(err).NotTo(HaveOccurred()) + Expect(len(result1.IPs)).Should(Equal(1)) + Expect(result1.IPs[0].Address.String()).Should(Equal("10.1.2.3/24")) + + // Allocate the IP with the same container ID again + _, _, err = testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).To(HaveOccurred()) + + ipFilePath := filepath.Join(tmpDir, "mynet0", "10.1.2.2") + + // Release the IPs + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + _, err = os.Stat(ipFilePath) + Expect(err).To(HaveOccurred()) + + err = testutils.CmdDelWithArgs(args1, func() error { + return cmdDel(args1) + }) + Expect(err).NotTo(HaveOccurred()) + }) + + It(fmt.Sprintf("[%s] verify DEL works on backwards compatible allocate", ver), func() { + err := ioutil.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0644) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "master": "foo", + "ipam": { + "type": "host-local", + "dataDir": "%s", + "resolvConf": "%s/resolv.conf", + "ranges": [ + [{ "subnet": "10.1.2.0/24" }] + ] + } + }`, ver, tmpDir, tmpDir) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: nspath, + IfName: ifname, + StdinData: []byte(conf), + } + + // Allocate the IP + r, raw, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + if testutils.SpecVersionHasIPVersion(ver) { + Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) + } + + _, err = types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + ipFilePath := filepath.Join(tmpDir, "mynet", "10.1.2.2") + contents, err := ioutil.ReadFile(ipFilePath) + Expect(err).NotTo(HaveOccurred()) + Expect(string(contents)).To(Equal(args.ContainerID + LineBreak + ifname)) + err = ioutil.WriteFile(ipFilePath, []byte(strings.TrimSpace(args.ContainerID)), 0644) + Expect(err).NotTo(HaveOccurred()) + + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + _, err = os.Stat(ipFilePath) + Expect(err).To(HaveOccurred()) + }) + + It(fmt.Sprintf("[%s] doesn't error when passed an unknown ID on DEL", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "master": "foo0", + "ipam": { + "type": "host-local", + "subnet": "10.1.2.0/24", + "dataDir": "%s" + } + }`, ver, tmpDir) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: nspath, + IfName: ifname, + StdinData: []byte(conf), + } + + // Release the IP + err := testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + }) + + It(fmt.Sprintf("[%s] ignores whitespace in disk files", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "master": "foo0", + "ipam": { + "type": "host-local", + "subnet": "10.1.2.0/24", + "dataDir": "%s" + } + }`, ver, tmpDir) + + args := &skel.CmdArgs{ + ContainerID: " dummy\n ", + Netns: nspath, + IfName: ifname, + StdinData: []byte(conf), + } + + // Allocate the IP + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + ipFilePath := filepath.Join(tmpDir, "mynet", result.IPs[0].Address.IP.String()) + contents, err := ioutil.ReadFile(ipFilePath) + Expect(err).NotTo(HaveOccurred()) + Expect(string(contents)).To(Equal("dummy" + LineBreak + ifname)) + + // Release the IP + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + + _, err = os.Stat(ipFilePath) + Expect(err).To(HaveOccurred()) + }) + + It(fmt.Sprintf("[%s] does not output an error message upon initial subnet creation", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "master": "foo0", + "ipam": { + "type": "host-local", + "subnet": "10.1.2.0/24", + "dataDir": "%s" + } + }`, ver, tmpDir) + + args := &skel.CmdArgs{ + ContainerID: "testing", + Netns: nspath, + IfName: ifname, + StdinData: []byte(conf), + } + + // Allocate the IP + _, out, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + Expect(strings.Index(string(out), "Error retriving last reserved ip")).To(Equal(-1)) + }) + + It(fmt.Sprintf("[%s] allocates a custom IP when requested by config args", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "master": "foo0", + "ipam": { + "type": "host-local", + "dataDir": "%s", + "ranges": [ + [{ "subnet": "10.1.2.0/24" }] + ] + }, + "args": { + "cni": { + "ips": ["10.1.2.88"] + } + } + }`, ver, tmpDir) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: nspath, + IfName: ifname, + StdinData: []byte(conf), + } + + // Allocate the IP + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + Expect(result.IPs).To(HaveLen(1)) + Expect(result.IPs[0].Address.IP).To(Equal(net.ParseIP("10.1.2.88"))) + }) + + It(fmt.Sprintf("[%s] allocates custom IPs from multiple ranges", ver), func() { + err := ioutil.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0644) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "master": "foo0", + "ipam": { + "type": "host-local", + "dataDir": "%s", + "ranges": [ + [{ "subnet": "10.1.2.0/24" }], + [{ "subnet": "10.1.3.0/24" }] + ] + }, + "args": { + "cni": { + "ips": ["10.1.2.88", "10.1.3.77"] + } + } + }`, ver, tmpDir) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: nspath, + IfName: ifname, + StdinData: []byte(conf), + } + + // Allocate the IP + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + if !testutils.SpecVersionHasMultipleIPs(ver) { + errStr := fmt.Sprintf("CNI version %s does not support more than 1 address per family", ver) + Expect(err).To(MatchError(errStr)) + } else { + Expect(err).NotTo(HaveOccurred()) + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + Expect(result.IPs).To(HaveLen(2)) + Expect(result.IPs[0].Address.IP).To(Equal(net.ParseIP("10.1.2.88"))) + Expect(result.IPs[1].Address.IP).To(Equal(net.ParseIP("10.1.3.77"))) + } + }) + + It(fmt.Sprintf("[%s] allocates custom IPs from multiple protocols", ver), func() { + err := ioutil.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0644) + Expect(err).NotTo(HaveOccurred()) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "master": "foo0", + "ipam": { + "type": "host-local", + "dataDir": "%s", + "ranges": [ + [{"subnet":"172.16.1.0/24"}, { "subnet": "10.1.2.0/24" }], + [{ "subnet": "2001:db8:1::/48" }] + ] + }, + "args": { + "cni": { + "ips": ["10.1.2.88", "2001:db8:1::999"] + } + } + }`, ver, tmpDir) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: nspath, + IfName: ifname, + StdinData: []byte(conf), + } + + // Allocate the IP + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + Expect(result.IPs).To(HaveLen(2)) + Expect(result.IPs[0].Address.IP).To(Equal(net.ParseIP("10.1.2.88"))) + Expect(result.IPs[1].Address.IP).To(Equal(net.ParseIP("2001:db8:1::999"))) + }) + + It(fmt.Sprintf("[%s] fails if a requested custom IP is not used", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "master": "foo0", + "ipam": { + "type": "host-local", + "dataDir": "%s", + "ranges": [ + [{ "subnet": "10.1.2.0/24" }], + [{ "subnet": "10.1.3.0/24" }] + ] + }, + "args": { + "cni": { + "ips": ["10.1.2.88", "10.1.2.77"] + } + } + }`, ver, tmpDir) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: nspath, + IfName: ifname, + StdinData: []byte(conf), + } + + // Allocate the IP + _, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + if !testutils.SpecVersionHasMultipleIPs(ver) { + errStr := fmt.Sprintf("CNI version %s does not support more than 1 address per family", ver) + Expect(err).To(MatchError(errStr)) + } else { + Expect(err).To(HaveOccurred()) + // Need to match prefix, because ordering is not guaranteed + Expect(err.Error()).To(HavePrefix("failed to allocate all requested IPs: 10.1.2.")) + } + }) + } +}) func mustCIDR(s string) net.IPNet { ip, n, err := net.ParseCIDR(s) diff --git a/plugins/ipam/host-local/main.go b/plugins/ipam/host-local/main.go index 79269013..9983c83f 100644 --- a/plugins/ipam/host-local/main.go +++ b/plugins/ipam/host-local/main.go @@ -25,7 +25,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" ) @@ -62,7 +62,7 @@ func cmdAdd(args *skel.CmdArgs) error { return err } - result := ¤t.Result{} + result := ¤t.Result{CNIVersion: current.ImplementedSpecVersion} if ipamConf.ResolvConf != "" { dns, err := parseResolvConf(ipamConf.ResolvConf) diff --git a/plugins/ipam/static/main.go b/plugins/ipam/static/main.go index e83f5adf..49e0dc00 100644 --- a/plugins/ipam/static/main.go +++ b/plugins/ipam/static/main.go @@ -22,8 +22,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - types020 "github.com/containernetworking/cni/pkg/types/020" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" bv "github.com/containernetworking/plugins/pkg/utils/buildversion" ) @@ -225,20 +224,16 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) { } if n.IPAM.Addresses[i].Address.IP.To4() != nil { - n.IPAM.Addresses[i].Version = "4" numV4++ } else { - n.IPAM.Addresses[i].Version = "6" numV6++ } } // CNI spec 0.2.0 and below supported only one v4 and v6 address if numV4 > 1 || numV6 > 1 { - for _, v := range types020.SupportedVersions { - if n.CNIVersion == v { - return nil, "", fmt.Errorf("CNI version %v does not support more than 1 address per family", n.CNIVersion) - } + if ok, _ := version.GreaterThanOrEqualTo(n.CNIVersion, "0.3.0"); !ok { + return nil, "", fmt.Errorf("CNI version %v does not support more than 1 address per family", n.CNIVersion) } } @@ -254,14 +249,16 @@ func cmdAdd(args *skel.CmdArgs) error { return err } - result := ¤t.Result{} - result.DNS = ipamConf.DNS - result.Routes = ipamConf.Routes + result := ¤t.Result{ + CNIVersion: current.ImplementedSpecVersion, + DNS: ipamConf.DNS, + Routes: ipamConf.Routes, + } for _, v := range ipamConf.Addresses { result.IPs = append(result.IPs, ¤t.IPConfig{ - Version: v.Version, Address: v.Address, - Gateway: v.Gateway}) + Gateway: v.Gateway, + }) } return types.PrintResult(result, confVersion) diff --git a/plugins/ipam/static/static_test.go b/plugins/ipam/static/static_test.go index 54272565..f75d6342 100644 --- a/plugins/ipam/static/static_test.go +++ b/plugins/ipam/static/static_test.go @@ -15,12 +15,13 @@ package main import ( + "fmt" "net" "strings" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/testutils" . "github.com/onsi/ginkgo" @@ -28,18 +29,101 @@ import ( ) var _ = Describe("static Operations", func() { - It("allocates and releases addresses with ADD/DEL", func() { - const ifname string = "eth0" - const nspath string = "/some/where" + for _, ver := range testutils.AllSpecVersions { + // Redefine ver inside for scope so real value is picked up by each dynamically defined It() + // See Gingkgo's "Patterns for dynamically generating tests" documentation. + ver := ver - conf := `{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ipvlan", - "master": "foo0", - "ipam": { - "type": "static", - "addresses": [ { + It(fmt.Sprintf("[%s] allocates and releases addresses with ADD/DEL", ver), func() { + const ifname string = "eth0" + const nspath string = "/some/where" + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "master": "foo0", + "ipam": { + "type": "static", + "addresses": [ { + "address": "10.10.0.1/24", + "gateway": "10.10.0.254" + }, + { + "address": "3ffe:ffff:0:01ff::1/64", + "gateway": "3ffe:ffff:0::1" + }], + "routes": [ + { "dst": "0.0.0.0/0" }, + { "dst": "192.168.0.0/16", "gw": "10.10.5.1" }, + { "dst": "3ffe:ffff:0:01ff::1/64" }], + "dns": { + "nameservers" : ["8.8.8.8"], + "domain": "example.com", + "search": [ "example.com" ] + } + } + }`, ver) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: nspath, + IfName: ifname, + StdinData: []byte(conf), + } + + // Allocate the IP + r, raw, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + if testutils.SpecVersionHasIPVersion(ver) { + Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) + } + + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + // Gomega is cranky about slices with different caps + Expect(*result.IPs[0]).To(Equal( + types100.IPConfig{ + Address: mustCIDR("10.10.0.1/24"), + Gateway: net.ParseIP("10.10.0.254"), + })) + + Expect(*result.IPs[1]).To(Equal( + types100.IPConfig{ + Address: mustCIDR("3ffe:ffff:0:01ff::1/64"), + Gateway: net.ParseIP("3ffe:ffff:0::1"), + }, + )) + Expect(len(result.IPs)).To(Equal(2)) + + Expect(result.Routes).To(Equal([]*types.Route{ + {Dst: mustCIDR("0.0.0.0/0")}, + {Dst: mustCIDR("192.168.0.0/16"), GW: net.ParseIP("10.10.5.1")}, + {Dst: mustCIDR("3ffe:ffff:0:01ff::1/64")}, + })) + + // Release the IP + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + }) + + It(fmt.Sprintf("[%s] doesn't error when passed an unknown ID on DEL", ver), func() { + const ifname string = "eth0" + const nspath string = "/some/where" + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "master": "foo0", + "ipam": { + "type": "static", + "addresses": [ { "address": "10.10.0.1/24", "gateway": "10.10.0.254" }, @@ -47,439 +131,370 @@ var _ = Describe("static Operations", func() { "address": "3ffe:ffff:0:01ff::1/64", "gateway": "3ffe:ffff:0::1" }], - "routes": [ + "routes": [ { "dst": "0.0.0.0/0" }, { "dst": "192.168.0.0/16", "gw": "10.10.5.1" }, { "dst": "3ffe:ffff:0:01ff::1/64" }], - "dns": { - "nameservers" : ["8.8.8.8"], - "domain": "example.com", - "search": [ "example.com" ] + "dns": { + "nameservers" : ["8.8.8.8"], + "domain": "example.com", + "search": [ "example.com" ] + } } + }`, ver) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: nspath, + IfName: ifname, + StdinData: []byte(conf), } - }` - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: nspath, - IfName: ifname, - StdinData: []byte(conf), - } - - // Allocate the IP - r, raw, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + // Release the IP + err := testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) }) - Expect(err).NotTo(HaveOccurred()) - Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) + It(fmt.Sprintf("[%s] allocates and releases addresses with ADD/DEL, with ENV variables", ver), func() { + const ifname string = "eth0" + const nspath string = "/some/where" - // Gomega is cranky about slices with different caps - Expect(*result.IPs[0]).To(Equal( - current.IPConfig{ - Version: "4", - Address: mustCIDR("10.10.0.1/24"), - Gateway: net.ParseIP("10.10.0.254"), + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "master": "foo0", + "ipam": { + "type": "static", + "routes": [ + { "dst": "0.0.0.0/0" }, + { "dst": "192.168.0.0/16", "gw": "10.10.5.1" }], + "dns": { + "nameservers" : ["8.8.8.8"], + "domain": "example.com", + "search": [ "example.com" ] + } + } + }`, ver) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: nspath, + IfName: ifname, + StdinData: []byte(conf), + Args: "IP=10.10.0.1/24;GATEWAY=10.10.0.254", + } + + // Allocate the IP + r, raw, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + if testutils.SpecVersionHasIPVersion(ver) { + Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) + } + + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + // Gomega is cranky about slices with different caps + Expect(*result.IPs[0]).To(Equal( + types100.IPConfig{ + Address: mustCIDR("10.10.0.1/24"), + Gateway: net.ParseIP("10.10.0.254"), + })) + + Expect(len(result.IPs)).To(Equal(1)) + + Expect(result.Routes).To(Equal([]*types.Route{ + {Dst: mustCIDR("0.0.0.0/0")}, + {Dst: mustCIDR("192.168.0.0/16"), GW: net.ParseIP("10.10.5.1")}, })) - Expect(*result.IPs[1]).To(Equal( - current.IPConfig{ - Version: "6", - Address: mustCIDR("3ffe:ffff:0:01ff::1/64"), - Gateway: net.ParseIP("3ffe:ffff:0::1"), - }, - )) - Expect(len(result.IPs)).To(Equal(2)) - - Expect(result.Routes).To(Equal([]*types.Route{ - {Dst: mustCIDR("0.0.0.0/0")}, - {Dst: mustCIDR("192.168.0.0/16"), GW: net.ParseIP("10.10.5.1")}, - {Dst: mustCIDR("3ffe:ffff:0:01ff::1/64")}, - })) - - // Release the IP - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + // Release the IP + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) }) - Expect(err).NotTo(HaveOccurred()) - }) - It("doesn't error when passed an unknown ID on DEL", func() { - const ifname string = "eth0" - const nspath string = "/some/where" + It(fmt.Sprintf("[%s] allocates and releases multiple addresses with ADD/DEL, with ENV variables", ver), func() { + const ifname string = "eth0" + const nspath string = "/some/where" - conf := `{ - "cniVersion": "0.3.0", - "name": "mynet", - "type": "ipvlan", - "master": "foo0", - "ipam": { - "type": "static", - "addresses": [ { - "address": "10.10.0.1/24", - "gateway": "10.10.0.254" + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "master": "foo0", + "ipam": { + "type": "static" + } + }`, ver) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: nspath, + IfName: ifname, + StdinData: []byte(conf), + Args: "IP=10.10.0.1/24,11.11.0.1/24;GATEWAY=10.10.0.254", + } + + // Allocate the IP + r, raw, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + if !testutils.SpecVersionHasMultipleIPs(ver) { + errStr := fmt.Sprintf("CNI version %s does not support more than 1 address per family", ver) + Expect(err).To(MatchError(errStr)) + return + } + + Expect(err).NotTo(HaveOccurred()) + if testutils.SpecVersionHasIPVersion(ver) { + Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) + } + + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + // Gomega is cranky about slices with different caps + Expect(*result.IPs[0]).To(Equal( + types100.IPConfig{ + Address: mustCIDR("10.10.0.1/24"), + Gateway: net.ParseIP("10.10.0.254"), + })) + Expect(*result.IPs[1]).To(Equal( + types100.IPConfig{ + Address: mustCIDR("11.11.0.1/24"), + Gateway: nil, + })) + + Expect(len(result.IPs)).To(Equal(2)) + + // Release the IP + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + }) + + It(fmt.Sprintf("[%s] allocates and releases multiple addresses with ADD/DEL, from RuntimeConfig", ver), func() { + const ifname string = "eth0" + const nspath string = "/some/where" + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "master": "foo0", + "capabilities": {"ips": true}, + "ipam": { + "type": "static", + "routes": [ + { "dst": "0.0.0.0/0", "gw": "10.10.0.254" }, + { "dst": "3ffe:ffff:0:01ff::1/64", + "gw": "3ffe:ffff:0::1" } ], + "dns": { + "nameservers" : ["8.8.8.8"], + "domain": "example.com", + "search": [ "example.com" ] + } }, - { - "address": "3ffe:ffff:0:01ff::1/64", - "gateway": "3ffe:ffff:0::1" - }], - "routes": [ - { "dst": "0.0.0.0/0" }, - { "dst": "192.168.0.0/16", "gw": "10.10.5.1" }, - { "dst": "3ffe:ffff:0:01ff::1/64" }], - "dns": { - "nameservers" : ["8.8.8.8"], - "domain": "example.com", - "search": [ "example.com" ] - }}}` - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: nspath, - IfName: ifname, - StdinData: []byte(conf), - } - - // Release the IP - err := testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("allocates and releases addresses with ADD/DEL, with ENV variables", func() { - const ifname string = "eth0" - const nspath string = "/some/where" - - conf := `{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ipvlan", - "master": "foo0", - "ipam": { - "type": "static", - "routes": [ - { "dst": "0.0.0.0/0" }, - { "dst": "192.168.0.0/16", "gw": "10.10.5.1" }], - "dns": { - "nameservers" : ["8.8.8.8"], - "domain": "example.com", - "search": [ "example.com" ] - } - } - }` - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: nspath, - IfName: ifname, - StdinData: []byte(conf), - Args: "IP=10.10.0.1/24;GATEWAY=10.10.0.254", - } - - // Allocate the IP - r, raw, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - // Gomega is cranky about slices with different caps - Expect(*result.IPs[0]).To(Equal( - current.IPConfig{ - Version: "4", - Address: mustCIDR("10.10.0.1/24"), - Gateway: net.ParseIP("10.10.0.254"), - })) - - Expect(len(result.IPs)).To(Equal(1)) - - Expect(result.Routes).To(Equal([]*types.Route{ - {Dst: mustCIDR("0.0.0.0/0")}, - {Dst: mustCIDR("192.168.0.0/16"), GW: net.ParseIP("10.10.5.1")}, - })) - - // Release the IP - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("allocates and releases multiple addresses with ADD/DEL, with ENV variables", func() { - const ifname string = "eth0" - const nspath string = "/some/where" - - conf := `{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ipvlan", - "master": "foo0", - "ipam": { - "type": "static" - } - }` - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: nspath, - IfName: ifname, - StdinData: []byte(conf), - Args: "IP=10.10.0.1/24,11.11.0.1/24;GATEWAY=10.10.0.254", - } - - // Allocate the IP - r, raw, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - // Gomega is cranky about slices with different caps - Expect(*result.IPs[0]).To(Equal( - current.IPConfig{ - Version: "4", - Address: mustCIDR("10.10.0.1/24"), - Gateway: net.ParseIP("10.10.0.254"), - })) - Expect(*result.IPs[1]).To(Equal( - current.IPConfig{ - Version: "4", - Address: mustCIDR("11.11.0.1/24"), - Gateway: nil, - })) - - Expect(len(result.IPs)).To(Equal(2)) - - // Release the IP - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("allocates and releases multiple addresses with ADD/DEL, from RuntimeConfig", func() { - const ifname string = "eth0" - const nspath string = "/some/where" - - conf := `{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ipvlan", - "master": "foo0", - "capabilities": {"ips": true}, - "ipam": { - "type": "static", - "routes": [ - { "dst": "0.0.0.0/0", "gw": "10.10.0.254" }, - { "dst": "3ffe:ffff:0:01ff::1/64", - "gw": "3ffe:ffff:0::1" } ], - "dns": { - "nameservers" : ["8.8.8.8"], - "domain": "example.com", - "search": [ "example.com" ] - } - }, - "RuntimeConfig": { - "ips" : ["10.10.0.1/24", "3ffe:ffff:0:01ff::1/64"] - } - }` - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: nspath, - IfName: ifname, - StdinData: []byte(conf), - } - - // Allocate the IP - r, raw, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - // Gomega is cranky about slices with different caps - Expect(*result.IPs[0]).To(Equal( - current.IPConfig{ - Version: "4", - Address: mustCIDR("10.10.0.1/24"), - })) - Expect(*result.IPs[1]).To(Equal( - current.IPConfig{ - Version: "6", - Address: mustCIDR("3ffe:ffff:0:01ff::1/64"), - }, - )) - Expect(len(result.IPs)).To(Equal(2)) - Expect(result.Routes).To(Equal([]*types.Route{ - {Dst: mustCIDR("0.0.0.0/0"), GW: net.ParseIP("10.10.0.254")}, - {Dst: mustCIDR("3ffe:ffff:0:01ff::1/64"), GW: net.ParseIP("3ffe:ffff:0::1")}, - })) - - // Release the IP - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("allocates and releases multiple addresses with ADD/DEL, from args", func() { - const ifname string = "eth0" - const nspath string = "/some/where" - - conf := `{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ipvlan", - "master": "foo0", - "ipam": { - "type": "static", - "routes": [ - { "dst": "0.0.0.0/0", "gw": "10.10.0.254" }, - { "dst": "3ffe:ffff:0:01ff::1/64", - "gw": "3ffe:ffff:0::1" } ], - "dns": { - "nameservers" : ["8.8.8.8"], - "domain": "example.com", - "search": [ "example.com" ] - } - }, - "args": { - "cni": { + "RuntimeConfig": { "ips" : ["10.10.0.1/24", "3ffe:ffff:0:01ff::1/64"] - } + } + }`, ver) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: nspath, + IfName: ifname, + StdinData: []byte(conf), } - }` - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: nspath, - IfName: ifname, - StdinData: []byte(conf), - } - - // Allocate the IP - r, raw, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - // Gomega is cranky about slices with different caps - Expect(*result.IPs[0]).To(Equal( - current.IPConfig{ - Version: "4", - Address: mustCIDR("10.10.0.1/24"), - })) - Expect(*result.IPs[1]).To(Equal( - current.IPConfig{ - Version: "6", - Address: mustCIDR("3ffe:ffff:0:01ff::1/64"), - }, - )) - Expect(len(result.IPs)).To(Equal(2)) - Expect(result.Routes).To(Equal([]*types.Route{ - {Dst: mustCIDR("0.0.0.0/0"), GW: net.ParseIP("10.10.0.254")}, - {Dst: mustCIDR("3ffe:ffff:0:01ff::1/64"), GW: net.ParseIP("3ffe:ffff:0::1")}, - })) - - // Release the IP - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("allocates and releases multiple addresses with ADD/DEL, from RuntimeConfig/ARGS/CNI_ARGS", func() { - const ifname string = "eth0" - const nspath string = "/some/where" - - conf := `{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ipvlan", - "master": "foo0", - "capabilities": {"ips": true}, - "ipam": { - "type": "static", - "routes": [ - { "dst": "0.0.0.0/0", "gw": "10.10.0.254" }, - { "dst": "3ffe:ffff:0:01ff::1/64", - "gw": "3ffe:ffff:0::1" } ], - "dns": { - "nameservers" : ["8.8.8.8"], - "domain": "example.com", - "search": [ "example.com" ] - } - }, - "RuntimeConfig": { - "ips" : ["10.10.0.1/24", "3ffe:ffff:0:01ff::1/64"] - }, - "args": { - "cni": { - "ips" : ["10.10.0.2/24", "3ffe:ffff:0:01ff::2/64"] - } + // Allocate the IP + r, raw, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + if testutils.SpecVersionHasIPVersion(ver) { + Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) } - }` - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: nspath, - IfName: ifname, - StdinData: []byte(conf), - Args: "IP=10.10.0.3/24,11.11.0.3/24;GATEWAY=10.10.0.254", - } + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) - // Allocate the IP - r, raw, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - // only addresses in runtimeConfig configured because of its priorities - Expect(*result.IPs[0]).To(Equal( - current.IPConfig{ - Version: "4", - Address: mustCIDR("10.10.0.1/24"), + // Gomega is cranky about slices with different caps + Expect(*result.IPs[0]).To(Equal( + types100.IPConfig{ + Address: mustCIDR("10.10.0.1/24"), + })) + Expect(*result.IPs[1]).To(Equal( + types100.IPConfig{ + Address: mustCIDR("3ffe:ffff:0:01ff::1/64"), + }, + )) + Expect(len(result.IPs)).To(Equal(2)) + Expect(result.Routes).To(Equal([]*types.Route{ + {Dst: mustCIDR("0.0.0.0/0"), GW: net.ParseIP("10.10.0.254")}, + {Dst: mustCIDR("3ffe:ffff:0:01ff::1/64"), GW: net.ParseIP("3ffe:ffff:0::1")}, })) - Expect(*result.IPs[1]).To(Equal( - current.IPConfig{ - Version: "6", - Address: mustCIDR("3ffe:ffff:0:01ff::1/64"), - }, - )) - Expect(len(result.IPs)).To(Equal(2)) - Expect(result.Routes).To(Equal([]*types.Route{ - {Dst: mustCIDR("0.0.0.0/0"), GW: net.ParseIP("10.10.0.254")}, - {Dst: mustCIDR("3ffe:ffff:0:01ff::1/64"), GW: net.ParseIP("3ffe:ffff:0::1")}, - })) - // Release the IP - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + // Release the IP + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) }) - Expect(err).NotTo(HaveOccurred()) - }) + It(fmt.Sprintf("[%s] allocates and releases multiple addresses with ADD/DEL, from args", ver), func() { + const ifname string = "eth0" + const nspath string = "/some/where" + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "master": "foo0", + "ipam": { + "type": "static", + "routes": [ + { "dst": "0.0.0.0/0", "gw": "10.10.0.254" }, + { "dst": "3ffe:ffff:0:01ff::1/64", + "gw": "3ffe:ffff:0::1" } ], + "dns": { + "nameservers" : ["8.8.8.8"], + "domain": "example.com", + "search": [ "example.com" ] + } + }, + "args": { + "cni": { + "ips" : ["10.10.0.1/24", "3ffe:ffff:0:01ff::1/64"] + } + } + }`, ver) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: nspath, + IfName: ifname, + StdinData: []byte(conf), + } + + // Allocate the IP + r, raw, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + if testutils.SpecVersionHasIPVersion(ver) { + Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) + } + + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + // Gomega is cranky about slices with different caps + Expect(*result.IPs[0]).To(Equal( + types100.IPConfig{ + Address: mustCIDR("10.10.0.1/24"), + })) + Expect(*result.IPs[1]).To(Equal( + types100.IPConfig{ + Address: mustCIDR("3ffe:ffff:0:01ff::1/64"), + }, + )) + Expect(len(result.IPs)).To(Equal(2)) + Expect(result.Routes).To(Equal([]*types.Route{ + {Dst: mustCIDR("0.0.0.0/0"), GW: net.ParseIP("10.10.0.254")}, + {Dst: mustCIDR("3ffe:ffff:0:01ff::1/64"), GW: net.ParseIP("3ffe:ffff:0::1")}, + })) + + // Release the IP + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + }) + + It(fmt.Sprintf("[%s] allocates and releases multiple addresses with ADD/DEL, from RuntimeConfig/ARGS/CNI_ARGS", ver), func() { + const ifname string = "eth0" + const nspath string = "/some/where" + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "master": "foo0", + "capabilities": {"ips": true}, + "ipam": { + "type": "static", + "routes": [ + { "dst": "0.0.0.0/0", "gw": "10.10.0.254" }, + { "dst": "3ffe:ffff:0:01ff::1/64", + "gw": "3ffe:ffff:0::1" } ], + "dns": { + "nameservers" : ["8.8.8.8"], + "domain": "example.com", + "search": [ "example.com" ] + } + }, + "RuntimeConfig": { + "ips" : ["10.10.0.1/24", "3ffe:ffff:0:01ff::1/64"] + }, + "args": { + "cni": { + "ips" : ["10.10.0.2/24", "3ffe:ffff:0:01ff::2/64"] + } + } + }`, ver) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: nspath, + IfName: ifname, + StdinData: []byte(conf), + Args: "IP=10.10.0.3/24,11.11.0.3/24;GATEWAY=10.10.0.254", + } + + // Allocate the IP + r, raw, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + if testutils.SpecVersionHasIPVersion(ver) { + Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) + } + + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + // only addresses in runtimeConfig configured because of its priorities + Expect(*result.IPs[0]).To(Equal( + types100.IPConfig{ + Address: mustCIDR("10.10.0.1/24"), + })) + Expect(*result.IPs[1]).To(Equal( + types100.IPConfig{ + Address: mustCIDR("3ffe:ffff:0:01ff::1/64"), + }, + )) + Expect(len(result.IPs)).To(Equal(2)) + Expect(result.Routes).To(Equal([]*types.Route{ + {Dst: mustCIDR("0.0.0.0/0"), GW: net.ParseIP("10.10.0.254")}, + {Dst: mustCIDR("3ffe:ffff:0:01ff::1/64"), GW: net.ParseIP("3ffe:ffff:0::1")}, + })) + + // Release the IP + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + }) + } }) func mustCIDR(s string) net.IPNet { diff --git a/plugins/main/bridge/bridge.go b/plugins/main/bridge/bridge.go index c6a0690d..69ffa98f 100644 --- a/plugins/main/bridge/bridge.go +++ b/plugins/main/bridge/bridge.go @@ -29,7 +29,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/plugins/pkg/ip" "github.com/containernetworking/plugins/pkg/ipam" @@ -412,7 +412,14 @@ func cmdAdd(args *skel.CmdArgs) error { } // Assume L2 interface only - result := ¤t.Result{CNIVersion: cniVersion, Interfaces: []*current.Interface{brInterface, hostInterface, containerInterface}} + result := ¤t.Result{ + CNIVersion: current.ImplementedSpecVersion, + Interfaces: []*current.Interface{ + brInterface, + hostInterface, + containerInterface, + }, + } if isLayer3 { // run the IPAM plugin and get back the config to apply @@ -453,7 +460,7 @@ func cmdAdd(args *skel.CmdArgs) error { // bridge. Hairpin mode causes echos of neighbor solicitation // packets, which causes DAD failures. for _, ipc := range result.IPs { - if ipc.Version == "6" && (n.HairpinMode || n.PromiscMode) { + if ipc.Address.IP.To4() == nil && (n.HairpinMode || n.PromiscMode) { if err := disableIPV6DAD(args.IfName); err != nil { return err } @@ -496,7 +503,7 @@ func cmdAdd(args *skel.CmdArgs) error { } for _, ipc := range result.IPs { - if ipc.Version == "4" { + if ipc.Address.IP.To4() != nil { _ = arping.GratuitousArpOverIface(ipc.Address.IP, *contVeth) } } diff --git a/plugins/main/bridge/bridge_test.go b/plugins/main/bridge/bridge_test.go index 513b0dc9..4e3b6287 100644 --- a/plugins/main/bridge/bridge_test.go +++ b/plugins/main/bridge/bridge_test.go @@ -27,8 +27,9 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - types020 "github.com/containernetworking/cni/pkg/types/020" - "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/cni/pkg/types/040" + "github.com/containernetworking/cni/pkg/types/100" + "github.com/containernetworking/plugins/pkg/ip" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" @@ -58,7 +59,7 @@ type Net struct { // A *IPAMArgs `json:"cni"` DNS types.DNS `json:"dns"` RawPrevResult map[string]interface{} `json:"prevResult,omitempty"` - PrevResult current.Result `json:"-"` + PrevResult types100.Result `json:"-"` } // testCase defines the CNI network configuration and the expected @@ -73,6 +74,10 @@ type testCase struct { expGWCIDRs []string // Expected gateway addresses in CIDR form vlan int ipMasq bool + AddErr020 string + DelErr020 string + AddErr010 string + DelErr010 string } // Range definition for each entry in the ranges list @@ -349,41 +354,54 @@ func checkVlan(vlanId int, bridgeVlanInfo []*nl.BridgeVlanInfo) bool { } type cmdAddDelTester interface { - setNS(testNS ns.NetNS, targetNS ns.NetNS) - cmdAddTest(tc testCase, dataDir string) (*current.Result, error) + cmdAddTest(tc testCase, dataDir string) (types.Result, error) cmdCheckTest(tc testCase, conf *Net, dataDir string) cmdDelTest(tc testCase, dataDir string) } -func testerByVersion(version string) cmdAddDelTester { - switch { - case strings.HasPrefix(version, "0.4."): - return &testerV04x{} - case strings.HasPrefix(version, "0.3."): - return &testerV03x{} - default: - return &testerV01xOr02x{} - } -} - -type testerV04x struct { +type testerBase struct { testNS ns.NetNS targetNS ns.NetNS args *skel.CmdArgs vethName string } -func (tester *testerV04x) setNS(testNS ns.NetNS, targetNS ns.NetNS) { - tester.testNS = testNS - tester.targetNS = targetNS +type testerV10x testerBase +type testerV04x testerBase +type testerV03x testerBase +type testerV01xOr02x testerBase + +func newTesterByVersion(version string, testNS, targetNS ns.NetNS) cmdAddDelTester { + switch { + case strings.HasPrefix(version, "1.0."): + return &testerV10x{ + testNS: testNS, + targetNS: targetNS, + } + case strings.HasPrefix(version, "0.4."): + return &testerV04x{ + testNS: testNS, + targetNS: targetNS, + } + case strings.HasPrefix(version, "0.3."): + return &testerV03x{ + testNS: testNS, + targetNS: targetNS, + } + default: + return &testerV01xOr02x{ + testNS: testNS, + targetNS: targetNS, + } + } } -func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (*current.Result, error) { +func (tester *testerV10x) cmdAddTest(tc testCase, dataDir string) (types.Result, error) { // Generate network config and command arguments tester.args = tc.createCmdArgs(tester.targetNS, dataDir) // Execute cmdADD on the plugin - var result *current.Result + var result *types100.Result err := tester.testNS.Do(func(ns.NetNS) error { defer GinkgoRecover() @@ -393,10 +411,16 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (*current.Resu Expect(err).NotTo(HaveOccurred()) Expect(strings.Index(string(raw), "\"interfaces\":")).Should(BeNumerically(">", 0)) - result, err = current.GetResult(r) + resultType, err := r.GetAsVersion(tc.cniVersion) Expect(err).NotTo(HaveOccurred()) + result = resultType.(*types100.Result) + + if !tc.isLayer2 && tc.vlan != 0 { + Expect(len(result.Interfaces)).To(Equal(4)) + } else { + Expect(len(result.Interfaces)).To(Equal(3)) + } - Expect(len(result.Interfaces)).To(Equal(3)) Expect(result.Interfaces[0].Name).To(Equal(BRNAME)) Expect(result.Interfaces[0].Mac).To(HaveLen(17)) @@ -415,8 +439,335 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (*current.Resu Expect(link.Attrs().HardwareAddr.String()).To(Equal(result.Interfaces[0].Mac)) bridgeMAC := link.Attrs().HardwareAddr.String() + var vlanLink netlink.Link + if !tc.isLayer2 && tc.vlan != 0 { + // Make sure vlan link exists + vlanLink, err = netlink.LinkByName(fmt.Sprintf("%s.%d", BRNAME, tc.vlan)) + Expect(err).NotTo(HaveOccurred()) + Expect(vlanLink.Attrs().Name).To(Equal(fmt.Sprintf("%s.%d", BRNAME, tc.vlan))) + Expect(vlanLink).To(BeAssignableToTypeOf(&netlink.Veth{})) + + // Check the bridge dot vlan interface have the vlan tag + peerLink, err := netlink.LinkByIndex(vlanLink.Attrs().Index - 1) + Expect(err).NotTo(HaveOccurred()) + interfaceMap, err := netlink.BridgeVlanList() + Expect(err).NotTo(HaveOccurred()) + vlans, isExist := interfaceMap[int32(peerLink.Attrs().Index)] + Expect(isExist).To(BeTrue()) + Expect(checkVlan(tc.vlan, vlans)).To(BeTrue()) + } + + // Check the bridge vlan filtering equals true + if tc.vlan != 0 { + Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(true)) + } else { + Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(false)) + } + // Ensure bridge has expected gateway address(es) - addrs, err := netlink.AddrList(link, netlink.FAMILY_ALL) + var addrs []netlink.Addr + if tc.vlan == 0 { + addrs, err = netlink.AddrList(link, netlink.FAMILY_ALL) + } else { + addrs, err = netlink.AddrList(vlanLink, netlink.FAMILY_ALL) + } + Expect(err).NotTo(HaveOccurred()) + Expect(len(addrs)).To(BeNumerically(">", 0)) + for _, cidr := range tc.expGWCIDRs { + ip, subnet, err := net.ParseCIDR(cidr) + Expect(err).NotTo(HaveOccurred()) + + found := false + subnetPrefix, subnetBits := subnet.Mask.Size() + for _, a := range addrs { + aPrefix, aBits := a.IPNet.Mask.Size() + if a.IPNet.IP.Equal(ip) && aPrefix == subnetPrefix && aBits == subnetBits { + found = true + break + } + } + Expect(found).To(Equal(true), fmt.Sprintf("failed to find %s", cidr)) + } + + // Check for the veth link in the main namespace + links, err := netlink.LinkList() + Expect(err).NotTo(HaveOccurred()) + if !tc.isLayer2 && tc.vlan != 0 { + Expect(len(links)).To(Equal(5)) // Bridge, Bridge vlan veth, veth, and loopback + } else { + Expect(len(links)).To(Equal(3)) // Bridge, veth, and loopback + } + + link, err = netlink.LinkByName(result.Interfaces[1].Name) + Expect(err).NotTo(HaveOccurred()) + Expect(link).To(BeAssignableToTypeOf(&netlink.Veth{})) + tester.vethName = result.Interfaces[1].Name + + // check vlan exist on the veth interface + if tc.vlan != 0 { + interfaceMap, err := netlink.BridgeVlanList() + Expect(err).NotTo(HaveOccurred()) + vlans, isExist := interfaceMap[int32(link.Attrs().Index)] + Expect(isExist).To(BeTrue()) + Expect(checkVlan(tc.vlan, vlans)).To(BeTrue()) + } + + // Check that the bridge has a different mac from the veth + // If not, it means the bridge has an unstable mac and will change + // as ifs are added and removed + // this check is not relevant for a layer 2 bridge + if !tc.isLayer2 && tc.vlan == 0 { + Expect(link.Attrs().HardwareAddr.String()).NotTo(Equal(bridgeMAC)) + } + + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Find the veth peer in the container namespace and the default route + err = tester.targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal(IFNAME)) + Expect(link).To(BeAssignableToTypeOf(&netlink.Veth{})) + + expCIDRsV4, expCIDRsV6 := tc.expectedCIDRs() + addrs, err := netlink.AddrList(link, netlink.FAMILY_V4) + Expect(err).NotTo(HaveOccurred()) + Expect(len(addrs)).To(Equal(len(expCIDRsV4))) + addrs, err = netlink.AddrList(link, netlink.FAMILY_V6) + Expect(len(addrs)).To(Equal(len(expCIDRsV6) + 1)) //add one for the link-local + Expect(err).NotTo(HaveOccurred()) + // Ignore link local address which may or may not be + // ready when we read addresses. + var foundAddrs int + for _, addr := range addrs { + if !addr.IP.IsLinkLocalUnicast() { + foundAddrs++ + } + } + Expect(foundAddrs).To(Equal(len(expCIDRsV6))) + + // Ensure the default route(s) + routes, err := netlink.RouteList(link, 0) + Expect(err).NotTo(HaveOccurred()) + + var defaultRouteFound4, defaultRouteFound6 bool + for _, cidr := range tc.expGWCIDRs { + gwIP, _, err := net.ParseCIDR(cidr) + Expect(err).NotTo(HaveOccurred()) + var found *bool + if ipVersion(gwIP) == "4" { + found = &defaultRouteFound4 + } else { + found = &defaultRouteFound6 + } + if *found == true { + continue + } + for _, route := range routes { + *found = (route.Dst == nil && route.Src == nil && route.Gw.Equal(gwIP)) + if *found { + break + } + } + Expect(*found).To(Equal(true)) + } + + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + return result, nil +} + +func (tester *testerV10x) cmdCheckTest(tc testCase, conf *Net, dataDir string) { + // Generate network config and command arguments + tester.args = tc.createCheckCmdArgs(tester.targetNS, conf, dataDir) + + // Execute cmdCHECK on the plugin + err := tester.testNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + err := testutils.CmdCheckWithArgs(tester.args, func() error { + return cmdCheck(tester.args) + }) + Expect(err).NotTo(HaveOccurred()) + + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Find the veth peer in the container namespace and the default route + err = tester.targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal(IFNAME)) + Expect(link).To(BeAssignableToTypeOf(&netlink.Veth{})) + + expCIDRsV4, expCIDRsV6 := tc.expectedCIDRs() + addrs, err := netlink.AddrList(link, netlink.FAMILY_V4) + Expect(err).NotTo(HaveOccurred()) + Expect(len(addrs)).To(Equal(len(expCIDRsV4))) + addrs, err = netlink.AddrList(link, netlink.FAMILY_V6) + Expect(len(addrs)).To(Equal(len(expCIDRsV6) + 1)) //add one for the link-local + Expect(err).NotTo(HaveOccurred()) + // Ignore link local address which may or may not be + // ready when we read addresses. + var foundAddrs int + for _, addr := range addrs { + if !addr.IP.IsLinkLocalUnicast() { + foundAddrs++ + } + } + Expect(foundAddrs).To(Equal(len(expCIDRsV6))) + + // Ensure the default route(s) + routes, err := netlink.RouteList(link, 0) + Expect(err).NotTo(HaveOccurred()) + + var defaultRouteFound4, defaultRouteFound6 bool + for _, cidr := range tc.expGWCIDRs { + gwIP, _, err := net.ParseCIDR(cidr) + Expect(err).NotTo(HaveOccurred()) + var found *bool + if ipVersion(gwIP) == "4" { + found = &defaultRouteFound4 + } else { + found = &defaultRouteFound6 + } + if *found == true { + continue + } + for _, route := range routes { + *found = (route.Dst == nil && route.Src == nil && route.Gw.Equal(gwIP)) + if *found { + break + } + } + Expect(*found).To(Equal(true)) + } + + return nil + }) + Expect(err).NotTo(HaveOccurred()) +} + +func (tester *testerV10x) cmdDelTest(tc testCase, dataDir string) { + tester.args = tc.createCmdArgs(tester.targetNS, dataDir) + err := tester.testNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + err := testutils.CmdDelWithArgs(tester.args, func() error { + return cmdDel(tester.args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure the host veth has been deleted + err = tester.targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).To(HaveOccurred()) + Expect(link).To(BeNil()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure the container veth has been deleted + err = tester.testNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(tester.vethName) + Expect(err).To(HaveOccurred()) + Expect(link).To(BeNil()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) +} + +func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (types.Result, error) { + // Generate network config and command arguments + tester.args = tc.createCmdArgs(tester.targetNS, dataDir) + + // Execute cmdADD on the plugin + var result *types040.Result + err := tester.testNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + r, raw, err := testutils.CmdAddWithArgs(tester.args, func() error { + return cmdAdd(tester.args) + }) + Expect(err).NotTo(HaveOccurred()) + Expect(strings.Index(string(raw), "\"interfaces\":")).Should(BeNumerically(">", 0)) + + resultType, err := r.GetAsVersion(tc.cniVersion) + Expect(err).NotTo(HaveOccurred()) + result = resultType.(*types040.Result) + + if !tc.isLayer2 && tc.vlan != 0 { + Expect(len(result.Interfaces)).To(Equal(4)) + } else { + Expect(len(result.Interfaces)).To(Equal(3)) + } + + Expect(result.Interfaces[0].Name).To(Equal(BRNAME)) + Expect(result.Interfaces[0].Mac).To(HaveLen(17)) + + Expect(result.Interfaces[1].Name).To(HavePrefix("veth")) + Expect(result.Interfaces[1].Mac).To(HaveLen(17)) + + Expect(result.Interfaces[2].Name).To(Equal(IFNAME)) + Expect(result.Interfaces[2].Mac).To(HaveLen(17)) //mac is random + Expect(result.Interfaces[2].Sandbox).To(Equal(tester.targetNS.Path())) + + // Make sure bridge link exists + link, err := netlink.LinkByName(result.Interfaces[0].Name) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal(BRNAME)) + Expect(link).To(BeAssignableToTypeOf(&netlink.Bridge{})) + Expect(link.Attrs().HardwareAddr.String()).To(Equal(result.Interfaces[0].Mac)) + bridgeMAC := link.Attrs().HardwareAddr.String() + + var vlanLink netlink.Link + if !tc.isLayer2 && tc.vlan != 0 { + // Make sure vlan link exists + vlanLink, err = netlink.LinkByName(fmt.Sprintf("%s.%d", BRNAME, tc.vlan)) + Expect(err).NotTo(HaveOccurred()) + Expect(vlanLink.Attrs().Name).To(Equal(fmt.Sprintf("%s.%d", BRNAME, tc.vlan))) + Expect(vlanLink).To(BeAssignableToTypeOf(&netlink.Veth{})) + + // Check the bridge dot vlan interface have the vlan tag + peerLink, err := netlink.LinkByIndex(vlanLink.Attrs().Index - 1) + Expect(err).NotTo(HaveOccurred()) + interfaceMap, err := netlink.BridgeVlanList() + Expect(err).NotTo(HaveOccurred()) + vlans, isExist := interfaceMap[int32(peerLink.Attrs().Index)] + Expect(isExist).To(BeTrue()) + Expect(checkVlan(tc.vlan, vlans)).To(BeTrue()) + } + + // Check the bridge vlan filtering equals true + if tc.vlan != 0 { + Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(true)) + } else { + Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(false)) + } + + // Ensure bridge has expected gateway address(es) + var addrs []netlink.Addr + if tc.vlan == 0 { + addrs, err = netlink.AddrList(link, netlink.FAMILY_ALL) + } else { + addrs, err = netlink.AddrList(vlanLink, netlink.FAMILY_ALL) + } Expect(err).NotTo(HaveOccurred()) Expect(len(addrs)).To(BeNumerically(">", 0)) for _, cidr := range tc.expGWCIDRs { @@ -438,17 +789,33 @@ func (tester *testerV04x) cmdAddTest(tc testCase, dataDir string) (*current.Resu // Check for the veth link in the main namespace links, err := netlink.LinkList() Expect(err).NotTo(HaveOccurred()) - Expect(len(links)).To(Equal(3)) // Bridge, veth, and loopback + if !tc.isLayer2 && tc.vlan != 0 { + Expect(len(links)).To(Equal(5)) // Bridge, Bridge vlan veth, veth, and loopback + } else { + Expect(len(links)).To(Equal(3)) // Bridge, veth, and loopback + } link, err = netlink.LinkByName(result.Interfaces[1].Name) Expect(err).NotTo(HaveOccurred()) Expect(link).To(BeAssignableToTypeOf(&netlink.Veth{})) tester.vethName = result.Interfaces[1].Name + // check vlan exist on the veth interface + if tc.vlan != 0 { + interfaceMap, err := netlink.BridgeVlanList() + Expect(err).NotTo(HaveOccurred()) + vlans, isExist := interfaceMap[int32(link.Attrs().Index)] + Expect(isExist).To(BeTrue()) + Expect(checkVlan(tc.vlan, vlans)).To(BeTrue()) + } + // Check that the bridge has a different mac from the veth // If not, it means the bridge has an unstable mac and will change // as ifs are added and removed - Expect(link.Attrs().HardwareAddr.String()).NotTo(Equal(bridgeMAC)) + // this check is not relevant for a layer 2 bridge + if !tc.isLayer2 && tc.vlan == 0 { + Expect(link.Attrs().HardwareAddr.String()).NotTo(Equal(bridgeMAC)) + } return nil }) @@ -623,24 +990,12 @@ func (tester *testerV04x) cmdDelTest(tc testCase, dataDir string) { Expect(err).NotTo(HaveOccurred()) } -type testerV03x struct { - testNS ns.NetNS - targetNS ns.NetNS - args *skel.CmdArgs - vethName string -} - -func (tester *testerV03x) setNS(testNS ns.NetNS, targetNS ns.NetNS) { - tester.testNS = testNS - tester.targetNS = targetNS -} - -func (tester *testerV03x) cmdAddTest(tc testCase, dataDir string) (*current.Result, error) { +func (tester *testerV03x) cmdAddTest(tc testCase, dataDir string) (types.Result, error) { // Generate network config and command arguments tester.args = tc.createCmdArgs(tester.targetNS, dataDir) // Execute cmdADD on the plugin - var result *current.Result + var result *types040.Result err := tester.testNS.Do(func(ns.NetNS) error { defer GinkgoRecover() @@ -650,8 +1005,9 @@ func (tester *testerV03x) cmdAddTest(tc testCase, dataDir string) (*current.Resu Expect(err).NotTo(HaveOccurred()) Expect(strings.Index(string(raw), "\"interfaces\":")).Should(BeNumerically(">", 0)) - result, err = current.GetResult(r) + resultType, err := r.GetAsVersion(tc.cniVersion) Expect(err).NotTo(HaveOccurred()) + result = resultType.(*types040.Result) if !tc.isLayer2 && tc.vlan != 0 { Expect(len(result.Interfaces)).To(Equal(4)) @@ -858,22 +1214,28 @@ func (tester *testerV03x) cmdDelTest(tc testCase, dataDir string) { Expect(err).NotTo(HaveOccurred()) } -type testerV01xOr02x struct { - testNS ns.NetNS - targetNS ns.NetNS - args *skel.CmdArgs - vethName string +func expect020AddError(tc testCase) bool { + return tc.AddErr020 != "" && tc.cniVersion == "0.2.0" } -func (tester *testerV01xOr02x) setNS(testNS ns.NetNS, targetNS ns.NetNS) { - tester.testNS = testNS - tester.targetNS = targetNS +func expect020DelError(tc testCase) bool { + return tc.DelErr020 != "" && tc.cniVersion == "0.2.0" } -func (tester *testerV01xOr02x) cmdAddTest(tc testCase, dataDir string) (*current.Result, error) { - // Generate network config and calculate gateway addresses +func expect010AddError(tc testCase) bool { + return tc.AddErr010 != "" && tc.cniVersion == "0.1.0" +} + +func expect010DelError(tc testCase) bool { + return tc.DelErr010 != "" && tc.cniVersion == "0.1.0" +} + +func (tester *testerV01xOr02x) cmdAddTest(tc testCase, dataDir string) (types.Result, error) { + // Generate network config and command arguments tester.args = tc.createCmdArgs(tester.targetNS, dataDir) + var hostNSVlanMap map[int32][]*nl.BridgeVlanInfo + // Execute cmdADD on the plugin err := tester.testNS.Do(func(ns.NetNS) error { defer GinkgoRecover() @@ -881,11 +1243,15 @@ func (tester *testerV01xOr02x) cmdAddTest(tc testCase, dataDir string) (*current r, raw, err := testutils.CmdAddWithArgs(tester.args, func() error { return cmdAdd(tester.args) }) + + if expect020AddError(tc) || expect010AddError(tc) { + return err + } Expect(err).NotTo(HaveOccurred()) Expect(strings.Index(string(raw), "\"ip\":")).Should(BeNumerically(">", 0)) - // We expect a version 0.1.0 result - _, err = types020.GetResult(r) + // We expect a version 0.1.0 or 0.2.0 result + _, err = r.GetAsVersion(tc.cniVersion) Expect(err).NotTo(HaveOccurred()) // Make sure bridge link exists @@ -894,8 +1260,38 @@ func (tester *testerV01xOr02x) cmdAddTest(tc testCase, dataDir string) (*current Expect(link.Attrs().Name).To(Equal(BRNAME)) Expect(link).To(BeAssignableToTypeOf(&netlink.Bridge{})) + var vlanLink netlink.Link + if !tc.isLayer2 && tc.vlan != 0 { + // Make sure vlan link exists + vlanLink, err = netlink.LinkByName(fmt.Sprintf("%s.%d", BRNAME, tc.vlan)) + Expect(err).NotTo(HaveOccurred()) + Expect(vlanLink.Attrs().Name).To(Equal(fmt.Sprintf("%s.%d", BRNAME, tc.vlan))) + Expect(vlanLink).To(BeAssignableToTypeOf(&netlink.Veth{})) + + // Check the bridge dot vlan interface have the vlan tag + peerLink, err := netlink.LinkByIndex(vlanLink.Attrs().Index - 1) + Expect(err).NotTo(HaveOccurred()) + interfaceMap, err := netlink.BridgeVlanList() + Expect(err).NotTo(HaveOccurred()) + vlans, isExist := interfaceMap[int32(peerLink.Attrs().Index)] + Expect(isExist).To(BeTrue()) + Expect(checkVlan(tc.vlan, vlans)).To(BeTrue()) + } + + // Check the bridge vlan filtering equals true + if tc.vlan != 0 { + Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(true)) + } else { + Expect(*link.(*netlink.Bridge).VlanFiltering).To(Equal(false)) + } + // Ensure bridge has expected gateway address(es) - addrs, err := netlink.AddrList(link, netlink.FAMILY_ALL) + var addrs []netlink.Addr + if tc.vlan == 0 { + addrs, err = netlink.AddrList(link, netlink.FAMILY_ALL) + } else { + addrs, err = netlink.AddrList(vlanLink, netlink.FAMILY_ALL) + } Expect(err).NotTo(HaveOccurred()) Expect(len(addrs)).To(BeNumerically(">", 0)) for _, cidr := range tc.expGWCIDRs { @@ -919,9 +1315,26 @@ func (tester *testerV01xOr02x) cmdAddTest(tc testCase, dataDir string) (*current // doesn't report interfaces links, err := netlink.LinkList() Expect(err).NotTo(HaveOccurred()) - Expect(len(links)).To(Equal(3)) // Bridge, veth, and loopback + if !tc.isLayer2 && tc.vlan != 0 { + Expect(len(links)).To(Equal(5)) // Bridge, Bridge vlan veth, veth, and loopback + } else { + Expect(len(links)).To(Equal(3)) // Bridge, veth, and loopback + } + + // Grab the vlan map in the host NS for checking later + if tc.vlan != 0 { + hostNSVlanMap, err = netlink.BridgeVlanList() + Expect(err).NotTo(HaveOccurred()) + } return nil }) + if expect020AddError(tc) { + Expect(err).To(MatchError(tc.AddErr020)) + return nil, nil + } else if expect010AddError(tc) { + Expect(err).To(MatchError(tc.AddErr010)) + return nil, nil + } Expect(err).NotTo(HaveOccurred()) // Find the veth peer in the container namespace and the default route @@ -939,23 +1352,52 @@ func (tester *testerV01xOr02x) cmdAddTest(tc testCase, dataDir string) (*current Expect(len(addrs)).To(Equal(len(expCIDRsV4))) addrs, err = netlink.AddrList(link, netlink.FAMILY_V6) Expect(err).NotTo(HaveOccurred()) - Expect(len(addrs)).To(Equal(len(expCIDRsV6) + 1)) // Link local address is automatic + // Ignore link local address which may or may not be + // ready when we read addresses. + var foundAddrs int + for _, addr := range addrs { + if !addr.IP.IsLinkLocalUnicast() { + foundAddrs++ + } + } + Expect(foundAddrs).To(Equal(len(expCIDRsV6))) - // Ensure the default route + // Ensure the default route(s) routes, err := netlink.RouteList(link, 0) Expect(err).NotTo(HaveOccurred()) - var defaultRouteFound bool + var defaultRouteFound4, defaultRouteFound6 bool for _, cidr := range tc.expGWCIDRs { gwIP, _, err := net.ParseCIDR(cidr) Expect(err).NotTo(HaveOccurred()) + var found *bool + if ipVersion(gwIP) == "4" { + found = &defaultRouteFound4 + } else { + found = &defaultRouteFound6 + } + if *found == true { + continue + } for _, route := range routes { - defaultRouteFound = (route.Dst == nil && route.Src == nil && route.Gw.Equal(gwIP)) - if defaultRouteFound { + *found = (route.Dst == nil && route.Src == nil && route.Gw.Equal(gwIP)) + if *found { break } } - Expect(defaultRouteFound).To(Equal(true)) + Expect(*found).To(Equal(true)) + } + + // Validate VLAN in the host NS. Since 0.1.0/0.2.0 don't return + // any host interface information, we have to look up the container + // namespace veth's peer index instead + if tc.vlan != 0 { + _, peerIndex, err := ip.GetVethPeerIfindex(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(peerIndex).To(BeNumerically(">", 0)) + vlans, isExist := hostNSVlanMap[int32(peerIndex)] + Expect(isExist).To(BeTrue()) + Expect(checkVlan(tc.vlan, vlans)).To(BeTrue()) } return nil @@ -975,7 +1417,13 @@ func (tester *testerV01xOr02x) cmdDelTest(tc testCase, dataDir string) { err := testutils.CmdDelWithArgs(tester.args, func() error { return cmdDel(tester.args) }) - Expect(err).NotTo(HaveOccurred()) + if expect020DelError(tc) { + Expect(err).To(MatchError(tc.DelErr020)) + } else if expect010DelError(tc) { + Expect(err).To(MatchError(tc.DelErr010)) + } else { + Expect(err).NotTo(HaveOccurred()) + } return nil }) Expect(err).NotTo(HaveOccurred()) @@ -993,30 +1441,18 @@ func (tester *testerV01xOr02x) cmdDelTest(tc testCase, dataDir string) { Expect(err).NotTo(HaveOccurred()) } -func cmdAddDelTest(testNS ns.NetNS, tc testCase, dataDir string) { - // Get a Add/Del tester based on test case version - tester := testerByVersion(tc.cniVersion) - - targetNS, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNS.Close() - tester.setNS(testNS, targetNS) +func cmdAddDelTest(origNS, targetNS ns.NetNS, tc testCase, dataDir string) { + tester := newTesterByVersion(tc.cniVersion, origNS, targetNS) // Test IP allocation - result, err := tester.cmdAddTest(tc, dataDir) + _, err := tester.cmdAddTest(tc, dataDir) Expect(err).NotTo(HaveOccurred()) - if strings.HasPrefix(tc.cniVersion, "0.3.") { - Expect(result).NotTo(BeNil()) - } else { - Expect(result).To(BeNil()) - } - // Test IP Release tester.cmdDelTest(tc, dataDir) // Clean up bridge addresses for next test case - delBridgeAddrs(testNS) + delBridgeAddrs(origNS) } func buildOneConfig(name, cniVersion string, orig *Net, prevResult types.Result) (*Net, error) { @@ -1061,16 +1497,8 @@ func buildOneConfig(name, cniVersion string, orig *Net, prevResult types.Result) } -func cmdAddDelCheckTest(testNS ns.NetNS, tc testCase, dataDir string) { - Expect(tc.cniVersion).To(Equal("0.4.0")) - - // Get a Add/Del tester based on test case version - tester := testerByVersion(tc.cniVersion) - - targetNS, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNS.Close() - tester.setNS(testNS, targetNS) +func cmdAddDelCheckTest(origNS, targetNS ns.NetNS, tc testCase, dataDir string) { + tester := newTesterByVersion(tc.cniVersion, origNS, targetNS) // Test IP allocation prevResult, err := tester.cmdAddTest(tc, dataDir) @@ -1097,15 +1525,15 @@ func cmdAddDelCheckTest(testNS ns.NetNS, tc testCase, dataDir string) { tester.cmdDelTest(tc, dataDir) // Clean up bridge addresses for next test case - delBridgeAddrs(testNS) + delBridgeAddrs(origNS) if tc.vlan != 0 && !tc.isLayer2 { - delVlanAddrs(testNS, tc.vlan) + delVlanAddrs(origNS, tc.vlan) } } var _ = Describe("bridge Operations", func() { - var originalNS ns.NetNS + var originalNS, targetNS ns.NetNS var dataDir string BeforeEach(func() { @@ -1113,6 +1541,8 @@ var _ = Describe("bridge Operations", func() { var err error originalNS, err = testutils.NewNS() Expect(err).NotTo(HaveOccurred()) + targetNS, err = testutils.NewNS() + Expect(err).NotTo(HaveOccurred()) dataDir, err = ioutil.TempDir("", "bridge_test") Expect(err).NotTo(HaveOccurred()) @@ -1125,62 +1555,68 @@ var _ = Describe("bridge Operations", func() { Expect(os.RemoveAll(dataDir)).To(Succeed()) Expect(originalNS.Close()).To(Succeed()) Expect(testutils.UnmountNS(originalNS)).To(Succeed()) + Expect(targetNS.Close()).To(Succeed()) + Expect(testutils.UnmountNS(targetNS)).To(Succeed()) }) - It("creates a bridge", func() { - conf := testCase{cniVersion: "0.3.1"}.netConf() - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + for _, ver := range testutils.AllSpecVersions { + // Redefine ver inside for scope so real value is picked up by each dynamically defined It() + // See Gingkgo's "Patterns for dynamically generating tests" documentation. + ver := ver - bridge, _, err := setupBridge(conf) - Expect(err).NotTo(HaveOccurred()) - Expect(bridge.Attrs().Name).To(Equal(BRNAME)) + It(fmt.Sprintf("[%s] creates a bridge", ver), func() { + conf := testCase{cniVersion: ver}.netConf() + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - // Double check that the link was added - link, err := netlink.LinkByName(BRNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal(BRNAME)) - Expect(link.Attrs().Promisc).To(Equal(0)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) + bridge, _, err := setupBridge(conf) + Expect(err).NotTo(HaveOccurred()) + Expect(bridge.Attrs().Name).To(Equal(BRNAME)) - It("handles an existing bridge", func() { - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - err := netlink.LinkAdd(&netlink.Bridge{ - LinkAttrs: netlink.LinkAttrs{ - Name: BRNAME, - }, + // Double check that the link was added + link, err := netlink.LinkByName(BRNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal(BRNAME)) + Expect(link.Attrs().Promisc).To(Equal(0)) + return nil }) Expect(err).NotTo(HaveOccurred()) - link, err := netlink.LinkByName(BRNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal(BRNAME)) - ifindex := link.Attrs().Index - - tc := testCase{cniVersion: "0.3.1", isGW: false} - conf := tc.netConf() - - bridge, _, err := setupBridge(conf) - Expect(err).NotTo(HaveOccurred()) - Expect(bridge.Attrs().Name).To(Equal(BRNAME)) - Expect(bridge.Attrs().Index).To(Equal(ifindex)) - - // Double check that the link has the same ifindex - link, err = netlink.LinkByName(BRNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal(BRNAME)) - Expect(link.Attrs().Index).To(Equal(ifindex)) - return nil }) - Expect(err).NotTo(HaveOccurred()) - }) - It("configures and deconfigures a bridge and veth with default route with ADD/DEL for 0.3.0 config", func() { - testCases := []testCase{ + It(fmt.Sprintf("[%s] handles an existing bridge", ver), func() { + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + err := netlink.LinkAdd(&netlink.Bridge{ + LinkAttrs: netlink.LinkAttrs{ + Name: BRNAME, + }, + }) + Expect(err).NotTo(HaveOccurred()) + link, err := netlink.LinkByName(BRNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal(BRNAME)) + ifindex := link.Attrs().Index + + tc := testCase{cniVersion: ver, isGW: false} + conf := tc.netConf() + + bridge, _, err := setupBridge(conf) + Expect(err).NotTo(HaveOccurred()) + Expect(bridge.Attrs().Name).To(Equal(BRNAME)) + Expect(bridge.Attrs().Index).To(Equal(ifindex)) + + // Double check that the link has the same ifindex + link, err = netlink.LinkByName(BRNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal(BRNAME)) + Expect(link.Attrs().Index).To(Equal(ifindex)) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + }) + + for i, tc := range []testCase{ { // IPv4 only subnet: "10.1.2.0/24", @@ -1214,36 +1650,42 @@ var _ = Describe("bridge Operations", func() { "fd00::1/64", "2001:db8::1/64", }, + AddErr020: "CNI version 0.2.0 does not support more than 1 address per family", + DelErr020: "CNI version 0.2.0 does not support more than 1 address per family", + AddErr010: "CNI version 0.1.0 does not support more than 1 address per family", + DelErr010: "CNI version 0.1.0 does not support more than 1 address per family", }, + } { + tc := tc + i := i + It(fmt.Sprintf("[%s] (%d) configures and deconfigures a bridge and veth with default route with ADD/DEL", ver, i), func() { + tc.cniVersion = ver + cmdAddDelTest(originalNS, targetNS, tc, dataDir) + }) } - for _, tc := range testCases { - tc.cniVersion = "0.3.0" - cmdAddDelTest(originalNS, tc, dataDir) - } - }) - It("configures and deconfigures a l2 bridge and veth with ADD/DEL for 0.3.1 config", func() { - tc := testCase{cniVersion: "0.3.0", isLayer2: true} - cmdAddDelTest(originalNS, tc, dataDir) - }) + It(fmt.Sprintf("[%s] configures and deconfigures a l2 bridge and veth with ADD/DEL", ver), func() { + tc := testCase{ + cniVersion: ver, + isLayer2: true, + AddErr020: "cannot convert: no valid IP addresses", + AddErr010: "cannot convert: no valid IP addresses", + } + cmdAddDelTest(originalNS, targetNS, tc, dataDir) + }) - It("configures and deconfigures a l2 bridge and veth with ADD/DEL for 0.3.1 config", func() { - tc := testCase{cniVersion: "0.3.1", isLayer2: true} - cmdAddDelTest(originalNS, tc, dataDir) - }) + It(fmt.Sprintf("[%s] configures and deconfigures a l2 bridge with vlan id 100 using ADD/DEL", ver), func() { + tc := testCase{ + cniVersion: ver, + isLayer2: true, + vlan: 100, + AddErr020: "cannot convert: no valid IP addresses", + AddErr010: "cannot convert: no valid IP addresses", + } + cmdAddDelTest(originalNS, targetNS, tc, dataDir) + }) - It("configures and deconfigures a l2 bridge with vlan id 100 using ADD/DEL for 0.3.1 config", func() { - tc := testCase{cniVersion: "0.3.0", isLayer2: true, vlan: 100} - cmdAddDelTest(originalNS, tc, dataDir) - }) - - It("configures and deconfigures a l2 bridge with vlan id 100 using ADD/DEL for 0.3.1 config", func() { - tc := testCase{cniVersion: "0.3.1", isLayer2: true, vlan: 100} - cmdAddDelTest(originalNS, tc, dataDir) - }) - - It("configures and deconfigures a bridge, veth with default route and vlanID 100 with ADD/DEL for 0.3.0 config", func() { - testCases := []testCase{ + for i, tc := range []testCase{ { // IPv4 only subnet: "10.1.2.0/24", @@ -1280,17 +1722,22 @@ var _ = Describe("bridge Operations", func() { "fd00::1/64", "2001:db8::1/64", }, - vlan: 100, + vlan: 100, + AddErr020: "CNI version 0.2.0 does not support more than 1 address per family", + DelErr020: "CNI version 0.2.0 does not support more than 1 address per family", + AddErr010: "CNI version 0.1.0 does not support more than 1 address per family", + DelErr010: "CNI version 0.1.0 does not support more than 1 address per family", }, + } { + tc := tc + i := i + It(fmt.Sprintf("[%s] (%d) configures and deconfigures a bridge, veth with default route and vlanID 100 with ADD/DEL", ver, i), func() { + tc.cniVersion = ver + cmdAddDelTest(originalNS, targetNS, tc, dataDir) + }) } - for _, tc := range testCases { - tc.cniVersion = "0.3.0" - cmdAddDelTest(originalNS, tc, dataDir) - } - }) - It("configures and deconfigures a bridge and veth with default route with ADD/DEL for 0.3.1 config", func() { - testCases := []testCase{ + for i, tc := range []testCase{ { // IPv4 only subnet: "10.1.2.0/24", @@ -1312,65 +1759,33 @@ var _ = Describe("bridge Operations", func() { "fd00::1/64", }, }, - } - for _, tc := range testCases { - tc.cniVersion = "0.3.1" - cmdAddDelTest(originalNS, tc, dataDir) - } - }) - - It("deconfigures an unconfigured bridge with DEL", func() { - tc := testCase{ - cniVersion: "0.3.0", - subnet: "10.1.2.0/24", - expGWCIDRs: []string{"10.1.2.1/24"}, + } { + tc := tc + i := i + It(fmt.Sprintf("[%s] (%d) configures and deconfigures a bridge and veth with default route with ADD/DEL", ver, i), func() { + tc.cniVersion = ver + cmdAddDelTest(originalNS, targetNS, tc, dataDir) + }) } - tester := testerV03x{} - targetNS, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNS.Close() - tester.setNS(originalNS, targetNS) - tester.args = tc.createCmdArgs(targetNS, dataDir) - - // Execute cmdDEL on the plugin, expect no errors - tester.cmdDelTest(tc, dataDir) - }) - - It("configures and deconfigures a bridge and veth with default route with ADD/DEL for 0.1.0 config", func() { - testCases := []testCase{ - { - // IPv4 only + It("deconfigures an unconfigured bridge with DEL", func() { + tc := testCase{ + cniVersion: ver, subnet: "10.1.2.0/24", expGWCIDRs: []string{"10.1.2.1/24"}, - }, - { - // IPv6 only - subnet: "2001:db8::0/64", - expGWCIDRs: []string{"2001:db8::1/64"}, - }, - { - // Dual-Stack - ranges: []rangeInfo{ - {subnet: "192.168.0.0/24"}, - {subnet: "fd00::0/64"}, - }, - expGWCIDRs: []string{ - "192.168.0.1/24", - "fd00::1/64", - }, - }, - } - for _, tc := range testCases { - tc.cniVersion = "0.1.0" - cmdAddDelTest(originalNS, tc, dataDir) - } - }) + } - It("ensure bridge address", func() { - conf := testCase{cniVersion: "0.3.1", isGW: true}.netConf() + tester := testerV03x{ + testNS: originalNS, + targetNS: targetNS, + args: tc.createCmdArgs(targetNS, dataDir), + } - testCases := []struct { + // Execute cmdDEL on the plugin, expect no errors + tester.cmdDelTest(tc, dataDir) + }) + + for i, tc := range []struct { gwCIDRFirst string gwCIDRSecond string }{ @@ -1389,298 +1804,302 @@ var _ = Describe("bridge Operations", func() { gwCIDRFirst: "2001:db8:1:2::1/64", gwCIDRSecond: "fd00:1234::1/64", }, - } - for _, tc := range testCases { + } { + tc := tc + i := i + It(fmt.Sprintf("[%s] (%d) ensure bridge address", ver, i), func() { + conf := testCase{cniVersion: ver, isGW: true}.netConf() - gwIP, gwSubnet, err := net.ParseCIDR(tc.gwCIDRFirst) - Expect(err).NotTo(HaveOccurred()) - gwnFirst := net.IPNet{IP: gwIP, Mask: gwSubnet.Mask} - gwIP, gwSubnet, err = net.ParseCIDR(tc.gwCIDRSecond) - Expect(err).NotTo(HaveOccurred()) - gwnSecond := net.IPNet{IP: gwIP, Mask: gwSubnet.Mask} - - var family, expNumAddrs int - switch { - case gwIP.To4() != nil: - family = netlink.FAMILY_V4 - expNumAddrs = 1 - default: - family = netlink.FAMILY_V6 - // Expect configured gw address plus link local - expNumAddrs = 2 - } - - subnetsOverlap := gwnFirst.Contains(gwnSecond.IP) || - gwnSecond.Contains(gwnFirst.IP) - - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - // Create the bridge - bridge, _, err := setupBridge(conf) + gwIP, gwSubnet, err := net.ParseCIDR(tc.gwCIDRFirst) Expect(err).NotTo(HaveOccurred()) + gwnFirst := net.IPNet{IP: gwIP, Mask: gwSubnet.Mask} + gwIP, gwSubnet, err = net.ParseCIDR(tc.gwCIDRSecond) + Expect(err).NotTo(HaveOccurred()) + gwnSecond := net.IPNet{IP: gwIP, Mask: gwSubnet.Mask} - // Function to check IP address(es) on bridge - checkBridgeIPs := func(cidr0, cidr1 string) { - addrs, err := netlink.AddrList(bridge, family) - Expect(err).NotTo(HaveOccurred()) - Expect(len(addrs)).To(Equal(expNumAddrs)) - addr := addrs[0].IPNet.String() - Expect(addr).To(Equal(cidr0)) - if cidr1 != "" { - addr = addrs[1].IPNet.String() - Expect(addr).To(Equal(cidr1)) - } + var family, expNumAddrs int + switch { + case gwIP.To4() != nil: + family = netlink.FAMILY_V4 + expNumAddrs = 1 + default: + family = netlink.FAMILY_V6 + // Expect configured gw address plus link local + expNumAddrs = 2 } + subnetsOverlap := gwnFirst.Contains(gwnSecond.IP) || + gwnSecond.Contains(gwnFirst.IP) + + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + // Create the bridge + bridge, _, err := setupBridge(conf) + Expect(err).NotTo(HaveOccurred()) + + // Function to check IP address(es) on bridge + checkBridgeIPs := func(cidr0, cidr1 string) { + addrs, err := netlink.AddrList(bridge, family) + Expect(err).NotTo(HaveOccurred()) + Expect(len(addrs)).To(Equal(expNumAddrs)) + addr := addrs[0].IPNet.String() + Expect(addr).To(Equal(cidr0)) + if cidr1 != "" { + addr = addrs[1].IPNet.String() + Expect(addr).To(Equal(cidr1)) + } + } + + // Check if ForceAddress has default value + Expect(conf.ForceAddress).To(Equal(false)) + + // Set first address on bridge + err = ensureAddr(bridge, family, &gwnFirst, conf.ForceAddress) + Expect(err).NotTo(HaveOccurred()) + checkBridgeIPs(tc.gwCIDRFirst, "") + + // Attempt to set the second address on the bridge + // with ForceAddress set to false. + err = ensureAddr(bridge, family, &gwnSecond, false) + if family == netlink.FAMILY_V4 || subnetsOverlap { + // IPv4 or overlapping IPv6 subnets: + // Expect an error, and address should remain the same + Expect(err).To(HaveOccurred()) + checkBridgeIPs(tc.gwCIDRFirst, "") + } else { + // Non-overlapping IPv6 subnets: + // There should be 2 addresses (in addition to link local) + Expect(err).NotTo(HaveOccurred()) + expNumAddrs++ + checkBridgeIPs(tc.gwCIDRSecond, tc.gwCIDRFirst) + } + + // Set the second address on the bridge + // with ForceAddress set to true. + err = ensureAddr(bridge, family, &gwnSecond, true) + Expect(err).NotTo(HaveOccurred()) + if family == netlink.FAMILY_V4 || subnetsOverlap { + // IPv4 or overlapping IPv6 subnets: + // IP address should be reconfigured. + checkBridgeIPs(tc.gwCIDRSecond, "") + } else { + // Non-overlapping IPv6 subnets: + // There should be 2 addresses (in addition to link local) + checkBridgeIPs(tc.gwCIDRSecond, tc.gwCIDRFirst) + } + + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Clean up bridge addresses for next test case + delBridgeAddrs(originalNS) + }) + } + + It(fmt.Sprintf("[%s] ensure promiscuous mode on bridge", ver), func() { + const IFNAME = "bridge0" + const EXPECTED_IP = "10.0.0.0/8" + const CHANGED_EXPECTED_IP = "10.1.2.3/16" + + conf := &NetConf{ + NetConf: types.NetConf{ + Name: "testConfig", + Type: "bridge", + }, + BrName: IFNAME, + IsGW: true, + IPMasq: false, + HairpinMode: false, + PromiscMode: true, + MTU: 5000, + } + + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + conf.NetConf.CNIVersion = ver + _, _, err := setupBridge(conf) + Expect(err).NotTo(HaveOccurred()) // Check if ForceAddress has default value Expect(conf.ForceAddress).To(Equal(false)) - // Set first address on bridge - err = ensureAddr(bridge, family, &gwnFirst, conf.ForceAddress) + //Check if promiscuous mode is set correctly + link, err := netlink.LinkByName("bridge0") Expect(err).NotTo(HaveOccurred()) - checkBridgeIPs(tc.gwCIDRFirst, "") - - // Attempt to set the second address on the bridge - // with ForceAddress set to false. - err = ensureAddr(bridge, family, &gwnSecond, false) - if family == netlink.FAMILY_V4 || subnetsOverlap { - // IPv4 or overlapping IPv6 subnets: - // Expect an error, and address should remain the same - Expect(err).To(HaveOccurred()) - checkBridgeIPs(tc.gwCIDRFirst, "") - } else { - // Non-overlapping IPv6 subnets: - // There should be 2 addresses (in addition to link local) - Expect(err).NotTo(HaveOccurred()) - expNumAddrs++ - checkBridgeIPs(tc.gwCIDRSecond, tc.gwCIDRFirst) - } - - // Set the second address on the bridge - // with ForceAddress set to true. - err = ensureAddr(bridge, family, &gwnSecond, true) - Expect(err).NotTo(HaveOccurred()) - if family == netlink.FAMILY_V4 || subnetsOverlap { - // IPv4 or overlapping IPv6 subnets: - // IP address should be reconfigured. - checkBridgeIPs(tc.gwCIDRSecond, "") - } else { - // Non-overlapping IPv6 subnets: - // There should be 2 addresses (in addition to link local) - checkBridgeIPs(tc.gwCIDRSecond, tc.gwCIDRFirst) - } + Expect(link.Attrs().Promisc).To(Equal(1)) return nil }) Expect(err).NotTo(HaveOccurred()) - - // Clean up bridge addresses for next test case - delBridgeAddrs(originalNS) - } - }) - It("ensure promiscuous mode on bridge", func() { - const IFNAME = "bridge0" - const EXPECTED_IP = "10.0.0.0/8" - const CHANGED_EXPECTED_IP = "10.1.2.3/16" - - conf := &NetConf{ - NetConf: types.NetConf{ - CNIVersion: "0.3.1", - Name: "testConfig", - Type: "bridge", - }, - BrName: IFNAME, - IsGW: true, - IPMasq: false, - HairpinMode: false, - PromiscMode: true, - MTU: 5000, - } - - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - _, _, err := setupBridge(conf) - Expect(err).NotTo(HaveOccurred()) - // Check if ForceAddress has default value - Expect(conf.ForceAddress).To(Equal(false)) - - //Check if promiscuous mode is set correctly - link, err := netlink.LinkByName("bridge0") - Expect(err).NotTo(HaveOccurred()) - - Expect(link.Attrs().Promisc).To(Equal(1)) - - return nil }) - Expect(err).NotTo(HaveOccurred()) - }) - It("creates a bridge with a stable MAC addresses", func() { - testCases := []testCase{ + + for i, tc := range []testCase{ { subnet: "10.1.2.0/24", }, { subnet: "2001:db8:42::/64", }, - } + } { + tc := tc + i := i + It(fmt.Sprintf("[%s] (%d) creates a bridge with a stable MAC addresses", ver, i), func() { + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - for _, tc := range testCases { - tc.cniVersion = "0.3.1" - _, _, err := setupBridge(tc.netConf()) - Expect(err).NotTo(HaveOccurred()) - link, err := netlink.LinkByName(BRNAME) - Expect(err).NotTo(HaveOccurred()) - origMac := link.Attrs().HardwareAddr + tc.cniVersion = ver + _, _, err := setupBridge(tc.netConf()) + Expect(err).NotTo(HaveOccurred()) + link, err := netlink.LinkByName(BRNAME) + Expect(err).NotTo(HaveOccurred()) + origMac := link.Attrs().HardwareAddr - cmdAddDelTest(originalNS, tc, dataDir) + cmdAddDelTest(originalNS, targetNS, tc, dataDir) - link, err = netlink.LinkByName(BRNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(origMac)) - } - }) - - It("checks ip release in case of error", func() { - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - tc := testCase{ - cniVersion: "0.3.1", - subnet: "10.1.2.0/24", - } - - _, _, err := setupBridge(tc.netConf()) - Expect(err).NotTo(HaveOccurred()) - - args := tc.createCmdArgs(originalNS, dataDir) - - // get number of allocated IPs before asking for a new one - before, err := countIPAMIPs(dataDir) - Expect(err).NotTo(HaveOccurred()) - - debugPostIPAMError = fmt.Errorf("debugPostIPAMError") - _, _, err = testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + link, err = netlink.LinkByName(BRNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(origMac)) + return nil + }) + Expect(err).NotTo(HaveOccurred()) }) - Expect(err).To(MatchError("debugPostIPAMError")) + } - // get number of allocated IPs after failure - after, err := countIPAMIPs(dataDir) + It(fmt.Sprintf("[%s] checks ip release in case of error", ver), func() { + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + tc := testCase{ + cniVersion: ver, + subnet: "10.1.2.0/24", + } + + _, _, err := setupBridge(tc.netConf()) + Expect(err).NotTo(HaveOccurred()) + + args := tc.createCmdArgs(originalNS, dataDir) + + // get number of allocated IPs before asking for a new one + before, err := countIPAMIPs(dataDir) + Expect(err).NotTo(HaveOccurred()) + + debugPostIPAMError = fmt.Errorf("debugPostIPAMError") + _, _, err = testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).To(MatchError("debugPostIPAMError")) + + // get number of allocated IPs after failure + after, err := countIPAMIPs(dataDir) + Expect(err).NotTo(HaveOccurred()) + + Expect(before).To(Equal(after)) + return nil + }) Expect(err).NotTo(HaveOccurred()) - - Expect(before).To(Equal(after)) - return nil }) - Expect(err).NotTo(HaveOccurred()) - }) - It("configures and deconfigures a bridge and veth with default route with ADD/DEL/CHECK for 0.4.0 config", func() { - testCases := []testCase{ - { - // IPv4 only - ranges: []rangeInfo{{ - subnet: "10.1.2.0/24", - }}, - expGWCIDRs: []string{"10.1.2.1/24"}, - }, - { - // IPv6 only - ranges: []rangeInfo{{ - subnet: "2001:db8::0/64", - }}, - expGWCIDRs: []string{"2001:db8::1/64"}, - }, - { - // Dual-Stack - ranges: []rangeInfo{ - {subnet: "192.168.0.0/24"}, - {subnet: "fd00::0/64"}, - }, - expGWCIDRs: []string{ - "192.168.0.1/24", - "fd00::1/64", - }, - }, - } - for _, tc := range testCases { - tc.cniVersion = "0.4.0" - cmdAddDelCheckTest(originalNS, tc, dataDir) - } - }) + if testutils.SpecVersionHasChaining(ver) { + It(fmt.Sprintf("[%s] configures a bridge and ipMasq rules", ver), func() { + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + tc := testCase{ + ranges: []rangeInfo{{ + subnet: "10.1.2.0/24", + }}, + ipMasq: true, + cniVersion: ver, + } - It("configures a bridge and ipMasq rules for 0.4.0 config", func() { - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - tc := testCase{ - ranges: []rangeInfo{{ - subnet: "10.1.2.0/24", - }}, - ipMasq: true, - cniVersion: "0.4.0", + args := tc.createCmdArgs(originalNS, dataDir) + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + Expect(result.IPs).Should(HaveLen(1)) + + ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4) + Expect(err).NotTo(HaveOccurred()) + + rules, err := ipt.List("nat", "POSTROUTING") + Expect(err).NotTo(HaveOccurred()) + Expect(rules).Should(ContainElement(ContainSubstring(result.IPs[0].Address.IP.String()))) + + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + }) + + for i, tc := range []testCase{ + { + // IPv4 only + ranges: []rangeInfo{{ + subnet: "10.1.2.0/24", + }}, + expGWCIDRs: []string{"10.1.2.1/24"}, + }, + { + // IPv6 only + ranges: []rangeInfo{{ + subnet: "2001:db8::0/64", + }}, + expGWCIDRs: []string{"2001:db8::1/64"}, + }, + { + // Dual-Stack + ranges: []rangeInfo{ + {subnet: "192.168.0.0/24"}, + {subnet: "fd00::0/64"}, + }, + expGWCIDRs: []string{ + "192.168.0.1/24", + "fd00::1/64", + }, + }, + } { + tc := tc + i := i + It(fmt.Sprintf("[%s] (%d) configures and deconfigures a bridge and veth with default route with ADD/DEL/CHECK", ver, i), func() { + tc.cniVersion = ver + cmdAddDelCheckTest(originalNS, targetNS, tc, dataDir) + }) } - - args := tc.createCmdArgs(originalNS, dataDir) - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - Expect(result.IPs).Should(HaveLen(1)) - - ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4) - Expect(err).NotTo(HaveOccurred()) - - rules, err := ipt.List("nat", "POSTROUTING") - Expect(err).NotTo(HaveOccurred()) - Expect(rules).Should(ContainElement(ContainSubstring(result.IPs[0].Address.IP.String()))) - - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) + } + } It("check vlan id when loading net conf", func() { - tests := []struct { - tc testCase + type vlanTC struct { + testCase err error - }{ - { - tc: testCase{ - cniVersion: "0.4.0", - }, - err: nil, - }, - { - tc: testCase{ - cniVersion: "0.4.0", - vlan: 0, - }, - err: nil, - }, - { - tc: testCase{ - cniVersion: "0.4.0", - vlan: -100, - }, - err: fmt.Errorf("invalid VLAN ID -100 (must be between 0 and 4094)"), - }, - { - tc: testCase{ - cniVersion: "0.4.0", - vlan: 5000, - }, - err: fmt.Errorf("invalid VLAN ID 5000 (must be between 0 and 4094)"), - }, } + createCaseFn := func(ver string, vlan int, err error) vlanTC { + return vlanTC{ + testCase: testCase{ + cniVersion: ver, + vlan: vlan, + }, + err: err, + } + } + + tests := []vlanTC{} + tests = append(tests, createCaseFn("1.0.0", 0, nil)) + tests = append(tests, createCaseFn("0.4.0", 0, nil)) + tests = append(tests, createCaseFn("1.0.0", -100, fmt.Errorf("invalid VLAN ID -100 (must be between 0 and 4094)"))) + tests = append(tests, createCaseFn("0.4.0", -100, fmt.Errorf("invalid VLAN ID -100 (must be between 0 and 4094)"))) + tests = append(tests, createCaseFn("1.0.0", 5000, fmt.Errorf("invalid VLAN ID 5000 (must be between 0 and 4094)"))) + tests = append(tests, createCaseFn("0.4.0", 5000, fmt.Errorf("invalid VLAN ID 5000 (must be between 0 and 4094)"))) + for _, test := range tests { - _, _, err := loadNetConf([]byte(test.tc.netConfJSON(""))) + _, _, err := loadNetConf([]byte(test.netConfJSON(""))) if test.err == nil { Expect(err).To(BeNil()) } else { diff --git a/plugins/main/host-device/host-device.go b/plugins/main/host-device/host-device.go index 81acdffe..f2c74f99 100644 --- a/plugins/main/host-device/host-device.go +++ b/plugins/main/host-device/host-device.go @@ -30,7 +30,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/plugins/pkg/ip" diff --git a/plugins/main/host-device/host-device_test.go b/plugins/main/host-device/host-device_test.go index 3671cef2..0a7b7c02 100644 --- a/plugins/main/host-device/host-device_test.go +++ b/plugins/main/host-device/host-device_test.go @@ -23,8 +23,9 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - types020 "github.com/containernetworking/cni/pkg/types/020" - "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/cni/pkg/types/040" + "github.com/containernetworking/cni/pkg/types/100" + "github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" @@ -44,7 +45,7 @@ type Net struct { IPAM *IPAMConfig `json:"ipam,omitempty"` DNS types.DNS `json:"dns"` RawPrevResult map[string]interface{} `json:"prevResult,omitempty"` - PrevResult current.Result `json:"-"` + PrevResult types100.Result `json:"-"` } type IPAMConfig struct { @@ -110,10 +111,8 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) { } if n.IPAM.Addresses[i].Address.IP.To4() != nil { - n.IPAM.Addresses[i].Version = "4" numV4++ } else { - n.IPAM.Addresses[i].Version = "6" numV6++ } } @@ -136,10 +135,8 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) { addr := Address{Address: net.IPNet{IP: ip, Mask: subnet.Mask}} if addr.Address.IP.To4() != nil { - addr.Version = "4" numV4++ } else { - addr.Version = "6" numV6++ } n.IPAM.Addresses = append(n.IPAM.Addresses, addr) @@ -164,10 +161,8 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) { // CNI spec 0.2.0 and below supported only one v4 and v6 address if numV4 > 1 || numV6 > 1 { - for _, v := range types020.SupportedVersions { - if n.CNIVersion == v { - return nil, "", fmt.Errorf("CNI version %v does not support more than 1 address per family", n.CNIVersion) - } + if ok, _ := version.GreaterThanOrEqualTo(n.CNIVersion, "0.3.0"); !ok { + return nil, "", fmt.Errorf("CNI version %v does not support more than 1 address per family", n.CNIVersion) } } @@ -220,14 +215,79 @@ func buildOneConfig(name, cniVersion string, orig *Net, prevResult types.Result) } +type tester interface { + expectInterfaces(result types.Result, name, mac, sandbox string) +} + +type testerBase struct{} + +type testerV10x testerBase +type testerV04x testerBase +type testerV03x testerBase + +func newTesterByVersion(version string) tester { + switch { + case strings.HasPrefix(version, "1.0."): + return &testerV10x{} + case strings.HasPrefix(version, "0.4."): + return &testerV04x{} + case strings.HasPrefix(version, "0.3."): + return &testerV03x{} + default: + Fail(fmt.Sprintf("unsupported config version %s", version)) + } + return nil +} + +func (t *testerV10x) expectInterfaces(result types.Result, name, mac, sandbox string) { + // check that the result was sane + res, err := types100.NewResultFromResult(result) + Expect(err).NotTo(HaveOccurred()) + Expect(res.Interfaces).To(Equal([]*types100.Interface{ + { + Name: name, + Mac: mac, + Sandbox: sandbox, + }, + })) +} + +func (t *testerV04x) expectInterfaces(result types.Result, name, mac, sandbox string) { + // check that the result was sane + res, err := types040.NewResultFromResult(result) + Expect(err).NotTo(HaveOccurred()) + Expect(res.Interfaces).To(Equal([]*types040.Interface{ + { + Name: name, + Mac: mac, + Sandbox: sandbox, + }, + })) +} + +func (t *testerV03x) expectInterfaces(result types.Result, name, mac, sandbox string) { + // check that the result was sane + res, err := types040.NewResultFromResult(result) + Expect(err).NotTo(HaveOccurred()) + Expect(res.Interfaces).To(Equal([]*types040.Interface{ + { + Name: name, + Mac: mac, + Sandbox: sandbox, + }, + })) +} + var _ = Describe("base functionality", func() { - var originalNS ns.NetNS + var originalNS, targetNS ns.NetNS var ifname string BeforeEach(func() { var err error originalNS, err = testutils.NewNS() Expect(err).NotTo(HaveOccurred()) + targetNS, err = testutils.NewNS() + Expect(err).NotTo(HaveOccurred()) ifname = fmt.Sprintf("dummy-%x", rand.Int31()) }) @@ -235,731 +295,679 @@ var _ = Describe("base functionality", func() { AfterEach(func() { Expect(originalNS.Close()).To(Succeed()) Expect(testutils.UnmountNS(originalNS)).To(Succeed()) + Expect(targetNS.Close()).To(Succeed()) + Expect(testutils.UnmountNS(targetNS)).To(Succeed()) }) - It("Works with a valid config without IPAM", func() { - var origLink netlink.Link + for _, ver := range []string{"0.3.0", "0.3.1", "0.4.0", "1.0.0"} { + // Redefine ver inside for scope so real value is picked up by each dynamically defined It() + // See Gingkgo's "Patterns for dynamically generating tests" documentation. + ver := ver - // prepare ifname in original namespace - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - err := netlink.LinkAdd(&netlink.Dummy{ - LinkAttrs: netlink.LinkAttrs{ - Name: ifname, + It(fmt.Sprintf("[%s] works with a valid config without IPAM", ver), func() { + var origLink netlink.Link + + // prepare ifname in original namespace + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + err := netlink.LinkAdd(&netlink.Dummy{ + LinkAttrs: netlink.LinkAttrs{ + Name: ifname, + }, + }) + Expect(err).NotTo(HaveOccurred()) + origLink, err = netlink.LinkByName(ifname) + Expect(err).NotTo(HaveOccurred()) + err = netlink.LinkSetUp(origLink) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + + // call CmdAdd + cniName := "eth0" + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "cni-plugin-host-device-test", + "type": "host-device", + "device": %q + }`, ver, ifname) + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: cniName, + StdinData: []byte(conf), + } + var resI types.Result + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + var err error + resI, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) + return err + }) + Expect(err).NotTo(HaveOccurred()) + + // check that the result was sane + t := newTesterByVersion(ver) + t.expectInterfaces(resI, cniName, origLink.Attrs().HardwareAddr.String(), targetNS.Path()) + + // assert that dummy0 is now in the target namespace + _ = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + link, err := netlink.LinkByName(cniName) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr)) + return nil + }) + + // assert that dummy0 is now NOT in the original namespace anymore + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + _, err := netlink.LinkByName(ifname) + Expect(err).To(HaveOccurred()) + return nil + }) + + // Check that deleting the device moves it back and restores the name + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + err := testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + + _, err = netlink.LinkByName(ifname) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + }) + + It(fmt.Sprintf("[%s] ensures CmdDel is idempotent", ver), func() { + var ( + origLink netlink.Link + conflictLink netlink.Link + ) + + // prepare host device in original namespace + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + err := netlink.LinkAdd(&netlink.Dummy{ + LinkAttrs: netlink.LinkAttrs{ + Name: ifname, + }, + }) + Expect(err).NotTo(HaveOccurred()) + origLink, err = netlink.LinkByName(ifname) + Expect(err).NotTo(HaveOccurred()) + err = netlink.LinkSetUp(origLink) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + + // call CmdAdd + cniName := "eth0" + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "cni-plugin-host-device-test", + "type": "host-device", + "device": %q + }`, ver, ifname) + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: cniName, + StdinData: []byte(conf), + } + var resI types.Result + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + var err error + resI, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) + return err + }) + Expect(err).NotTo(HaveOccurred()) + + // check that the result was sane + t := newTesterByVersion(ver) + t.expectInterfaces(resI, cniName, origLink.Attrs().HardwareAddr.String(), targetNS.Path()) + + // assert that dummy0 is now in the target namespace + _ = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + link, err := netlink.LinkByName(cniName) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr)) + return nil + }) + + // assert that dummy0 is now NOT in the original namespace anymore + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + _, err := netlink.LinkByName(ifname) + Expect(err).To(HaveOccurred()) + return nil + }) + + // create another conflict host device with same name "dummy0" + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + err := netlink.LinkAdd(&netlink.Dummy{ + LinkAttrs: netlink.LinkAttrs{ + Name: ifname, + }, + }) + Expect(err).NotTo(HaveOccurred()) + conflictLink, err = netlink.LinkByName(ifname) + Expect(err).NotTo(HaveOccurred()) + err = netlink.LinkSetUp(conflictLink) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + + // try to call CmdDel and fails + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).To(HaveOccurred()) + return nil + }) + + // assert container interface "eth0" still exists in target namespace + _ = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + link, err := netlink.LinkByName(cniName) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr)) + return nil + }) + + // remove the conflict host device + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + err = netlink.LinkDel(conflictLink) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + + // try to call CmdDel and succeed + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + + // assert that dummy0 is now back in the original namespace + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + _, err := netlink.LinkByName(ifname) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + }) + + It(fmt.Sprintf("Works with a valid %s config with IPAM", ver), func() { + var origLink netlink.Link + + // prepare ifname in original namespace + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + err := netlink.LinkAdd(&netlink.Dummy{ + LinkAttrs: netlink.LinkAttrs{ + Name: ifname, + }, + }) + Expect(err).NotTo(HaveOccurred()) + origLink, err = netlink.LinkByName(ifname) + Expect(err).NotTo(HaveOccurred()) + err = netlink.LinkSetUp(origLink) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + + // call CmdAdd + targetIP := "10.10.0.1/24" + cniName := "eth0" + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "cni-plugin-host-device-test", + "type": "host-device", + "ipam": { + "type": "static", + "addresses": [ + { + "address":"`+targetIP+`", + "gateway": "10.10.0.254" + }] }, - }) - Expect(err).NotTo(HaveOccurred()) - origLink, err = netlink.LinkByName(ifname) - Expect(err).NotTo(HaveOccurred()) - err = netlink.LinkSetUp(origLink) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - - // call CmdAdd - targetNS, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - - cniName := "eth0" - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.0", - "name": "cni-plugin-host-device-test", - "type": "host-device", - "device": %q - }`, ifname) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: cniName, - StdinData: []byte(conf), - } - var resI types.Result - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - var err error - resI, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) - return err - }) - Expect(err).NotTo(HaveOccurred()) - - // check that the result was sane - res, err := current.NewResultFromResult(resI) - Expect(err).NotTo(HaveOccurred()) - Expect(res.Interfaces).To(Equal([]*current.Interface{ - { - Name: cniName, - Mac: origLink.Attrs().HardwareAddr.String(), - Sandbox: targetNS.Path(), - }, - })) - - // assert that dummy0 is now in the target namespace - _ = targetNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - link, err := netlink.LinkByName(cniName) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr)) - return nil - }) - - // assert that dummy0 is now NOT in the original namespace anymore - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - _, err := netlink.LinkByName(ifname) - Expect(err).To(HaveOccurred()) - return nil - }) - - // Check that deleting the device moves it back and restores the name - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + "device": %q + }`, ver, ifname) + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: cniName, + StdinData: []byte(conf), + } + var resI types.Result + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + var err error + resI, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) + return err }) Expect(err).NotTo(HaveOccurred()) - _, err := netlink.LinkByName(ifname) - Expect(err).NotTo(HaveOccurred()) - return nil + // check that the result was sane + t := newTesterByVersion(ver) + t.expectInterfaces(resI, cniName, origLink.Attrs().HardwareAddr.String(), targetNS.Path()) + + // assert that dummy0 is now in the target namespace + _ = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + link, err := netlink.LinkByName(cniName) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr)) + + //get the IP address of the interface in the target namespace + addrs, err := netlink.AddrList(link, netlink.FAMILY_V4) + Expect(err).NotTo(HaveOccurred()) + addr := addrs[0].IPNet.String() + //assert that IP address is what we set + Expect(addr).To(Equal(targetIP)) + + return nil + }) + + // assert that dummy0 is now NOT in the original namespace anymore + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + _, err := netlink.LinkByName(ifname) + Expect(err).To(HaveOccurred()) + return nil + }) + + // Check that deleting the device moves it back and restores the name + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + + _, err := netlink.LinkByName(ifname) + Expect(err).NotTo(HaveOccurred()) + return nil + }) }) - }) - It("Test idempotence of CmdDel", func() { - var ( - origLink netlink.Link - conflictLink netlink.Link - ) + It(fmt.Sprintf("[%s] fails an invalid config", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "cni-plugin-host-device-test", + "type": "host-device" + }`, ver) - // prepare host device in original namespace - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - err := netlink.LinkAdd(&netlink.Dummy{ - LinkAttrs: netlink.LinkAttrs{ - Name: ifname, + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: originalNS.Path(), + IfName: ifname, + StdinData: []byte(conf), + } + _, _, err := testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) + Expect(err).To(MatchError(`specify either "device", "hwaddr", "kernelpath" or "pciBusID"`)) + + }) + + It(fmt.Sprintf("[%s] works with a valid config without IPAM", ver), func() { + var origLink netlink.Link + + // prepare ifname in original namespace + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + err := netlink.LinkAdd(&netlink.Dummy{ + LinkAttrs: netlink.LinkAttrs{ + Name: ifname, + }, + }) + Expect(err).NotTo(HaveOccurred()) + origLink, err = netlink.LinkByName(ifname) + Expect(err).NotTo(HaveOccurred()) + err = netlink.LinkSetUp(origLink) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + + // call CmdAdd + cniName := "eth0" + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "cni-plugin-host-device-test", + "type": "host-device", + "device": %q + }`, ver, ifname) + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: cniName, + StdinData: []byte(conf), + } + var resI types.Result + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + var err error + resI, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) + return err + }) + Expect(err).NotTo(HaveOccurred()) + + // check that the result was sane + t := newTesterByVersion(ver) + t.expectInterfaces(resI, cniName, origLink.Attrs().HardwareAddr.String(), targetNS.Path()) + + // assert that dummy0 is now in the target namespace + _ = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + link, err := netlink.LinkByName(cniName) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr)) + return nil + }) + + // assert that dummy0 is now NOT in the original namespace anymore + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + _, err := netlink.LinkByName(ifname) + Expect(err).To(HaveOccurred()) + return nil + }) + + if testutils.SpecVersionHasCHECK(ver) { + // call CmdCheck + n := &Net{} + err = json.Unmarshal([]byte(conf), &n) + Expect(err).NotTo(HaveOccurred()) + + newConf, err := buildOneConfig("testConfig", ver, n, resI) + Expect(err).NotTo(HaveOccurred()) + + confString, err := json.Marshal(newConf) + Expect(err).NotTo(HaveOccurred()) + + args.StdinData = confString + + // CNI Check host-device in the target namespace + + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + return testutils.CmdCheckWithArgs(args, func() error { return cmdCheck(args) }) + }) + Expect(err).NotTo(HaveOccurred()) + } + + // Check that deleting the device moves it back and restores the name + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + + _, err := netlink.LinkByName(ifname) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + }) + + It(fmt.Sprintf("Works with a valid %s config with IPAM", ver), func() { + var origLink netlink.Link + + // prepare ifname in original namespace + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + err := netlink.LinkAdd(&netlink.Dummy{ + LinkAttrs: netlink.LinkAttrs{ + Name: ifname, + }, + }) + Expect(err).NotTo(HaveOccurred()) + origLink, err = netlink.LinkByName(ifname) + Expect(err).NotTo(HaveOccurred()) + err = netlink.LinkSetUp(origLink) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + + // call CmdAdd + targetIP := "10.10.0.1/24" + cniName := "eth0" + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "cni-plugin-host-device-test", + "type": "host-device", + "ipam": { + "type": "static", + "addresses": [ + { + "address":"`+targetIP+`", + "gateway": "10.10.0.254" + }] }, - }) - Expect(err).NotTo(HaveOccurred()) - origLink, err = netlink.LinkByName(ifname) - Expect(err).NotTo(HaveOccurred()) - err = netlink.LinkSetUp(origLink) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - - // call CmdAdd - targetNS, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - - cniName := "eth0" - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.0", - "name": "cni-plugin-host-device-test", - "type": "host-device", - "device": %q - }`, ifname) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: cniName, - StdinData: []byte(conf), - } - var resI types.Result - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - var err error - resI, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) - return err - }) - Expect(err).NotTo(HaveOccurred()) - - // check that the result is sane - res, err := current.NewResultFromResult(resI) - Expect(err).NotTo(HaveOccurred()) - Expect(res.Interfaces).To(Equal([]*current.Interface{ - { - Name: cniName, - Mac: origLink.Attrs().HardwareAddr.String(), - Sandbox: targetNS.Path(), - }, - })) - - // assert that dummy0 is now in the target namespace - _ = targetNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - link, err := netlink.LinkByName(cniName) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr)) - return nil - }) - - // assert that dummy0 is now NOT in the original namespace anymore - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - _, err := netlink.LinkByName(ifname) - Expect(err).To(HaveOccurred()) - return nil - }) - - // create another conflict host device with same name "dummy0" - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - err := netlink.LinkAdd(&netlink.Dummy{ - LinkAttrs: netlink.LinkAttrs{ - Name: ifname, - }, - }) - Expect(err).NotTo(HaveOccurred()) - conflictLink, err = netlink.LinkByName(ifname) - Expect(err).NotTo(HaveOccurred()) - err = netlink.LinkSetUp(conflictLink) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - - // try to call CmdDel and fails - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).To(HaveOccurred()) - return nil - }) - - // assert container interface "eth0" still exists in target namespace - _ = targetNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - link, err := netlink.LinkByName(cniName) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr)) - return nil - }) - - // remove the conflict host device - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - err = netlink.LinkDel(conflictLink) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - - // try to call CmdDel and succeed - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - - // assert that dummy0 is now back in the original namespace - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - _, err := netlink.LinkByName(ifname) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - }) - - It("Works with a valid config with IPAM", func() { - var origLink netlink.Link - - // prepare ifname in original namespace - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - err := netlink.LinkAdd(&netlink.Dummy{ - LinkAttrs: netlink.LinkAttrs{ - Name: ifname, - }, - }) - Expect(err).NotTo(HaveOccurred()) - origLink, err = netlink.LinkByName(ifname) - Expect(err).NotTo(HaveOccurred()) - err = netlink.LinkSetUp(origLink) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - - // call CmdAdd - targetNS, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - targetIP := "10.10.0.1/24" - cniName := "eth0" - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.0", - "name": "cni-plugin-host-device-test", - "type": "host-device", - "ipam": { - "type": "static", - "addresses": [ - { - "address":"`+targetIP+`", - "gateway": "10.10.0.254" - }] - }, - "device": %q - }`, ifname) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: cniName, - StdinData: []byte(conf), - } - var resI types.Result - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - var err error - resI, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) - return err - }) - Expect(err).NotTo(HaveOccurred()) - - // check that the result was sane - res, err := current.NewResultFromResult(resI) - Expect(err).NotTo(HaveOccurred()) - Expect(res.Interfaces).To(Equal([]*current.Interface{ - { - Name: cniName, - Mac: origLink.Attrs().HardwareAddr.String(), - Sandbox: targetNS.Path(), - }, - })) - - // assert that dummy0 is now in the target namespace - _ = targetNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - link, err := netlink.LinkByName(cniName) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr)) - - //get the IP address of the interface in the target namespace - addrs, err := netlink.AddrList(link, netlink.FAMILY_V4) - Expect(err).NotTo(HaveOccurred()) - addr := addrs[0].IPNet.String() - //assert that IP address is what we set - Expect(addr).To(Equal(targetIP)) - - return nil - }) - - // assert that dummy0 is now NOT in the original namespace anymore - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - _, err := netlink.LinkByName(ifname) - Expect(err).To(HaveOccurred()) - return nil - }) - - // Check that deleting the device moves it back and restores the name - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + "device": %q + }`, ver, ifname) + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: cniName, + StdinData: []byte(conf), + } + var resI types.Result + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + var err error + resI, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) + return err }) Expect(err).NotTo(HaveOccurred()) - _, err := netlink.LinkByName(ifname) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - }) + // check that the result was sane + t := newTesterByVersion(ver) + t.expectInterfaces(resI, cniName, origLink.Attrs().HardwareAddr.String(), targetNS.Path()) - It("fails an invalid config", func() { - conf := `{ - "cniVersion": "0.3.0", - "name": "cni-plugin-host-device-test", - "type": "host-device" - }` + // assert that dummy0 is now in the target namespace + _ = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + link, err := netlink.LinkByName(cniName) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr)) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: originalNS.Path(), - IfName: ifname, - StdinData: []byte(conf), - } - _, _, err := testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) - Expect(err).To(MatchError(`specify either "device", "hwaddr", "kernelpath" or "pciBusID"`)) + //get the IP address of the interface in the target namespace + addrs, err := netlink.AddrList(link, netlink.FAMILY_V4) + Expect(err).NotTo(HaveOccurred()) + addr := addrs[0].IPNet.String() + //assert that IP address is what we set + Expect(addr).To(Equal(targetIP)) - }) - - It("Works with a valid 0.4.0 config without IPAM", func() { - var origLink netlink.Link - - // prepare ifname in original namespace - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - err := netlink.LinkAdd(&netlink.Dummy{ - LinkAttrs: netlink.LinkAttrs{ - Name: ifname, - }, + return nil }) + + // assert that dummy0 is now NOT in the original namespace anymore + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + _, err := netlink.LinkByName(ifname) + Expect(err).To(HaveOccurred()) + return nil + }) + + // call CmdCheck + n := &Net{} + err = json.Unmarshal([]byte(conf), &n) Expect(err).NotTo(HaveOccurred()) - origLink, err = netlink.LinkByName(ifname) + + n.IPAM, _, err = LoadIPAMConfig([]byte(conf), "") Expect(err).NotTo(HaveOccurred()) - err = netlink.LinkSetUp(origLink) - Expect(err).NotTo(HaveOccurred()) - return nil + + if testutils.SpecVersionHasCHECK(ver) { + newConf, err := buildOneConfig("testConfig", ver, n, resI) + Expect(err).NotTo(HaveOccurred()) + + confString, err := json.Marshal(newConf) + Expect(err).NotTo(HaveOccurred()) + + args.StdinData = confString + + // CNI Check host-device in the target namespace + + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + return testutils.CmdCheckWithArgs(args, func() error { return cmdCheck(args) }) + }) + Expect(err).NotTo(HaveOccurred()) + } + + // Check that deleting the device moves it back and restores the name + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + + _, err := netlink.LinkByName(ifname) + Expect(err).NotTo(HaveOccurred()) + return nil + }) }) - // call CmdAdd - targetNS, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) + It(fmt.Sprintf("Test idempotence of CmdDel with %s config", ver), func() { + var ( + origLink netlink.Link + conflictLink netlink.Link + ) - cniName := "eth0" - conf := fmt.Sprintf(`{ - "cniVersion": "0.4.0", - "name": "cni-plugin-host-device-test", - "type": "host-device", - "device": %q - }`, ifname) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: cniName, - StdinData: []byte(conf), - } - var resI types.Result - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - var err error - resI, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) - return err - }) - Expect(err).NotTo(HaveOccurred()) + // prepare host device in original namespace + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + err := netlink.LinkAdd(&netlink.Dummy{ + LinkAttrs: netlink.LinkAttrs{ + Name: ifname, + }, + }) + Expect(err).NotTo(HaveOccurred()) + origLink, err = netlink.LinkByName(ifname) + Expect(err).NotTo(HaveOccurred()) + err = netlink.LinkSetUp(origLink) + Expect(err).NotTo(HaveOccurred()) + return nil + }) - // check that the result was sane - res, err := current.NewResultFromResult(resI) - Expect(err).NotTo(HaveOccurred()) - Expect(res.Interfaces).To(Equal([]*current.Interface{ - { - Name: cniName, - Mac: origLink.Attrs().HardwareAddr.String(), - Sandbox: targetNS.Path(), - }, - })) - - // assert that dummy0 is now in the target namespace - _ = targetNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - link, err := netlink.LinkByName(cniName) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr)) - return nil - }) - - // assert that dummy0 is now NOT in the original namespace anymore - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - _, err := netlink.LinkByName(ifname) - Expect(err).To(HaveOccurred()) - return nil - }) - - // call CmdCheck - n := &Net{} - err = json.Unmarshal([]byte(conf), &n) - Expect(err).NotTo(HaveOccurred()) - - cniVersion := "0.4.0" - newConf, err := buildOneConfig("testConfig", cniVersion, n, res) - Expect(err).NotTo(HaveOccurred()) - - confString, err := json.Marshal(newConf) - Expect(err).NotTo(HaveOccurred()) - - args.StdinData = confString - - // CNI Check host-device in the target namespace - - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - var err error - err = testutils.CmdCheckWithArgs(args, func() error { return cmdCheck(args) }) - return err - }) - Expect(err).NotTo(HaveOccurred()) - - // Check that deleting the device moves it back and restores the name - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + // call CmdAdd + cniName := "eth0" + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "cni-plugin-host-device-test", + "type": "host-device", + "device": %q + }`, ver, ifname) + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: cniName, + StdinData: []byte(conf), + } + var resI types.Result + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + var err error + resI, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) + return err }) Expect(err).NotTo(HaveOccurred()) - _, err := netlink.LinkByName(ifname) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - }) + // check that the result was sane + t := newTesterByVersion(ver) + t.expectInterfaces(resI, cniName, origLink.Attrs().HardwareAddr.String(), targetNS.Path()) - It("Works with a valid 0.4.0 config with IPAM", func() { - var origLink netlink.Link - - // prepare ifname in original namespace - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - err := netlink.LinkAdd(&netlink.Dummy{ - LinkAttrs: netlink.LinkAttrs{ - Name: ifname, - }, + // assert that dummy0 is now in the target namespace + _ = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + link, err := netlink.LinkByName(cniName) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr)) + return nil }) - Expect(err).NotTo(HaveOccurred()) - origLink, err = netlink.LinkByName(ifname) - Expect(err).NotTo(HaveOccurred()) - err = netlink.LinkSetUp(origLink) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - // call CmdAdd - targetNS, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - targetIP := "10.10.0.1/24" - cniName := "eth0" - conf := fmt.Sprintf(`{ - "cniVersion": "0.4.0", - "name": "cni-plugin-host-device-test", - "type": "host-device", - "ipam": { - "type": "static", - "addresses": [ - { - "address":"`+targetIP+`", - "gateway": "10.10.0.254" - }] - }, - "device": %q - }`, ifname) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: cniName, - StdinData: []byte(conf), - } - var resI types.Result - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - var err error - resI, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) - return err - }) - Expect(err).NotTo(HaveOccurred()) + // assert that dummy0 is now NOT in the original namespace anymore + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + _, err := netlink.LinkByName(ifname) + Expect(err).To(HaveOccurred()) + return nil + }) - // check that the result was sane - res, err := current.NewResultFromResult(resI) - Expect(err).NotTo(HaveOccurred()) - Expect(res.Interfaces).To(Equal([]*current.Interface{ - { - Name: cniName, - Mac: origLink.Attrs().HardwareAddr.String(), - Sandbox: targetNS.Path(), - }, - })) + // create another conflict host device with same name "dummy0" + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + err := netlink.LinkAdd(&netlink.Dummy{ + LinkAttrs: netlink.LinkAttrs{ + Name: ifname, + }, + }) + Expect(err).NotTo(HaveOccurred()) + conflictLink, err = netlink.LinkByName(ifname) + Expect(err).NotTo(HaveOccurred()) + err = netlink.LinkSetUp(conflictLink) + Expect(err).NotTo(HaveOccurred()) + return nil + }) - // assert that dummy0 is now in the target namespace - _ = targetNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - link, err := netlink.LinkByName(cniName) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr)) + // try to call CmdDel and fails + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).To(HaveOccurred()) + return nil + }) - //get the IP address of the interface in the target namespace - addrs, err := netlink.AddrList(link, netlink.FAMILY_V4) - Expect(err).NotTo(HaveOccurred()) - addr := addrs[0].IPNet.String() - //assert that IP address is what we set - Expect(addr).To(Equal(targetIP)) - - return nil - }) - - // assert that dummy0 is now NOT in the original namespace anymore - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - _, err := netlink.LinkByName(ifname) - Expect(err).To(HaveOccurred()) - return nil - }) - - // call CmdCheck - n := &Net{} - err = json.Unmarshal([]byte(conf), &n) - Expect(err).NotTo(HaveOccurred()) - - n.IPAM, _, err = LoadIPAMConfig([]byte(conf), "") - Expect(err).NotTo(HaveOccurred()) - - cniVersion := "0.4.0" - newConf, err := buildOneConfig("testConfig", cniVersion, n, res) - Expect(err).NotTo(HaveOccurred()) - - confString, err := json.Marshal(newConf) - Expect(err).NotTo(HaveOccurred()) - - args.StdinData = confString - - // CNI Check host-device in the target namespace - - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - var err error - err = testutils.CmdCheckWithArgs(args, func() error { return cmdCheck(args) }) - return err - }) - Expect(err).NotTo(HaveOccurred()) - - // Check that deleting the device moves it back and restores the name - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + // assert container interface "eth0" still exists in target namespace + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + link, err := netlink.LinkByName(cniName) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr)) + return nil }) Expect(err).NotTo(HaveOccurred()) - _, err := netlink.LinkByName(ifname) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - }) - - It("Test idempotence of CmdDel with 0.4.0 config", func() { - var ( - origLink netlink.Link - conflictLink netlink.Link - ) - - // prepare host device in original namespace - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - err := netlink.LinkAdd(&netlink.Dummy{ - LinkAttrs: netlink.LinkAttrs{ - Name: ifname, - }, + // remove the conflict host device + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + err = netlink.LinkDel(conflictLink) + Expect(err).NotTo(HaveOccurred()) + return nil }) - Expect(err).NotTo(HaveOccurred()) - origLink, err = netlink.LinkByName(ifname) - Expect(err).NotTo(HaveOccurred()) - err = netlink.LinkSetUp(origLink) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - // call CmdAdd - targetNS, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - - cniName := "eth0" - conf := fmt.Sprintf(`{ - "cniVersion": "0.4.0", - "name": "cni-plugin-host-device-test", - "type": "host-device", - "device": %q - }`, ifname) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: cniName, - StdinData: []byte(conf), - } - var resI types.Result - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - var err error - resI, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) - return err - }) - Expect(err).NotTo(HaveOccurred()) - - // check that the result is sane - res, err := current.NewResultFromResult(resI) - Expect(err).NotTo(HaveOccurred()) - Expect(res.Interfaces).To(Equal([]*current.Interface{ - { - Name: cniName, - Mac: origLink.Attrs().HardwareAddr.String(), - Sandbox: targetNS.Path(), - }, - })) - - // assert that dummy0 is now in the target namespace - _ = targetNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - link, err := netlink.LinkByName(cniName) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr)) - return nil - }) - - // assert that dummy0 is now NOT in the original namespace anymore - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - _, err := netlink.LinkByName(ifname) - Expect(err).To(HaveOccurred()) - return nil - }) - - // create another conflict host device with same name "dummy0" - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - err := netlink.LinkAdd(&netlink.Dummy{ - LinkAttrs: netlink.LinkAttrs{ - Name: ifname, - }, + // try to call CmdDel and succeed + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil }) - Expect(err).NotTo(HaveOccurred()) - conflictLink, err = netlink.LinkByName(ifname) - Expect(err).NotTo(HaveOccurred()) - err = netlink.LinkSetUp(conflictLink) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - // try to call CmdDel and fails - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + // assert that dummy0 is now back in the original namespace + _ = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + _, err := netlink.LinkByName(ifname) + Expect(err).NotTo(HaveOccurred()) + return nil }) - Expect(err).To(HaveOccurred()) - return nil }) - - // assert container interface "eth0" still exists in target namespace - err = targetNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - link, err := netlink.LinkByName(cniName) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(origLink.Attrs().HardwareAddr)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // remove the conflict host device - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - err = netlink.LinkDel(conflictLink) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - - // try to call CmdDel and succeed - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - - // assert that dummy0 is now back in the original namespace - _ = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - _, err := netlink.LinkByName(ifname) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - }) + } }) diff --git a/plugins/main/ipvlan/ipvlan.go b/plugins/main/ipvlan/ipvlan.go index 19a69903..417a850d 100644 --- a/plugins/main/ipvlan/ipvlan.go +++ b/plugins/main/ipvlan/ipvlan.go @@ -24,7 +24,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/plugins/pkg/ip" diff --git a/plugins/main/ipvlan/ipvlan_test.go b/plugins/main/ipvlan/ipvlan_test.go index 114dab42..0cda1876 100644 --- a/plugins/main/ipvlan/ipvlan_test.go +++ b/plugins/main/ipvlan/ipvlan_test.go @@ -17,12 +17,17 @@ package main import ( "encoding/json" "fmt" + "io/ioutil" "net" + "os" + "strings" "syscall" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/cni/pkg/types/020" + "github.com/containernetworking/cni/pkg/types/040" + "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" @@ -44,37 +49,33 @@ type Net struct { IPAM *allocator.IPAMConfig `json:"ipam"` DNS types.DNS `json:"dns"` RawPrevResult map[string]interface{} `json:"prevResult,omitempty"` - PrevResult current.Result `json:"-"` + PrevResult types100.Result `json:"-"` } -func buildOneConfig(netName string, cniVersion string, master string, orig *Net, prevResult types.Result) (*Net, error) { - var err error - - inject := map[string]interface{}{ - "name": netName, - "cniVersion": cniVersion, - } - // Add previous plugin result - if prevResult != nil { - inject["prevResult"] = prevResult - } - if orig.IPAM == nil { - inject["master"] = master - } - - // Ensure every config uses the same name and version - config := make(map[string]interface{}) - +func buildOneConfig(cniVersion string, master string, orig *Net, prevResult types.Result) (*Net, error) { confBytes, err := json.Marshal(orig) if err != nil { return nil, err } + config := make(map[string]interface{}) err = json.Unmarshal(confBytes, &config) if err != nil { return nil, fmt.Errorf("unmarshal existing network bytes: %s", err) } + inject := map[string]interface{}{ + "name": orig.Name, + "cniVersion": orig.CNIVersion, + } + // Add previous plugin result + if prevResult != nil && testutils.SpecVersionHasChaining(cniVersion) { + inject["prevResult"] = prevResult + } + if master != "" { + inject["master"] = master + } + for key, value := range inject { config[key] = value } @@ -93,121 +94,49 @@ func buildOneConfig(netName string, cniVersion string, master string, orig *Net, } -func ipvlanAddDelTest(conf, IFNAME string, originalNS ns.NetNS) { - targetNs, err := testutils.NewNS() +func ipvlanAddCheckDelTest(conf, masterName string, originalNS, targetNS ns.NetNS) { + // Unmarshal to pull out CNI spec version + rawConfig := make(map[string]interface{}) + err := json.Unmarshal([]byte(conf), &rawConfig) Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() + cniVersion := rawConfig["cniVersion"].(string) args := &skel.CmdArgs{ ContainerID: "dummy", - Netns: targetNs.Path(), - IfName: IFNAME, + Netns: targetNS.Path(), + IfName: "ipvl0", StdinData: []byte(conf), } - var result *current.Result + var result types.Result + var macAddress string err = originalNS.Do(func(ns.NetNS) error { defer GinkgoRecover() - r, _, err := testutils.CmdAddWithArgs(args, func() error { + result, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) Expect(err).NotTo(HaveOccurred()) - result, err = current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(1)) + t := newTesterByVersion(cniVersion) + macAddress = t.verifyResult(result, args.IfName) return nil }) Expect(err).NotTo(HaveOccurred()) // Make sure ipvlan link exists in the target namespace - err = targetNs.Do(func(ns.NetNS) error { + err = targetNS.Do(func(ns.NetNS) error { defer GinkgoRecover() - link, err := netlink.LinkByName(IFNAME) + link, err := netlink.LinkByName(args.IfName) Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal(IFNAME)) + Expect(link.Attrs().Name).To(Equal(args.IfName)) - hwaddr, err := net.ParseMAC(result.Interfaces[0].Mac) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) - - addrs, err := netlink.AddrList(link, syscall.AF_INET) - Expect(err).NotTo(HaveOccurred()) - Expect(len(addrs)).To(Equal(1)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // Make sure ipvlan link has been deleted - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(IFNAME) - Expect(err).To(HaveOccurred()) - Expect(link).To(BeNil()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) -} - -func ipvlanAddCheckDelTest(conf string, netName string, IFNAME string, originalNS ns.NetNS) { - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNs.Path(), - IfName: IFNAME, - StdinData: []byte(conf), - } - - var result *current.Result - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - - result, err = current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(1)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // Make sure ipvlan link exists in the target namespace - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal(IFNAME)) - - hwaddr, err := net.ParseMAC(result.Interfaces[0].Mac) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) + if macAddress != "" { + hwaddr, err := net.ParseMAC(macAddress) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) + } addrs, err := netlink.AddrList(link, syscall.AF_INET) Expect(err).NotTo(HaveOccurred()) @@ -225,26 +154,26 @@ func ipvlanAddCheckDelTest(conf string, netName string, IFNAME string, originalN Expect(err).NotTo(HaveOccurred()) } - cniVersion := "0.4.0" - newConf, err := buildOneConfig(netName, cniVersion, MASTER_NAME, n, result) + // build chained/cached config for DEL + newConf, err := buildOneConfig(cniVersion, masterName, n, result) + Expect(err).NotTo(HaveOccurred()) + confBytes, err := json.Marshal(newConf) Expect(err).NotTo(HaveOccurred()) - confString, err := json.Marshal(newConf) - Expect(err).NotTo(HaveOccurred()) + args.StdinData = confBytes + GinkgoT().Logf(string(confBytes)) - args.StdinData = confString + if testutils.SpecVersionHasCHECK(cniVersion) { + // CNI Check on ipvlan in the target namespace + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - // CNI Check on ipvlan in the target namespace - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - err := testutils.CmdCheckWithArgs(args, func() error { - return cmdCheck(args) + return testutils.CmdCheckWithArgs(args, func() error { + return cmdCheck(args) + }) }) Expect(err).NotTo(HaveOccurred()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) + } err = originalNS.Do(func(ns.NetNS) error { defer GinkgoRecover() @@ -258,10 +187,10 @@ func ipvlanAddCheckDelTest(conf string, netName string, IFNAME string, originalN Expect(err).NotTo(HaveOccurred()) // Make sure ipvlan link has been deleted - err = targetNs.Do(func(ns.NetNS) error { + err = targetNS.Do(func(ns.NetNS) error { defer GinkgoRecover() - link, err := netlink.LinkByName(IFNAME) + link, err := netlink.LinkByName(args.IfName) Expect(err).To(HaveOccurred()) Expect(link).To(BeNil()) return nil @@ -269,8 +198,70 @@ func ipvlanAddCheckDelTest(conf string, netName string, IFNAME string, originalN Expect(err).NotTo(HaveOccurred()) } +type tester interface { + // verifyResult minimally verifies the Result and returns the interface's MAC address + verifyResult(result types.Result, name string) string +} + +type testerBase struct{} + +type testerV10x testerBase +type testerV04x testerBase +type testerV02x testerBase + +func newTesterByVersion(version string) tester { + switch { + case strings.HasPrefix(version, "1.0."): + return &testerV10x{} + case strings.HasPrefix(version, "0.4.") || strings.HasPrefix(version, "0.3."): + return &testerV04x{} + case strings.HasPrefix(version, "0.1.") || strings.HasPrefix(version, "0.2."): + return &testerV02x{} + } + Fail(fmt.Sprintf("unsupported config version %s", version)) + return nil +} + +// verifyResult minimally verifies the Result and returns the interface's MAC address +func (t *testerV10x) verifyResult(result types.Result, name string) string { + r, err := types100.GetResult(result) + Expect(err).NotTo(HaveOccurred()) + + Expect(len(r.Interfaces)).To(Equal(1)) + Expect(r.Interfaces[0].Name).To(Equal(name)) + Expect(len(r.IPs)).To(Equal(1)) + + return r.Interfaces[0].Mac +} + +// verifyResult minimally verifies the Result and returns the interface's MAC address +func (t *testerV04x) verifyResult(result types.Result, name string) string { + r, err := types040.GetResult(result) + Expect(err).NotTo(HaveOccurred()) + + Expect(len(r.Interfaces)).To(Equal(1)) + Expect(r.Interfaces[0].Name).To(Equal(name)) + Expect(len(r.IPs)).To(Equal(1)) + + return r.Interfaces[0].Mac +} + +// verifyResult minimally verifies the Result and returns the interface's MAC address +func (t *testerV02x) verifyResult(result types.Result, name string) string { + r, err := types020.GetResult(result) + Expect(err).NotTo(HaveOccurred()) + + Expect(r.IP4.IP).NotTo(BeNil()) + Expect(r.IP4.IP.IP).NotTo(BeNil()) + Expect(r.IP6).To(BeNil()) + + // 0.2 and earlier don't return MAC address + return "" +} + var _ = Describe("ipvlan Operations", func() { - var originalNS ns.NetNS + var originalNS, targetNS ns.NetNS + var dataDir string BeforeEach(func() { // Create a new NetNS so we don't modify the host @@ -278,6 +269,12 @@ var _ = Describe("ipvlan Operations", func() { originalNS, err = testutils.NewNS() Expect(err).NotTo(HaveOccurred()) + targetNS, err = testutils.NewNS() + Expect(err).NotTo(HaveOccurred()) + + dataDir, err = ioutil.TempDir("", "ipvlan_test") + Expect(err).NotTo(HaveOccurred()) + err = originalNS.Do(func(ns.NetNS) error { defer GinkgoRecover() @@ -296,219 +293,170 @@ var _ = Describe("ipvlan Operations", func() { }) AfterEach(func() { + Expect(os.RemoveAll(dataDir)).To(Succeed()) + Expect(originalNS.Close()).To(Succeed()) Expect(testutils.UnmountNS(originalNS)).To(Succeed()) + + Expect(targetNS.Close()).To(Succeed()) + Expect(testutils.UnmountNS(targetNS)).To(Succeed()) }) - It("creates an ipvlan link in a non-default namespace", func() { - conf := &NetConf{ - NetConf: types.NetConf{ - CNIVersion: "0.3.1", - Name: "testConfig", - Type: "ipvlan", - }, - Master: MASTER_NAME, - Mode: "l2", - MTU: 1500, - } + for _, ver := range testutils.AllSpecVersions { + // Redefine ver inside for scope so real value is picked up by each dynamically defined It() + // See Gingkgo's "Patterns for dynamically generating tests" documentation. + ver := ver - // Create ipvlan in other namespace - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() + It(fmt.Sprintf("[%s] creates an ipvlan link in a non-default namespace", ver), func() { + conf := &NetConf{ + NetConf: types.NetConf{ + CNIVersion: ver, + Name: "testConfig", + Type: "ipvlan", + }, + Master: MASTER_NAME, + Mode: "l2", + MTU: 1500, + } - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + _, err := createIpvlan(conf, "foobar0", targetNS) + Expect(err).NotTo(HaveOccurred()) + return nil + }) - _, err := createIpvlan(conf, "foobar0", targetNs) Expect(err).NotTo(HaveOccurred()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) + // Make sure ipvlan link exists in the target namespace + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - // Make sure ipvlan link exists in the target namespace - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName("foobar0") - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal("foobar0")) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("configures and deconfigures an iplvan link with ADD/DEL", func() { - const IFNAME = "ipvl0" - - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ipvlan", - "master": "%s", - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24" - } -}`, MASTER_NAME) - - ipvlanAddDelTest(conf, IFNAME, originalNS) - }) - - It("configures and deconfigures an iplvan link with ADD/DEL when chained", func() { - const IFNAME = "ipvl0" - - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ipvlan", - "prevResult": { - "interfaces": [ - { - "name": "%s" - } - ], - "ips": [ - { - "version": "4", - "address": "10.1.2.2/24", - "gateway": "10.1.2.1", - "interface": 0 - } - ], - "routes": [] - } -}`, MASTER_NAME) - - ipvlanAddDelTest(conf, IFNAME, originalNS) - }) - - It("deconfigures an unconfigured ipvlan link with DEL", func() { - const IFNAME = "ipvl0" - - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.0", - "name": "mynet", - "type": "ipvlan", - "master": "%s", - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24" - } -}`, MASTER_NAME) - - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNs.Path(), - IfName: IFNAME, - StdinData: []byte(conf), - } - - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + link, err := netlink.LinkByName("foobar0") + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal("foobar0")) + return nil }) Expect(err).NotTo(HaveOccurred()) - return nil }) - Expect(err).NotTo(HaveOccurred()) - }) - It("configures and deconfigures a cniVersion 0.4.0 iplvan link with ADD/CHECK/DEL", func() { - const IFNAME = "ipvl0" + It(fmt.Sprintf("[%s] configures and deconfigures an iplvan link with ADD/DEL", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "master": "%s", + "ipam": { + "type": "host-local", + "subnet": "10.1.2.0/24", + "dataDir": "%s" + } + }`, ver, MASTER_NAME, dataDir) - conf := fmt.Sprintf(`{ - "cniVersion": "0.4.0", - "name": "ipvlanTest1", - "type": "ipvlan", - "master": "%s", - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24" - } -}`, MASTER_NAME) + ipvlanAddCheckDelTest(conf, "", originalNS, targetNS) + }) - ipvlanAddCheckDelTest(conf, "ipvlanTest1", IFNAME, originalNS) - }) + if testutils.SpecVersionHasChaining(ver) { + It(fmt.Sprintf("[%s] configures and deconfigures an iplvan link with ADD/DEL when chained", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "prevResult": { + "interfaces": [ + { + "name": "%s" + } + ], + "ips": [ + { + "version": "4", + "address": "10.1.2.2/24", + "gateway": "10.1.2.1", + "interface": 0 + } + ], + "routes": [] + } + }`, ver, MASTER_NAME) - It("configures and deconfigures a cniVersion 0.4.0 iplvan link with ADD/CHECK/DEL when chained", func() { - const IFNAME = "ipvl0" + ipvlanAddCheckDelTest(conf, MASTER_NAME, originalNS, targetNS) + }) + } - conf := fmt.Sprintf(`{ - "cniVersion": "0.4.0", - "name": "ipvlanTest2", - "type": "ipvlan", - "prevResult": { - "interfaces": [ - { - "name": "%s" - } - ], - "ips": [ - { - "version": "4", - "address": "10.1.2.2/24", - "gateway": "10.1.2.1", - "interface": 0 - } - ], - "routes": [] - } - }`, MASTER_NAME) + It(fmt.Sprintf("[%s] deconfigures an unconfigured ipvlan link with DEL", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "master": "%s", + "ipam": { + "type": "host-local", + "subnet": "10.1.2.0/24", + "dataDir": "%s" + } + }`, ver, MASTER_NAME, dataDir) - ipvlanAddCheckDelTest(conf, "ipvlanTest2", IFNAME, originalNS) - }) - - It("configures and deconfigures a ipvlan link with ADD/DEL, without master config", func() { - const IFNAME = "ipvl0" - conf := `{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ipvlan", - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24" - } -}` - - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() - - // Make MASTER_NAME as default route interface - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(MASTER_NAME) - Expect(err).NotTo(HaveOccurred()) - err = netlink.LinkSetUp(link) - Expect(err).NotTo(HaveOccurred()) - - var address = &net.IPNet{IP: net.IPv4(192, 0, 0, 1), Mask: net.CIDRMask(24, 32)} - var addr = &netlink.Addr{IPNet: address} - err = netlink.AddrAdd(link, addr) - Expect(err).NotTo(HaveOccurred()) - - // add default gateway into MASTER - dst := &net.IPNet{ - IP: net.IPv4(0, 0, 0, 0), - Mask: net.CIDRMask(0, 0), + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: "ipvl0", + StdinData: []byte(conf), } - ip := net.IPv4(192, 0, 0, 254) - route := netlink.Route{LinkIndex: link.Attrs().Index, Dst: dst, Gw: ip} - err = netlink.RouteAdd(&route) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - ipvlanAddDelTest(conf, IFNAME, originalNS) - }) + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + err := testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + }) + + It(fmt.Sprintf("[%s] configures and deconfigures a ipvlan link with ADD/DEL, without master config", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ipvlan", + "ipam": { + "type": "host-local", + "subnet": "10.1.2.0/24", + "dataDir": "%s" + } + }`, ver, dataDir) + + // Make MASTER_NAME as default route interface + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(MASTER_NAME) + Expect(err).NotTo(HaveOccurred()) + err = netlink.LinkSetUp(link) + Expect(err).NotTo(HaveOccurred()) + + var address = &net.IPNet{IP: net.IPv4(192, 0, 0, 1), Mask: net.CIDRMask(24, 32)} + var addr = &netlink.Addr{IPNet: address} + err = netlink.AddrAdd(link, addr) + Expect(err).NotTo(HaveOccurred()) + + // add default gateway into MASTER + dst := &net.IPNet{ + IP: net.IPv4(0, 0, 0, 0), + Mask: net.CIDRMask(0, 0), + } + ip := net.IPv4(192, 0, 0, 254) + route := netlink.Route{LinkIndex: link.Attrs().Index, Dst: dst, Gw: ip} + err = netlink.RouteAdd(&route) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + ipvlanAddCheckDelTest(conf, MASTER_NAME, originalNS, targetNS) + }) + } }) diff --git a/plugins/main/loopback/loopback.go b/plugins/main/loopback/loopback.go index 2b07f7eb..2c003af9 100644 --- a/plugins/main/loopback/loopback.go +++ b/plugins/main/loopback/loopback.go @@ -24,7 +24,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/plugins/pkg/ns" @@ -109,12 +109,19 @@ func cmdAdd(args *skel.CmdArgs) error { // loopback should pass it transparently result = conf.PrevResult } else { - loopbackInterface := ¤t.Interface{Name: args.IfName, Mac: "00:00:00:00:00:00", Sandbox: args.Netns} - r := ¤t.Result{CNIVersion: conf.CNIVersion, Interfaces: []*current.Interface{loopbackInterface}} + r := ¤t.Result{ + CNIVersion: conf.CNIVersion, + Interfaces: []*current.Interface{ + ¤t.Interface{ + Name: args.IfName, + Mac: "00:00:00:00:00:00", + Sandbox: args.Netns, + }, + }, + } if v4Addr != nil { r.IPs = append(r.IPs, ¤t.IPConfig{ - Version: "4", Interface: current.Int(0), Address: *v4Addr, }) @@ -122,7 +129,6 @@ func cmdAdd(args *skel.CmdArgs) error { if v6Addr != nil { r.IPs = append(r.IPs, ¤t.IPConfig{ - Version: "6", Interface: current.Int(0), Address: *v6Addr, }) diff --git a/plugins/main/loopback/loopback_test.go b/plugins/main/loopback/loopback_test.go index 68cfb15e..b2aec44c 100644 --- a/plugins/main/loopback/loopback_test.go +++ b/plugins/main/loopback/loopback_test.go @@ -28,6 +28,10 @@ import ( "github.com/onsi/gomega/gexec" ) +func generateConfig(cniVersion string) *strings.Reader { + return strings.NewReader(fmt.Sprintf(`{ "name": "loopback-test", "cniVersion": "%s" }`, cniVersion)) +} + var _ = Describe("Loopback", func() { var ( networkNS ns.NetNS @@ -49,7 +53,6 @@ var _ = Describe("Loopback", func() { fmt.Sprintf("CNI_ARGS=%s", "none"), fmt.Sprintf("CNI_PATH=%s", "/some/test/path"), } - command.Stdin = strings.NewReader(`{ "name": "loopback-test", "cniVersion": "0.1.0" }`) }) AfterEach(func() { @@ -57,45 +60,53 @@ var _ = Describe("Loopback", func() { Expect(testutils.UnmountNS(networkNS)).To(Succeed()) }) - Context("when given a network namespace", func() { - It("sets the lo device to UP", func() { - command.Env = append(environ, fmt.Sprintf("CNI_COMMAND=%s", "ADD")) + for _, ver := range testutils.AllSpecVersions { + // Redefine ver inside for scope so real value is picked up by each dynamically defined It() + // See Gingkgo's "Patterns for dynamically generating tests" documentation. + ver := ver - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).NotTo(HaveOccurred()) + Context("when given a network namespace", func() { + It(fmt.Sprintf("[%s] sets the lo device to UP", ver), func() { + command.Stdin = generateConfig(ver) + command.Env = append(environ, fmt.Sprintf("CNI_COMMAND=%s", "ADD")) - Eventually(session).Should(gbytes.Say(`{.*}`)) - Eventually(session).Should(gexec.Exit(0)) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).NotTo(HaveOccurred()) - var lo *net.Interface - err = networkNS.Do(func(ns.NetNS) error { - var err error - lo, err = net.InterfaceByName("lo") - return err + Eventually(session).Should(gbytes.Say(`{.*}`)) + Eventually(session).Should(gexec.Exit(0)) + + var lo *net.Interface + err = networkNS.Do(func(ns.NetNS) error { + var err error + lo, err = net.InterfaceByName("lo") + return err + }) + Expect(err).NotTo(HaveOccurred()) + + Expect(lo.Flags & net.FlagUp).To(Equal(net.FlagUp)) }) - Expect(err).NotTo(HaveOccurred()) - Expect(lo.Flags & net.FlagUp).To(Equal(net.FlagUp)) - }) + It(fmt.Sprintf("[%s] sets the lo device to DOWN", ver), func() { + command.Stdin = generateConfig(ver) + command.Env = append(environ, fmt.Sprintf("CNI_COMMAND=%s", "DEL")) - It("sets the lo device to DOWN", func() { - command.Env = append(environ, fmt.Sprintf("CNI_COMMAND=%s", "DEL")) + session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) + Expect(err).NotTo(HaveOccurred()) - session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) - Expect(err).NotTo(HaveOccurred()) + Eventually(session).Should(gbytes.Say(``)) + Eventually(session).Should(gexec.Exit(0)) - Eventually(session).Should(gbytes.Say(``)) - Eventually(session).Should(gexec.Exit(0)) + var lo *net.Interface + err = networkNS.Do(func(ns.NetNS) error { + var err error + lo, err = net.InterfaceByName("lo") + return err + }) + Expect(err).NotTo(HaveOccurred()) - var lo *net.Interface - err = networkNS.Do(func(ns.NetNS) error { - var err error - lo, err = net.InterfaceByName("lo") - return err + Expect(lo.Flags & net.FlagUp).NotTo(Equal(net.FlagUp)) }) - Expect(err).NotTo(HaveOccurred()) - - Expect(lo.Flags & net.FlagUp).NotTo(Equal(net.FlagUp)) }) - }) + } }) diff --git a/plugins/main/macvlan/macvlan.go b/plugins/main/macvlan/macvlan.go index f8e1a2c1..99044a33 100644 --- a/plugins/main/macvlan/macvlan.go +++ b/plugins/main/macvlan/macvlan.go @@ -26,7 +26,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/plugins/pkg/ip" @@ -256,7 +256,10 @@ func cmdAdd(args *skel.CmdArgs) error { }() // Assume L2 interface only - result := ¤t.Result{CNIVersion: cniVersion, Interfaces: []*current.Interface{macvlanInterface}} + result := ¤t.Result{ + CNIVersion: current.ImplementedSpecVersion, + Interfaces: []*current.Interface{macvlanInterface}, + } if isLayer3 { // run the IPAM plugin and get back the config to apply @@ -301,7 +304,7 @@ func cmdAdd(args *skel.CmdArgs) error { } for _, ipc := range result.IPs { - if ipc.Version == "4" { + if ipc.Address.IP.To4() != nil { _ = arping.GratuitousArpOverIface(ipc.Address.IP, *contVeth) } } diff --git a/plugins/main/macvlan/macvlan_test.go b/plugins/main/macvlan/macvlan_test.go index e0890ff9..f95d3b87 100644 --- a/plugins/main/macvlan/macvlan_test.go +++ b/plugins/main/macvlan/macvlan_test.go @@ -17,12 +17,17 @@ package main import ( "encoding/json" "fmt" + "io/ioutil" "net" + "os" + "strings" "syscall" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/cni/pkg/types/020" + "github.com/containernetworking/cni/pkg/types/040" + "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" @@ -49,7 +54,7 @@ type Net struct { // A *IPAMArgs `json:"cni"` DNS types.DNS `json:"dns"` RawPrevResult map[string]interface{} `json:"prevResult,omitempty"` - PrevResult current.Result `json:"-"` + PrevResult types100.Result `json:"-"` } func buildOneConfig(netName string, cniVersion string, orig *Net, prevResult types.Result) (*Net, error) { @@ -95,14 +100,107 @@ func buildOneConfig(netName string, cniVersion string, orig *Net, prevResult typ } +type tester interface { + // verifyResult minimally verifies the Result and returns the interface's MAC address + verifyResult(result types.Result, err error, name string, numAddrs int) string +} + +type testerBase struct{} + +type testerV10x testerBase +type testerV04x testerBase +type testerV03x testerBase +type testerV01xOr02x testerBase + +func newTesterByVersion(version string) tester { + switch { + case strings.HasPrefix(version, "1.0."): + return &testerV10x{} + case strings.HasPrefix(version, "0.4."): + return &testerV04x{} + case strings.HasPrefix(version, "0.3."): + return &testerV03x{} + default: + return &testerV01xOr02x{} + } +} + +// verifyResult minimally verifies the Result and returns the interface's MAC address +func (t *testerV10x) verifyResult(result types.Result, err error, name string, numAddrs int) string { + // Validate error from the CNI ADD + Expect(err).NotTo(HaveOccurred()) + + r, err := types100.GetResult(result) + Expect(err).NotTo(HaveOccurred()) + + Expect(len(r.Interfaces)).To(Equal(1)) + Expect(r.Interfaces[0].Name).To(Equal(name)) + Expect(len(r.IPs)).To(Equal(numAddrs)) + + return r.Interfaces[0].Mac +} + +func verify0403(result types.Result, err error, name string, numAddrs int) string { + // Validate error from the CNI ADD + Expect(err).NotTo(HaveOccurred()) + + r, err := types040.GetResult(result) + Expect(err).NotTo(HaveOccurred()) + + Expect(len(r.Interfaces)).To(Equal(1)) + Expect(r.Interfaces[0].Name).To(Equal(name)) + Expect(len(r.IPs)).To(Equal(numAddrs)) + + return r.Interfaces[0].Mac +} + +// verifyResult minimally verifies the Result and returns the interface's MAC address +func (t *testerV04x) verifyResult(result types.Result, err error, name string, numAddrs int) string { + return verify0403(result, err, name, numAddrs) +} + +// verifyResult minimally verifies the Result and returns the interface's MAC address +func (t *testerV03x) verifyResult(result types.Result, err error, name string, numAddrs int) string { + return verify0403(result, err, name, numAddrs) +} + +// verifyResult minimally verifies the Result and returns the interface's MAC address +func (t *testerV01xOr02x) verifyResult(result types.Result, err error, name string, numAddrs int) string { + if result == nil && numAddrs == 0 { + Expect(err).To(MatchError("cannot convert: no valid IP addresses")) + return "" + } + + r, err := types020.GetResult(result) + Expect(err).NotTo(HaveOccurred()) + + var numIPs int + if r.IP4 != nil && r.IP4.IP.IP != nil { + numIPs++ + } + if r.IP6 != nil && r.IP6.IP.IP != nil { + numIPs++ + } + Expect(numIPs).To(Equal(numAddrs)) + + // 0.2 and earlier don't return MAC address + return "" +} + var _ = Describe("macvlan Operations", func() { - var originalNS ns.NetNS + var originalNS, targetNS ns.NetNS + var dataDir string BeforeEach(func() { // Create a new NetNS so we don't modify the host var err error originalNS, err = testutils.NewNS() Expect(err).NotTo(HaveOccurred()) + targetNS, err = testutils.NewNS() + Expect(err).NotTo(HaveOccurred()) + + dataDir, err = ioutil.TempDir("", "macvlan_test") + Expect(err).NotTo(HaveOccurred()) err = originalNS.Do(func(ns.NetNS) error { defer GinkgoRecover() @@ -122,648 +220,625 @@ var _ = Describe("macvlan Operations", func() { }) AfterEach(func() { + Expect(os.RemoveAll(dataDir)).To(Succeed()) Expect(originalNS.Close()).To(Succeed()) Expect(testutils.UnmountNS(originalNS)).To(Succeed()) + Expect(targetNS.Close()).To(Succeed()) + Expect(testutils.UnmountNS(targetNS)).To(Succeed()) }) - It("creates an macvlan link in a non-default namespace", func() { - conf := &NetConf{ - NetConf: types.NetConf{ - CNIVersion: "0.3.1", - Name: "testConfig", - Type: "macvlan", - }, - Master: MASTER_NAME, - Mode: "bridge", - MTU: 1500, - } - - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() - - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - _, err = createMacvlan(conf, "foobar0", targetNs) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // Make sure macvlan link exists in the target namespace - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName("foobar0") - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal("foobar0")) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("configures and deconfigures a macvlan link with ADD/DEL", func() { - const IFNAME = "macvl0" - - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "macvlan", - "master": "%s", - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24" - } -}`, MASTER_NAME) - - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNs.Path(), - IfName: IFNAME, - StdinData: []byte(conf), - } - - var result *current.Result - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - - result, err = current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(1)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // Make sure macvlan link exists in the target namespace - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal(IFNAME)) - - hwaddr, err := net.ParseMAC(result.Interfaces[0].Mac) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) - - addrs, err := netlink.AddrList(link, syscall.AF_INET) - Expect(err).NotTo(HaveOccurred()) - Expect(len(addrs)).To(Equal(1)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - err := testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // Make sure macvlan link has been deleted - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(IFNAME) - Expect(err).To(HaveOccurred()) - Expect(link).To(BeNil()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("deconfigures an unconfigured macvlan link with DEL", func() { - const IFNAME = "macvl0" - - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.0", - "name": "mynet", - "type": "macvlan", - "master": "%s", - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24" - } -}`, MASTER_NAME) - - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNs.Path(), - IfName: IFNAME, - StdinData: []byte(conf), - } - - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - err := testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - }) - - It("configures and deconfigures a l2 macvlan link with ADD/DEL", func() { - const IFNAME = "macvl0" - - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "macvlan", - "master": "%s", - "ipam": {} -}`, MASTER_NAME) - - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNs.Path(), - IfName: IFNAME, - StdinData: []byte(conf), - } - - var result *current.Result - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - - result, err = current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(0)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // Make sure macvlan link exists in the target namespace - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal(IFNAME)) - - hwaddr, err := net.ParseMAC(result.Interfaces[0].Mac) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) - - addrs, err := netlink.AddrList(link, syscall.AF_INET) - Expect(err).NotTo(HaveOccurred()) - Expect(len(addrs)).To(Equal(0)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - err := testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // Make sure macvlan link has been deleted - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(IFNAME) - Expect(err).To(HaveOccurred()) - Expect(link).To(BeNil()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("configures and deconfigures a cniVersion 0.4.0 macvlan link with ADD/DEL", func() { - const IFNAME = "macvl0" - - conf := fmt.Sprintf(`{ - "cniVersion": "0.4.0", - "name": "macvlanTestv4", - "type": "macvlan", - "master": "%s", - "ipam": { - "type": "host-local", - "ranges": [[ {"subnet": "10.1.2.0/24", "gateway": "10.1.2.1"} ]] - } -}`, MASTER_NAME) - - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNs.Path(), - IfName: IFNAME, - StdinData: []byte(conf), - } - - var result *current.Result - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - - result, err = current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(1)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // Make sure macvlan link exists in the target namespace - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal(IFNAME)) - - hwaddr, err := net.ParseMAC(result.Interfaces[0].Mac) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) - - addrs, err := netlink.AddrList(link, syscall.AF_INET) - Expect(err).NotTo(HaveOccurred()) - Expect(len(addrs)).To(Equal(1)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - n := &Net{} - err = json.Unmarshal([]byte(conf), &n) - Expect(err).NotTo(HaveOccurred()) - - n.IPAM, _, err = allocator.LoadIPAMConfig([]byte(conf), "") - Expect(err).NotTo(HaveOccurred()) - - cniVersion := "0.4.0" - newConf, err := buildOneConfig("macvlanTestv4", cniVersion, n, result) - Expect(err).NotTo(HaveOccurred()) - - confString, err := json.Marshal(newConf) - Expect(err).NotTo(HaveOccurred()) - - args.StdinData = confString - - // CNI Check on macvlan in the target namespace - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - err := testutils.CmdCheckWithArgs(args, func() error { - return cmdCheck(args) - }) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - err := testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // Make sure macvlan link has been deleted - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(IFNAME) - Expect(err).To(HaveOccurred()) - Expect(link).To(BeNil()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("configures and deconfigures a macvlan link with ADD/DEL, without master config", func() { - const IFNAME = "macvl0" - - conf := `{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "macvlan", - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24" - } -}` - - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNs.Path(), - IfName: IFNAME, - StdinData: []byte(conf), - } - - // Make MASTER_NAME as default route interface - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(MASTER_NAME) - Expect(err).NotTo(HaveOccurred()) - err = netlink.LinkSetUp(link) - Expect(err).NotTo(HaveOccurred()) - - var address = &net.IPNet{IP: net.IPv4(192, 0, 0, 1), Mask: net.CIDRMask(24, 32)} - var addr = &netlink.Addr{IPNet: address} - err = netlink.AddrAdd(link, addr) - Expect(err).NotTo(HaveOccurred()) - - // add default gateway into MASTER - dst := &net.IPNet{ - IP: net.IPv4(0, 0, 0, 0), - Mask: net.CIDRMask(0, 0), + for _, ver := range testutils.AllSpecVersions { + // Redefine ver inside for scope so real value is picked up by each dynamically defined It() + // See Gingkgo's "Patterns for dynamically generating tests" documentation. + ver := ver + + It(fmt.Sprintf("[%s] creates an macvlan link in a non-default namespace", ver), func() { + conf := &NetConf{ + NetConf: types.NetConf{ + CNIVersion: ver, + Name: "testConfig", + Type: "macvlan", + }, + Master: MASTER_NAME, + Mode: "bridge", + MTU: 1500, } - ip := net.IPv4(192, 0, 0, 254) - route := netlink.Route{LinkIndex: link.Attrs().Index, Dst: dst, Gw: ip} - err = netlink.RouteAdd(&route) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - var result *current.Result - err = originalNS.Do(func(ns.NetNS) error { - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + _, err := createMacvlan(conf, "foobar0", targetNS) + Expect(err).NotTo(HaveOccurred()) + return nil }) Expect(err).NotTo(HaveOccurred()) - result, err = current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) + // Make sure macvlan link exists in the target namespace + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(1)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // Make sure macvlan link exists in the target namespace - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal(IFNAME)) - - hwaddr, err := net.ParseMAC(result.Interfaces[0].Mac) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) - - addrs, err := netlink.AddrList(link, syscall.AF_INET) - Expect(err).NotTo(HaveOccurred()) - Expect(len(addrs)).To(Equal(1)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - err := testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + link, err := netlink.LinkByName("foobar0") + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal("foobar0")) + return nil }) Expect(err).NotTo(HaveOccurred()) - return nil }) - Expect(err).NotTo(HaveOccurred()) - // Make sure macvlan link has been deleted - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() + It(fmt.Sprintf("[%s] configures and deconfigures a macvlan link with ADD/DEL", ver), func() { + const IFNAME = "macvl0" - link, err := netlink.LinkByName(IFNAME) - Expect(err).To(HaveOccurred()) - Expect(link).To(BeNil()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "macvlan", + "master": "%s", + "ipam": { + "type": "host-local", + "subnet": "10.1.2.0/24", + "dataDir": "%s" + } + }`, ver, MASTER_NAME, dataDir) - It("configures and deconfigures l2 macvlan link with mac address (from CNI_ARGS) with CNI v4 ADD/DEL", func() { - const IFNAME = "macvl0" + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: []byte(conf), + } - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "macvlan", - "master": "%s", - "ipam": {} -}`, MASTER_NAME) + var macAddress string + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() + result, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNs.Path(), - IfName: IFNAME, - StdinData: []byte(conf), - Args: "IgnoreUnknown=true;MAC=c2:11:22:33:44:55", - } - - var result *current.Result - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + t := newTesterByVersion(ver) + macAddress = t.verifyResult(result, err, IFNAME, 1) + return nil }) Expect(err).NotTo(HaveOccurred()) - result, err = current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) + // Make sure macvlan link exists in the target namespace + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(0)) + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal(IFNAME)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) + if macAddress != "" { + hwaddr, err := net.ParseMAC(macAddress) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) + } - // Make sure macvlan link exists in the target namespace - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal(IFNAME)) - - hwaddr, err := net.ParseMAC("c2:11:22:33:44:55") - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) - - addrs, err := netlink.AddrList(link, syscall.AF_INET) - Expect(err).NotTo(HaveOccurred()) - Expect(len(addrs)).To(Equal(0)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - err := testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // Make sure macvlan link has been deleted - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(IFNAME) - Expect(err).To(HaveOccurred()) - Expect(link).To(BeNil()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("configures and deconfigures l2 macvlan link with mac address (from RuntimeConfig) with ADD/DEL", func() { - const IFNAME = "macvl0" - - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "capabilities": {"mac": true}, - "RuntimeConfig": { - "mac": "c2:11:22:33:44:55" - }, - "name": "mynet", - "type": "macvlan", - "master": "%s", - "ipam": {} -}`, MASTER_NAME) - - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNs.Path(), - IfName: IFNAME, - StdinData: []byte(conf), - } - - var result *current.Result - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + addrs, err := netlink.AddrList(link, syscall.AF_INET) + Expect(err).NotTo(HaveOccurred()) + Expect(len(addrs)).To(Equal(1)) + return nil }) Expect(err).NotTo(HaveOccurred()) - result, err = current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(0)) - - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // Make sure macvlan link exists in the target namespace - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal(IFNAME)) - - hwaddr, err := net.ParseMAC("c2:11:22:33:44:55") - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) - - addrs, err := netlink.AddrList(link, syscall.AF_INET) - Expect(err).NotTo(HaveOccurred()) - Expect(len(addrs)).To(Equal(0)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - err := testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + err := testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil }) Expect(err).NotTo(HaveOccurred()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - // Make sure macvlan link has been deleted - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() + // Make sure macvlan link has been deleted + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - link, err := netlink.LinkByName(IFNAME) - Expect(err).To(HaveOccurred()) - Expect(link).To(BeNil()) - return nil + link, err := netlink.LinkByName(IFNAME) + Expect(err).To(HaveOccurred()) + Expect(link).To(BeNil()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) }) - Expect(err).NotTo(HaveOccurred()) - }) + + It(fmt.Sprintf("[%s] deconfigures an unconfigured macvlan link with DEL", ver), func() { + const IFNAME = "macvl0" + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "macvlan", + "master": "%s", + "ipam": { + "type": "host-local", + "subnet": "10.1.2.0/24", + "dataDir": "%s" + } + }`, ver, MASTER_NAME, dataDir) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: []byte(conf), + } + + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + err := testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + }) + + It(fmt.Sprintf("[%s] configures and deconfigures a l2 macvlan link with ADD/DEL", ver), func() { + const IFNAME = "macvl0" + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "macvlan", + "master": "%s", + "ipam": {} + }`, ver, MASTER_NAME) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: []byte(conf), + } + + var macAddress string + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + result, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + + t := newTesterByVersion(ver) + macAddress = t.verifyResult(result, err, IFNAME, 0) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link exists in the target namespace + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal(IFNAME)) + + if macAddress != "" { + hwaddr, err := net.ParseMAC(macAddress) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) + } + + addrs, err := netlink.AddrList(link, syscall.AF_INET) + Expect(err).NotTo(HaveOccurred()) + Expect(len(addrs)).To(Equal(0)) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + err := testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link has been deleted + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).To(HaveOccurred()) + Expect(link).To(BeNil()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + }) + + It(fmt.Sprintf("[%s] configures and deconfigures a macvlan link with ADD/CHECK/DEL", ver), func() { + const IFNAME = "macvl0" + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "macvlanTestv4", + "type": "macvlan", + "master": "%s", + "ipam": { + "type": "host-local", + "ranges": [[ {"subnet": "10.1.2.0/24", "gateway": "10.1.2.1"} ]], + "dataDir": "%s" + } + }`, ver, MASTER_NAME, dataDir) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: []byte(conf), + } + + var ( + macAddress string + t tester + result types.Result + ) + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + var err error + result, _, err = testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + + t = newTesterByVersion(ver) + macAddress = t.verifyResult(result, err, IFNAME, 1) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link exists in the target namespace + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal(IFNAME)) + + if macAddress != "" { + hwaddr, err := net.ParseMAC(macAddress) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) + } + + addrs, err := netlink.AddrList(link, syscall.AF_INET) + Expect(err).NotTo(HaveOccurred()) + Expect(len(addrs)).To(Equal(1)) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + n := &Net{} + err = json.Unmarshal([]byte(conf), &n) + Expect(err).NotTo(HaveOccurred()) + + n.IPAM, _, err = allocator.LoadIPAMConfig([]byte(conf), "") + Expect(err).NotTo(HaveOccurred()) + + newConf, err := buildOneConfig("macvlanTestv4", ver, n, result) + Expect(err).NotTo(HaveOccurred()) + + confString, err := json.Marshal(newConf) + Expect(err).NotTo(HaveOccurred()) + + args.StdinData = confString + + // CNI Check on macvlan in the target namespace + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + return testutils.CmdCheckWithArgs(args, func() error { + return cmdCheck(args) + }) + }) + if testutils.SpecVersionHasCHECK(ver) { + Expect(err).NotTo(HaveOccurred()) + } else { + Expect(err).To(MatchError("config version does not allow CHECK")) + } + + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + err := testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link has been deleted + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).To(HaveOccurred()) + Expect(link).To(BeNil()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + }) + + It(fmt.Sprintf("[%s] configures and deconfigures a macvlan link with ADD/DEL, without master config", ver), func() { + const IFNAME = "macvl0" + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "macvlan", + "ipam": { + "type": "host-local", + "subnet": "10.1.2.0/24", + "dataDir": "%s" + } + }`, ver, dataDir) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: []byte(conf), + } + + // Make MASTER_NAME as default route interface + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(MASTER_NAME) + Expect(err).NotTo(HaveOccurred()) + err = netlink.LinkSetUp(link) + Expect(err).NotTo(HaveOccurred()) + + var address = &net.IPNet{IP: net.IPv4(192, 0, 0, 1), Mask: net.CIDRMask(24, 32)} + var addr = &netlink.Addr{IPNet: address} + err = netlink.AddrAdd(link, addr) + Expect(err).NotTo(HaveOccurred()) + + // add default gateway into MASTER + dst := &net.IPNet{ + IP: net.IPv4(0, 0, 0, 0), + Mask: net.CIDRMask(0, 0), + } + ip := net.IPv4(192, 0, 0, 254) + route := netlink.Route{LinkIndex: link.Attrs().Index, Dst: dst, Gw: ip} + err = netlink.RouteAdd(&route) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + var macAddress string + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + result, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + + t := newTesterByVersion(ver) + macAddress = t.verifyResult(result, err, IFNAME, 1) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link exists in the target namespace + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal(IFNAME)) + + if macAddress != "" { + hwaddr, err := net.ParseMAC(macAddress) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) + } + + addrs, err := netlink.AddrList(link, syscall.AF_INET) + Expect(err).NotTo(HaveOccurred()) + Expect(len(addrs)).To(Equal(1)) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + err := testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link has been deleted + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).To(HaveOccurred()) + Expect(link).To(BeNil()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + }) + + It(fmt.Sprintf("[%s] configures and deconfigures l2 macvlan link with mac address (from CNI_ARGS) with ADD/DEL", ver), func() { + const ( + IFNAME = "macvl0" + EXPECTED_MAC = "c2:11:22:33:44:55" + ) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "macvlan", + "master": "%s", + "ipam": {} + }`, ver, MASTER_NAME) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: []byte(conf), + Args: fmt.Sprintf("IgnoreUnknown=true;MAC=%s", EXPECTED_MAC), + } + + var macAddress string + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + result, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + + t := newTesterByVersion(ver) + macAddress = t.verifyResult(result, err, IFNAME, 0) + if macAddress != "" { + Expect(macAddress).To(Equal(EXPECTED_MAC)) + } + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link exists in the target namespace + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal(IFNAME)) + + hwaddr, err := net.ParseMAC(EXPECTED_MAC) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) + + addrs, err := netlink.AddrList(link, syscall.AF_INET) + Expect(err).NotTo(HaveOccurred()) + Expect(len(addrs)).To(Equal(0)) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + err := testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link has been deleted + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).To(HaveOccurred()) + Expect(link).To(BeNil()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + }) + + It(fmt.Sprintf("[%s] configures and deconfigures l2 macvlan link with mac address (from RuntimeConfig) with ADD/DEL", ver), func() { + const ( + IFNAME = "macvl0" + EXPECTED_MAC = "c2:11:22:33:44:55" + ) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "capabilities": {"mac": true}, + "RuntimeConfig": { + "mac": "c2:11:22:33:44:55" + }, + "name": "mynet", + "type": "macvlan", + "master": "%s", + "ipam": {} + }`, ver, MASTER_NAME) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: []byte(conf), + } + + var macAddress string + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + result, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + + t := newTesterByVersion(ver) + macAddress = t.verifyResult(result, err, IFNAME, 0) + if macAddress != "" { + Expect(macAddress).To(Equal(EXPECTED_MAC)) + } + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link exists in the target namespace + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal(IFNAME)) + + hwaddr, err := net.ParseMAC(EXPECTED_MAC) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) + + addrs, err := netlink.AddrList(link, syscall.AF_INET) + Expect(err).NotTo(HaveOccurred()) + Expect(len(addrs)).To(Equal(0)) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + err := testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure macvlan link has been deleted + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).To(HaveOccurred()) + Expect(link).To(BeNil()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + }) + } }) diff --git a/plugins/main/ptp/ptp.go b/plugins/main/ptp/ptp.go index 2e9bf5af..58e41087 100644 --- a/plugins/main/ptp/ptp.go +++ b/plugins/main/ptp/ptp.go @@ -27,7 +27,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/plugins/pkg/ip" @@ -108,7 +108,7 @@ func setupContainerVeth(netns ns.NetNS, ifName string, mtu int, pr *current.Resu } addrBits := 32 - if ipc.Version == "6" { + if ipc.Address.IP.To4() == nil { addrBits = 128 } @@ -141,7 +141,7 @@ func setupContainerVeth(netns ns.NetNS, ifName string, mtu int, pr *current.Resu // Send a gratuitous arp for all v4 addresses for _, ipc := range pr.IPs { - if ipc.Version == "4" { + if ipc.Address.IP.To4() != nil { _ = arping.GratuitousArpOverIface(ipc.Address.IP, *contVeth) } } diff --git a/plugins/main/ptp/ptp_test.go b/plugins/main/ptp/ptp_test.go index 7cc2cce0..fd362b86 100644 --- a/plugins/main/ptp/ptp_test.go +++ b/plugins/main/ptp/ptp_test.go @@ -17,11 +17,15 @@ package main import ( "encoding/json" "fmt" + "io/ioutil" "os" + "strings" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/cni/pkg/types/020" + "github.com/containernetworking/cni/pkg/types/040" + "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" @@ -41,7 +45,7 @@ type Net struct { IPAM *allocator.IPAMConfig `json:"ipam"` DNS types.DNS `json:"dns"` RawPrevResult map[string]interface{} `json:"prevResult,omitempty"` - PrevResult current.Result `json:"-"` + PrevResult types100.Result `json:"-"` } func buildOneConfig(netName string, cniVersion string, orig *Net, prevResult types.Result) (*Net, error) { @@ -87,43 +91,166 @@ func buildOneConfig(netName string, cniVersion string, orig *Net, prevResult typ } +type tester interface { + // verifyResult minimally verifies the Result and returns the interface's IP addresses and MAC address + verifyResult(result types.Result, expectedIfName, expectedSandbox string, expectedDNS types.DNS) ([]resultIP, string) +} + +type testerBase struct{} + +type testerV10x testerBase +type testerV04x testerBase +type testerV03x testerBase +type testerV01xOr02x testerBase + +func newTesterByVersion(version string) tester { + switch { + case strings.HasPrefix(version, "1.0."): + return &testerV10x{} + case strings.HasPrefix(version, "0.4."): + return &testerV04x{} + case strings.HasPrefix(version, "0.3."): + return &testerV03x{} + default: + return &testerV01xOr02x{} + } +} + +type resultIP struct { + ip string + gw string +} + +// verifyResult minimally verifies the Result and returns the interface's IP addresses and MAC address +func (t *testerV10x) verifyResult(result types.Result, expectedIfName, expectedSandbox string, expectedDNS types.DNS) ([]resultIP, string) { + r, err := types100.GetResult(result) + Expect(err).NotTo(HaveOccurred()) + + Expect(r.Interfaces).To(HaveLen(2)) + Expect(r.Interfaces[0].Name).To(HavePrefix("veth")) + Expect(r.Interfaces[0].Mac).To(HaveLen(17)) + Expect(r.Interfaces[0].Sandbox).To(BeEmpty()) + Expect(r.Interfaces[1].Name).To(Equal(expectedIfName)) + Expect(r.Interfaces[1].Sandbox).To(Equal(expectedSandbox)) + + Expect(r.DNS).To(Equal(expectedDNS)) + + // Grab IPs from container interface + ips := []resultIP{} + for _, ipc := range r.IPs { + if *ipc.Interface == 1 { + ips = append(ips, resultIP{ + ip: ipc.Address.IP.String(), + gw: ipc.Gateway.String(), + }) + } + } + + return ips, r.Interfaces[1].Mac +} + +func verify0403(result types.Result, expectedIfName, expectedSandbox string, expectedDNS types.DNS) ([]resultIP, string) { + r, err := types040.GetResult(result) + Expect(err).NotTo(HaveOccurred()) + + Expect(r.Interfaces).To(HaveLen(2)) + Expect(r.Interfaces[0].Name).To(HavePrefix("veth")) + Expect(r.Interfaces[0].Mac).To(HaveLen(17)) + Expect(r.Interfaces[0].Sandbox).To(BeEmpty()) + Expect(r.Interfaces[1].Name).To(Equal(expectedIfName)) + Expect(r.Interfaces[1].Sandbox).To(Equal(expectedSandbox)) + + Expect(r.DNS).To(Equal(expectedDNS)) + + // Grab IPs from container interface + ips := []resultIP{} + for _, ipc := range r.IPs { + if *ipc.Interface == 1 { + ips = append(ips, resultIP{ + ip: ipc.Address.IP.String(), + gw: ipc.Gateway.String(), + }) + } + } + + return ips, r.Interfaces[1].Mac +} + +// verifyResult minimally verifies the Result and returns the interface's IP addresses and MAC address +func (t *testerV04x) verifyResult(result types.Result, expectedIfName, expectedSandbox string, expectedDNS types.DNS) ([]resultIP, string) { + return verify0403(result, expectedIfName, expectedSandbox, expectedDNS) +} + +// verifyResult minimally verifies the Result and returns the interface's IP addresses and MAC address +func (t *testerV03x) verifyResult(result types.Result, expectedIfName, expectedSandbox string, expectedDNS types.DNS) ([]resultIP, string) { + return verify0403(result, expectedIfName, expectedSandbox, expectedDNS) +} + +// verifyResult minimally verifies the Result and returns the interface's IP addresses and MAC address +func (t *testerV01xOr02x) verifyResult(result types.Result, expectedIfName, expectedSandbox string, expectedDNS types.DNS) ([]resultIP, string) { + r, err := types020.GetResult(result) + Expect(err).NotTo(HaveOccurred()) + + ips := []resultIP{} + if r.IP4 != nil && r.IP4.IP.IP != nil { + ips = append(ips, resultIP{ + ip: r.IP4.IP.IP.String(), + gw: r.IP4.Gateway.String(), + }) + } + if r.IP6 != nil && r.IP6.IP.IP != nil { + ips = append(ips, resultIP{ + ip: r.IP6.IP.IP.String(), + gw: r.IP6.Gateway.String(), + }) + } + + // 0.2 and earlier don't return MAC address + return ips, "" +} + var _ = Describe("ptp Operations", func() { - var originalNS ns.NetNS + var originalNS, targetNS ns.NetNS + var dataDir string BeforeEach(func() { // Create a new NetNS so we don't modify the host var err error originalNS, err = testutils.NewNS() Expect(err).NotTo(HaveOccurred()) + targetNS, err = testutils.NewNS() + Expect(err).NotTo(HaveOccurred()) + + dataDir, err = ioutil.TempDir("", "ptp_test") + Expect(err).NotTo(HaveOccurred()) }) AfterEach(func() { + Expect(os.RemoveAll(dataDir)).To(Succeed()) Expect(originalNS.Close()).To(Succeed()) Expect(testutils.UnmountNS(originalNS)).To(Succeed()) + Expect(targetNS.Close()).To(Succeed()) + Expect(testutils.UnmountNS(targetNS)).To(Succeed()) }) - doTest := func(conf string, numIPs int, expectedDNSConf types.DNS) { + doTest := func(conf, cniVersion string, numIPs int, expectedDNSConf types.DNS, targetNS ns.NetNS) { const IFNAME = "ptp0" - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() - args := &skel.CmdArgs{ ContainerID: "dummy", - Netns: targetNs.Path(), + Netns: targetNS.Path(), IfName: IFNAME, StdinData: []byte(conf), } - var resI types.Result - var res *current.Result + var result types.Result // Execute the plugin with the ADD command, creating the veth endpoints - err = originalNS.Do(func(ns.NetNS) error { + err := originalNS.Do(func(ns.NetNS) error { defer GinkgoRecover() - resI, _, err = testutils.CmdAddWithArgs(args, func() error { + var err error + result, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) Expect(err).NotTo(HaveOccurred()) @@ -131,32 +258,25 @@ var _ = Describe("ptp Operations", func() { }) Expect(err).NotTo(HaveOccurred()) - res, err = current.NewResultFromResult(resI) - Expect(err).NotTo(HaveOccurred()) + t := newTesterByVersion(cniVersion) + ips, mac := t.verifyResult(result, IFNAME, targetNS.Path(), expectedDNSConf) + Expect(len(ips)).To(Equal(numIPs)) // Make sure ptp link exists in the target namespace // Then, ping the gateway - seenIPs := 0 - - wantMac := "" - err = targetNs.Do(func(ns.NetNS) error { + err = targetNS.Do(func(ns.NetNS) error { defer GinkgoRecover() link, err := netlink.LinkByName(IFNAME) Expect(err).NotTo(HaveOccurred()) - wantMac = link.Attrs().HardwareAddr.String() + if mac != "" { + Expect(mac).To(Equal(link.Attrs().HardwareAddr.String())) + } - for _, ipc := range res.IPs { - if *ipc.Interface != 1 { - continue - } - seenIPs += 1 - saddr := ipc.Address.IP.String() - daddr := ipc.Gateway.String() - fmt.Fprintln(GinkgoWriter, "ping", saddr, "->", daddr) - - if err := testutils.Ping(saddr, daddr, (ipc.Version == "6"), 30); err != nil { - return fmt.Errorf("ping %s -> %s failed: %s", saddr, daddr, err) + for _, ipc := range ips { + fmt.Fprintln(GinkgoWriter, "ping", ipc.ip, "->", ipc.gw) + if err := testutils.Ping(ipc.ip, ipc.gw, 30); err != nil { + return fmt.Errorf("ping %s -> %s failed: %s", ipc.ip, ipc.gw, err) } } @@ -164,121 +284,6 @@ var _ = Describe("ptp Operations", func() { }) Expect(err).NotTo(HaveOccurred()) - Expect(seenIPs).To(Equal(numIPs)) - - // make sure the interfaces are correct - Expect(res.Interfaces).To(HaveLen(2)) - - Expect(res.Interfaces[0].Name).To(HavePrefix("veth")) - Expect(res.Interfaces[0].Mac).To(HaveLen(17)) - Expect(res.Interfaces[0].Sandbox).To(BeEmpty()) - - Expect(res.Interfaces[1].Name).To(Equal(IFNAME)) - Expect(res.Interfaces[1].Mac).To(Equal(wantMac)) - Expect(res.Interfaces[1].Sandbox).To(Equal(targetNs.Path())) - - // make sure DNS is correct - Expect(res.DNS).To(Equal(expectedDNSConf)) - - // Call the plugins with the DEL command, deleting the veth endpoints - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - err := testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // Make sure ptp link has been deleted - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(IFNAME) - Expect(err).To(HaveOccurred()) - Expect(link).To(BeNil()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - } - - doTestv4 := func(conf string, netName string, numIPs int) { - const IFNAME = "ptp0" - - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNs.Path(), - IfName: IFNAME, - StdinData: []byte(conf), - } - - var resI types.Result - var res *current.Result - - // Execute the plugin with the ADD command, creating the veth endpoints - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - resI, _, err = testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - res, err = current.NewResultFromResult(resI) - Expect(err).NotTo(HaveOccurred()) - - // Make sure ptp link exists in the target namespace - // Then, ping the gateway - seenIPs := 0 - - wantMac := "" - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - wantMac = link.Attrs().HardwareAddr.String() - - for _, ipc := range res.IPs { - if *ipc.Interface != 1 { - continue - } - seenIPs += 1 - saddr := ipc.Address.IP.String() - daddr := ipc.Gateway.String() - fmt.Fprintln(GinkgoWriter, "ping", saddr, "->", daddr) - - if err := testutils.Ping(saddr, daddr, (ipc.Version == "6"), 30); err != nil { - return fmt.Errorf("ping %s -> %s failed: %s", saddr, daddr, err) - } - } - - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - Expect(seenIPs).To(Equal(numIPs)) - - // make sure the interfaces are correct - Expect(res.Interfaces).To(HaveLen(2)) - - Expect(res.Interfaces[0].Name).To(HavePrefix("veth")) - Expect(res.Interfaces[0].Mac).To(HaveLen(17)) - Expect(res.Interfaces[0].Sandbox).To(BeEmpty()) - - Expect(res.Interfaces[1].Name).To(Equal(IFNAME)) - Expect(res.Interfaces[1].Mac).To(Equal(wantMac)) - Expect(res.Interfaces[1].Sandbox).To(Equal(targetNs.Path())) - // call CmdCheck n := &Net{} err = json.Unmarshal([]byte(conf), &n) @@ -287,8 +292,7 @@ var _ = Describe("ptp Operations", func() { n.IPAM, _, err = allocator.LoadIPAMConfig([]byte(conf), "") Expect(err).NotTo(HaveOccurred()) - cniVersion := "0.4.0" - newConf, err := buildOneConfig(netName, cniVersion, n, res) + newConf, err := buildOneConfig(n.Name, cniVersion, n, result) Expect(err).NotTo(HaveOccurred()) confString, err := json.Marshal(newConf) @@ -299,11 +303,13 @@ var _ = Describe("ptp Operations", func() { // CNI Check host-device in the target namespace err = originalNS.Do(func(ns.NetNS) error { defer GinkgoRecover() - var err error - err = testutils.CmdCheckWithArgs(args, func() error { return cmdCheck(args) }) - return err + return testutils.CmdCheckWithArgs(args, func() error { return cmdCheck(args) }) }) - Expect(err).NotTo(HaveOccurred()) + if testutils.SpecVersionHasCHECK(cniVersion) { + Expect(err).NotTo(HaveOccurred()) + } else { + Expect(err).To(MatchError("config version does not allow CHECK")) + } args.StdinData = []byte(conf) @@ -320,7 +326,7 @@ var _ = Describe("ptp Operations", func() { Expect(err).NotTo(HaveOccurred()) // Make sure ptp link has been deleted - err = targetNs.Do(func(ns.NetNS) error { + err = targetNS.Do(func(ns.NetNS) error { defer GinkgoRecover() link, err := netlink.LinkByName(IFNAME) @@ -331,227 +337,200 @@ var _ = Describe("ptp Operations", func() { Expect(err).NotTo(HaveOccurred()) } - It("configures and deconfigures a ptp link with ADD/DEL", func() { - dnsConf := types.DNS{ - Nameservers: []string{"10.1.2.123"}, - Domain: "some.domain.test", - Search: []string{"search.test"}, - Options: []string{"option1:foo"}, - } - dnsConfBytes, err := json.Marshal(dnsConf) - Expect(err).NotTo(HaveOccurred()) + for _, ver := range testutils.AllSpecVersions { + // Redefine ver inside for scope so real value is picked up by each dynamically defined It() + // See Gingkgo's "Patterns for dynamically generating tests" documentation. + ver := ver - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ptp", - "ipMasq": true, - "mtu": 5000, - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24" - }, - "dns": %s -}`, string(dnsConfBytes)) - - doTest(conf, 1, dnsConf) - }) - - It("configures and deconfigures a dual-stack ptp link with ADD/DEL", func() { - conf := `{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ptp", - "ipMasq": true, - "mtu": 5000, - "ipam": { - "type": "host-local", - "ranges": [ - [{ "subnet": "10.1.2.0/24"}], - [{ "subnet": "2001:db8:1::0/66"}] - ] - } -}` - - doTest(conf, 2, types.DNS{}) - }) - - It("does not override IPAM DNS settings if no DNS settings provided", func() { - ipamDNSConf := types.DNS{ - Nameservers: []string{"10.1.2.123"}, - Domain: "some.domain.test", - Search: []string{"search.test"}, - Options: []string{"option1:foo"}, - } - resolvConfPath, err := testutils.TmpResolvConf(ipamDNSConf) - Expect(err).NotTo(HaveOccurred()) - defer os.RemoveAll(resolvConfPath) - - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ptp", - "ipMasq": true, - "mtu": 5000, - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24", - "resolvConf": "%s" - } -}`, resolvConfPath) - - doTest(conf, 1, ipamDNSConf) - }) - - It("overrides IPAM DNS settings if any DNS settings provided", func() { - ipamDNSConf := types.DNS{ - Nameservers: []string{"10.1.2.123"}, - Domain: "some.domain.test", - Search: []string{"search.test"}, - Options: []string{"option1:foo"}, - } - resolvConfPath, err := testutils.TmpResolvConf(ipamDNSConf) - Expect(err).NotTo(HaveOccurred()) - defer os.RemoveAll(resolvConfPath) - - for _, ptpDNSConf := range []types.DNS{ - { - Nameservers: []string{"10.1.2.234"}, - }, - { - Domain: "someother.domain.test", - }, - { - Search: []string{"search.elsewhere.test"}, - }, - { - Options: []string{"option2:bar"}, - }, - } { - dnsConfBytes, err := json.Marshal(ptpDNSConf) + It(fmt.Sprintf("[%s] configures and deconfigures a ptp link with ADD/DEL", ver), func() { + dnsConf := types.DNS{ + Nameservers: []string{"10.1.2.123"}, + Domain: "some.domain.test", + Search: []string{"search.test"}, + Options: []string{"option1:foo"}, + } + dnsConfBytes, err := json.Marshal(dnsConf) Expect(err).NotTo(HaveOccurred()) conf := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ptp", - "ipMasq": true, - "mtu": 5000, - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24", - "resolvConf": "%s" - }, - "dns": %s -}`, resolvConfPath, string(dnsConfBytes)) + "cniVersion": "%s", + "name": "mynet", + "type": "ptp", + "ipMasq": true, + "mtu": 5000, + "ipam": { + "type": "host-local", + "subnet": "10.1.2.0/24", + "dataDir": "%s" + }, + "dns": %s + }`, ver, dataDir, string(dnsConfBytes)) - doTest(conf, 1, ptpDNSConf) - } - }) + doTest(conf, ver, 1, dnsConf, targetNS) + }) - It("overrides IPAM DNS settings if any empty list DNS settings provided", func() { - ipamDNSConf := types.DNS{ - Nameservers: []string{"10.1.2.123"}, - Domain: "some.domain.test", - Search: []string{"search.test"}, - Options: []string{"option1:foo"}, - } - resolvConfPath, err := testutils.TmpResolvConf(ipamDNSConf) - Expect(err).NotTo(HaveOccurred()) - defer os.RemoveAll(resolvConfPath) + It(fmt.Sprintf("[%s] configures and deconfigures a dual-stack ptp link with ADD/DEL", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ptp", + "ipMasq": true, + "mtu": 5000, + "ipam": { + "type": "host-local", + "ranges": [ + [{ "subnet": "10.1.2.0/24"}], + [{ "subnet": "2001:db8:1::0/66"}] + ], + "dataDir": "%s" + } + }`, ver, dataDir) - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "ptp", - "ipMasq": true, - "mtu": 5000, - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24", - "resolvConf": "%s" - }, - "dns": { - "nameservers": [], - "search": [], - "options": [] - } -}`, resolvConfPath) + doTest(conf, ver, 2, types.DNS{}, targetNS) + }) - doTest(conf, 1, types.DNS{}) - }) + It(fmt.Sprintf("[%s] does not override IPAM DNS settings if no DNS settings provided", ver), func() { + ipamDNSConf := types.DNS{ + Nameservers: []string{"10.1.2.123"}, + Domain: "some.domain.test", + Search: []string{"search.test"}, + Options: []string{"option1:foo"}, + } + resolvConfPath, err := testutils.TmpResolvConf(ipamDNSConf) + Expect(err).NotTo(HaveOccurred()) + defer os.RemoveAll(resolvConfPath) - It("deconfigures an unconfigured ptp link with DEL", func() { - const IFNAME = "ptp0" + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ptp", + "ipMasq": true, + "mtu": 5000, + "ipam": { + "type": "host-local", + "subnet": "10.1.2.0/24", + "resolvConf": "%s", + "dataDir": "%s" + } + }`, ver, resolvConfPath, dataDir) - conf := `{ - "cniVersion": "0.3.0", - "name": "mynet", - "type": "ptp", - "ipMasq": true, - "mtu": 5000, - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24" - } -}` + doTest(conf, ver, 1, ipamDNSConf, targetNS) + }) - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() + It(fmt.Sprintf("[%s] overrides IPAM DNS settings if any DNS settings provided", ver), func() { + ipamDNSConf := types.DNS{ + Nameservers: []string{"10.1.2.123"}, + Domain: "some.domain.test", + Search: []string{"search.test"}, + Options: []string{"option1:foo"}, + } + resolvConfPath, err := testutils.TmpResolvConf(ipamDNSConf) + Expect(err).NotTo(HaveOccurred()) + defer os.RemoveAll(resolvConfPath) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNs.Path(), - IfName: IFNAME, - StdinData: []byte(conf), - } + for _, ptpDNSConf := range []types.DNS{ + { + Nameservers: []string{"10.1.2.234"}, + }, + { + Domain: "someother.domain.test", + }, + { + Search: []string{"search.elsewhere.test"}, + }, + { + Options: []string{"option2:bar"}, + }, + } { + dnsConfBytes, err := json.Marshal(ptpDNSConf) + Expect(err).NotTo(HaveOccurred()) - // Call the plugins with the DEL command. It should not error even though the veth doesn't exist. - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ptp", + "ipMasq": true, + "mtu": 5000, + "ipam": { + "type": "host-local", + "subnet": "10.1.2.0/24", + "resolvConf": "%s", + "dataDir": "%s" + }, + "dns": %s + }`, ver, resolvConfPath, dataDir, string(dnsConfBytes)) - err := testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + doTest(conf, ver, 1, ptpDNSConf, targetNS) + } + }) + + It(fmt.Sprintf("[%s] overrides IPAM DNS settings if any empty list DNS settings provided", ver), func() { + ipamDNSConf := types.DNS{ + Nameservers: []string{"10.1.2.123"}, + Domain: "some.domain.test", + Search: []string{"search.test"}, + Options: []string{"option1:foo"}, + } + resolvConfPath, err := testutils.TmpResolvConf(ipamDNSConf) + Expect(err).NotTo(HaveOccurred()) + defer os.RemoveAll(resolvConfPath) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ptp", + "ipMasq": true, + "mtu": 5000, + "ipam": { + "type": "host-local", + "subnet": "10.1.2.0/24", + "dataDir": "%s", + "resolvConf": "%s" + }, + "dns": { + "nameservers": [], + "search": [], + "options": [] + } + }`, ver, dataDir, resolvConfPath) + + doTest(conf, ver, 1, types.DNS{}, targetNS) + }) + + It(fmt.Sprintf("[%s] deconfigures an unconfigured ptp link with DEL", ver), func() { + const IFNAME = "ptp0" + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "mynet", + "type": "ptp", + "ipMasq": true, + "mtu": 5000, + "ipam": { + "type": "host-local", + "dataDir": "%s", + "subnet": "10.1.2.0/24" + } + }`, ver, dataDir) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: []byte(conf), + } + + // Call the plugins with the DEL command. It should not error even though the veth doesn't exist. + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + err := testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + return nil }) Expect(err).NotTo(HaveOccurred()) - return nil }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("configures and deconfigures a CNI V4 ptp link with ADD/DEL", func() { - conf := `{ - "cniVersion": "0.4.0", - "name": "ptpNetv4", - "type": "ptp", - "ipMasq": true, - "mtu": 5000, - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24" - } -}` - - doTestv4(conf, "ptpNetv4", 1) - }) - - It("configures and deconfigures a CNI V4 dual-stack ptp link with ADD/DEL", func() { - conf := `{ - "cniVersion": "0.4.0", - "name": "ptpNetv4ds", - "type": "ptp", - "ipMasq": true, - "mtu": 5000, - "ipam": { - "type": "host-local", - "ranges": [ - [{ "subnet": "10.1.2.0/24"}], - [{ "subnet": "2001:db8:1::0/66"}] - ] - } -}` - - doTestv4(conf, "ptpNetv4ds", 2) - }) + } }) diff --git a/plugins/main/vlan/vlan.go b/plugins/main/vlan/vlan.go index 371ce14a..99b3b3d7 100644 --- a/plugins/main/vlan/vlan.go +++ b/plugins/main/vlan/vlan.go @@ -24,7 +24,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/plugins/pkg/ip" diff --git a/plugins/main/vlan/vlan_test.go b/plugins/main/vlan/vlan_test.go index 01b7a734..9c07500f 100644 --- a/plugins/main/vlan/vlan_test.go +++ b/plugins/main/vlan/vlan_test.go @@ -17,12 +17,17 @@ package main import ( "encoding/json" "fmt" + "io/ioutil" "net" + "os" + "strings" "syscall" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/cni/pkg/types/020" + "github.com/containernetworking/cni/pkg/types/040" + "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" @@ -45,7 +50,7 @@ type Net struct { IPAM *allocator.IPAMConfig `json:"ipam"` DNS types.DNS `json:"dns"` RawPrevResult map[string]interface{} `json:"prevResult,omitempty"` - PrevResult current.Result `json:"-"` + PrevResult types100.Result `json:"-"` } func buildOneConfig(netName string, cniVersion string, orig *Net, prevResult types.Result) (*Net, error) { @@ -91,14 +96,90 @@ func buildOneConfig(netName string, cniVersion string, orig *Net, prevResult typ } +type tester interface { + // verifyResult minimally verifies the Result and returns the interface's MAC address + verifyResult(result types.Result, name string) string +} + +type testerBase struct{} + +type testerV10x testerBase +type testerV04x testerBase +type testerV03x testerBase +type testerV01xOr02x testerBase + +func newTesterByVersion(version string) tester { + switch { + case strings.HasPrefix(version, "1.0."): + return &testerV10x{} + case strings.HasPrefix(version, "0.4."): + return &testerV04x{} + case strings.HasPrefix(version, "0.3."): + return &testerV03x{} + default: + return &testerV01xOr02x{} + } +} + +// verifyResult minimally verifies the Result and returns the interface's MAC address +func (t *testerV10x) verifyResult(result types.Result, name string) string { + r, err := types100.GetResult(result) + Expect(err).NotTo(HaveOccurred()) + + Expect(len(r.Interfaces)).To(Equal(1)) + Expect(r.Interfaces[0].Name).To(Equal(name)) + Expect(len(r.IPs)).To(Equal(1)) + + return r.Interfaces[0].Mac +} + +func verify0403(result types.Result, name string) string { + r, err := types040.GetResult(result) + Expect(err).NotTo(HaveOccurred()) + + Expect(len(r.Interfaces)).To(Equal(1)) + Expect(r.Interfaces[0].Name).To(Equal(name)) + Expect(len(r.IPs)).To(Equal(1)) + + return r.Interfaces[0].Mac +} + +// verifyResult minimally verifies the Result and returns the interface's MAC address +func (t *testerV04x) verifyResult(result types.Result, name string) string { + return verify0403(result, name) +} + +// verifyResult minimally verifies the Result and returns the interface's MAC address +func (t *testerV03x) verifyResult(result types.Result, name string) string { + return verify0403(result, name) +} + +// verifyResult minimally verifies the Result and returns the interface's MAC address +func (t *testerV01xOr02x) verifyResult(result types.Result, name string) string { + r, err := types020.GetResult(result) + Expect(err).NotTo(HaveOccurred()) + + Expect(r.IP4.IP.IP).NotTo(BeNil()) + Expect(r.IP6).To(BeNil()) + + // 0.2 and earlier don't return MAC address + return "" +} + var _ = Describe("vlan Operations", func() { - var originalNS ns.NetNS + var originalNS, targetNS ns.NetNS + var dataDir string BeforeEach(func() { // Create a new NetNS so we don't modify the host var err error originalNS, err = testutils.NewNS() Expect(err).NotTo(HaveOccurred()) + targetNS, err = testutils.NewNS() + Expect(err).NotTo(HaveOccurred()) + + dataDir, err = ioutil.TempDir("", "vlan_test") + Expect(err).NotTo(HaveOccurred()) err = originalNS.Do(func(ns.NetNS) error { defer GinkgoRecover() @@ -120,376 +201,290 @@ var _ = Describe("vlan Operations", func() { }) AfterEach(func() { + Expect(os.RemoveAll(dataDir)).To(Succeed()) Expect(originalNS.Close()).To(Succeed()) Expect(testutils.UnmountNS(originalNS)).To(Succeed()) + Expect(targetNS.Close()).To(Succeed()) + Expect(testutils.UnmountNS(targetNS)).To(Succeed()) }) - It("creates an vlan link in a non-default namespace with given MTU", func() { - conf := &NetConf{ - NetConf: types.NetConf{ - CNIVersion: "0.3.0", - Name: "testConfig", - Type: "vlan", - }, - Master: MASTER_NAME, - VlanId: 33, - MTU: 1500, - } + for _, ver := range testutils.AllSpecVersions { + // Redefine ver inside for scope so real value is picked up by each dynamically defined It() + // See Gingkgo's "Patterns for dynamically generating tests" documentation. + ver := ver - // Create vlan in other namespace - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() + It(fmt.Sprintf("[%s] creates an vlan link in a non-default namespace with given MTU", ver), func() { + conf := &NetConf{ + NetConf: types.NetConf{ + CNIVersion: ver, + Name: "testConfig", + Type: "vlan", + }, + Master: MASTER_NAME, + VlanId: 33, + MTU: 1500, + } - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + // Create vlan in other namespace + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - _, err := createVlan(conf, "foobar0", targetNs) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - - Expect(err).NotTo(HaveOccurred()) - - // Make sure vlan link exists in the target namespace - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName("foobar0") - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal("foobar0")) - Expect(link.Attrs().MTU).To(Equal(1500)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("creates an vlan link in a non-default namespace with master's MTU", func() { - conf := &NetConf{ - NetConf: types.NetConf{ - CNIVersion: "0.3.0", - Name: "testConfig", - Type: "vlan", - }, - Master: MASTER_NAME, - VlanId: 33, - } - - // Create vlan in other namespace - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() - - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - m, err := netlink.LinkByName(MASTER_NAME) - Expect(err).NotTo(HaveOccurred()) - err = netlink.LinkSetMTU(m, 1200) - Expect(err).NotTo(HaveOccurred()) - - _, err = createVlan(conf, "foobar0", targetNs) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - - Expect(err).NotTo(HaveOccurred()) - - // Make sure vlan link exists in the target namespace - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName("foobar0") - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal("foobar0")) - Expect(link.Attrs().MTU).To(Equal(1200)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("configures and deconfigures an vlan link with ADD/DEL", func() { - const IFNAME = "eth0" - - conf := fmt.Sprintf(`{ - "cniVersion": "0.3.0", - "name": "mynet", - "type": "vlan", - "master": "%s", - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24" - } -}`, MASTER_NAME) - - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNs.Path(), - IfName: IFNAME, - StdinData: []byte(conf), - } - - var result *current.Result - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + _, err := createVlan(conf, "foobar0", targetNS) + Expect(err).NotTo(HaveOccurred()) + return nil }) Expect(err).NotTo(HaveOccurred()) - result, err = current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) + // Make sure vlan link exists in the target namespace + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(1)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // Make sure vlan link exists in the target namespace - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal(IFNAME)) - - hwaddr, err := net.ParseMAC(result.Interfaces[0].Mac) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) - - addrs, err := netlink.AddrList(link, syscall.AF_INET) - Expect(err).NotTo(HaveOccurred()) - Expect(len(addrs)).To(Equal(1)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + link, err := netlink.LinkByName("foobar0") + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal("foobar0")) + Expect(link.Attrs().MTU).To(Equal(1500)) + return nil }) Expect(err).NotTo(HaveOccurred()) - return nil }) - Expect(err).NotTo(HaveOccurred()) - // Make sure vlan link has been deleted - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() + It(fmt.Sprintf("[%s] creates an vlan link in a non-default namespace with master's MTU", ver), func() { + conf := &NetConf{ + NetConf: types.NetConf{ + CNIVersion: ver, + Name: "testConfig", + Type: "vlan", + }, + Master: MASTER_NAME, + VlanId: 33, + } - link, err := netlink.LinkByName(IFNAME) - Expect(err).To(HaveOccurred()) - Expect(link).To(BeNil()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) + // Create vlan in other namespace + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - // DEL can be called multiple times, make sure no error is returned - // if the device is already removed. - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + m, err := netlink.LinkByName(MASTER_NAME) + Expect(err).NotTo(HaveOccurred()) + err = netlink.LinkSetMTU(m, 1200) + Expect(err).NotTo(HaveOccurred()) - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("configures and deconfigures an CNI V4 vlan link with ADD/CHECK/DEL", func() { - const IFNAME = "eth0" - - conf := fmt.Sprintf(`{ - "cniVersion": "0.4.0", - "name": "vlanTestv4", - "type": "vlan", - "master": "%s", - "vlanId": 1234, - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24" - } -}`, MASTER_NAME) - - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNs.Path(), - IfName: IFNAME, - StdinData: []byte(conf), - } - - var result *current.Result - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + _, err = createVlan(conf, "foobar0", targetNS) + Expect(err).NotTo(HaveOccurred()) + return nil }) Expect(err).NotTo(HaveOccurred()) - result, err = current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) + // Make sure vlan link exists in the target namespace + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(1)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // Make sure vlan link exists in the target namespace - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Name).To(Equal(IFNAME)) - - hwaddr, err := net.ParseMAC(result.Interfaces[0].Mac) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) - - addrs, err := netlink.AddrList(link, syscall.AF_INET) - Expect(err).NotTo(HaveOccurred()) - Expect(len(addrs)).To(Equal(1)) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - - // call CmdCheck - n := &Net{} - err = json.Unmarshal([]byte(conf), &n) - Expect(err).NotTo(HaveOccurred()) - - n.IPAM, _, err = allocator.LoadIPAMConfig([]byte(conf), "") - Expect(err).NotTo(HaveOccurred()) - - cniVersion := "0.4.0" - newConf, err := buildOneConfig("vlanTestv4", cniVersion, n, result) - Expect(err).NotTo(HaveOccurred()) - - confString, err := json.Marshal(newConf) - Expect(err).NotTo(HaveOccurred()) - - args.StdinData = confString - - // CNI Check host-device in the target namespace - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - var err error - err = testutils.CmdCheckWithArgs(args, func() error { return cmdCheck(args) }) - return err - }) - Expect(err).NotTo(HaveOccurred()) - - args.StdinData = []byte(conf) - - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + link, err := netlink.LinkByName("foobar0") + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal("foobar0")) + Expect(link.Attrs().MTU).To(Equal(1200)) + return nil }) Expect(err).NotTo(HaveOccurred()) - return nil }) - Expect(err).NotTo(HaveOccurred()) - // Make sure vlan link has been deleted - err = targetNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() + It(fmt.Sprintf("[%s] configures and deconfigures a vlan link with ADD/CHECK/DEL", ver), func() { + const IFNAME = "eth0" - link, err := netlink.LinkByName(IFNAME) - Expect(err).To(HaveOccurred()) - Expect(link).To(BeNil()) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "vlanTestv4", + "type": "vlan", + "master": "%s", + "vlanId": 1234, + "ipam": { + "type": "host-local", + "subnet": "10.1.2.0/24", + "dataDir": "%s" + } + }`, ver, MASTER_NAME, dataDir) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: []byte(conf), + } + + t := newTesterByVersion(ver) + + var result types.Result + var macAddress string + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + var err error + result, _, err = testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + + macAddress = t.verifyResult(result, IFNAME) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // Make sure vlan link exists in the target namespace + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Name).To(Equal(IFNAME)) + + if macAddress != "" { + hwaddr, err := net.ParseMAC(macAddress) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) + } + + addrs, err := netlink.AddrList(link, syscall.AF_INET) + Expect(err).NotTo(HaveOccurred()) + Expect(len(addrs)).To(Equal(1)) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // call CmdCheck + n := &Net{} + err = json.Unmarshal([]byte(conf), &n) + Expect(err).NotTo(HaveOccurred()) + + n.IPAM, _, err = allocator.LoadIPAMConfig([]byte(conf), "") + Expect(err).NotTo(HaveOccurred()) + + newConf, err := buildOneConfig("vlanTestv4", ver, n, result) + Expect(err).NotTo(HaveOccurred()) + + confString, err := json.Marshal(newConf) + Expect(err).NotTo(HaveOccurred()) + + args.StdinData = confString + + // CNI Check host-device in the target namespace + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + return testutils.CmdCheckWithArgs(args, func() error { return cmdCheck(args) }) + }) + if testutils.SpecVersionHasCHECK(ver) { + Expect(err).NotTo(HaveOccurred()) + } else { + Expect(err).To(MatchError("config version does not allow CHECK")) + } + + args.StdinData = []byte(conf) - Describe("fails to create vlan link with invalid MTU", func() { - conf := `{ - "cniVersion": "0.3.1", - "name": "mynet", - "type": "vlan", - "master": "%s", - "mtu": %d, - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24" - } -}` - BeforeEach(func() { - var err error err = originalNS.Do(func(ns.NetNS) error { defer GinkgoRecover() - // set master link's MTU to 1500 - link, err := netlink.LinkByName(MASTER_NAME) - Expect(err).NotTo(HaveOccurred()) - err = netlink.LinkSetMTU(link, 1500) + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) Expect(err).NotTo(HaveOccurred()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + // Make sure vlan link has been deleted + err = targetNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + link, err := netlink.LinkByName(IFNAME) + Expect(err).To(HaveOccurred()) + Expect(link).To(BeNil()) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + + // DEL can be called multiple times, make sure no error is returned + // if the device is already removed. + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) return nil }) Expect(err).NotTo(HaveOccurred()) }) - It("fails to create vlan link with greater MTU than master interface", func() { - var err error + Describe("fails to create vlan link with invalid MTU", func() { + const confFmt = `{ + "cniVersion": "%s", + "name": "mynet", + "type": "vlan", + "master": "%s", + "mtu": %d, + "ipam": { + "type": "host-local", + "subnet": "10.1.2.0/24", + "dataDir": "%s" + } + }` - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: "/var/run/netns/test", - IfName: "eth0", - StdinData: []byte(fmt.Sprintf(conf, MASTER_NAME, 1600)), - } + BeforeEach(func() { + var err error + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - _ = originalNS.Do(func(netNS ns.NetNS) error { - defer GinkgoRecover() + // set master link's MTU to 1500 + link, err := netlink.LinkByName(MASTER_NAME) + Expect(err).NotTo(HaveOccurred()) + err = netlink.LinkSetMTU(link, 1500) + Expect(err).NotTo(HaveOccurred()) - _, _, err = testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + return nil + }) + Expect(err).NotTo(HaveOccurred()) + }) + + It(fmt.Sprintf("[%s] fails to create vlan link with greater MTU than master interface", ver), func() { + var err error + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: "/var/run/netns/test", + IfName: "eth0", + StdinData: []byte(fmt.Sprintf(confFmt, ver, MASTER_NAME, 1600, dataDir)), + } + + _ = originalNS.Do(func(netNS ns.NetNS) error { + defer GinkgoRecover() + + _, _, err = testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).To(Equal(fmt.Errorf("invalid MTU 1600, must be [0, master MTU(1500)]"))) + return nil + }) + }) + + It(fmt.Sprintf("[%s] fails to create vlan link with negative MTU", ver), func() { + var err error + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: "/var/run/netns/test", + IfName: "eth0", + StdinData: []byte(fmt.Sprintf(confFmt, ver, MASTER_NAME, -100, dataDir)), + } + + _ = originalNS.Do(func(netNS ns.NetNS) error { + defer GinkgoRecover() + + _, _, err = testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).To(Equal(fmt.Errorf("invalid MTU -100, must be [0, master MTU(1500)]"))) + return nil }) - Expect(err).To(Equal(fmt.Errorf("invalid MTU 1600, must be [0, master MTU(1500)]"))) - return nil }) }) - - It("fails to create vlan link with negative MTU", func() { - var err error - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: "/var/run/netns/test", - IfName: "eth0", - StdinData: []byte(fmt.Sprintf(conf, MASTER_NAME, -100)), - } - - _ = originalNS.Do(func(netNS ns.NetNS) error { - defer GinkgoRecover() - - _, _, err = testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).To(Equal(fmt.Errorf("invalid MTU -100, must be [0, master MTU(1500)]"))) - return nil - }) - }) - }) + } }) diff --git a/plugins/main/windows/win-bridge/win-bridge_windows.go b/plugins/main/windows/win-bridge/win-bridge_windows.go index 40843d4c..36f1035a 100644 --- a/plugins/main/windows/win-bridge/win-bridge_windows.go +++ b/plugins/main/windows/win-bridge/win-bridge_windows.go @@ -25,7 +25,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/plugins/pkg/errors" diff --git a/plugins/main/windows/win-overlay/win-overlay_windows.go b/plugins/main/windows/win-overlay/win-overlay_windows.go index c133e921..77bb1714 100644 --- a/plugins/main/windows/win-overlay/win-overlay_windows.go +++ b/plugins/main/windows/win-overlay/win-overlay_windows.go @@ -24,7 +24,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/plugins/pkg/errors" diff --git a/plugins/meta/bandwidth/bandwidth_linux_test.go b/plugins/meta/bandwidth/bandwidth_linux_test.go index a75b2729..a9ab2677 100644 --- a/plugins/meta/bandwidth/bandwidth_linux_test.go +++ b/plugins/meta/bandwidth/bandwidth_linux_test.go @@ -18,7 +18,9 @@ import ( "context" "encoding/json" "fmt" + "io/ioutil" "net" + "os" "time" "github.com/vishvananda/netlink" @@ -26,7 +28,7 @@ import ( "github.com/containernetworking/cni/pkg/invoke" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" @@ -44,7 +46,9 @@ func buildOneConfig(name, cniVersion string, orig *PluginConf, prevResult types. } // Add previous plugin result if prevResult != nil { - inject["prevResult"] = prevResult + r, err := prevResult.GetAsVersion(cniVersion) + Expect(err).NotTo(HaveOccurred()) + inject["prevResult"] = r } // Ensure every config uses the same name and version @@ -107,519 +111,948 @@ var _ = Describe("bandwidth test", func() { hostIfaceMTU = 1024 ifbDeviceName = "bwpa8eda89404b7" - createVeth(hostNs.Path(), hostIfname, containerNs.Path(), containerIfname, hostIP, containerIP, hostIfaceMTU) + createVeth(hostNs, hostIfname, containerNs, containerIfname, hostIP, containerIP, hostIfaceMTU) }) AfterEach(func() { - containerNs.Close() - hostNs.Close() + Expect(containerNs.Close()).To(Succeed()) + Expect(testutils.UnmountNS(containerNs)).To(Succeed()) + Expect(hostNs.Close()).To(Succeed()) + Expect(testutils.UnmountNS(hostNs)).To(Succeed()) }) - Describe("cmdADD", func() { - It("Works with a Veth pair using 0.3.0 config", func() { - conf := `{ - "cniVersion": "0.3.0", - "name": "cni-plugin-bandwidth-test", - "type": "bandwidth", - "ingressRate": 8, - "ingressBurst": 8, - "egressRate": 16, - "egressBurst": 8, - "prevResult": { - "interfaces": [ - { - "name": "%s", - "sandbox": "" - }, - { - "name": "%s", - "sandbox": "%s" - } - ], - "ips": [ - { - "version": "4", - "address": "%s/24", - "gateway": "10.0.0.1", - "interface": 1 - } - ], - "routes": [] - } -}` + // Bandwidth requires host-side interface info, and thus only + // supports 0.3.0 and later CNI versions + for _, ver := range []string{"0.3.0", "0.3.1", "0.4.0", "1.0.0"} { + // Redefine ver inside for scope so real value is picked up by each dynamically defined It() + // See Gingkgo's "Patterns for dynamically generating tests" documentation. + ver := ver - conf = fmt.Sprintf(conf, hostIfname, containerIfname, containerNs.Path(), containerIP.String()) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: containerNs.Path(), - IfName: containerIfname, - StdinData: []byte(conf), - } + Describe("cmdADD", func() { + It(fmt.Sprintf("[%s] works with a Veth pair", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "cni-plugin-bandwidth-test", + "type": "bandwidth", + "ingressRate": 8, + "ingressBurst": 8, + "egressRate": 16, + "egressBurst": 8, + "prevResult": { + "interfaces": [ + { + "name": "%s", + "sandbox": "" + }, + { + "name": "%s", + "sandbox": "%s" + } + ], + "ips": [ + { + "version": "4", + "address": "%s/24", + "gateway": "10.0.0.1", + "interface": 1 + } + ], + "routes": [] + } + }`, ver, hostIfname, containerIfname, containerNs.Path(), containerIP.String()) - Expect(hostNs.Do(func(netNS ns.NetNS) error { - defer GinkgoRecover() - r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) - Expect(err).NotTo(HaveOccurred(), string(out)) - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: containerNs.Path(), + IfName: containerIfname, + StdinData: []byte(conf), + } - Expect(result.Interfaces).To(HaveLen(3)) - Expect(result.Interfaces[2].Name).To(Equal(ifbDeviceName)) - Expect(result.Interfaces[2].Sandbox).To(Equal("")) + Expect(hostNs.Do(func(netNS ns.NetNS) error { + defer GinkgoRecover() + r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) + Expect(err).NotTo(HaveOccurred(), string(out)) + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) - ifbLink, err := netlink.LinkByName(ifbDeviceName) - Expect(err).NotTo(HaveOccurred()) - Expect(ifbLink.Attrs().MTU).To(Equal(hostIfaceMTU)) + Expect(result.Interfaces).To(HaveLen(3)) + Expect(result.Interfaces[2].Name).To(Equal(ifbDeviceName)) + Expect(result.Interfaces[2].Sandbox).To(Equal("")) - qdiscs, err := netlink.QdiscList(ifbLink) - Expect(err).NotTo(HaveOccurred()) + ifbLink, err := netlink.LinkByName(ifbDeviceName) + Expect(err).NotTo(HaveOccurred()) + Expect(ifbLink.Attrs().MTU).To(Equal(hostIfaceMTU)) - Expect(qdiscs).To(HaveLen(1)) - Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index)) + qdiscs, err := netlink.QdiscList(ifbLink) + Expect(err).NotTo(HaveOccurred()) - Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Tbf{})) - Expect(qdiscs[0].(*netlink.Tbf).Rate).To(Equal(uint64(2))) - Expect(qdiscs[0].(*netlink.Tbf).Limit).To(Equal(uint32(1))) + Expect(qdiscs).To(HaveLen(1)) + Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index)) - hostVethLink, err := netlink.LinkByName(hostIfname) - Expect(err).NotTo(HaveOccurred()) + Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Tbf{})) + Expect(qdiscs[0].(*netlink.Tbf).Rate).To(Equal(uint64(2))) + Expect(qdiscs[0].(*netlink.Tbf).Limit).To(Equal(uint32(1))) - qdiscFilters, err := netlink.FilterList(hostVethLink, netlink.MakeHandle(0xffff, 0)) - Expect(err).NotTo(HaveOccurred()) + hostVethLink, err := netlink.LinkByName(hostIfname) + Expect(err).NotTo(HaveOccurred()) - Expect(qdiscFilters).To(HaveLen(1)) - Expect(qdiscFilters[0].(*netlink.U32).Actions[0].(*netlink.MirredAction).Ifindex).To(Equal(ifbLink.Attrs().Index)) + qdiscFilters, err := netlink.FilterList(hostVethLink, netlink.MakeHandle(0xffff, 0)) + Expect(err).NotTo(HaveOccurred()) - return nil - })).To(Succeed()) + Expect(qdiscFilters).To(HaveLen(1)) + Expect(qdiscFilters[0].(*netlink.U32).Actions[0].(*netlink.MirredAction).Ifindex).To(Equal(ifbLink.Attrs().Index)) - Expect(hostNs.Do(func(n ns.NetNS) error { - defer GinkgoRecover() + return nil + })).To(Succeed()) - ifbLink, err := netlink.LinkByName(hostIfname) - Expect(err).NotTo(HaveOccurred()) + Expect(hostNs.Do(func(n ns.NetNS) error { + defer GinkgoRecover() - qdiscs, err := netlink.QdiscList(ifbLink) - Expect(err).NotTo(HaveOccurred()) + ifbLink, err := netlink.LinkByName(hostIfname) + Expect(err).NotTo(HaveOccurred()) - Expect(qdiscs).To(HaveLen(2)) - Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index)) + qdiscs, err := netlink.QdiscList(ifbLink) + Expect(err).NotTo(HaveOccurred()) - Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Tbf{})) - Expect(qdiscs[0].(*netlink.Tbf).Rate).To(Equal(uint64(1))) - Expect(qdiscs[0].(*netlink.Tbf).Limit).To(Equal(uint32(1))) - return nil - })).To(Succeed()) + Expect(qdiscs).To(HaveLen(2)) + Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index)) + Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Tbf{})) + Expect(qdiscs[0].(*netlink.Tbf).Rate).To(Equal(uint64(1))) + Expect(qdiscs[0].(*netlink.Tbf).Limit).To(Equal(uint32(1))) + return nil + })).To(Succeed()) + + }) + + It(fmt.Sprintf("[%s] does not apply ingress when disabled", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "cni-plugin-bandwidth-test", + "type": "bandwidth", + "ingressRate": 0, + "ingressBurst": 0, + "egressRate": 8000, + "egressBurst": 80, + "prevResult": { + "interfaces": [ + { + "name": "%s", + "sandbox": "" + }, + { + "name": "%s", + "sandbox": "%s" + } + ], + "ips": [ + { + "version": "4", + "address": "%s/24", + "gateway": "10.0.0.1", + "interface": 1 + } + ], + "routes": [] + } + }`, ver, hostIfname, containerIfname, containerNs.Path(), containerIP.String()) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: containerNs.Path(), + IfName: containerIfname, + StdinData: []byte(conf), + } + + Expect(hostNs.Do(func(netNS ns.NetNS) error { + defer GinkgoRecover() + + _, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, ifbDeviceName, []byte(conf), func() error { return cmdAdd(args) }) + Expect(err).NotTo(HaveOccurred(), string(out)) + + _, err = netlink.LinkByName(ifbDeviceName) + Expect(err).NotTo(HaveOccurred()) + return nil + })).To(Succeed()) + + Expect(hostNs.Do(func(n ns.NetNS) error { + defer GinkgoRecover() + + containerIfLink, err := netlink.LinkByName(hostIfname) + Expect(err).NotTo(HaveOccurred()) + + qdiscs, err := netlink.QdiscList(containerIfLink) + Expect(err).NotTo(HaveOccurred()) + + Expect(qdiscs).To(HaveLen(2)) + Expect(qdiscs[0]).NotTo(BeAssignableToTypeOf(&netlink.Tbf{})) + Expect(qdiscs[1]).NotTo(BeAssignableToTypeOf(&netlink.Tbf{})) + + return nil + })).To(Succeed()) + + }) + + It(fmt.Sprintf("[%s] does not apply egress when disabled", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "cni-plugin-bandwidth-test", + "type": "bandwidth", + "egressRate": 0, + "egressBurst": 0, + "ingressRate": 8000, + "ingressBurst": 80, + "prevResult": { + "interfaces": [ + { + "name": "%s", + "sandbox": "" + }, + { + "name": "%s", + "sandbox": "%s" + } + ], + "ips": [ + { + "version": "4", + "address": "%s/24", + "gateway": "10.0.0.1", + "interface": 1 + } + ], + "routes": [] + } + }`, ver, hostIfname, containerIfname, containerNs.Path(), containerIP.String()) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: containerNs.Path(), + IfName: containerIfname, + StdinData: []byte(conf), + } + + Expect(hostNs.Do(func(netNS ns.NetNS) error { + defer GinkgoRecover() + + _, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, ifbDeviceName, []byte(conf), func() error { return cmdAdd(args) }) + Expect(err).NotTo(HaveOccurred(), string(out)) + + _, err = netlink.LinkByName(ifbDeviceName) + Expect(err).To(HaveOccurred()) + return nil + })).To(Succeed()) + + Expect(hostNs.Do(func(n ns.NetNS) error { + defer GinkgoRecover() + + containerIfLink, err := netlink.LinkByName(hostIfname) + Expect(err).NotTo(HaveOccurred()) + + qdiscs, err := netlink.QdiscList(containerIfLink) + Expect(err).NotTo(HaveOccurred()) + + Expect(qdiscs).To(HaveLen(1)) + Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(containerIfLink.Attrs().Index)) + + Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Tbf{})) + Expect(qdiscs[0].(*netlink.Tbf).Rate).To(Equal(uint64(1000))) + Expect(qdiscs[0].(*netlink.Tbf).Limit).To(Equal(uint32(35))) + return nil + })).To(Succeed()) + + }) + + It(fmt.Sprintf("[%s] fails an invalid ingress config", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "cni-plugin-bandwidth-test", + "type": "bandwidth", + "ingressRate": 0, + "ingressBurst": 123, + "egressRate": 123, + "egressBurst": 123, + "prevResult": { + "interfaces": [ + { + "name": "%s", + "sandbox": "" + }, + { + "name": "%s", + "sandbox": "%s" + } + ], + "ips": [ + { + "version": "4", + "address": "%s/24", + "gateway": "10.0.0.1", + "interface": 1 + } + ], + "routes": [] + } + }`, ver, hostIfname, containerIfname, containerNs.Path(), containerIP.String()) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: containerNs.Path(), + IfName: "eth0", + StdinData: []byte(conf), + } + + Expect(hostNs.Do(func(netNS ns.NetNS) error { + defer GinkgoRecover() + + _, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) + Expect(err).To(MatchError("if burst is set, rate must also be set")) + return nil + })).To(Succeed()) + }) + + It(fmt.Sprintf("[%s] works with a Veth pair using runtime config", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "cni-plugin-bandwidth-test", + "type": "bandwidth", + "runtimeConfig": { + "bandWidth": { + "ingressRate": 8, + "ingressBurst": 8, + "egressRate": 16, + "egressBurst": 9 + } + }, + "prevResult": { + "interfaces": [ + { + "name": "%s", + "sandbox": "" + }, + { + "name": "%s", + "sandbox": "%s" + } + ], + "ips": [ + { + "version": "4", + "address": "%s/24", + "gateway": "10.0.0.1", + "interface": 1 + } + ], + "routes": [] + } + }`, ver, hostIfname, containerIfname, containerNs.Path(), containerIP.String()) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: containerNs.Path(), + IfName: containerIfname, + StdinData: []byte(conf), + } + + Expect(hostNs.Do(func(netNS ns.NetNS) error { + defer GinkgoRecover() + r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) + Expect(err).NotTo(HaveOccurred(), string(out)) + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + Expect(result.Interfaces).To(HaveLen(3)) + Expect(result.Interfaces[2].Name).To(Equal(ifbDeviceName)) + Expect(result.Interfaces[2].Sandbox).To(Equal("")) + + ifbLink, err := netlink.LinkByName(ifbDeviceName) + Expect(err).NotTo(HaveOccurred()) + Expect(ifbLink.Attrs().MTU).To(Equal(hostIfaceMTU)) + + qdiscs, err := netlink.QdiscList(ifbLink) + Expect(err).NotTo(HaveOccurred()) + + Expect(qdiscs).To(HaveLen(1)) + Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index)) + + Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Tbf{})) + Expect(qdiscs[0].(*netlink.Tbf).Rate).To(Equal(uint64(2))) + Expect(qdiscs[0].(*netlink.Tbf).Limit).To(Equal(uint32(1))) + + hostVethLink, err := netlink.LinkByName(hostIfname) + Expect(err).NotTo(HaveOccurred()) + + qdiscFilters, err := netlink.FilterList(hostVethLink, netlink.MakeHandle(0xffff, 0)) + Expect(err).NotTo(HaveOccurred()) + + Expect(qdiscFilters).To(HaveLen(1)) + Expect(qdiscFilters[0].(*netlink.U32).Actions[0].(*netlink.MirredAction).Ifindex).To(Equal(ifbLink.Attrs().Index)) + + return nil + })).To(Succeed()) + + Expect(hostNs.Do(func(n ns.NetNS) error { + defer GinkgoRecover() + + ifbLink, err := netlink.LinkByName(hostIfname) + Expect(err).NotTo(HaveOccurred()) + + qdiscs, err := netlink.QdiscList(ifbLink) + Expect(err).NotTo(HaveOccurred()) + + Expect(qdiscs).To(HaveLen(2)) + Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index)) + + Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Tbf{})) + Expect(qdiscs[0].(*netlink.Tbf).Rate).To(Equal(uint64(1))) + Expect(qdiscs[0].(*netlink.Tbf).Limit).To(Equal(uint32(1))) + return nil + })).To(Succeed()) + + }) + + It(fmt.Sprintf("[%s] should apply static config when both static config and runtime config exist", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "cni-plugin-bandwidth-test", + "type": "bandwidth", + "ingressRate": 0, + "ingressBurst": 123, + "egressRate": 123, + "egressBurst": 123, + "runtimeConfig": { + "bandWidth": { + "ingressRate": 8, + "ingressBurst": 8, + "egressRate": 16, + "egressBurst": 9 + } + }, + "prevResult": { + "interfaces": [ + { + "name": "%s", + "sandbox": "" + }, + { + "name": "%s", + "sandbox": "%s" + } + ], + "ips": [ + { + "version": "4", + "address": "%s/24", + "gateway": "10.0.0.1", + "interface": 1 + } + ], + "routes": [] + } + }`, ver, hostIfname, containerIfname, containerNs.Path(), containerIP.String()) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: containerNs.Path(), + IfName: "eth0", + StdinData: []byte(conf), + } + + Expect(hostNs.Do(func(netNS ns.NetNS) error { + defer GinkgoRecover() + + _, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) + Expect(err).To(MatchError("if burst is set, rate must also be set")) + return nil + })).To(Succeed()) + }) }) - It("Does not apply ingress when disabled", func() { - conf := `{ - "cniVersion": "0.3.0", - "name": "cni-plugin-bandwidth-test", - "type": "bandwidth", - "ingressRate": 0, - "ingressBurst": 0, - "egressRate": 8000, - "egressBurst": 80, - "prevResult": { - "interfaces": [ - { - "name": "%s", - "sandbox": "" - }, - { - "name": "%s", - "sandbox": "%s" - } - ], - "ips": [ - { - "version": "4", - "address": "%s/24", - "gateway": "10.0.0.1", - "interface": 1 - } - ], - "routes": [] - } -}` + Describe("cmdDEL", func() { + It(fmt.Sprintf("[%s] works with a Veth pair using 0.3.0 config", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "cni-plugin-bandwidth-test", + "type": "bandwidth", + "ingressRate": 8, + "ingressBurst": 8, + "egressRate": 9, + "egressBurst": 9, + "prevResult": { + "interfaces": [ + { + "name": "%s", + "sandbox": "" + }, + { + "name": "%s", + "sandbox": "%s" + } + ], + "ips": [ + { + "version": "4", + "address": "%s/24", + "gateway": "10.0.0.1", + "interface": 1 + } + ], + "routes": [] + } + }`, ver, hostIfname, containerIfname, containerNs.Path(), containerIP.String()) - conf = fmt.Sprintf(conf, hostIfname, containerIfname, containerNs.Path(), containerIP.String()) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: containerNs.Path(), - IfName: containerIfname, - StdinData: []byte(conf), - } + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: containerNs.Path(), + IfName: containerIfname, + StdinData: []byte(conf), + } - Expect(hostNs.Do(func(netNS ns.NetNS) error { - defer GinkgoRecover() + Expect(hostNs.Do(func(netNS ns.NetNS) error { + defer GinkgoRecover() + _, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) + Expect(err).NotTo(HaveOccurred(), string(out)) - _, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, ifbDeviceName, []byte(conf), func() error { return cmdAdd(args) }) - Expect(err).NotTo(HaveOccurred(), string(out)) + err = testutils.CmdDel(containerNs.Path(), args.ContainerID, "", func() error { return cmdDel(args) }) + Expect(err).NotTo(HaveOccurred(), string(out)) - _, err = netlink.LinkByName(ifbDeviceName) - Expect(err).NotTo(HaveOccurred()) - return nil - })).To(Succeed()) + _, err = netlink.LinkByName(ifbDeviceName) + Expect(err).To(HaveOccurred()) - Expect(hostNs.Do(func(n ns.NetNS) error { - defer GinkgoRecover() - - containerIfLink, err := netlink.LinkByName(hostIfname) - Expect(err).NotTo(HaveOccurred()) - - qdiscs, err := netlink.QdiscList(containerIfLink) - Expect(err).NotTo(HaveOccurred()) - - Expect(qdiscs).To(HaveLen(2)) - Expect(qdiscs[0]).NotTo(BeAssignableToTypeOf(&netlink.Tbf{})) - Expect(qdiscs[1]).NotTo(BeAssignableToTypeOf(&netlink.Tbf{})) - - return nil - })).To(Succeed()) + return nil + })).To(Succeed()) + }) }) - It("Does not apply egress when disabled", func() { - conf := `{ - "cniVersion": "0.3.0", - "name": "cni-plugin-bandwidth-test", - "type": "bandwidth", - "egressRate": 0, - "egressBurst": 0, - "ingressRate": 8000, - "ingressBurst": 80, - "prevResult": { - "interfaces": [ - { - "name": "%s", - "sandbox": "" - }, - { - "name": "%s", - "sandbox": "%s" - } - ], - "ips": [ - { - "version": "4", - "address": "%s/24", - "gateway": "10.0.0.1", - "interface": 1 - } - ], - "routes": [] - } -}` + Describe("Getting the host interface which plugin should work on from veth peer of container interface", func() { + It(fmt.Sprintf("[%s] should work with multiple host veth interfaces", ver), func() { + // create veth peer in host ns + vethName, peerName := "host-veth-peer1", "host-veth-peer2" + createVethInOneNs(hostNs, vethName, peerName) - conf = fmt.Sprintf(conf, hostIfname, containerIfname, containerNs.Path(), containerIP.String()) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: containerNs.Path(), - IfName: containerIfname, - StdinData: []byte(conf), - } + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "cni-plugin-bandwidth-test", + "type": "bandwidth", + "ingressRate": 8, + "ingressBurst": 8, + "egressRate": 16, + "egressBurst": 8, + "prevResult": { + "interfaces": [ + { + "name": "%s", + "sandbox": "" + }, + { + "name": "%s", + "sandbox": "" + }, + { + "name": "%s", + "sandbox": "" + }, + { + "name": "%s", + "sandbox": "%s" + } + ], + "ips": [ + { + "version": "4", + "address": "%s/24", + "gateway": "10.0.0.1", + "interface": 1 + } + ], + "routes": [] + } + }`, ver, vethName, peerName, hostIfname, containerIfname, containerNs.Path(), containerIP.String()) - Expect(hostNs.Do(func(netNS ns.NetNS) error { - defer GinkgoRecover() + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: containerNs.Path(), + IfName: containerIfname, + StdinData: []byte(conf), + } - _, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, ifbDeviceName, []byte(conf), func() error { return cmdAdd(args) }) - Expect(err).NotTo(HaveOccurred(), string(out)) + Expect(hostNs.Do(func(netNS ns.NetNS) error { + defer GinkgoRecover() + r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) + Expect(err).NotTo(HaveOccurred(), string(out)) + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) - _, err = netlink.LinkByName(ifbDeviceName) - Expect(err).To(HaveOccurred()) - return nil - })).To(Succeed()) + Expect(result.Interfaces).To(HaveLen(5)) + Expect(result.Interfaces[4].Name).To(Equal(ifbDeviceName)) + Expect(result.Interfaces[4].Sandbox).To(Equal("")) - Expect(hostNs.Do(func(n ns.NetNS) error { - defer GinkgoRecover() + ifbLink, err := netlink.LinkByName(ifbDeviceName) + Expect(err).NotTo(HaveOccurred()) + Expect(ifbLink.Attrs().MTU).To(Equal(hostIfaceMTU)) - containerIfLink, err := netlink.LinkByName(hostIfname) - Expect(err).NotTo(HaveOccurred()) + qdiscs, err := netlink.QdiscList(ifbLink) + Expect(err).NotTo(HaveOccurred()) - qdiscs, err := netlink.QdiscList(containerIfLink) - Expect(err).NotTo(HaveOccurred()) + Expect(qdiscs).To(HaveLen(1)) + Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index)) - Expect(qdiscs).To(HaveLen(1)) - Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(containerIfLink.Attrs().Index)) + Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Tbf{})) + Expect(qdiscs[0].(*netlink.Tbf).Rate).To(Equal(uint64(2))) + Expect(qdiscs[0].(*netlink.Tbf).Limit).To(Equal(uint32(1))) - Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Tbf{})) - Expect(qdiscs[0].(*netlink.Tbf).Rate).To(Equal(uint64(1000))) - Expect(qdiscs[0].(*netlink.Tbf).Limit).To(Equal(uint32(35))) - return nil - })).To(Succeed()) + hostVethLink, err := netlink.LinkByName(hostIfname) + Expect(err).NotTo(HaveOccurred()) + qdiscFilters, err := netlink.FilterList(hostVethLink, netlink.MakeHandle(0xffff, 0)) + Expect(err).NotTo(HaveOccurred()) + + Expect(qdiscFilters).To(HaveLen(1)) + Expect(qdiscFilters[0].(*netlink.U32).Actions[0].(*netlink.MirredAction).Ifindex).To(Equal(ifbLink.Attrs().Index)) + + return nil + })).To(Succeed()) + + Expect(hostNs.Do(func(n ns.NetNS) error { + defer GinkgoRecover() + + ifbLink, err := netlink.LinkByName(hostIfname) + Expect(err).NotTo(HaveOccurred()) + + qdiscs, err := netlink.QdiscList(ifbLink) + Expect(err).NotTo(HaveOccurred()) + + Expect(qdiscs).To(HaveLen(2)) + Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index)) + + Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Tbf{})) + Expect(qdiscs[0].(*netlink.Tbf).Rate).To(Equal(uint64(1))) + Expect(qdiscs[0].(*netlink.Tbf).Limit).To(Equal(uint32(1))) + return nil + })).To(Succeed()) + + }) + + It(fmt.Sprintf("[%s] should fail when container interface has no veth peer", ver), func() { + // create a macvlan device to be container interface + macvlanContainerIfname := "container-macv" + createMacvlan(containerNs, containerIfname, macvlanContainerIfname) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "cni-plugin-bandwidth-test", + "type": "bandwidth", + "ingressRate": 8, + "ingressBurst": 8, + "egressRate": 16, + "egressBurst": 8, + "prevResult": { + "interfaces": [ + { + "name": "%s", + "sandbox": "" + }, + { + "name": "%s", + "sandbox": "%s" + } + ], + "ips": [ + { + "version": "4", + "address": "%s/24", + "gateway": "10.0.0.1", + "interface": 1 + } + ], + "routes": [] + } + }`, ver, hostIfname, macvlanContainerIfname, containerNs.Path(), containerIP.String()) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: containerNs.Path(), + IfName: macvlanContainerIfname, + StdinData: []byte(conf), + } + + Expect(hostNs.Do(func(netNS ns.NetNS) error { + defer GinkgoRecover() + + _, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) + Expect(err).To(HaveOccurred()) + + return nil + })).To(Succeed()) + }) + + It(fmt.Sprintf("[%s] should fail when preResult has no interfaces", ver), func() { + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "cni-plugin-bandwidth-test", + "type": "bandwidth", + "ingressRate": 8, + "ingressBurst": 8, + "egressRate": 16, + "egressBurst": 8, + "prevResult": { + "interfaces": [], + "ips": [], + "routes": [] + } + }`, ver) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: containerNs.Path(), + IfName: "eth0", + StdinData: []byte(conf), + } + + Expect(hostNs.Do(func(netNS ns.NetNS) error { + defer GinkgoRecover() + + _, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) + Expect(err).To(HaveOccurred()) + + return nil + })).To(Succeed()) + }) + + It(fmt.Sprintf("[%s] should fail when veth peer of container interface does not match any of host interfaces in preResult", ver), func() { + // fake a non-exist host interface name + fakeHostIfname := fmt.Sprintf("%s-fake", hostIfname) + + conf := fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "cni-plugin-bandwidth-test", + "type": "bandwidth", + "ingressRate": 8, + "ingressBurst": 8, + "egressRate": 16, + "egressBurst": 8, + "prevResult": { + "interfaces": [ + { + "name": "%s", + "sandbox": "" + }, + { + "name": "%s", + "sandbox": "%s" + } + ], + "ips": [ + { + "version": "4", + "address": "%s/24", + "gateway": "10.0.0.1", + "interface": 1 + } + ], + "routes": [] + } + }`, ver, fakeHostIfname, containerIfname, containerNs.Path(), containerIP.String()) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: containerNs.Path(), + IfName: containerIfname, + StdinData: []byte(conf), + } + + Expect(hostNs.Do(func(netNS ns.NetNS) error { + defer GinkgoRecover() + + _, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) + Expect(err).To(HaveOccurred()) + + return nil + })).To(Succeed()) + }) }) - It("fails an invalid ingress config", func() { - conf := `{ - "cniVersion": "0.3.0", - "name": "cni-plugin-bandwidth-test", - "type": "bandwidth", - "ingressRate": 0, - "ingressBurst": 123, - "egressRate": 123, - "egressBurst": 123, - "prevResult": { - "interfaces": [ - { - "name": "%s", - "sandbox": "" - }, - { - "name": "%s", - "sandbox": "%s" - } - ], - "ips": [ - { - "version": "4", - "address": "%s/24", - "gateway": "10.0.0.1", - "interface": 1 - } - ], - "routes": [] - } -}` + Context(fmt.Sprintf("[%s] when chaining bandwidth plugin with PTP", ver), func() { + var ptpConf string + var rateInBits uint64 + var burstInBits uint64 + var packetInBytes int + var containerWithoutTbfNS ns.NetNS + var containerWithTbfNS ns.NetNS + var portServerWithTbf int + var portServerWithoutTbf int - conf = fmt.Sprintf(conf, hostIfname, containerIfname, containerNs.Path(), containerIP.String()) + var containerWithTbfRes types.Result + var containerWithoutTbfRes types.Result + var echoServerWithTbf *gexec.Session + var echoServerWithoutTbf *gexec.Session + var dataDir string - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: containerNs.Path(), - IfName: "eth0", - StdinData: []byte(conf), - } + BeforeEach(func() { + rateInBytes := 1000 + rateInBits = uint64(rateInBytes * 8) + burstInBits = rateInBits * 2 + packetInBytes = rateInBytes * 25 - Expect(hostNs.Do(func(netNS ns.NetNS) error { - defer GinkgoRecover() + var err error + dataDir, err = ioutil.TempDir("", "bandwidth_linux_test") + Expect(err).NotTo(HaveOccurred()) - _, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) - Expect(err).To(MatchError("if burst is set, rate must also be set")) - return nil - })).To(Succeed()) + ptpConf = fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "myBWnet", + "type": "ptp", + "ipMasq": true, + "mtu": 512, + "ipam": { + "type": "host-local", + "subnet": "10.1.2.0/24", + "dataDir": "%s" + } + }`, ver, dataDir) + + const ( + containerWithTbfIFName = "ptp0" + containerWithoutTbfIFName = "ptp1" + ) + + containerWithTbfNS, err = testutils.NewNS() + Expect(err).NotTo(HaveOccurred()) + + containerWithoutTbfNS, err = testutils.NewNS() + Expect(err).NotTo(HaveOccurred()) + + By("create two containers, and use the bandwidth plugin on one of them") + Expect(hostNs.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + containerWithTbfRes, _, err = testutils.CmdAdd(containerWithTbfNS.Path(), "dummy", containerWithTbfIFName, []byte(ptpConf), func() error { + r, err := invoke.DelegateAdd(context.TODO(), "ptp", []byte(ptpConf), nil) + Expect(err).NotTo(HaveOccurred()) + Expect(r.Print()).To(Succeed()) + + return err + }) + Expect(err).NotTo(HaveOccurred()) + + containerWithoutTbfRes, _, err = testutils.CmdAdd(containerWithoutTbfNS.Path(), "dummy2", containerWithoutTbfIFName, []byte(ptpConf), func() error { + r, err := invoke.DelegateAdd(context.TODO(), "ptp", []byte(ptpConf), nil) + Expect(err).NotTo(HaveOccurred()) + Expect(r.Print()).To(Succeed()) + + return err + }) + Expect(err).NotTo(HaveOccurred()) + + containerWithTbfResult, err := types100.GetResult(containerWithTbfRes) + Expect(err).NotTo(HaveOccurred()) + + tbfPluginConf := &PluginConf{} + err = json.Unmarshal([]byte(ptpConf), &tbfPluginConf) + Expect(err).NotTo(HaveOccurred()) + + tbfPluginConf.RuntimeConfig.Bandwidth = &BandwidthEntry{ + IngressBurst: burstInBits, + IngressRate: rateInBits, + EgressBurst: burstInBits, + EgressRate: rateInBits, + } + tbfPluginConf.Type = "bandwidth" + _, newConfBytes, err := buildOneConfig("myBWnet", ver, tbfPluginConf, containerWithTbfResult) + Expect(err).NotTo(HaveOccurred()) + + args := &skel.CmdArgs{ + ContainerID: "dummy3", + Netns: containerWithTbfNS.Path(), + IfName: containerWithTbfIFName, + StdinData: newConfBytes, + } + + result, out, err := testutils.CmdAdd(containerWithTbfNS.Path(), args.ContainerID, "", newConfBytes, func() error { return cmdAdd(args) }) + Expect(err).NotTo(HaveOccurred(), string(out)) + + if testutils.SpecVersionHasCHECK(ver) { + // Do CNI Check + checkConf := &PluginConf{} + err = json.Unmarshal([]byte(ptpConf), &checkConf) + Expect(err).NotTo(HaveOccurred()) + + checkConf.RuntimeConfig.Bandwidth = &BandwidthEntry{ + IngressBurst: burstInBits, + IngressRate: rateInBits, + EgressBurst: burstInBits, + EgressRate: rateInBits, + } + checkConf.Type = "bandwidth" + + _, newCheckBytes, err := buildOneConfig("myBWnet", ver, checkConf, result) + Expect(err).NotTo(HaveOccurred()) + + args = &skel.CmdArgs{ + ContainerID: "dummy3", + Netns: containerWithTbfNS.Path(), + IfName: containerWithTbfIFName, + StdinData: newCheckBytes, + } + + err = testutils.CmdCheck(containerWithTbfNS.Path(), args.ContainerID, "", newCheckBytes, func() error { return cmdCheck(args) }) + Expect(err).NotTo(HaveOccurred()) + } + + return nil + })).To(Succeed()) + + By("starting a tcp server on both containers") + portServerWithTbf, echoServerWithTbf, err = startEchoServerInNamespace(containerWithTbfNS) + Expect(err).NotTo(HaveOccurred()) + portServerWithoutTbf, echoServerWithoutTbf, err = startEchoServerInNamespace(containerWithoutTbfNS) + Expect(err).NotTo(HaveOccurred()) + }) + + AfterEach(func() { + Expect(os.RemoveAll(dataDir)).To(Succeed()) + + Expect(containerWithTbfNS.Close()).To(Succeed()) + Expect(testutils.UnmountNS(containerWithTbfNS)).To(Succeed()) + Expect(containerWithoutTbfNS.Close()).To(Succeed()) + Expect(testutils.UnmountNS(containerWithoutTbfNS)).To(Succeed()) + + if echoServerWithoutTbf != nil { + echoServerWithoutTbf.Kill() + } + if echoServerWithTbf != nil { + echoServerWithTbf.Kill() + } + }) + + Measure("limits ingress traffic on veth device", func(b Benchmarker) { + var runtimeWithLimit time.Duration + var runtimeWithoutLimit time.Duration + + By("gather timing statistics about both containers") + By("sending tcp traffic to the container that has traffic shaped", func() { + runtimeWithLimit = b.Time("with tbf", func() { + result, err := types100.GetResult(containerWithTbfRes) + Expect(err).NotTo(HaveOccurred()) + + makeTcpClientInNS(hostNs.Path(), result.IPs[0].Address.IP.String(), portServerWithTbf, packetInBytes) + }) + }) + + By("sending tcp traffic to the container that does not have traffic shaped", func() { + runtimeWithoutLimit = b.Time("without tbf", func() { + result, err := types100.GetResult(containerWithoutTbfRes) + Expect(err).NotTo(HaveOccurred()) + + makeTcpClientInNS(hostNs.Path(), result.IPs[0].Address.IP.String(), portServerWithoutTbf, packetInBytes) + }) + }) + + Expect(runtimeWithLimit).To(BeNumerically(">", runtimeWithoutLimit+1000*time.Millisecond)) + }, 1) }) - - It("Works with a Veth pair using runtime config", func() { - conf := `{ - "cniVersion": "0.3.0", - "name": "cni-plugin-bandwidth-test", - "type": "bandwidth", - "runtimeConfig": { - "bandWidth": { - "ingressRate": 8, - "ingressBurst": 8, - "egressRate": 16, - "egressBurst": 9 - } - }, - "prevResult": { - "interfaces": [ - { - "name": "%s", - "sandbox": "" - }, - { - "name": "%s", - "sandbox": "%s" - } - ], - "ips": [ - { - "version": "4", - "address": "%s/24", - "gateway": "10.0.0.1", - "interface": 1 - } - ], - "routes": [] } -}` - - conf = fmt.Sprintf(conf, hostIfname, containerIfname, containerNs.Path(), containerIP.String()) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: containerNs.Path(), - IfName: containerIfname, - StdinData: []byte(conf), - } - - Expect(hostNs.Do(func(netNS ns.NetNS) error { - defer GinkgoRecover() - r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) - Expect(err).NotTo(HaveOccurred(), string(out)) - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(result.Interfaces).To(HaveLen(3)) - Expect(result.Interfaces[2].Name).To(Equal(ifbDeviceName)) - Expect(result.Interfaces[2].Sandbox).To(Equal("")) - - ifbLink, err := netlink.LinkByName(ifbDeviceName) - Expect(err).NotTo(HaveOccurred()) - Expect(ifbLink.Attrs().MTU).To(Equal(hostIfaceMTU)) - - qdiscs, err := netlink.QdiscList(ifbLink) - Expect(err).NotTo(HaveOccurred()) - - Expect(qdiscs).To(HaveLen(1)) - Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index)) - - Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Tbf{})) - Expect(qdiscs[0].(*netlink.Tbf).Rate).To(Equal(uint64(2))) - Expect(qdiscs[0].(*netlink.Tbf).Limit).To(Equal(uint32(1))) - - hostVethLink, err := netlink.LinkByName(hostIfname) - Expect(err).NotTo(HaveOccurred()) - - qdiscFilters, err := netlink.FilterList(hostVethLink, netlink.MakeHandle(0xffff, 0)) - Expect(err).NotTo(HaveOccurred()) - - Expect(qdiscFilters).To(HaveLen(1)) - Expect(qdiscFilters[0].(*netlink.U32).Actions[0].(*netlink.MirredAction).Ifindex).To(Equal(ifbLink.Attrs().Index)) - - return nil - })).To(Succeed()) - - Expect(hostNs.Do(func(n ns.NetNS) error { - defer GinkgoRecover() - - ifbLink, err := netlink.LinkByName(hostIfname) - Expect(err).NotTo(HaveOccurred()) - - qdiscs, err := netlink.QdiscList(ifbLink) - Expect(err).NotTo(HaveOccurred()) - - Expect(qdiscs).To(HaveLen(2)) - Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index)) - - Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Tbf{})) - Expect(qdiscs[0].(*netlink.Tbf).Rate).To(Equal(uint64(1))) - Expect(qdiscs[0].(*netlink.Tbf).Limit).To(Equal(uint32(1))) - return nil - })).To(Succeed()) - - }) - - It("Should apply static config when both static config and runtime config exist", func() { - conf := `{ - "cniVersion": "0.3.0", - "name": "cni-plugin-bandwidth-test", - "type": "bandwidth", - "ingressRate": 0, - "ingressBurst": 123, - "egressRate": 123, - "egressBurst": 123, - "runtimeConfig": { - "bandWidth": { - "ingressRate": 8, - "ingressBurst": 8, - "egressRate": 16, - "egressBurst": 9 - } - }, - "prevResult": { - "interfaces": [ - { - "name": "%s", - "sandbox": "" - }, - { - "name": "%s", - "sandbox": "%s" - } - ], - "ips": [ - { - "version": "4", - "address": "%s/24", - "gateway": "10.0.0.1", - "interface": 1 - } - ], - "routes": [] - } -}` - - conf = fmt.Sprintf(conf, hostIfname, containerIfname, containerNs.Path(), containerIP.String()) - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: containerNs.Path(), - IfName: "eth0", - StdinData: []byte(conf), - } - - Expect(hostNs.Do(func(netNS ns.NetNS) error { - defer GinkgoRecover() - - _, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) - Expect(err).To(MatchError("if burst is set, rate must also be set")) - return nil - })).To(Succeed()) - }) - }) - - Describe("cmdDEL", func() { - It("Works with a Veth pair using 0.3.0 config", func() { - conf := `{ - "cniVersion": "0.3.0", - "name": "cni-plugin-bandwidth-test", - "type": "bandwidth", - "ingressRate": 8, - "ingressBurst": 8, - "egressRate": 9, - "egressBurst": 9, - "prevResult": { - "interfaces": [ - { - "name": "%s", - "sandbox": "" - }, - { - "name": "%s", - "sandbox": "%s" - } - ], - "ips": [ - { - "version": "4", - "address": "%s/24", - "gateway": "10.0.0.1", - "interface": 1 - } - ], - "routes": [] - } -}` - - conf = fmt.Sprintf(conf, hostIfname, containerIfname, containerNs.Path(), containerIP.String()) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: containerNs.Path(), - IfName: containerIfname, - StdinData: []byte(conf), - } - - Expect(hostNs.Do(func(netNS ns.NetNS) error { - defer GinkgoRecover() - _, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) - Expect(err).NotTo(HaveOccurred(), string(out)) - - err = testutils.CmdDel(containerNs.Path(), args.ContainerID, "", func() error { return cmdDel(args) }) - Expect(err).NotTo(HaveOccurred(), string(out)) - - _, err = netlink.LinkByName(ifbDeviceName) - Expect(err).To(HaveOccurred()) - - return nil - })).To(Succeed()) - - }) - }) Describe("Validating input", func() { It("Should allow only 4GB burst rate", func() { @@ -635,570 +1068,4 @@ var _ = Describe("bandwidth test", func() { Expect(err).NotTo(HaveOccurred()) }) }) - - Describe("Getting the host interface which plugin should work on from veth peer of container interface", func() { - It("Should work with multiple host veth interfaces", func() { - conf := `{ - "cniVersion": "0.4.0", - "name": "cni-plugin-bandwidth-test", - "type": "bandwidth", - "ingressRate": 8, - "ingressBurst": 8, - "egressRate": 16, - "egressBurst": 8, - "prevResult": { - "interfaces": [ - { - "name": "%s", - "sandbox": "" - }, - { - "name": "%s", - "sandbox": "" - }, - { - "name": "%s", - "sandbox": "" - }, - { - "name": "%s", - "sandbox": "%s" - } - ], - "ips": [ - { - "version": "4", - "address": "%s/24", - "gateway": "10.0.0.1", - "interface": 1 - } - ], - "routes": [] - } -}` - - // create veth peer in host ns - vethName, peerName := "host-veth-peer1", "host-veth-peer2" - createVethInOneNs(hostNs.Path(), vethName, peerName) - - conf = fmt.Sprintf(conf, vethName, peerName, hostIfname, containerIfname, containerNs.Path(), containerIP.String()) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: containerNs.Path(), - IfName: containerIfname, - StdinData: []byte(conf), - } - - Expect(hostNs.Do(func(netNS ns.NetNS) error { - defer GinkgoRecover() - r, out, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) - Expect(err).NotTo(HaveOccurred(), string(out)) - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(result.Interfaces).To(HaveLen(5)) - Expect(result.Interfaces[4].Name).To(Equal(ifbDeviceName)) - Expect(result.Interfaces[4].Sandbox).To(Equal("")) - - ifbLink, err := netlink.LinkByName(ifbDeviceName) - Expect(err).NotTo(HaveOccurred()) - Expect(ifbLink.Attrs().MTU).To(Equal(hostIfaceMTU)) - - qdiscs, err := netlink.QdiscList(ifbLink) - Expect(err).NotTo(HaveOccurred()) - - Expect(qdiscs).To(HaveLen(1)) - Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index)) - - Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Tbf{})) - Expect(qdiscs[0].(*netlink.Tbf).Rate).To(Equal(uint64(2))) - Expect(qdiscs[0].(*netlink.Tbf).Limit).To(Equal(uint32(1))) - - hostVethLink, err := netlink.LinkByName(hostIfname) - Expect(err).NotTo(HaveOccurred()) - - qdiscFilters, err := netlink.FilterList(hostVethLink, netlink.MakeHandle(0xffff, 0)) - Expect(err).NotTo(HaveOccurred()) - - Expect(qdiscFilters).To(HaveLen(1)) - Expect(qdiscFilters[0].(*netlink.U32).Actions[0].(*netlink.MirredAction).Ifindex).To(Equal(ifbLink.Attrs().Index)) - - return nil - })).To(Succeed()) - - Expect(hostNs.Do(func(n ns.NetNS) error { - defer GinkgoRecover() - - ifbLink, err := netlink.LinkByName(hostIfname) - Expect(err).NotTo(HaveOccurred()) - - qdiscs, err := netlink.QdiscList(ifbLink) - Expect(err).NotTo(HaveOccurred()) - - Expect(qdiscs).To(HaveLen(2)) - Expect(qdiscs[0].Attrs().LinkIndex).To(Equal(ifbLink.Attrs().Index)) - - Expect(qdiscs[0]).To(BeAssignableToTypeOf(&netlink.Tbf{})) - Expect(qdiscs[0].(*netlink.Tbf).Rate).To(Equal(uint64(1))) - Expect(qdiscs[0].(*netlink.Tbf).Limit).To(Equal(uint32(1))) - return nil - })).To(Succeed()) - - }) - - It("Should fail when container interface has no veth peer", func() { - conf := `{ - "cniVersion": "0.4.0", - "name": "cni-plugin-bandwidth-test", - "type": "bandwidth", - "ingressRate": 8, - "ingressBurst": 8, - "egressRate": 16, - "egressBurst": 8, - "prevResult": { - "interfaces": [ - { - "name": "%s", - "sandbox": "" - }, - { - "name": "%s", - "sandbox": "%s" - } - ], - "ips": [ - { - "version": "4", - "address": "%s/24", - "gateway": "10.0.0.1", - "interface": 1 - } - ], - "routes": [] - } -}` - - // create a macvlan device to be container interface - macvlanContainerIfname := "container-macv" - createMacvlan(containerNs.Path(), containerIfname, macvlanContainerIfname) - - conf = fmt.Sprintf(conf, hostIfname, macvlanContainerIfname, containerNs.Path(), containerIP.String()) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: containerNs.Path(), - IfName: macvlanContainerIfname, - StdinData: []byte(conf), - } - - Expect(hostNs.Do(func(netNS ns.NetNS) error { - defer GinkgoRecover() - - _, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) - Expect(err).To(HaveOccurred()) - - return nil - })).To(Succeed()) - }) - - It("Should fail when preResult has no interfaces", func() { - conf := `{ - "cniVersion": "0.4.0", - "name": "cni-plugin-bandwidth-test", - "type": "bandwidth", - "ingressRate": 8, - "ingressBurst": 8, - "egressRate": 16, - "egressBurst": 8, - "prevResult": { - "interfaces": [], - "ips": [], - "routes": [] - } -}` - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: containerNs.Path(), - IfName: "eth0", - StdinData: []byte(conf), - } - - Expect(hostNs.Do(func(netNS ns.NetNS) error { - defer GinkgoRecover() - - _, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) - Expect(err).To(HaveOccurred()) - - return nil - })).To(Succeed()) - }) - - It("Should fail when veth peer of container interface does not match any of host interfaces in preResult", func() { - conf := `{ - "cniVersion": "0.4.0", - "name": "cni-plugin-bandwidth-test", - "type": "bandwidth", - "ingressRate": 8, - "ingressBurst": 8, - "egressRate": 16, - "egressBurst": 8, - "prevResult": { - "interfaces": [ - { - "name": "%s", - "sandbox": "" - }, - { - "name": "%s", - "sandbox": "%s" - } - ], - "ips": [ - { - "version": "4", - "address": "%s/24", - "gateway": "10.0.0.1", - "interface": 1 - } - ], - "routes": [] - } -}` - - // fake a non-exist host interface name - fakeHostIfname := fmt.Sprintf("%s-fake", hostIfname) - - conf = fmt.Sprintf(conf, fakeHostIfname, containerIfname, containerNs.Path(), containerIP.String()) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: containerNs.Path(), - IfName: containerIfname, - StdinData: []byte(conf), - } - - Expect(hostNs.Do(func(netNS ns.NetNS) error { - defer GinkgoRecover() - - _, _, err := testutils.CmdAdd(containerNs.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) - Expect(err).To(HaveOccurred()) - - return nil - })).To(Succeed()) - }) - }) - - Context("when chaining bandwidth plugin with PTP using 0.3.0 config", func() { - var ptpConf string - var rateInBits uint64 - var burstInBits uint64 - var packetInBytes int - var containerWithoutTbfNS ns.NetNS - var containerWithTbfNS ns.NetNS - var portServerWithTbf int - var portServerWithoutTbf int - - var containerWithTbfRes types.Result - var containerWithoutTbfRes types.Result - var echoServerWithTbf *gexec.Session - var echoServerWithoutTbf *gexec.Session - - BeforeEach(func() { - rateInBytes := 1000 - rateInBits = uint64(rateInBytes * 8) - burstInBits = rateInBits * 2 - packetInBytes = rateInBytes * 25 - - ptpConf = `{ - "cniVersion": "0.3.0", - "name": "mynet", - "type": "ptp", - "ipMasq": true, - "mtu": 512, - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24" - } -}` - - containerWithTbfIFName := "ptp0" - containerWithoutTbfIFName := "ptp1" - - var err error - containerWithTbfNS, err = testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - - containerWithoutTbfNS, err = testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - - By("create two containers, and use the bandwidth plugin on one of them") - Expect(hostNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - containerWithTbfRes, _, err = testutils.CmdAdd(containerWithTbfNS.Path(), "dummy", containerWithTbfIFName, []byte(ptpConf), func() error { - r, err := invoke.DelegateAdd(context.TODO(), "ptp", []byte(ptpConf), nil) - Expect(err).NotTo(HaveOccurred()) - Expect(r.Print()).To(Succeed()) - - return err - }) - Expect(err).NotTo(HaveOccurred()) - - containerWithoutTbfRes, _, err = testutils.CmdAdd(containerWithoutTbfNS.Path(), "dummy2", containerWithoutTbfIFName, []byte(ptpConf), func() error { - r, err := invoke.DelegateAdd(context.TODO(), "ptp", []byte(ptpConf), nil) - Expect(err).NotTo(HaveOccurred()) - Expect(r.Print()).To(Succeed()) - - return err - }) - Expect(err).NotTo(HaveOccurred()) - - containerWithTbfResult, err := current.GetResult(containerWithTbfRes) - Expect(err).NotTo(HaveOccurred()) - tbfPluginConf := PluginConf{} - tbfPluginConf.RuntimeConfig.Bandwidth = &BandwidthEntry{ - IngressBurst: burstInBits, - IngressRate: rateInBits, - EgressBurst: burstInBits, - EgressRate: rateInBits, - } - tbfPluginConf.Name = "mynet" - tbfPluginConf.CNIVersion = "0.3.0" - tbfPluginConf.Type = "bandwidth" - tbfPluginConf.RawPrevResult = map[string]interface{}{ - "ips": containerWithTbfResult.IPs, - "interfaces": containerWithTbfResult.Interfaces, - } - - tbfPluginConf.PrevResult = ¤t.Result{ - IPs: containerWithTbfResult.IPs, - Interfaces: containerWithTbfResult.Interfaces, - } - conf, err := json.Marshal(tbfPluginConf) - Expect(err).NotTo(HaveOccurred()) - - args := &skel.CmdArgs{ - ContainerID: "dummy3", - Netns: containerWithTbfNS.Path(), - IfName: containerWithTbfIFName, - StdinData: []byte(conf), - } - - _, out, err := testutils.CmdAdd(containerWithTbfNS.Path(), args.ContainerID, "", []byte(conf), func() error { return cmdAdd(args) }) - Expect(err).NotTo(HaveOccurred(), string(out)) - - return nil - })).To(Succeed()) - - By("starting a tcp server on both containers") - portServerWithTbf, echoServerWithTbf, err = startEchoServerInNamespace(containerWithTbfNS) - Expect(err).NotTo(HaveOccurred()) - portServerWithoutTbf, echoServerWithoutTbf, err = startEchoServerInNamespace(containerWithoutTbfNS) - Expect(err).NotTo(HaveOccurred()) - }) - - AfterEach(func() { - containerWithTbfNS.Close() - containerWithoutTbfNS.Close() - if echoServerWithoutTbf != nil { - echoServerWithoutTbf.Kill() - } - if echoServerWithTbf != nil { - echoServerWithTbf.Kill() - } - }) - - Measure("limits ingress traffic on veth device", func(b Benchmarker) { - var runtimeWithLimit time.Duration - var runtimeWithoutLimit time.Duration - - By("gather timing statistics about both containers") - By("sending tcp traffic to the container that has traffic shaped", func() { - runtimeWithLimit = b.Time("with tbf", func() { - result, err := current.GetResult(containerWithTbfRes) - Expect(err).NotTo(HaveOccurred()) - - makeTcpClientInNS(hostNs.Path(), result.IPs[0].Address.IP.String(), portServerWithTbf, packetInBytes) - }) - }) - - By("sending tcp traffic to the container that does not have traffic shaped", func() { - runtimeWithoutLimit = b.Time("without tbf", func() { - result, err := current.GetResult(containerWithoutTbfRes) - Expect(err).NotTo(HaveOccurred()) - - makeTcpClientInNS(hostNs.Path(), result.IPs[0].Address.IP.String(), portServerWithoutTbf, packetInBytes) - }) - }) - - Expect(runtimeWithLimit).To(BeNumerically(">", runtimeWithoutLimit+1000*time.Millisecond)) - }, 1) - }) - - Context("when chaining bandwidth plugin with PTP using 0.4.0 config", func() { - var ptpConf string - var rateInBits uint64 - var burstInBits uint64 - var packetInBytes int - var containerWithoutTbfNS ns.NetNS - var containerWithTbfNS ns.NetNS - var portServerWithTbf int - var portServerWithoutTbf int - - var containerWithTbfRes types.Result - var containerWithoutTbfRes types.Result - var echoServerWithTbf *gexec.Session - var echoServerWithoutTbf *gexec.Session - - BeforeEach(func() { - rateInBytes := 1000 - rateInBits = uint64(rateInBytes * 8) - burstInBits = rateInBits * 2 - packetInBytes = rateInBytes * 25 - - ptpConf = `{ - "cniVersion": "0.4.0", - "name": "myBWnet", - "type": "ptp", - "ipMasq": true, - "mtu": 512, - "ipam": { - "type": "host-local", - "subnet": "10.1.2.0/24" - } -}` - - containerWithTbfIFName := "ptp0" - containerWithoutTbfIFName := "ptp1" - - var err error - containerWithTbfNS, err = testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - - containerWithoutTbfNS, err = testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - - By("create two containers, and use the bandwidth plugin on one of them") - Expect(hostNs.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - containerWithTbfRes, _, err = testutils.CmdAdd(containerWithTbfNS.Path(), "dummy", containerWithTbfIFName, []byte(ptpConf), func() error { - r, err := invoke.DelegateAdd(context.TODO(), "ptp", []byte(ptpConf), nil) - Expect(err).NotTo(HaveOccurred()) - Expect(r.Print()).To(Succeed()) - - return err - }) - Expect(err).NotTo(HaveOccurred()) - - containerWithoutTbfRes, _, err = testutils.CmdAdd(containerWithoutTbfNS.Path(), "dummy2", containerWithoutTbfIFName, []byte(ptpConf), func() error { - r, err := invoke.DelegateAdd(context.TODO(), "ptp", []byte(ptpConf), nil) - Expect(err).NotTo(HaveOccurred()) - Expect(r.Print()).To(Succeed()) - - return err - }) - Expect(err).NotTo(HaveOccurred()) - - containerWithTbfResult, err := current.GetResult(containerWithTbfRes) - Expect(err).NotTo(HaveOccurred()) - - tbfPluginConf := &PluginConf{} - err = json.Unmarshal([]byte(ptpConf), &tbfPluginConf) - Expect(err).NotTo(HaveOccurred()) - - tbfPluginConf.RuntimeConfig.Bandwidth = &BandwidthEntry{ - IngressBurst: burstInBits, - IngressRate: rateInBits, - EgressBurst: burstInBits, - EgressRate: rateInBits, - } - tbfPluginConf.Type = "bandwidth" - cniVersion := "0.4.0" - _, newConfBytes, err := buildOneConfig("myBWnet", cniVersion, tbfPluginConf, containerWithTbfResult) - Expect(err).NotTo(HaveOccurred()) - - args := &skel.CmdArgs{ - ContainerID: "dummy3", - Netns: containerWithTbfNS.Path(), - IfName: containerWithTbfIFName, - StdinData: newConfBytes, - } - - result, out, err := testutils.CmdAdd(containerWithTbfNS.Path(), args.ContainerID, "", newConfBytes, func() error { return cmdAdd(args) }) - Expect(err).NotTo(HaveOccurred(), string(out)) - - // Do CNI Check - checkConf := &PluginConf{} - err = json.Unmarshal([]byte(ptpConf), &checkConf) - Expect(err).NotTo(HaveOccurred()) - - checkConf.RuntimeConfig.Bandwidth = &BandwidthEntry{ - IngressBurst: burstInBits, - IngressRate: rateInBits, - EgressBurst: burstInBits, - EgressRate: rateInBits, - } - checkConf.Type = "bandwidth" - - _, newCheckBytes, err := buildOneConfig("myBWnet", cniVersion, checkConf, result) - Expect(err).NotTo(HaveOccurred()) - - args = &skel.CmdArgs{ - ContainerID: "dummy3", - Netns: containerWithTbfNS.Path(), - IfName: containerWithTbfIFName, - StdinData: newCheckBytes, - } - - err = testutils.CmdCheck(containerWithTbfNS.Path(), args.ContainerID, "", newCheckBytes, func() error { return cmdCheck(args) }) - Expect(err).NotTo(HaveOccurred()) - - return nil - })).To(Succeed()) - - By("starting a tcp server on both containers") - portServerWithTbf, echoServerWithTbf, err = startEchoServerInNamespace(containerWithTbfNS) - Expect(err).NotTo(HaveOccurred()) - portServerWithoutTbf, echoServerWithoutTbf, err = startEchoServerInNamespace(containerWithoutTbfNS) - Expect(err).NotTo(HaveOccurred()) - }) - - AfterEach(func() { - containerWithTbfNS.Close() - containerWithoutTbfNS.Close() - if echoServerWithoutTbf != nil { - echoServerWithoutTbf.Kill() - } - if echoServerWithTbf != nil { - echoServerWithTbf.Kill() - } - }) - - Measure("limits ingress traffic on veth device", func(b Benchmarker) { - var runtimeWithLimit time.Duration - var runtimeWithoutLimit time.Duration - - By("gather timing statistics about both containers") - By("sending tcp traffic to the container that has traffic shaped", func() { - runtimeWithLimit = b.Time("with tbf", func() { - result, err := current.GetResult(containerWithTbfRes) - Expect(err).NotTo(HaveOccurred()) - - makeTcpClientInNS(hostNs.Path(), result.IPs[0].Address.IP.String(), portServerWithTbf, packetInBytes) - }) - }) - - By("sending tcp traffic to the container that does not have traffic shaped", func() { - runtimeWithoutLimit = b.Time("without tbf", func() { - result, err := current.GetResult(containerWithoutTbfRes) - Expect(err).NotTo(HaveOccurred()) - - makeTcpClientInNS(hostNs.Path(), result.IPs[0].Address.IP.String(), portServerWithoutTbf, packetInBytes) - }) - }) - - Expect(runtimeWithLimit).To(BeNumerically(">", runtimeWithoutLimit+1000*time.Millisecond)) - }, 1) - }) - }) diff --git a/plugins/meta/bandwidth/bandwidth_suite_test.go b/plugins/meta/bandwidth/bandwidth_suite_test.go index a0e34ac0..f9cf9e0a 100644 --- a/plugins/meta/bandwidth/bandwidth_suite_test.go +++ b/plugins/meta/bandwidth/bandwidth_suite_test.go @@ -106,7 +106,7 @@ func makeTcpClientInNS(netns string, address string, port int, numBytes int) { Expect(string(out)).To(Equal(message)) } -func createVeth(hostNamespace string, hostVethIfName string, containerNamespace string, containerVethIfName string, hostIP []byte, containerIP []byte, hostIfaceMTU int) { +func createVeth(hostNs ns.NetNS, hostVethIfName string, containerNs ns.NetNS, containerVethIfName string, hostIP []byte, containerIP []byte, hostIfaceMTU int) { vethDeviceRequest := &netlink.Veth{ LinkAttrs: netlink.LinkAttrs{ Name: hostVethIfName, @@ -116,10 +116,7 @@ func createVeth(hostNamespace string, hostVethIfName string, containerNamespace PeerName: containerVethIfName, } - hostNs, err := ns.GetNS(hostNamespace) - Expect(err).NotTo(HaveOccurred()) - - err = hostNs.Do(func(_ ns.NetNS) error { + err := hostNs.Do(func(_ ns.NetNS) error { if err := netlink.LinkAdd(vethDeviceRequest); err != nil { return fmt.Errorf("creating veth pair: %s", err) } @@ -129,11 +126,6 @@ func createVeth(hostNamespace string, hostVethIfName string, containerNamespace return fmt.Errorf("failed to find newly-created veth device %q: %v", containerVethIfName, err) } - containerNs, err := ns.GetNS(containerNamespace) - if err != nil { - return err - } - err = netlink.LinkSetNsFd(containerVeth, int(containerNs.Fd())) if err != nil { return fmt.Errorf("failed to move veth to container namespace: %s", err) @@ -169,8 +161,6 @@ func createVeth(hostNamespace string, hostVethIfName string, containerNamespace }) Expect(err).NotTo(HaveOccurred()) - containerNs, err := ns.GetNS(containerNamespace) - Expect(err).NotTo(HaveOccurred()) err = containerNs.Do(func(_ ns.NetNS) error { peerAddr := &net.IPNet{ IP: hostIP, @@ -203,7 +193,7 @@ func createVeth(hostNamespace string, hostVethIfName string, containerNamespace Expect(err).NotTo(HaveOccurred()) } -func createVethInOneNs(namespace, vethName, peerName string) { +func createVethInOneNs(netNS ns.NetNS, vethName, peerName string) { vethDeviceRequest := &netlink.Veth{ LinkAttrs: netlink.LinkAttrs{ Name: vethName, @@ -212,10 +202,7 @@ func createVethInOneNs(namespace, vethName, peerName string) { PeerName: peerName, } - netNS, err := ns.GetNS(namespace) - Expect(err).NotTo(HaveOccurred()) - - err = netNS.Do(func(_ ns.NetNS) error { + err := netNS.Do(func(_ ns.NetNS) error { if err := netlink.LinkAdd(vethDeviceRequest); err != nil { return fmt.Errorf("failed to create veth pair: %v", err) } @@ -229,11 +216,8 @@ func createVethInOneNs(namespace, vethName, peerName string) { Expect(err).NotTo(HaveOccurred()) } -func createMacvlan(namespace, master, macvlanName string) { - netNS, err := ns.GetNS(namespace) - Expect(err).NotTo(HaveOccurred()) - - err = netNS.Do(func(_ ns.NetNS) error { +func createMacvlan(netNS ns.NetNS, master, macvlanName string) { + err := netNS.Do(func(_ ns.NetNS) error { m, err := netlink.LinkByName(master) if err != nil { return fmt.Errorf("failed to lookup master %q: %v", master, err) diff --git a/plugins/meta/bandwidth/main.go b/plugins/meta/bandwidth/main.go index f113d363..68c64257 100644 --- a/plugins/meta/bandwidth/main.go +++ b/plugins/meta/bandwidth/main.go @@ -23,7 +23,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/plugins/pkg/ip" diff --git a/plugins/meta/firewall/firewall.go b/plugins/meta/firewall/firewall.go index 875943be..f4d0c639 100644 --- a/plugins/meta/firewall/firewall.go +++ b/plugins/meta/firewall/firewall.go @@ -24,7 +24,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" bv "github.com/containernetworking/plugins/pkg/utils/buildversion" @@ -130,7 +130,9 @@ func cmdAdd(args *skel.CmdArgs) error { } if result == nil { - result = ¤t.Result{} + result = ¤t.Result{ + CNIVersion: current.ImplementedSpecVersion, + } } return types.PrintResult(result, conf.CNIVersion) } diff --git a/plugins/meta/firewall/firewall_firewalld_test.go b/plugins/meta/firewall/firewall_firewalld_test.go index 68a00793..4c342893 100644 --- a/plugins/meta/firewall/firewall_firewalld_test.go +++ b/plugins/meta/firewall/firewall_firewalld_test.go @@ -24,7 +24,7 @@ import ( "github.com/containernetworking/cni/pkg/invoke" "github.com/containernetworking/cni/pkg/skel" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" @@ -34,30 +34,7 @@ import ( . "github.com/onsi/gomega" ) -const ( - confTmpl = `{ - "cniVersion": "0.3.1", - "name": "firewalld-test", - "type": "firewall", - "backend": "firewalld", - "zone": "trusted", - "prevResult": { - "cniVersion": "0.3.0", - "interfaces": [ - {"name": "%s", "sandbox": "%s"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.2/24", - "gateway": "10.0.0.1", - "interface": 0 - } - ] - } -}` - ifname = "eth0" -) +const ifname = "eth0" type fakeFirewalld struct { zone string @@ -125,6 +102,30 @@ func spawnSessionDbus(wg *sync.WaitGroup) (string, *exec.Cmd) { return busAddr, cmd } +func makeFirewalldConf(ver, ifname string, ns ns.NetNS) []byte { + return []byte(fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "firewalld-test", + "type": "firewall", + "backend": "firewalld", + "zone": "trusted", + "prevResult": { + "cniVersion": "%s", + "interfaces": [ + {"name": "%s", "sandbox": "%s"} + ], + "ips": [ + { + "version": "4", + "address": "10.0.0.2/24", + "gateway": "10.0.0.1", + "interface": 0 + } + ] + } + }`, ver, ver, ifname, ns.Path())) +} + var _ = Describe("firewalld test", func() { var ( targetNs ns.NetNS @@ -177,167 +178,119 @@ var _ = Describe("firewalld test", func() { Expect(err).NotTo(HaveOccurred()) wg.Wait() + + Expect(targetNs.Close()).To(Succeed()) + Expect(testutils.UnmountNS(targetNs)).To(Succeed()) }) - It("works with a 0.3.1 config", func() { - Expect(isFirewalldRunning()).To(BeTrue()) + // firewall plugin requires a prevResult and thus only supports 0.3.0 + // and later CNI versions + for _, ver := range []string{"0.3.0", "0.3.1", "0.4.0", "1.0.0"} { + // Redefine ver inside for scope so real value is picked up by each dynamically defined It() + // See Gingkgo's "Patterns for dynamically generating tests" documentation. + ver := ver - conf := fmt.Sprintf(confTmpl, ifname, targetNs.Path()) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNs.Path(), - IfName: ifname, - StdinData: []byte(conf), - } - _, _, err := testutils.CmdAdd(targetNs.Path(), args.ContainerID, ifname, []byte(conf), func() error { - return cmdAdd(args) + It(fmt.Sprintf("[%s] works with a config", ver), func() { + Expect(isFirewalldRunning()).To(BeTrue()) + + conf := makeFirewalldConf(ver, ifname, targetNs) + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNs.Path(), + IfName: ifname, + StdinData: []byte(conf), + } + _, _, err := testutils.CmdAdd(targetNs.Path(), args.ContainerID, ifname, []byte(conf), func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + Expect(fwd.zone).To(Equal("trusted")) + Expect(fwd.source).To(Equal("10.0.0.2/32")) + fwd.clear() + + err = testutils.CmdDel(targetNs.Path(), args.ContainerID, ifname, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + Expect(fwd.zone).To(Equal("trusted")) + Expect(fwd.source).To(Equal("10.0.0.2/32")) }) - Expect(err).NotTo(HaveOccurred()) - Expect(fwd.zone).To(Equal("trusted")) - Expect(fwd.source).To(Equal("10.0.0.2/32")) - fwd.clear() - err = testutils.CmdDel(targetNs.Path(), args.ContainerID, ifname, func() error { - return cmdDel(args) + It(fmt.Sprintf("[%s] defaults to the firewalld backend", ver), func() { + Expect(isFirewalldRunning()).To(BeTrue()) + + conf := makeFirewalldConf(ver, ifname, targetNs) + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNs.Path(), + IfName: ifname, + StdinData: []byte(conf), + } + _, _, err := testutils.CmdAdd(targetNs.Path(), args.ContainerID, ifname, []byte(conf), func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + Expect(fwd.zone).To(Equal("trusted")) + Expect(fwd.source).To(Equal("10.0.0.2/32")) }) - Expect(err).NotTo(HaveOccurred()) - Expect(fwd.zone).To(Equal("trusted")) - Expect(fwd.source).To(Equal("10.0.0.2/32")) - }) - It("defaults to the firewalld backend", func() { - conf := `{ - "cniVersion": "0.3.1", - "name": "firewalld-test", - "type": "firewall", - "zone": "trusted", - "prevResult": { - "cniVersion": "0.3.0", - "interfaces": [ - {"name": "eth0", "sandbox": "/foobar"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.2/24", - "gateway": "10.0.0.1", - "interface": 0 - } - ] - } - }` + It(fmt.Sprintf("[%s] passes through the prevResult", ver), func() { + Expect(isFirewalldRunning()).To(BeTrue()) - Expect(isFirewalldRunning()).To(BeTrue()) + conf := makeFirewalldConf(ver, ifname, targetNs) + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNs.Path(), + IfName: ifname, + StdinData: []byte(conf), + } + r, _, err := testutils.CmdAdd(targetNs.Path(), args.ContainerID, ifname, []byte(conf), func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNs.Path(), - IfName: ifname, - StdinData: []byte(conf), - } - _, _, err := testutils.CmdAdd(targetNs.Path(), args.ContainerID, ifname, []byte(conf), func() error { - return cmdAdd(args) + result, err := current.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + Expect(len(result.Interfaces)).To(Equal(1)) + Expect(result.Interfaces[0].Name).To(Equal("eth0")) + Expect(len(result.IPs)).To(Equal(1)) + Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) }) - Expect(err).NotTo(HaveOccurred()) - Expect(fwd.zone).To(Equal("trusted")) - Expect(fwd.source).To(Equal("10.0.0.2/32")) - }) - It("passes through the prevResult", func() { - conf := `{ - "cniVersion": "0.3.1", - "name": "firewalld-test", - "type": "firewall", - "zone": "trusted", - "prevResult": { - "cniVersion": "0.3.0", - "interfaces": [ - {"name": "eth0", "sandbox": "/foobar"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.2/24", - "gateway": "10.0.0.1", - "interface": 0 - } - ] - } - }` + It(fmt.Sprintf("[%s] works with Check", ver), func() { + Expect(isFirewalldRunning()).To(BeTrue()) - Expect(isFirewalldRunning()).To(BeTrue()) + conf := makeFirewalldConf(ver, ifname, targetNs) + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNs.Path(), + IfName: ifname, + StdinData: []byte(conf), + } + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + Expect(fwd.zone).To(Equal("trusted")) + Expect(fwd.source).To(Equal("10.0.0.2/32")) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNs.Path(), - IfName: ifname, - StdinData: []byte(conf), - } - r, _, err := testutils.CmdAdd(targetNs.Path(), args.ContainerID, ifname, []byte(conf), func() error { - return cmdAdd(args) + if testutils.SpecVersionHasCHECK(ver) { + _, err = current.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + err = testutils.CmdCheckWithArgs(args, func() error { + return cmdCheck(args) + }) + Expect(err).NotTo(HaveOccurred()) + } + + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + Expect(fwd.zone).To(Equal("trusted")) + Expect(fwd.source).To(Equal("10.0.0.2/32")) }) - Expect(err).NotTo(HaveOccurred()) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal("eth0")) - Expect(len(result.IPs)).To(Equal(1)) - Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) - }) - - It("works with a 0.4.0 config, including Check", func() { - Expect(isFirewalldRunning()).To(BeTrue()) - - conf := `{ - "cniVersion": "0.4.0", - "name": "firewalld-test", - "type": "firewall", - "backend": "firewalld", - "zone": "trusted", - "prevResult": { - "cniVersion": "0.4.0", - "interfaces": [ - {"name": "eth0", "sandbox": "/foobar"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.2/24", - "gateway": "10.0.0.1", - "interface": 0 - } - ] - } - }` - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNs.Path(), - IfName: ifname, - StdinData: []byte(conf), - } - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - Expect(fwd.zone).To(Equal("trusted")) - Expect(fwd.source).To(Equal("10.0.0.2/32")) - - _, err = current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - err = testutils.CmdCheckWithArgs(args, func() error { - return cmdCheck(args) - }) - Expect(err).NotTo(HaveOccurred()) - - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - Expect(fwd.zone).To(Equal("trusted")) - Expect(fwd.source).To(Equal("10.0.0.2/32")) - }) + } }) diff --git a/plugins/meta/firewall/firewall_iptables_test.go b/plugins/meta/firewall/firewall_iptables_test.go index ac474dc8..364cf4bb 100644 --- a/plugins/meta/firewall/firewall_iptables_test.go +++ b/plugins/meta/firewall/firewall_iptables_test.go @@ -21,7 +21,8 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/cni/pkg/types/040" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" @@ -165,35 +166,38 @@ func validateCleanedUp(bytes []byte) { } } +func makeIptablesConf(ver string) []byte { + return []byte(fmt.Sprintf(`{ + "name": "test", + "type": "firewall", + "backend": "iptables", + "ifName": "dummy0", + "cniVersion": "%s", + "prevResult": { + "cniVersion": "%s", + "interfaces": [ + {"name": "dummy0"} + ], + "ips": [ + { + "version": "4", + "address": "10.0.0.2/24", + "interface": 0 + }, + { + "version": "6", + "address": "2001:db8:1:2::1/64", + "interface": 0 + } + ] + } + }`, ver, ver)) +} + var _ = Describe("firewall plugin iptables backend", func() { var originalNS, targetNS ns.NetNS const IFNAME string = "dummy0" - fullConf := []byte(`{ - "name": "test", - "type": "firewall", - "backend": "iptables", - "ifName": "dummy0", - "cniVersion": "0.3.1", - "prevResult": { - "interfaces": [ - {"name": "dummy0"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.2/24", - "interface": 0 - }, - { - "version": "6", - "address": "2001:db8:1:2::1/64", - "interface": 0 - } - ] - } - }`) - BeforeEach(func() { // Create a new NetNS so we don't modify the host var err error @@ -224,296 +228,174 @@ var _ = Describe("firewall plugin iptables backend", func() { Expect(targetNS.Close()).To(Succeed()) }) - It("passes prevResult through unchanged", func() { - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: IFNAME, - StdinData: fullConf, - } + // firewall plugin requires a prevResult and thus only supports 0.3.0 + // and later CNI versions + for _, ver := range []string{"0.3.0", "0.3.1", "0.4.0", "1.0.0"} { + // Redefine ver inside for scope so real value is picked up by each dynamically defined It() + // See Gingkgo's "Patterns for dynamically generating tests" documentation. + ver := ver - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - r, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(2)) - Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) - Expect(result.IPs[1].Address.String()).To(Equal("2001:db8:1:2::1/64")) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("installs the right iptables rules on the host", func() { - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: IFNAME, - StdinData: fullConf, - } - - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - _, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - - validateFullRuleset(fullConf) - - // ensure creation is idempotent - _, _, err = testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("correctly handles a custom IptablesAdminChainName", func() { - conf := []byte(`{ - "name": "test", - "type": "firewall", - "backend": "iptables", - "ifName": "dummy0", - "cniVersion": "0.3.1", - "iptablesAdminChainName": "CNI-foobar", - "prevResult": { - "interfaces": [ - {"name": "dummy0"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.2/24", - "interface": 0 - }, - { - "version": "6", - "address": "2001:db8:1:2::1/64", - "interface": 0 + It(fmt.Sprintf("[%s] passes prevResult through unchanged", ver), func() { + fullConf := makeIptablesConf(ver) + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: fullConf, } - ] - } -}`) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: IFNAME, - StdinData: conf, - } + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + r, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) - _, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, conf, func() error { - return cmdAdd(args) + result, err := current.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + Expect(len(result.Interfaces)).To(Equal(1)) + Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) + Expect(len(result.IPs)).To(Equal(2)) + Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) + Expect(result.IPs[1].Address.String()).To(Equal("2001:db8:1:2::1/64")) + return nil }) Expect(err).NotTo(HaveOccurred()) + }) - var ipt *iptables.IPTables - for _, proto := range []iptables.Protocol{iptables.ProtocolIPv4, iptables.ProtocolIPv6} { - ipt, err = iptables.NewWithProtocol(proto) + It(fmt.Sprintf("[%s] installs the right iptables rules on the host", ver), func() { + fullConf := makeIptablesConf(ver) + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: fullConf, + } + + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + _, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error { + return cmdAdd(args) + }) Expect(err).NotTo(HaveOccurred()) - // Ensure custom admin chain name - chains, err := ipt.ListChains("filter") + validateFullRuleset(fullConf) + + // ensure creation is idempotent + _, _, err = testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error { + return cmdAdd(args) + }) Expect(err).NotTo(HaveOccurred()) - var foundAdmin bool - for _, ch := range chains { - if ch == "CNI-foobar" { - foundAdmin = true + + return nil + }) + Expect(err).NotTo(HaveOccurred()) + }) + + It(fmt.Sprintf("[%s] correctly handles a custom IptablesAdminChainName", ver), func() { + conf := []byte(fmt.Sprintf(`{ + "name": "test", + "type": "firewall", + "backend": "iptables", + "ifName": "dummy0", + "cniVersion": "%s", + "iptablesAdminChainName": "CNI-foobar", + "prevResult": { + "cniVersion": "%s", + "interfaces": [ + {"name": "dummy0"} + ], + "ips": [ + { + "version": "4", + "address": "10.0.0.2/24", + "interface": 0 + }, + { + "version": "6", + "address": "2001:db8:1:2::1/64", + "interface": 0 + } + ] + } + }`, ver, ver)) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: conf, + } + + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + _, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, conf, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) + + var ipt *iptables.IPTables + for _, proto := range []iptables.Protocol{iptables.ProtocolIPv4, iptables.ProtocolIPv6} { + ipt, err = iptables.NewWithProtocol(proto) + Expect(err).NotTo(HaveOccurred()) + + // Ensure custom admin chain name + chains, err := ipt.ListChains("filter") + Expect(err).NotTo(HaveOccurred()) + var foundAdmin bool + for _, ch := range chains { + if ch == "CNI-foobar" { + foundAdmin = true + } } + Expect(foundAdmin).To(Equal(true)) } - Expect(foundAdmin).To(Equal(true)) + + return nil + }) + Expect(err).NotTo(HaveOccurred()) + }) + + It(fmt.Sprintf("[%s] installs iptables rules, checks rules, then cleans up on delete", ver), func() { + fullConf := makeIptablesConf(ver) + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: fullConf, } - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - It("cleans up on delete", func() { - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: IFNAME, - StdinData: fullConf, - } + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + _, err = types040.GetResult(r) + Expect(err).NotTo(HaveOccurred()) - _, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - validateFullRuleset(fullConf) - - err = testutils.CmdDel(targetNS.Path(), args.ContainerID, IFNAME, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - validateCleanedUp(fullConf) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("installs the right iptables rules on the host v4.0.x and check is successful", func() { - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: IFNAME, - StdinData: fullConf, - } - - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - _, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - - validateFullRuleset(fullConf) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("cleans up on delete v4.0.x", func() { - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: IFNAME, - StdinData: fullConf, - } - - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - _, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - validateFullRuleset(fullConf) - - err = testutils.CmdDel(targetNS.Path(), args.ContainerID, IFNAME, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - validateCleanedUp(fullConf) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) -}) - -var _ = Describe("firewall plugin iptables backend v0.4.x", func() { - var originalNS, targetNS ns.NetNS - const IFNAME string = "dummy0" - - fullConf := []byte(`{ - "name": "test", - "type": "firewall", - "backend": "iptables", - "ifName": "dummy0", - "cniVersion": "0.4.0", - "prevResult": { - "interfaces": [ - {"name": "dummy0"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.2/24", - "interface": 0 - }, - { - "version": "6", - "address": "2001:db8:1:2::1/64", - "interface": 0 + if testutils.SpecVersionHasCHECK(ver) { + err = testutils.CmdCheckWithArgs(args, func() error { + return cmdCheck(args) + }) + Expect(err).NotTo(HaveOccurred()) + validateFullRuleset(fullConf) } - ] - } - }`) - BeforeEach(func() { - // Create a new NetNS so we don't modify the host - var err error - originalNS, err = testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - err = netlink.LinkAdd(&netlink.Dummy{ - LinkAttrs: netlink.LinkAttrs{ - Name: IFNAME, - }, + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + validateCleanedUp(fullConf) + return nil }) Expect(err).NotTo(HaveOccurred()) - _, err = netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - return nil }) - Expect(err).NotTo(HaveOccurred()) - - targetNS, err = testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - }) - - AfterEach(func() { - Expect(originalNS.Close()).To(Succeed()) - Expect(targetNS.Close()).To(Succeed()) - }) - - It("installs iptables rules, Check rules then cleans up on delete using v4.0.x", func() { - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNS.Path(), - IfName: IFNAME, - StdinData: fullConf, - } - - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - - _, err = current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - err = testutils.CmdCheckWithArgs(args, func() error { - return cmdCheck(args) - }) - Expect(err).NotTo(HaveOccurred()) - validateFullRuleset(fullConf) - - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - validateCleanedUp(fullConf) - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) + } }) diff --git a/plugins/meta/firewall/firewalld.go b/plugins/meta/firewall/firewalld.go index ac9328c3..fd8a6547 100644 --- a/plugins/meta/firewall/firewalld.go +++ b/plugins/meta/firewall/firewalld.go @@ -18,7 +18,7 @@ import ( "fmt" "strings" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/godbus/dbus" ) diff --git a/plugins/meta/firewall/iptables.go b/plugins/meta/firewall/iptables.go index ba0c9836..dd5fdb38 100644 --- a/plugins/meta/firewall/iptables.go +++ b/plugins/meta/firewall/iptables.go @@ -21,7 +21,7 @@ import ( "fmt" "net" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/utils" "github.com/coreos/go-iptables/iptables" ) diff --git a/plugins/meta/flannel/flannel.go b/plugins/meta/flannel/flannel.go index 5d07efaf..92432f05 100644 --- a/plugins/meta/flannel/flannel.go +++ b/plugins/meta/flannel/flannel.go @@ -189,7 +189,7 @@ func consumeScratchNetConf(containerID, dataDir string) (func(error), []byte, er return cleanup, netConfBytes, err } -func delegateAdd(cid, dataDir string, netconf map[string]interface{}) error { +func delegateAdd(cid, dataDir, cniVersion string, netconf map[string]interface{}) error { netconfBytes, err := json.Marshal(netconf) if err != nil { return fmt.Errorf("error serializing delegate netconf: %v", err) @@ -205,7 +205,7 @@ func delegateAdd(cid, dataDir string, netconf map[string]interface{}) error { return err } - return result.Print() + return types.PrintResult(result, cniVersion) } func hasKey(m map[string]interface{}, k string) bool { @@ -247,7 +247,10 @@ func cmdAdd(args *skel.CmdArgs) error { n.Delegate["runtimeConfig"] = n.RuntimeConfig } - return doCmdAdd(args, n, fenv) + // Delegate CNI config version must match flannel plugin config version + n.Delegate["cniVersion"] = n.CNIVersion + + return doCmdAdd(args, n.CNIVersion, n, fenv) } func cmdDel(args *skel.CmdArgs) error { diff --git a/plugins/meta/flannel/flannel_linux.go b/plugins/meta/flannel/flannel_linux.go index 7d1a806e..8e8f645d 100644 --- a/plugins/meta/flannel/flannel_linux.go +++ b/plugins/meta/flannel/flannel_linux.go @@ -72,7 +72,7 @@ func getDelegateIPAM(n *NetConf, fenv *subnetEnv) (map[string]interface{}, error return ipam, nil } -func doCmdAdd(args *skel.CmdArgs, n *NetConf, fenv *subnetEnv) error { +func doCmdAdd(args *skel.CmdArgs, cniVersion string, n *NetConf, fenv *subnetEnv) error { n.Delegate["name"] = n.Name if !hasKey(n.Delegate, "type") { @@ -105,7 +105,7 @@ func doCmdAdd(args *skel.CmdArgs, n *NetConf, fenv *subnetEnv) error { } n.Delegate["ipam"] = ipam - return delegateAdd(args.ContainerID, n.DataDir, n.Delegate) + return delegateAdd(args.ContainerID, n.DataDir, cniVersion, n.Delegate) } func doCmdDel(args *skel.CmdArgs, n *NetConf) (err error) { diff --git a/plugins/meta/flannel/flannel_linux_test.go b/plugins/meta/flannel/flannel_linux_test.go index a3b68e37..8c49ffad 100644 --- a/plugins/meta/flannel/flannel_linux_test.go +++ b/plugins/meta/flannel/flannel_linux_test.go @@ -20,7 +20,7 @@ import ( "os" "github.com/containernetworking/cni/pkg/skel" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" @@ -31,6 +31,7 @@ import ( var _ = Describe("Flannel", func() { var ( originalNS ns.NetNS + targetNS ns.NetNS onlyIpv4Input string onlyIpv6Input string dualStackInput string @@ -40,22 +41,12 @@ var _ = Describe("Flannel", func() { dataDir string ) - BeforeEach(func() { - var err error - originalNS, err = testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - }) - - AfterEach(func() { - Expect(originalNS.Close()).To(Succeed()) - }) - - const inputTemplate = ` -{ - "name": "cni-flannel", - "type": "flannel", - "subnetFile": "%s", - "dataDir": "%s"%s + const inputTemplate = `{ + "name": "cni-flannel", + "type": "flannel", + "cniVersion": "%s", + "subnetFile": "%s", + "dataDir": "%s"%s }` const inputIPAMTemplate = ` @@ -95,6 +86,8 @@ FLANNEL_MTU=1472 FLANNEL_IPMASQ=true ` + const IFNAME = "eth0" + var writeSubnetEnv = func(contents string) string { file, err := ioutil.TempFile("", "subnet.env") Expect(err).NotTo(HaveOccurred()) @@ -114,17 +107,29 @@ FLANNEL_IPMASQ=true return c } - var makeInput = func(inputIPAM string, subnetFile string) string { + var makeInput = func(cniVersion, inputIPAM string, subnetFile string) string { ipamPart := "" if len(inputIPAM) > 0 { ipamPart = ",\n \"ipam\":\n" + inputIPAM } - return fmt.Sprintf(inputTemplate, subnetFile, dataDir, ipamPart) + return fmt.Sprintf(inputTemplate, cniVersion, subnetFile, dataDir, ipamPart) + } + + var makeHostLocalIPAM = func(dataDir string) string { + return fmt.Sprintf(`{ + "type": "host-local", + "dataDir": "%s" + }`, dataDir) } BeforeEach(func() { var err error + originalNS, err = testutils.NewNS() + Expect(err).NotTo(HaveOccurred()) + targetNS, err = testutils.NewNS() + Expect(err).NotTo(HaveOccurred()) + // flannel subnet.env onlyIpv4SubnetFile = writeSubnetEnv(onlyIpv4FlannelSubnetEnv) onlyIpv6SubnetFile = writeSubnetEnv(onlyIpv6FlannelSubnetEnv) @@ -133,264 +138,274 @@ FLANNEL_IPMASQ=true // flannel state dir dataDir, err = ioutil.TempDir("", "dataDir") Expect(err).NotTo(HaveOccurred()) - onlyIpv4Input = makeInput("", onlyIpv4SubnetFile) - onlyIpv6Input = makeInput("", onlyIpv6SubnetFile) - dualStackInput = makeInput("", dualStackSubnetFile) }) AfterEach(func() { + Expect(targetNS.Close()).To(Succeed()) + Expect(testutils.UnmountNS(targetNS)).To(Succeed()) + Expect(originalNS.Close()).To(Succeed()) + Expect(testutils.UnmountNS(originalNS)).To(Succeed()) + os.Remove(onlyIpv4SubnetFile) os.Remove(onlyIpv6SubnetFile) os.Remove(dualStackSubnetFile) - os.Remove(dataDir) + Expect(os.RemoveAll(dataDir)).To(Succeed()) }) Describe("CNI lifecycle", func() { - Context("when using only ipv4 stack", func() { - It("uses dataDir for storing network configuration with ipv4 stack", func() { - const IFNAME = "eth0" + for _, ver := range testutils.AllSpecVersions { + // Redefine ver inside for scope so real value is picked up by each dynamically defined It() + // See Gingkgo's "Patterns for dynamically generating tests" documentation. + ver := ver - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() + Context("when using only ipv4 stack", func() { + It(fmt.Sprintf("[%s] uses dataDir for storing network configuration with ipv4 stack", ver), func() { + inputIPAM := makeHostLocalIPAM(dataDir) + args := &skel.CmdArgs{ + ContainerID: "some-container-id-ipv4", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: []byte(makeInput(ver, inputIPAM, onlyIpv4SubnetFile)), + } - args := &skel.CmdArgs{ - ContainerID: "some-container-id-ipv4", - Netns: targetNs.Path(), - IfName: IFNAME, - StdinData: []byte(onlyIpv4Input), - } + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + By("calling ADD with ipv4 stack") + GinkgoT().Logf("dataDir is %s", dataDir) + GinkgoT().Logf("conf is %s", args.StdinData) + resI, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) - By("calling ADD with ipv4 stack") - resI, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + By("check that plugin writes the net config to dataDir with ipv4 stack") + path := fmt.Sprintf("%s/%s", dataDir, "some-container-id-ipv4") + Expect(path).Should(BeAnExistingFile()) + + netConfBytes, err := ioutil.ReadFile(path) + Expect(err).NotTo(HaveOccurred()) + expected := fmt.Sprintf(`{ + "cniVersion": "%s", + "ipMasq": false, + "ipam": { + "routes": [ + { + "dst": "10.1.0.0/16" + } + ], + "ranges": [ + [{ + "subnet": "10.1.17.0/24" + }] + ], + "type": "host-local", + "dataDir": "%s" + }, + "isGateway": true, + "mtu": 1472, + "name": "cni-flannel", + "type": "bridge" + }`, ver, dataDir) + Expect(netConfBytes).Should(MatchJSON(expected)) + + result, err := current.NewResultFromResult(resI) + Expect(err).NotTo(HaveOccurred()) + Expect(result.IPs).To(HaveLen(1)) + + By("calling DEL with ipv4 stack") + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + + By("check that plugin removes net config from state dir with ipv4 stack") + Expect(path).ShouldNot(BeAnExistingFile()) + + By("calling DEL again with ipv4 stack") + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + By("check that plugin does not fail due to missing net config with ipv4 stack") + Expect(err).NotTo(HaveOccurred()) + + return nil }) Expect(err).NotTo(HaveOccurred()) - - By("check that plugin writes the net config to dataDir with ipv4 stack") - path := fmt.Sprintf("%s/%s", dataDir, "some-container-id-ipv4") - Expect(path).Should(BeAnExistingFile()) - - netConfBytes, err := ioutil.ReadFile(path) - Expect(err).NotTo(HaveOccurred()) - expected := `{ - "ipMasq": false, - "ipam": { - "routes": [ - { - "dst": "10.1.0.0/16" - } - ], - "ranges": [ - [{ - "subnet": "10.1.17.0/24" - }] - ], - "type": "host-local" - }, - "isGateway": true, - "mtu": 1472, - "name": "cni-flannel", - "type": "bridge" -} -` - Expect(netConfBytes).Should(MatchJSON(expected)) - - result, err := current.NewResultFromResult(resI) - Expect(err).NotTo(HaveOccurred()) - Expect(result.IPs).To(HaveLen(1)) - - By("calling DEL with ipv4 stack") - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - - By("check that plugin removes net config from state dir with ipv4 stack") - Expect(path).ShouldNot(BeAnExistingFile()) - - By("calling DEL again with ipv4 stack") - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - By("check that plugin does not fail due to missing net config with ipv4 stack") - Expect(err).NotTo(HaveOccurred()) - - return nil }) - Expect(err).NotTo(HaveOccurred()) }) - }) - Context("when using only ipv6 stack", func() { - It("uses dataDir for storing network configuration with ipv6 stack", func() { - const IFNAME = "eth0" + Context("when using only ipv6 stack", func() { + It(fmt.Sprintf("[%s] uses dataDir for storing network configuration with ipv6 stack", ver), func() { + inputIPAM := makeHostLocalIPAM(dataDir) + args := &skel.CmdArgs{ + ContainerID: "some-container-id-ipv6", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: []byte(makeInput(ver, inputIPAM, onlyIpv6SubnetFile)), + } - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - args := &skel.CmdArgs{ - ContainerID: "some-container-id-ipv6", - Netns: targetNs.Path(), - IfName: IFNAME, - StdinData: []byte(onlyIpv6Input), - } + By("calling ADD with ipv6 stack") + resI, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + By("check that plugin writes the net config to dataDir with ipv6 stack") + path := fmt.Sprintf("%s/%s", dataDir, "some-container-id-ipv6") + Expect(path).Should(BeAnExistingFile()) - By("calling ADD with ipv6 stack") - resI, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + netConfBytes, err := ioutil.ReadFile(path) + Expect(err).NotTo(HaveOccurred()) + expected := fmt.Sprintf(`{ + "cniVersion": "%s", + "ipMasq": false, + "ipam": { + "routes": [ + { + "dst": "fc00::/48" + } + ], + "ranges": [ + [{ + "subnet": "fc00::/64" + }] + ], + "type": "host-local", + "dataDir": "%s" + }, + "isGateway": true, + "mtu": 1472, + "name": "cni-flannel", + "type": "bridge" + }`, ver, dataDir) + Expect(netConfBytes).Should(MatchJSON(expected)) + + result, err := current.NewResultFromResult(resI) + Expect(err).NotTo(HaveOccurred()) + Expect(result.IPs).To(HaveLen(1)) + + By("calling DEL with ipv6 stack") + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + + By("check that plugin removes net config from state dir with ipv6 stack") + Expect(path).ShouldNot(BeAnExistingFile()) + + By("calling DEL again with ipv6 stack") + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + By("check that plugin does not fail due to missing net config with ipv6 stack") + Expect(err).NotTo(HaveOccurred()) + + return nil }) Expect(err).NotTo(HaveOccurred()) - - By("check that plugin writes the net config to dataDir with ipv6 stack") - path := fmt.Sprintf("%s/%s", dataDir, "some-container-id-ipv6") - Expect(path).Should(BeAnExistingFile()) - - netConfBytes, err := ioutil.ReadFile(path) - Expect(err).NotTo(HaveOccurred()) - expected := `{ - "ipMasq": false, - "ipam": { - "routes": [ - { - "dst": "fc00::/48" - } - ], - "ranges": [ - [{ - "subnet": "fc00::/64" - }] - ], - "type": "host-local" - }, - "isGateway": true, - "mtu": 1472, - "name": "cni-flannel", - "type": "bridge" -} -` - Expect(netConfBytes).Should(MatchJSON(expected)) - - result, err := current.NewResultFromResult(resI) - Expect(err).NotTo(HaveOccurred()) - Expect(result.IPs).To(HaveLen(1)) - - By("calling DEL with ipv6 stack") - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - - By("check that plugin removes net config from state dir with ipv6 stack") - Expect(path).ShouldNot(BeAnExistingFile()) - - By("calling DEL again with ipv6 stack") - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - By("check that plugin does not fail due to missing net config with ipv6 stack") - Expect(err).NotTo(HaveOccurred()) - - return nil }) - Expect(err).NotTo(HaveOccurred()) }) - }) - Context("when using dual stack", func() { - It("uses dataDir for storing network configuration with dual stack", func() { - const IFNAME = "eth0" + Context("when using dual stack", func() { + It(fmt.Sprintf("[%s] uses dataDir for storing network configuration with dual stack", ver), func() { + inputIPAM := makeHostLocalIPAM(dataDir) + args := &skel.CmdArgs{ + ContainerID: "some-container-id-dual-stack", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: []byte(makeInput(ver, inputIPAM, dualStackSubnetFile)), + } - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - args := &skel.CmdArgs{ - ContainerID: "some-container-id-dual-stack", - Netns: targetNs.Path(), - IfName: IFNAME, - StdinData: []byte(dualStackInput), - } + By("calling ADD with dual stack") + resI, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + By("check that plugin writes the net config to dataDir with dual stack") + path := fmt.Sprintf("%s/%s", dataDir, "some-container-id-dual-stack") + Expect(path).Should(BeAnExistingFile()) - By("calling ADD with dual stack") - resI, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + netConfBytes, err := ioutil.ReadFile(path) + Expect(err).NotTo(HaveOccurred()) + expected := fmt.Sprintf(`{ + "cniVersion": "%s", + "ipMasq": false, + "ipam": { + "routes": [ + { + "dst": "10.1.0.0/16" + }, + { + "dst": "fc00::/48" + } + ], + "ranges": [ + [{ + "subnet": "10.1.17.0/24" + }], + [{ + "subnet": "fc00::/64" + }] + ], + "type": "host-local", + "dataDir": "%s" + }, + "isGateway": true, + "mtu": 1472, + "name": "cni-flannel", + "type": "bridge" + }`, ver, dataDir) + Expect(netConfBytes).Should(MatchJSON(expected)) + + result, err := current.NewResultFromResult(resI) + Expect(err).NotTo(HaveOccurred()) + Expect(result.IPs).To(HaveLen(2)) + + By("calling DEL with dual stack") + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + Expect(err).NotTo(HaveOccurred()) + + By("check that plugin removes net config from state dir with dual stack") + Expect(path).ShouldNot(BeAnExistingFile()) + + By("calling DEL again with dual stack") + err = testutils.CmdDelWithArgs(args, func() error { + return cmdDel(args) + }) + By("check that plugin does not fail due to missing net config with dual stack") + Expect(err).NotTo(HaveOccurred()) + + return nil }) Expect(err).NotTo(HaveOccurred()) - - By("check that plugin writes the net config to dataDir with dual stack") - path := fmt.Sprintf("%s/%s", dataDir, "some-container-id-dual-stack") - Expect(path).Should(BeAnExistingFile()) - - netConfBytes, err := ioutil.ReadFile(path) - Expect(err).NotTo(HaveOccurred()) - expected := `{ - "ipMasq": false, - "ipam": { - "routes": [ - { - "dst": "10.1.0.0/16" - }, - { - "dst": "fc00::/48" - } - ], - "ranges": [ - [{ - "subnet": "10.1.17.0/24" - }], - [{ - "subnet": "fc00::/64" - }] - ], - "type": "host-local" - }, - "isGateway": true, - "mtu": 1472, - "name": "cni-flannel", - "type": "bridge" -} -` - Expect(netConfBytes).Should(MatchJSON(expected)) - - result, err := current.NewResultFromResult(resI) - Expect(err).NotTo(HaveOccurred()) - Expect(result.IPs).To(HaveLen(2)) - - By("calling DEL with dual stack") - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - Expect(err).NotTo(HaveOccurred()) - - By("check that plugin removes net config from state dir with dual stack") - Expect(path).ShouldNot(BeAnExistingFile()) - - By("calling DEL again with dual stack") - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - By("check that plugin does not fail due to missing net config with dual stack") - Expect(err).NotTo(HaveOccurred()) - - return nil }) - Expect(err).NotTo(HaveOccurred()) }) - }) + } }) Describe("loadFlannelNetConf", func() { + var ( + onlyIpv4Input string + onlyIpv6Input string + dualStackInput string + ) + + BeforeEach(func() { + onlyIpv4Input = makeInput(current.ImplementedSpecVersion, "", onlyIpv4SubnetFile) + onlyIpv6Input = makeInput(current.ImplementedSpecVersion, "", onlyIpv6SubnetFile) + dualStackInput = makeInput(current.ImplementedSpecVersion, "", dualStackSubnetFile) + }) + Context("when subnetFile and dataDir are specified with ipv4 stack", func() { It("loads flannel network config with ipv4 stack", func() { conf, err := loadFlannelNetConf([]byte(onlyIpv4Input)) @@ -553,7 +568,7 @@ FLANNEL_IPMASQ=true Context("when input IPAM is provided with ipv4 stack", func() { BeforeEach(func() { inputIPAM := makeInputIPAM(inputIPAMType, inputIPAMRoutes, "") - onlyIpv4Input = makeInput(inputIPAM, onlyIpv4SubnetFile) + onlyIpv4Input = makeInput(current.ImplementedSpecVersion, inputIPAM, onlyIpv4SubnetFile) }) It("configures Delegate IPAM accordingly with ipv4 stack", func() { conf, err := loadFlannelNetConf([]byte(onlyIpv4Input)) @@ -575,7 +590,7 @@ FLANNEL_IPMASQ=true Context("when input IPAM is provided with ipv6 stack", func() { BeforeEach(func() { inputIPAM := makeInputIPAM(inputIPAMType, inputIPAMRoutes, "") - onlyIpv6Input = makeInput(inputIPAM, onlyIpv6SubnetFile) + onlyIpv6Input = makeInput(current.ImplementedSpecVersion, inputIPAM, onlyIpv6SubnetFile) }) It("configures Delegate IPAM accordingly with ipv6 stack", func() { conf, err := loadFlannelNetConf([]byte(onlyIpv6Input)) @@ -597,7 +612,7 @@ FLANNEL_IPMASQ=true Context("when input IPAM is provided with dual stack", func() { BeforeEach(func() { inputIPAM := makeInputIPAM(inputIPAMType, inputIPAMRoutes, "") - dualStackInput = makeInput(inputIPAM, dualStackSubnetFile) + dualStackInput = makeInput(current.ImplementedSpecVersion, inputIPAM, dualStackSubnetFile) }) It("configures Delegate IPAM accordingly with dual stack", func() { conf, err := loadFlannelNetConf([]byte(dualStackInput)) @@ -619,7 +634,7 @@ FLANNEL_IPMASQ=true Context("when input IPAM is provided without 'type' with ipv4 stack", func() { BeforeEach(func() { inputIPAM := makeInputIPAM("", inputIPAMRoutes, "") - onlyIpv4Input = makeInput(inputIPAM, onlyIpv4SubnetFile) + onlyIpv4Input = makeInput(current.ImplementedSpecVersion, inputIPAM, onlyIpv4SubnetFile) }) It("configures Delegate IPAM with 'host-local' ipam with ipv4 stack", func() { conf, err := loadFlannelNetConf([]byte(onlyIpv4Input)) @@ -640,7 +655,7 @@ FLANNEL_IPMASQ=true Context("when input IPAM is provided without 'type' with ipv6 stack", func() { BeforeEach(func() { inputIPAM := makeInputIPAM("", inputIPAMRoutes, "") - onlyIpv6Input = makeInput(inputIPAM, onlyIpv6SubnetFile) + onlyIpv6Input = makeInput(current.ImplementedSpecVersion, inputIPAM, onlyIpv6SubnetFile) }) It("configures Delegate IPAM with 'host-local' ipam with ipv6 stack", func() { conf, err := loadFlannelNetConf([]byte(onlyIpv6Input)) @@ -661,7 +676,7 @@ FLANNEL_IPMASQ=true Context("when input IPAM is provided without 'type' with dual stack", func() { BeforeEach(func() { inputIPAM := makeInputIPAM("", inputIPAMRoutes, "") - dualStackInput = makeInput(inputIPAM, dualStackSubnetFile) + dualStackInput = makeInput(current.ImplementedSpecVersion, inputIPAM, dualStackSubnetFile) }) It("configures Delegate IPAM with 'host-local' ipam with dual stack", func() { conf, err := loadFlannelNetConf([]byte(dualStackInput)) diff --git a/plugins/meta/flannel/flannel_windows.go b/plugins/meta/flannel/flannel_windows.go index 7c0c560b..82f98db4 100644 --- a/plugins/meta/flannel/flannel_windows.go +++ b/plugins/meta/flannel/flannel_windows.go @@ -30,7 +30,7 @@ import ( "os" ) -func doCmdAdd(args *skel.CmdArgs, n *NetConf, fenv *subnetEnv) error { +func doCmdAdd(args *skel.CmdArgs, cniVersion string, n *NetConf, fenv *subnetEnv) error { n.Delegate["name"] = n.Name if !hasKey(n.Delegate, "type") { @@ -52,7 +52,8 @@ func doCmdAdd(args *skel.CmdArgs, n *NetConf, fenv *subnetEnv) error { "subnet": fenv.sn.String(), } - return delegateAdd(hns.GetSandboxContainerID(args.ContainerID, args.Netns), n.DataDir, n.Delegate) + sandboxID := hns.GetSandboxContainerID(args.ContainerID, args.Netns) + return delegateAdd(sandboxID, n.DataDir, cniVersion, n.Delegate) } func doCmdDel(args *skel.CmdArgs, n *NetConf) (err error) { diff --git a/plugins/meta/portmap/main.go b/plugins/meta/portmap/main.go index bfd11bba..003d4d60 100644 --- a/plugins/meta/portmap/main.go +++ b/plugins/meta/portmap/main.go @@ -33,7 +33,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" "golang.org/x/sys/unix" @@ -223,9 +223,10 @@ func parseConfig(stdin []byte, ifName string) (*PortMapConf, *current.Result, er if conf.PrevResult != nil { for _, ip := range result.IPs { - if ip.Version == "6" && conf.ContIPv6.IP != nil { + isIPv4 := ip.Address.IP.To4() != nil + if !isIPv4 && conf.ContIPv6.IP != nil { continue - } else if ip.Version == "4" && conf.ContIPv4.IP != nil { + } else if isIPv4 && conf.ContIPv4.IP != nil { continue } @@ -239,11 +240,10 @@ func parseConfig(stdin []byte, ifName string) (*PortMapConf, *current.Result, er continue } } - switch ip.Version { - case "6": - conf.ContIPv6 = ip.Address - case "4": + if ip.Address.IP.To4() != nil { conf.ContIPv4 = ip.Address + } else { + conf.ContIPv6 = ip.Address } } } diff --git a/plugins/meta/portmap/portmap_integ_test.go b/plugins/meta/portmap/portmap_integ_test.go index b4cca147..3e8201e0 100644 --- a/plugins/meta/portmap/portmap_integ_test.go +++ b/plugins/meta/portmap/portmap_integ_test.go @@ -25,7 +25,7 @@ import ( "path/filepath" "github.com/containernetworking/cni/libcni" - "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" "github.com/coreos/go-iptables/iptables" @@ -37,9 +37,36 @@ import ( const TIMEOUT = 90 +func makeConfig(ver string) *libcni.NetworkConfigList { + configList, err := libcni.ConfListFromBytes([]byte(fmt.Sprintf(`{ + "cniVersion": "%s", + "name": "cni-portmap-unit-test", + "plugins": [ + { + "type": "ptp", + "ipMasq": true, + "ipam": { + "type": "host-local", + "subnet": "172.16.31.0/24", + "routes": [ + {"dst": "0.0.0.0/0"} + ] + } + }, + { + "type": "portmap", + "capabilities": { + "portMappings": true + } + } + ] + }`, ver))) + Expect(err).NotTo(HaveOccurred()) + return configList +} + var _ = Describe("portmap integration tests", func() { var ( - configList *libcni.NetworkConfigList cniConf *libcni.CNIConfig targetNS ns.NetNS containerPort int @@ -47,38 +74,11 @@ var _ = Describe("portmap integration tests", func() { ) BeforeEach(func() { - var err error - rawConfig := `{ - "cniVersion": "0.3.0", - "name": "cni-portmap-unit-test", - "plugins": [ - { - "type": "ptp", - "ipMasq": true, - "ipam": { - "type": "host-local", - "subnet": "172.16.31.0/24", - "routes": [ - {"dst": "0.0.0.0/0"} - ] - } - }, - { - "type": "portmap", - "capabilities": { - "portMappings": true - } - } - ] -}` - - configList, err = libcni.ConfListFromBytes([]byte(rawConfig)) - Expect(err).NotTo(HaveOccurred()) - // turn PATH in to CNI_PATH dirs := filepath.SplitList(os.Getenv("PATH")) cniConf = &libcni.CNIConfig{Path: dirs} + var err error targetNS, err = testutils.NewNS() Expect(err).NotTo(HaveOccurred()) fmt.Fprintln(GinkgoWriter, "namespace:", targetNS.Path()) @@ -90,333 +90,340 @@ var _ = Describe("portmap integration tests", func() { AfterEach(func() { session.Terminate().Wait() - if targetNS != nil { - targetNS.Close() - } + targetNS.Close() + testutils.UnmountNS(targetNS) }) - Describe("Creating an interface in a namespace with the ptp plugin", func() { - // This needs to be done using Ginkgo's asynchronous testing mode. - It("forwards a TCP port on ipv4", func(done Done) { - var err error - hostPort := rand.Intn(10000) + 1025 - runtimeConfig := libcni.RuntimeConf{ - ContainerID: fmt.Sprintf("unit-test-%d", hostPort), - NetNS: targetNS.Path(), - IfName: "eth0", - CapabilityArgs: map[string]interface{}{ - "portMappings": []map[string]interface{}{ - { - "hostPort": hostPort, - "containerPort": containerPort, - "protocol": "tcp", + for _, ver := range []string{"0.3.0", "0.3.1", "0.4.0", "1.0.0"} { + // Redefine ver inside for scope so real value is picked up by each dynamically defined It() + // See Gingkgo's "Patterns for dynamically generating tests" documentation. + ver := ver + + Describe("Creating an interface in a namespace with the ptp plugin", func() { + // This needs to be done using Ginkgo's asynchronous testing mode. + It(fmt.Sprintf("[%s] forwards a TCP port on ipv4", ver), func(done Done) { + var err error + hostPort := rand.Intn(10000) + 1025 + runtimeConfig := libcni.RuntimeConf{ + ContainerID: fmt.Sprintf("unit-test-%d", hostPort), + NetNS: targetNS.Path(), + IfName: "eth0", + CapabilityArgs: map[string]interface{}{ + "portMappings": []map[string]interface{}{ + { + "hostPort": hostPort, + "containerPort": containerPort, + "protocol": "tcp", + }, }, }, - }, - } - - // Make delete idempotent, so we can clean up on failure - netDeleted := false - deleteNetwork := func() error { - if netDeleted { - return nil } - netDeleted = true - return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig) - } + configList := makeConfig(ver) - // we'll also manually check the iptables chains - ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4) - Expect(err).NotTo(HaveOccurred()) - dnatChainName := genDnatChain("cni-portmap-unit-test", runtimeConfig.ContainerID).name - - // Create the network - resI, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig) - Expect(err).NotTo(HaveOccurred()) - defer deleteNetwork() - - // Undo Docker's forwarding policy - cmd := exec.Command("iptables", "-t", "filter", - "-P", "FORWARD", "ACCEPT") - cmd.Stderr = GinkgoWriter - err = cmd.Run() - Expect(err).NotTo(HaveOccurred()) - - // Check the chain exists - _, err = ipt.List("nat", dnatChainName) - Expect(err).NotTo(HaveOccurred()) - - result, err := current.GetResult(resI) - Expect(err).NotTo(HaveOccurred()) - var contIP net.IP - - for _, ip := range result.IPs { - intfIndex := *ip.Interface - if result.Interfaces[intfIndex].Sandbox == "" { - continue + // Make delete idempotent, so we can clean up on failure + netDeleted := false + deleteNetwork := func() error { + if netDeleted { + return nil + } + netDeleted = true + return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig) } - contIP = ip.Address.IP - } - if contIP == nil { - Fail("could not determine container IP") - } - hostIP := getLocalIP() - fmt.Fprintf(GinkgoWriter, "hostIP: %s:%d, contIP: %s:%d\n", - hostIP, hostPort, contIP, containerPort) + // we'll also manually check the iptables chains + ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4) + Expect(err).NotTo(HaveOccurred()) + dnatChainName := genDnatChain("cni-portmap-unit-test", runtimeConfig.ContainerID).name - // dump iptables-save output for debugging - cmd = exec.Command("iptables-save") - cmd.Stderr = GinkgoWriter - cmd.Stdout = GinkgoWriter - Expect(cmd.Run()).To(Succeed()) + // Create the network + resI, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig) + Expect(err).NotTo(HaveOccurred()) + defer deleteNetwork() - // dump ip routes output for debugging - cmd = exec.Command("ip", "route") - cmd.Stderr = GinkgoWriter - cmd.Stdout = GinkgoWriter - Expect(cmd.Run()).To(Succeed()) + // Undo Docker's forwarding policy + cmd := exec.Command("iptables", "-t", "filter", + "-P", "FORWARD", "ACCEPT") + cmd.Stderr = GinkgoWriter + err = cmd.Run() + Expect(err).NotTo(HaveOccurred()) - // dump ip addresses output for debugging - cmd = exec.Command("ip", "addr") - cmd.Stderr = GinkgoWriter - cmd.Stdout = GinkgoWriter - Expect(cmd.Run()).To(Succeed()) + // Check the chain exists + _, err = ipt.List("nat", dnatChainName) + Expect(err).NotTo(HaveOccurred()) - // Sanity check: verify that the container is reachable directly - contOK := testEchoServer(contIP.String(), "tcp", containerPort, "") + result, err := types100.GetResult(resI) + Expect(err).NotTo(HaveOccurred()) + var contIP net.IP - // Verify that a connection to the forwarded port works - dnatOK := testEchoServer(hostIP, "tcp", hostPort, "") + for _, ip := range result.IPs { + intfIndex := *ip.Interface + if result.Interfaces[intfIndex].Sandbox == "" { + continue + } + contIP = ip.Address.IP + } + if contIP == nil { + Fail("could not determine container IP") + } - // Verify that a connection to localhost works - snatOK := testEchoServer("127.0.0.1", "tcp", hostPort, "") + hostIP := getLocalIP() + fmt.Fprintf(GinkgoWriter, "hostIP: %s:%d, contIP: %s:%d\n", + hostIP, hostPort, contIP, containerPort) - // verify that hairpin works - hairpinOK := testEchoServer(hostIP, "tcp", hostPort, targetNS.Path()) + // dump iptables-save output for debugging + cmd = exec.Command("iptables-save") + cmd.Stderr = GinkgoWriter + cmd.Stdout = GinkgoWriter + Expect(cmd.Run()).To(Succeed()) - // Cleanup - session.Terminate() - err = deleteNetwork() - Expect(err).NotTo(HaveOccurred()) + // dump ip routes output for debugging + cmd = exec.Command("ip", "route") + cmd.Stderr = GinkgoWriter + cmd.Stdout = GinkgoWriter + Expect(cmd.Run()).To(Succeed()) - // Verify iptables rules are gone - _, err = ipt.List("nat", dnatChainName) - Expect(err).To(MatchError(ContainSubstring("iptables: No chain/target/match by that name."))) + // dump ip addresses output for debugging + cmd = exec.Command("ip", "addr") + cmd.Stderr = GinkgoWriter + cmd.Stdout = GinkgoWriter + Expect(cmd.Run()).To(Succeed()) - // Check that everything succeeded *after* we clean up the network - if !contOK { - Fail("connection direct to " + contIP.String() + " failed") - } - if !dnatOK { - Fail("Connection to " + hostIP + " was not forwarded") - } - if !snatOK { - Fail("connection to 127.0.0.1 was not forwarded") - } - if !hairpinOK { - Fail("Hairpin connection failed") - } + // Sanity check: verify that the container is reachable directly + contOK := testEchoServer(contIP.String(), "tcp", containerPort, "") - close(done) - }, TIMEOUT*9) + // Verify that a connection to the forwarded port works + dnatOK := testEchoServer(hostIP, "tcp", hostPort, "") - It("forwards a UDP port on ipv4 and keep working after creating a second container with the same HostPort", func(done Done) { - var err error - hostPort := rand.Intn(10000) + 1025 - runtimeConfig := libcni.RuntimeConf{ - ContainerID: fmt.Sprintf("unit-test-%d", hostPort), - NetNS: targetNS.Path(), - IfName: "eth0", - CapabilityArgs: map[string]interface{}{ - "portMappings": []map[string]interface{}{ - { - "hostPort": hostPort, - "containerPort": containerPort, - "protocol": "udp", + // Verify that a connection to localhost works + snatOK := testEchoServer("127.0.0.1", "tcp", hostPort, "") + + // verify that hairpin works + hairpinOK := testEchoServer(hostIP, "tcp", hostPort, targetNS.Path()) + + // Cleanup + session.Terminate() + err = deleteNetwork() + Expect(err).NotTo(HaveOccurred()) + + // Verify iptables rules are gone + _, err = ipt.List("nat", dnatChainName) + Expect(err).To(MatchError(ContainSubstring("iptables: No chain/target/match by that name."))) + + // Check that everything succeeded *after* we clean up the network + if !contOK { + Fail("connection direct to " + contIP.String() + " failed") + } + if !dnatOK { + Fail("Connection to " + hostIP + " was not forwarded") + } + if !snatOK { + Fail("connection to 127.0.0.1 was not forwarded") + } + if !hairpinOK { + Fail("Hairpin connection failed") + } + + close(done) + }, TIMEOUT*9) + + It(fmt.Sprintf("[%s] forwards a UDP port on ipv4 and keep working after creating a second container with the same HostPort", ver), func(done Done) { + var err error + hostPort := rand.Intn(10000) + 1025 + runtimeConfig := libcni.RuntimeConf{ + ContainerID: fmt.Sprintf("unit-test-%d", hostPort), + NetNS: targetNS.Path(), + IfName: "eth0", + CapabilityArgs: map[string]interface{}{ + "portMappings": []map[string]interface{}{ + { + "hostPort": hostPort, + "containerPort": containerPort, + "protocol": "udp", + }, }, }, - }, - } - - // Make delete idempotent, so we can clean up on failure - netDeleted := false - deleteNetwork := func() error { - if netDeleted { - return nil } - netDeleted = true - return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig) - } + configList := makeConfig(ver) - // Create the network - resI, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig) - Expect(err).NotTo(HaveOccurred()) - defer deleteNetwork() - - // Undo Docker's forwarding policy - cmd := exec.Command("iptables", "-t", "filter", - "-P", "FORWARD", "ACCEPT") - cmd.Stderr = GinkgoWriter - err = cmd.Run() - Expect(err).NotTo(HaveOccurred()) - - result, err := current.GetResult(resI) - Expect(err).NotTo(HaveOccurred()) - var contIP net.IP - - for _, ip := range result.IPs { - intfIndex := *ip.Interface - if result.Interfaces[intfIndex].Sandbox == "" { - continue + // Make delete idempotent, so we can clean up on failure + netDeleted := false + deleteNetwork := func() error { + if netDeleted { + return nil + } + netDeleted = true + return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig) } - contIP = ip.Address.IP - } - if contIP == nil { - Fail("could not determine container IP") - } - hostIP := getLocalIP() - fmt.Fprintf(GinkgoWriter, "First container hostIP: %s:%d, contIP: %s:%d\n", - hostIP, hostPort, contIP, containerPort) + // Create the network + resI, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig) + Expect(err).NotTo(HaveOccurred()) + defer deleteNetwork() - // dump iptables-save output for debugging - cmd = exec.Command("iptables-save") - cmd.Stderr = GinkgoWriter - cmd.Stdout = GinkgoWriter - Expect(cmd.Run()).To(Succeed()) + // Undo Docker's forwarding policy + cmd := exec.Command("iptables", "-t", "filter", + "-P", "FORWARD", "ACCEPT") + cmd.Stderr = GinkgoWriter + err = cmd.Run() + Expect(err).NotTo(HaveOccurred()) - // dump ip routes output for debugging - cmd = exec.Command("ip", "route") - cmd.Stderr = GinkgoWriter - cmd.Stdout = GinkgoWriter - Expect(cmd.Run()).To(Succeed()) + result, err := types100.GetResult(resI) + Expect(err).NotTo(HaveOccurred()) + var contIP net.IP - // dump ip addresses output for debugging - cmd = exec.Command("ip", "addr") - cmd.Stderr = GinkgoWriter - cmd.Stdout = GinkgoWriter - Expect(cmd.Run()).To(Succeed()) + for _, ip := range result.IPs { + intfIndex := *ip.Interface + if result.Interfaces[intfIndex].Sandbox == "" { + continue + } + contIP = ip.Address.IP + } + if contIP == nil { + Fail("could not determine container IP") + } - // Sanity check: verify that the container is reachable directly - fmt.Fprintln(GinkgoWriter, "Connect to container:", contIP.String(), containerPort) - contOK := testEchoServer(contIP.String(), "udp", containerPort, "") + hostIP := getLocalIP() + fmt.Fprintf(GinkgoWriter, "First container hostIP: %s:%d, contIP: %s:%d\n", + hostIP, hostPort, contIP, containerPort) - // Verify that a connection to the forwarded port works - fmt.Fprintln(GinkgoWriter, "Connect to host:", hostIP, hostPort) - dnatOK := testEchoServer(hostIP, "udp", hostPort, "") + // dump iptables-save output for debugging + cmd = exec.Command("iptables-save") + cmd.Stderr = GinkgoWriter + cmd.Stdout = GinkgoWriter + Expect(cmd.Run()).To(Succeed()) - // Cleanup - session.Terminate() - err = deleteNetwork() - Expect(err).NotTo(HaveOccurred()) + // dump ip routes output for debugging + cmd = exec.Command("ip", "route") + cmd.Stderr = GinkgoWriter + cmd.Stdout = GinkgoWriter + Expect(cmd.Run()).To(Succeed()) - // Check that everything succeeded *after* we clean up the network - if !contOK { - Fail("connection direct to " + contIP.String() + " failed") - } - if !dnatOK { - Fail("Connection to " + hostIP + " was not forwarded") - } - // Create a second container - targetNS2, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - fmt.Fprintln(GinkgoWriter, "namespace:", targetNS2.Path()) + // dump ip addresses output for debugging + cmd = exec.Command("ip", "addr") + cmd.Stderr = GinkgoWriter + cmd.Stdout = GinkgoWriter + Expect(cmd.Run()).To(Succeed()) - // Start an echo server and get the port - containerPort, session2, err := StartEchoServerInNamespace(targetNS2) - Expect(err).NotTo(HaveOccurred()) + // Sanity check: verify that the container is reachable directly + fmt.Fprintln(GinkgoWriter, "Connect to container:", contIP.String(), containerPort) + contOK := testEchoServer(contIP.String(), "udp", containerPort, "") - runtimeConfig2 := libcni.RuntimeConf{ - ContainerID: fmt.Sprintf("unit-test2-%d", hostPort), - NetNS: targetNS2.Path(), - IfName: "eth0", - CapabilityArgs: map[string]interface{}{ - "portMappings": []map[string]interface{}{ - { - "hostPort": hostPort, - "containerPort": containerPort, - "protocol": "udp", + // Verify that a connection to the forwarded port works + fmt.Fprintln(GinkgoWriter, "Connect to host:", hostIP, hostPort) + dnatOK := testEchoServer(hostIP, "udp", hostPort, "") + + // Cleanup + session.Terminate() + err = deleteNetwork() + Expect(err).NotTo(HaveOccurred()) + + // Check that everything succeeded *after* we clean up the network + if !contOK { + Fail("connection direct to " + contIP.String() + " failed") + } + if !dnatOK { + Fail("Connection to " + hostIP + " was not forwarded") + } + // Create a second container + targetNS2, err := testutils.NewNS() + Expect(err).NotTo(HaveOccurred()) + fmt.Fprintln(GinkgoWriter, "namespace:", targetNS2.Path()) + + // Start an echo server and get the port + containerPort, session2, err := StartEchoServerInNamespace(targetNS2) + Expect(err).NotTo(HaveOccurred()) + + runtimeConfig2 := libcni.RuntimeConf{ + ContainerID: fmt.Sprintf("unit-test2-%d", hostPort), + NetNS: targetNS2.Path(), + IfName: "eth0", + CapabilityArgs: map[string]interface{}{ + "portMappings": []map[string]interface{}{ + { + "hostPort": hostPort, + "containerPort": containerPort, + "protocol": "udp", + }, }, }, - }, - } - - // Make delete idempotent, so we can clean up on failure - net2Deleted := false - deleteNetwork2 := func() error { - if net2Deleted { - return nil } - net2Deleted = true - return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig2) - } - // Create the network - resI2, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig2) - Expect(err).NotTo(HaveOccurred()) - defer deleteNetwork2() - - result2, err := current.GetResult(resI2) - Expect(err).NotTo(HaveOccurred()) - var contIP2 net.IP - - for _, ip := range result2.IPs { - intfIndex := *ip.Interface - if result2.Interfaces[intfIndex].Sandbox == "" { - continue + // Make delete idempotent, so we can clean up on failure + net2Deleted := false + deleteNetwork2 := func() error { + if net2Deleted { + return nil + } + net2Deleted = true + return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig2) } - contIP2 = ip.Address.IP - } - if contIP2 == nil { - Fail("could not determine container IP") - } - fmt.Fprintf(GinkgoWriter, "Second container: hostIP: %s:%d, contIP: %s:%d\n", - hostIP, hostPort, contIP2, containerPort) + // Create the network + resI2, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig2) + Expect(err).NotTo(HaveOccurred()) + defer deleteNetwork2() - // dump iptables-save output for debugging - cmd = exec.Command("iptables-save") - cmd.Stderr = GinkgoWriter - cmd.Stdout = GinkgoWriter - Expect(cmd.Run()).To(Succeed()) + result2, err := types100.GetResult(resI2) + Expect(err).NotTo(HaveOccurred()) + var contIP2 net.IP - // dump ip routes output for debugging - cmd = exec.Command("ip", "route") - cmd.Stderr = GinkgoWriter - cmd.Stdout = GinkgoWriter - Expect(cmd.Run()).To(Succeed()) + for _, ip := range result2.IPs { + intfIndex := *ip.Interface + if result2.Interfaces[intfIndex].Sandbox == "" { + continue + } + contIP2 = ip.Address.IP + } + if contIP2 == nil { + Fail("could not determine container IP") + } - // dump ip addresses output for debugging - cmd = exec.Command("ip", "addr") - cmd.Stderr = GinkgoWriter - cmd.Stdout = GinkgoWriter - Expect(cmd.Run()).To(Succeed()) + fmt.Fprintf(GinkgoWriter, "Second container: hostIP: %s:%d, contIP: %s:%d\n", + hostIP, hostPort, contIP2, containerPort) - // Sanity check: verify that the container is reachable directly - fmt.Fprintln(GinkgoWriter, "Connect to container:", contIP2.String(), containerPort) - cont2OK := testEchoServer(contIP2.String(), "udp", containerPort, "") + // dump iptables-save output for debugging + cmd = exec.Command("iptables-save") + cmd.Stderr = GinkgoWriter + cmd.Stdout = GinkgoWriter + Expect(cmd.Run()).To(Succeed()) - // Verify that a connection to the forwarded port works - fmt.Fprintln(GinkgoWriter, "Connect to host:", hostIP, hostPort) - dnat2OK := testEchoServer(hostIP, "udp", hostPort, "") + // dump ip routes output for debugging + cmd = exec.Command("ip", "route") + cmd.Stderr = GinkgoWriter + cmd.Stdout = GinkgoWriter + Expect(cmd.Run()).To(Succeed()) - // Cleanup - session2.Terminate() - err = deleteNetwork2() - Expect(err).NotTo(HaveOccurred()) + // dump ip addresses output for debugging + cmd = exec.Command("ip", "addr") + cmd.Stderr = GinkgoWriter + cmd.Stdout = GinkgoWriter + Expect(cmd.Run()).To(Succeed()) - // Check that everything succeeded *after* we clean up the network - if !cont2OK { - Fail("connection direct to " + contIP2.String() + " failed") - } - if !dnat2OK { - Fail("Connection to " + hostIP + " was not forwarded") - } + // Sanity check: verify that the container is reachable directly + fmt.Fprintln(GinkgoWriter, "Connect to container:", contIP2.String(), containerPort) + cont2OK := testEchoServer(contIP2.String(), "udp", containerPort, "") - close(done) - }, TIMEOUT*9) - }) + // Verify that a connection to the forwarded port works + fmt.Fprintln(GinkgoWriter, "Connect to host:", hostIP, hostPort) + dnat2OK := testEchoServer(hostIP, "udp", hostPort, "") + + // Cleanup + session2.Terminate() + err = deleteNetwork2() + Expect(err).NotTo(HaveOccurred()) + + // Check that everything succeeded *after* we clean up the network + if !cont2OK { + Fail("connection direct to " + contIP2.String() + " failed") + } + if !dnat2OK { + Fail("Connection to " + hostIP + " was not forwarded") + } + + close(done) + }, TIMEOUT*9) + }) + } }) // testEchoServer returns true if we found an echo server on the port diff --git a/plugins/meta/portmap/portmap_test.go b/plugins/meta/portmap/portmap_test.go index 12df96c5..dd68391d 100644 --- a/plugins/meta/portmap/portmap_test.go +++ b/plugins/meta/portmap/portmap_test.go @@ -27,336 +27,342 @@ var _ = Describe("portmapping configuration", func() { netName := "testNetName" containerID := "icee6giejonei6sohng6ahngee7laquohquee9shiGo7fohferakah3Feiyoolu2pei7ciPhoh7shaoX6vai3vuf0ahfaeng8yohb9ceu0daez5hashee8ooYai5wa3y" - Context("config parsing", func() { - It("Correctly parses an ADD config", func() { - configBytes := []byte(`{ - "name": "test", - "type": "portmap", - "cniVersion": "0.3.1", - "runtimeConfig": { - "portMappings": [ - { "hostPort": 8080, "containerPort": 80, "protocol": "tcp"}, - { "hostPort": 8081, "containerPort": 81, "protocol": "udp"} - ] - }, - "snat": false, - "conditionsV4": ["a", "b"], - "conditionsV6": ["c", "d"], - "prevResult": { - "interfaces": [ - {"name": "host"}, - {"name": "container", "sandbox":"netns"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.1/24", - "gateway": "10.0.0.1", - "interface": 0 - }, - { - "version": "6", - "address": "2001:db8:1::2/64", - "gateway": "2001:db8:1::1", - "interface": 1 - }, - { - "version": "4", - "address": "10.0.0.2/24", - "gateway": "10.0.0.1", - "interface": 1 - } - ] - } -}`) - c, _, err := parseConfig(configBytes, "container") - Expect(err).NotTo(HaveOccurred()) - Expect(c.CNIVersion).To(Equal("0.3.1")) - Expect(c.ConditionsV4).To(Equal(&[]string{"a", "b"})) - Expect(c.ConditionsV6).To(Equal(&[]string{"c", "d"})) - fvar := false - Expect(c.SNAT).To(Equal(&fvar)) - Expect(c.Name).To(Equal("test")) + for _, ver := range []string{"0.3.0", "0.3.1", "0.4.0", "1.0.0"} { + // Redefine ver inside for scope so real value is picked up by each dynamically defined It() + // See Gingkgo's "Patterns for dynamically generating tests" documentation. + ver := ver - n, err := types.ParseCIDR("10.0.0.2/24") - Expect(c.ContIPv4).To(Equal(*n)) - n, err = types.ParseCIDR("2001:db8:1::2/64") - Expect(c.ContIPv6).To(Equal(*n)) - }) - - It("Correctly parses a DEL config", func() { - // When called with DEL, neither runtimeConfig nor prevResult may be specified - configBytes := []byte(`{ - "name": "test", - "type": "portmap", - "cniVersion": "0.3.1", - "snat": false, - "conditionsV4": ["a", "b"], - "conditionsV6": ["c", "d"] -}`) - c, _, err := parseConfig(configBytes, "container") - Expect(err).NotTo(HaveOccurred()) - Expect(c.CNIVersion).To(Equal("0.3.1")) - Expect(c.ConditionsV4).To(Equal(&[]string{"a", "b"})) - Expect(c.ConditionsV6).To(Equal(&[]string{"c", "d"})) - fvar := false - Expect(c.SNAT).To(Equal(&fvar)) - Expect(c.Name).To(Equal("test")) - }) - - It("fails with invalid mappings", func() { - configBytes := []byte(`{ - "name": "test", - "type": "portmap", - "cniVersion": "0.3.1", - "snat": false, - "conditionsV4": ["a", "b"], - "conditionsV6": ["c", "d"], - "runtimeConfig": { - "portMappings": [ - { "hostPort": 0, "containerPort": 80, "protocol": "tcp"} - ] - } -}`) - _, _, err := parseConfig(configBytes, "container") - Expect(err).To(MatchError("Invalid host port number: 0")) - }) - - It("Does not fail on missing prevResult interface index", func() { - configBytes := []byte(`{ - "name": "test", - "type": "portmap", - "cniVersion": "0.3.1", - "runtimeConfig": { - "portMappings": [ - { "hostPort": 8080, "containerPort": 80, "protocol": "tcp"} - ] - }, - "conditionsV4": ["a", "b"], - "prevResult": { - "interfaces": [ - {"name": "host"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.1/24", - "gateway": "10.0.0.1" - } - ] - } -}`) - _, _, err := parseConfig(configBytes, "container") - Expect(err).NotTo(HaveOccurred()) - }) - }) - - Describe("Generating chains", func() { - Context("for DNAT", func() { - It("generates a correct standard container chain", func() { - ch := genDnatChain(netName, containerID) - - Expect(ch).To(Equal(chain{ - table: "nat", - name: "CNI-DN-bfd599665540dd91d5d28", - entryChains: []string{TopLevelDNATChainName}, - })) - configBytes := []byte(`{ - "name": "test", - "type": "portmap", - "cniVersion": "0.3.1", - "runtimeConfig": { - "portMappings": [ - { "hostPort": 8080, "containerPort": 80, "protocol": "tcp"}, - { "hostPort": 8081, "containerPort": 80, "protocol": "tcp"}, - { "hostPort": 8080, "containerPort": 81, "protocol": "udp"}, - { "hostPort": 8082, "containerPort": 82, "protocol": "udp"}, - { "hostPort": 8083, "containerPort": 83, "protocol": "tcp", "hostIP": "192.168.0.2"}, - { "hostPort": 8084, "containerPort": 84, "protocol": "tcp", "hostIP": "0.0.0.0"}, - { "hostPort": 8085, "containerPort": 85, "protocol": "tcp", "hostIP": "2001:db8:a::1"}, - { "hostPort": 8086, "containerPort": 86, "protocol": "tcp", "hostIP": "::"} - ] - }, - "snat": true, - "conditionsV4": ["a", "b"], - "conditionsV6": ["c", "d"] -}`) - - conf, _, err := parseConfig(configBytes, "foo") + Context("config parsing", func() { + It(fmt.Sprintf("[%s] correctly parses an ADD config", ver), func() { + configBytes := []byte(fmt.Sprintf(`{ + "name": "test", + "type": "portmap", + "cniVersion": "%s", + "runtimeConfig": { + "portMappings": [ + { "hostPort": 8080, "containerPort": 80, "protocol": "tcp"}, + { "hostPort": 8081, "containerPort": 81, "protocol": "udp"} + ] + }, + "snat": false, + "conditionsV4": ["a", "b"], + "conditionsV6": ["c", "d"], + "prevResult": { + "interfaces": [ + {"name": "host"}, + {"name": "container", "sandbox":"netns"} + ], + "ips": [ + { + "version": "4", + "address": "10.0.0.1/24", + "gateway": "10.0.0.1", + "interface": 0 + }, + { + "version": "6", + "address": "2001:db8:1::2/64", + "gateway": "2001:db8:1::1", + "interface": 1 + }, + { + "version": "4", + "address": "10.0.0.2/24", + "gateway": "10.0.0.1", + "interface": 1 + } + ] + } + }`, ver)) + c, _, err := parseConfig(configBytes, "container") Expect(err).NotTo(HaveOccurred()) - conf.ContainerID = containerID - - ch = genDnatChain(conf.Name, containerID) - Expect(ch).To(Equal(chain{ - table: "nat", - name: "CNI-DN-67e92b96e692a494b6b85", - entryChains: []string{"CNI-HOSTPORT-DNAT"}, - })) - - n, err := types.ParseCIDR("10.0.0.2/24") - fillDnatRules(&ch, conf, *n) - - Expect(ch.entryRules).To(Equal([][]string{ - {"-m", "comment", "--comment", - fmt.Sprintf("dnat name: \"test\" id: \"%s\"", containerID), - "-m", "multiport", - "-p", "tcp", - "--destination-ports", "8080,8081,8083,8084,8085,8086", - "a", "b"}, - {"-m", "comment", "--comment", - fmt.Sprintf("dnat name: \"test\" id: \"%s\"", containerID), - "-m", "multiport", - "-p", "udp", - "--destination-ports", "8080,8082", - "a", "b"}, - })) - - Expect(ch.rules).To(Equal([][]string{ - // tcp rules and not hostIP - {"-p", "tcp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"}, - {"-p", "tcp", "--dport", "8080", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"}, - {"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"}, - {"-p", "tcp", "--dport", "8081", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"}, - {"-p", "tcp", "--dport", "8081", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"}, - {"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "10.0.0.2:80"}, - // udp rules and not hostIP - {"-p", "udp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"}, - {"-p", "udp", "--dport", "8080", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"}, - {"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:81"}, - {"-p", "udp", "--dport", "8082", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"}, - {"-p", "udp", "--dport", "8082", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"}, - {"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "10.0.0.2:82"}, - // tcp rules and hostIP - {"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"}, - {"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"}, - {"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-j", "DNAT", "--to-destination", "10.0.0.2:83"}, - // tcp rules and hostIP = "0.0.0.0" - {"-p", "tcp", "--dport", "8084", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"}, - {"-p", "tcp", "--dport", "8084", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"}, - {"-p", "tcp", "--dport", "8084", "-j", "DNAT", "--to-destination", "10.0.0.2:84"}, - })) - - ch.rules = nil - ch.entryRules = nil - - n, err = types.ParseCIDR("2001:db8::2/64") - fillDnatRules(&ch, conf, *n) - - Expect(ch.rules).To(Equal([][]string{ - // tcp rules and not hostIP - {"-p", "tcp", "--dport", "8080", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"}, - {"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "[2001:db8::2]:80"}, - {"-p", "tcp", "--dport", "8081", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"}, - {"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "[2001:db8::2]:80"}, - // udp rules and not hostIP - {"-p", "udp", "--dport", "8080", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"}, - {"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "[2001:db8::2]:81"}, - {"-p", "udp", "--dport", "8082", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"}, - {"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "[2001:db8::2]:82"}, - // tcp rules and hostIP - {"-p", "tcp", "--dport", "8085", "-d", "2001:db8:a::1", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"}, - {"-p", "tcp", "--dport", "8085", "-d", "2001:db8:a::1", "-j", "DNAT", "--to-destination", "[2001:db8::2]:85"}, - // tcp rules and hostIP = "::" - {"-p", "tcp", "--dport", "8086", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"}, - {"-p", "tcp", "--dport", "8086", "-j", "DNAT", "--to-destination", "[2001:db8::2]:86"}, - })) - - // Disable snat, generate rules - ch.rules = nil - ch.entryRules = nil + Expect(c.CNIVersion).To(Equal(ver)) + Expect(c.ConditionsV4).To(Equal(&[]string{"a", "b"})) + Expect(c.ConditionsV6).To(Equal(&[]string{"c", "d"})) fvar := false - conf.SNAT = &fvar + Expect(c.SNAT).To(Equal(&fvar)) + Expect(c.Name).To(Equal("test")) - n, err = types.ParseCIDR("10.0.0.2/24") - fillDnatRules(&ch, conf, *n) - Expect(ch.rules).To(Equal([][]string{ - {"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"}, - {"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "10.0.0.2:80"}, - {"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:81"}, - {"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "10.0.0.2:82"}, - {"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-j", "DNAT", "--to-destination", "10.0.0.2:83"}, - {"-p", "tcp", "--dport", "8084", "-j", "DNAT", "--to-destination", "10.0.0.2:84"}, - })) - }) - - It("generates a correct chain with external mark", func() { - ch := genDnatChain(netName, containerID) - - Expect(ch).To(Equal(chain{ - table: "nat", - name: "CNI-DN-bfd599665540dd91d5d28", - entryChains: []string{TopLevelDNATChainName}, - })) - configBytes := []byte(`{ - "name": "test", - "type": "portmap", - "cniVersion": "0.3.1", - "runtimeConfig": { - "portMappings": [ - { "hostPort": 8080, "containerPort": 80, "protocol": "tcp"} - ] - }, - "externalSetMarkChain": "PLZ-SET-MARK", - "conditionsV4": ["a", "b"], - "conditionsV6": ["c", "d"] -}`) - - conf, _, err := parseConfig(configBytes, "foo") - Expect(err).NotTo(HaveOccurred()) - conf.ContainerID = containerID - - ch = genDnatChain(conf.Name, containerID) n, err := types.ParseCIDR("10.0.0.2/24") - fillDnatRules(&ch, conf, *n) - Expect(ch.rules).To(Equal([][]string{ - {"-p", "tcp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "PLZ-SET-MARK"}, - {"-p", "tcp", "--dport", "8080", "-s", "127.0.0.1", "-j", "PLZ-SET-MARK"}, - {"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"}, - })) + Expect(c.ContIPv4).To(Equal(*n)) + n, err = types.ParseCIDR("2001:db8:1::2/64") + Expect(c.ContIPv6).To(Equal(*n)) }) - It("generates a correct top-level chain", func() { - ch := genToplevelDnatChain() - - Expect(ch).To(Equal(chain{ - table: "nat", - name: "CNI-HOSTPORT-DNAT", - entryChains: []string{"PREROUTING", "OUTPUT"}, - entryRules: [][]string{{"-m", "addrtype", "--dst-type", "LOCAL"}}, - })) + It(fmt.Sprintf("[%s] correctly parses a DEL config", ver), func() { + // When called with DEL, neither runtimeConfig nor prevResult may be specified + configBytes := []byte(fmt.Sprintf(`{ + "name": "test", + "type": "portmap", + "cniVersion": "%s", + "snat": false, + "conditionsV4": ["a", "b"], + "conditionsV6": ["c", "d"] + }`, ver)) + c, _, err := parseConfig(configBytes, "container") + Expect(err).NotTo(HaveOccurred()) + Expect(c.CNIVersion).To(Equal(ver)) + Expect(c.ConditionsV4).To(Equal(&[]string{"a", "b"})) + Expect(c.ConditionsV6).To(Equal(&[]string{"c", "d"})) + fvar := false + Expect(c.SNAT).To(Equal(&fvar)) + Expect(c.Name).To(Equal("test")) }) - It("generates the correct mark chains", func() { - masqBit := 5 - ch := genSetMarkChain(masqBit) - Expect(ch).To(Equal(chain{ - table: "nat", - name: "CNI-HOSTPORT-SETMARK", - rules: [][]string{{ - "-m", "comment", - "--comment", "CNI portfwd masquerade mark", - "-j", "MARK", - "--set-xmark", "0x20/0x20", - }}, - })) + It(fmt.Sprintf("[%s] fails with invalid mappings", ver), func() { + configBytes := []byte(fmt.Sprintf(`{ + "name": "test", + "type": "portmap", + "cniVersion": "%s", + "snat": false, + "conditionsV4": ["a", "b"], + "conditionsV6": ["c", "d"], + "runtimeConfig": { + "portMappings": [ + { "hostPort": 0, "containerPort": 80, "protocol": "tcp"} + ] + } + }`, ver)) + _, _, err := parseConfig(configBytes, "container") + Expect(err).To(MatchError("Invalid host port number: 0")) + }) - ch = genMarkMasqChain(masqBit) - Expect(ch).To(Equal(chain{ - table: "nat", - name: "CNI-HOSTPORT-MASQ", - entryChains: []string{"POSTROUTING"}, - entryRules: [][]string{{ - "-m", "comment", - "--comment", "CNI portfwd requiring masquerade", - }}, - rules: [][]string{{ - "-m", "mark", - "--mark", "0x20/0x20", - "-j", "MASQUERADE", - }}, - prependEntry: true, - })) + It(fmt.Sprintf("[%s] does not fail on missing prevResult interface index", ver), func() { + configBytes := []byte(fmt.Sprintf(`{ + "name": "test", + "type": "portmap", + "cniVersion": "%s", + "runtimeConfig": { + "portMappings": [ + { "hostPort": 8080, "containerPort": 80, "protocol": "tcp"} + ] + }, + "conditionsV4": ["a", "b"], + "prevResult": { + "interfaces": [ + {"name": "host"} + ], + "ips": [ + { + "version": "4", + "address": "10.0.0.1/24", + "gateway": "10.0.0.1" + } + ] + } + }`, ver)) + _, _, err := parseConfig(configBytes, "container") + Expect(err).NotTo(HaveOccurred()) }) }) - }) + + Describe("Generating chains", func() { + Context("for DNAT", func() { + It(fmt.Sprintf("[%s] generates a correct standard container chain", ver), func() { + ch := genDnatChain(netName, containerID) + + Expect(ch).To(Equal(chain{ + table: "nat", + name: "CNI-DN-bfd599665540dd91d5d28", + entryChains: []string{TopLevelDNATChainName}, + })) + configBytes := []byte(fmt.Sprintf(`{ + "name": "test", + "type": "portmap", + "cniVersion": "%s", + "runtimeConfig": { + "portMappings": [ + { "hostPort": 8080, "containerPort": 80, "protocol": "tcp"}, + { "hostPort": 8081, "containerPort": 80, "protocol": "tcp"}, + { "hostPort": 8080, "containerPort": 81, "protocol": "udp"}, + { "hostPort": 8082, "containerPort": 82, "protocol": "udp"}, + { "hostPort": 8083, "containerPort": 83, "protocol": "tcp", "hostIP": "192.168.0.2"}, + { "hostPort": 8084, "containerPort": 84, "protocol": "tcp", "hostIP": "0.0.0.0"}, + { "hostPort": 8085, "containerPort": 85, "protocol": "tcp", "hostIP": "2001:db8:a::1"}, + { "hostPort": 8086, "containerPort": 86, "protocol": "tcp", "hostIP": "::"} + ] + }, + "snat": true, + "conditionsV4": ["a", "b"], + "conditionsV6": ["c", "d"] + }`, ver)) + + conf, _, err := parseConfig(configBytes, "foo") + Expect(err).NotTo(HaveOccurred()) + conf.ContainerID = containerID + + ch = genDnatChain(conf.Name, containerID) + Expect(ch).To(Equal(chain{ + table: "nat", + name: "CNI-DN-67e92b96e692a494b6b85", + entryChains: []string{"CNI-HOSTPORT-DNAT"}, + })) + + n, err := types.ParseCIDR("10.0.0.2/24") + fillDnatRules(&ch, conf, *n) + + Expect(ch.entryRules).To(Equal([][]string{ + {"-m", "comment", "--comment", + fmt.Sprintf("dnat name: \"test\" id: \"%s\"", containerID), + "-m", "multiport", + "-p", "tcp", + "--destination-ports", "8080,8081,8083,8084,8085,8086", + "a", "b"}, + {"-m", "comment", "--comment", + fmt.Sprintf("dnat name: \"test\" id: \"%s\"", containerID), + "-m", "multiport", + "-p", "udp", + "--destination-ports", "8080,8082", + "a", "b"}, + })) + + Expect(ch.rules).To(Equal([][]string{ + // tcp rules and not hostIP + {"-p", "tcp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"}, + {"-p", "tcp", "--dport", "8080", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"}, + {"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"}, + {"-p", "tcp", "--dport", "8081", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"}, + {"-p", "tcp", "--dport", "8081", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"}, + {"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "10.0.0.2:80"}, + // udp rules and not hostIP + {"-p", "udp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"}, + {"-p", "udp", "--dport", "8080", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"}, + {"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:81"}, + {"-p", "udp", "--dport", "8082", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"}, + {"-p", "udp", "--dport", "8082", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"}, + {"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "10.0.0.2:82"}, + // tcp rules and hostIP + {"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"}, + {"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"}, + {"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-j", "DNAT", "--to-destination", "10.0.0.2:83"}, + // tcp rules and hostIP = "0.0.0.0" + {"-p", "tcp", "--dport", "8084", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"}, + {"-p", "tcp", "--dport", "8084", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"}, + {"-p", "tcp", "--dport", "8084", "-j", "DNAT", "--to-destination", "10.0.0.2:84"}, + })) + + ch.rules = nil + ch.entryRules = nil + + n, err = types.ParseCIDR("2001:db8::2/64") + fillDnatRules(&ch, conf, *n) + + Expect(ch.rules).To(Equal([][]string{ + // tcp rules and not hostIP + {"-p", "tcp", "--dport", "8080", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"}, + {"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "[2001:db8::2]:80"}, + {"-p", "tcp", "--dport", "8081", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"}, + {"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "[2001:db8::2]:80"}, + // udp rules and not hostIP + {"-p", "udp", "--dport", "8080", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"}, + {"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "[2001:db8::2]:81"}, + {"-p", "udp", "--dport", "8082", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"}, + {"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "[2001:db8::2]:82"}, + // tcp rules and hostIP + {"-p", "tcp", "--dport", "8085", "-d", "2001:db8:a::1", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"}, + {"-p", "tcp", "--dport", "8085", "-d", "2001:db8:a::1", "-j", "DNAT", "--to-destination", "[2001:db8::2]:85"}, + // tcp rules and hostIP = "::" + {"-p", "tcp", "--dport", "8086", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"}, + {"-p", "tcp", "--dport", "8086", "-j", "DNAT", "--to-destination", "[2001:db8::2]:86"}, + })) + + // Disable snat, generate rules + ch.rules = nil + ch.entryRules = nil + fvar := false + conf.SNAT = &fvar + + n, err = types.ParseCIDR("10.0.0.2/24") + fillDnatRules(&ch, conf, *n) + Expect(ch.rules).To(Equal([][]string{ + {"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"}, + {"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "10.0.0.2:80"}, + {"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:81"}, + {"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "10.0.0.2:82"}, + {"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-j", "DNAT", "--to-destination", "10.0.0.2:83"}, + {"-p", "tcp", "--dport", "8084", "-j", "DNAT", "--to-destination", "10.0.0.2:84"}, + })) + }) + + It(fmt.Sprintf("[%s] generates a correct chain with external mark", ver), func() { + ch := genDnatChain(netName, containerID) + + Expect(ch).To(Equal(chain{ + table: "nat", + name: "CNI-DN-bfd599665540dd91d5d28", + entryChains: []string{TopLevelDNATChainName}, + })) + configBytes := []byte(fmt.Sprintf(`{ + "name": "test", + "type": "portmap", + "cniVersion": "%s", + "runtimeConfig": { + "portMappings": [ + { "hostPort": 8080, "containerPort": 80, "protocol": "tcp"} + ] + }, + "externalSetMarkChain": "PLZ-SET-MARK", + "conditionsV4": ["a", "b"], + "conditionsV6": ["c", "d"] + }`, ver)) + + conf, _, err := parseConfig(configBytes, "foo") + Expect(err).NotTo(HaveOccurred()) + conf.ContainerID = containerID + + ch = genDnatChain(conf.Name, containerID) + n, err := types.ParseCIDR("10.0.0.2/24") + fillDnatRules(&ch, conf, *n) + Expect(ch.rules).To(Equal([][]string{ + {"-p", "tcp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "PLZ-SET-MARK"}, + {"-p", "tcp", "--dport", "8080", "-s", "127.0.0.1", "-j", "PLZ-SET-MARK"}, + {"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"}, + })) + }) + + It(fmt.Sprintf("[%s] generates a correct top-level chain", ver), func() { + ch := genToplevelDnatChain() + + Expect(ch).To(Equal(chain{ + table: "nat", + name: "CNI-HOSTPORT-DNAT", + entryChains: []string{"PREROUTING", "OUTPUT"}, + entryRules: [][]string{{"-m", "addrtype", "--dst-type", "LOCAL"}}, + })) + }) + + It(fmt.Sprintf("[%s] generates the correct mark chains", ver), func() { + masqBit := 5 + ch := genSetMarkChain(masqBit) + Expect(ch).To(Equal(chain{ + table: "nat", + name: "CNI-HOSTPORT-SETMARK", + rules: [][]string{{ + "-m", "comment", + "--comment", "CNI portfwd masquerade mark", + "-j", "MARK", + "--set-xmark", "0x20/0x20", + }}, + })) + + ch = genMarkMasqChain(masqBit) + Expect(ch).To(Equal(chain{ + table: "nat", + name: "CNI-HOSTPORT-MASQ", + entryChains: []string{"POSTROUTING"}, + entryRules: [][]string{{ + "-m", "comment", + "--comment", "CNI portfwd requiring masquerade", + }}, + rules: [][]string{{ + "-m", "mark", + "--mark", "0x20/0x20", + "-j", "MASQUERADE", + }}, + prependEntry: true, + })) + }) + }) + }) + } }) diff --git a/plugins/meta/sbr/main.go b/plugins/meta/sbr/main.go index c0ab2f86..0dd1c6ad 100644 --- a/plugins/meta/sbr/main.go +++ b/plugins/meta/sbr/main.go @@ -26,7 +26,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/plugins/pkg/ns" @@ -234,7 +234,7 @@ func doRoutes(ipCfgs []*current.IPConfig, origRoutes []*types.Route, iface strin // Source must be restricted to a single IP, not a full subnet var src net.IPNet src.IP = ipCfg.Address.IP - if ipCfg.Version == "4" { + if src.IP.To4() != nil { src.Mask = net.CIDRMask(32, 32) } else { src.Mask = net.CIDRMask(128, 128) @@ -253,7 +253,7 @@ func doRoutes(ipCfgs []*current.IPConfig, origRoutes []*types.Route, iface strin log.Printf("Adding default route to gateway %s", ipCfg.Gateway.String()) var dest net.IPNet - if ipCfg.Version == "4" { + if ipCfg.Address.IP.To4() != nil { dest.IP = net.IPv4zero dest.Mask = net.CIDRMask(0, 32) } else { diff --git a/plugins/meta/sbr/sbr_linux_test.go b/plugins/meta/sbr/sbr_linux_test.go index 442d4aa1..d0efecd4 100644 --- a/plugins/meta/sbr/sbr_linux_test.go +++ b/plugins/meta/sbr/sbr_linux_test.go @@ -241,6 +241,7 @@ var _ = Describe("sbr test", func() { "name": "cni-plugin-sbr-test", "type": "sbr", "prevResult": { + "cniVersion": "0.3.0", "interfaces": [ { "name": "%s", @@ -332,6 +333,7 @@ var _ = Describe("sbr test", func() { "name": "cni-plugin-sbr-test", "type": "sbr", "prevResult": { + "cniVersion": "0.3.0", "interfaces": [ { "name": "%s", @@ -399,19 +401,12 @@ var _ = Describe("sbr test", func() { Expect(equalRoutes(expNet1.Routes, devNet1.Routes)).To(BeTrue()) }) - It("works with a 0.2.0 config", func() { + It("fails with CNI spec versions that don't support plugin chaining", func() { conf := `{ "cniVersion": "0.2.0", "name": "cni-plugin-sbr-test", "type": "sbr", - "anotherAwesomeArg": "foo", - "prevResult": { - "ip4": { - "ip": "192.168.1.209/24", - "gateway": "192.168.1.1", - "routes": [] - } - } + "anotherAwesomeArg": "foo" }` args := &skel.CmdArgs{ @@ -424,7 +419,7 @@ var _ = Describe("sbr test", func() { Expect(err).NotTo(HaveOccurred()) _, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) - Expect(err).NotTo(HaveOccurred()) + Expect(err).To(MatchError("This plugin must be called as chained plugin")) }) }) diff --git a/plugins/meta/tuning/tuning.go b/plugins/meta/tuning/tuning.go index efd3a27a..39017e05 100644 --- a/plugins/meta/tuning/tuning.go +++ b/plugins/meta/tuning/tuning.go @@ -32,7 +32,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/plugins/pkg/ns" @@ -322,7 +322,7 @@ func cmdAdd(args *skel.CmdArgs) error { } for _, ipc := range result.IPs { - if ipc.Version == "4" { + if ipc.Address.IP.To4() != nil { _ = arping.GratuitousArpOverIfaceByName(ipc.Address.IP, args.IfName) } } diff --git a/plugins/meta/tuning/tuning_test.go b/plugins/meta/tuning/tuning_test.go index 134291d3..81973c95 100644 --- a/plugins/meta/tuning/tuning_test.go +++ b/plugins/meta/tuning/tuning_test.go @@ -21,7 +21,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" @@ -75,7 +75,7 @@ func buildOneConfig(name, cniVersion string, orig *TuningConf, prevResult types. } var _ = Describe("tuning plugin", func() { - var originalNS ns.NetNS + var originalNS, targetNS ns.NetNS const IFNAME string = "dummy0" var beforeConf configToRestore @@ -85,6 +85,9 @@ var _ = Describe("tuning plugin", func() { originalNS, err = testutils.NewNS() Expect(err).NotTo(HaveOccurred()) + targetNS, err = testutils.NewNS() + Expect(err).NotTo(HaveOccurred()) + err = originalNS.Do(func(ns.NetNS) error { defer GinkgoRecover() @@ -108,976 +111,736 @@ var _ = Describe("tuning plugin", func() { AfterEach(func() { Expect(originalNS.Close()).To(Succeed()) + Expect(testutils.UnmountNS(originalNS)).To(Succeed()) + Expect(targetNS.Close()).To(Succeed()) + Expect(testutils.UnmountNS(targetNS)).To(Succeed()) }) - It("passes prevResult through unchanged", func() { - conf := []byte(`{ - "name": "test", - "type": "tuning", - "cniVersion": "0.3.1", - "sysctl": { - "net.ipv4.conf.all.log_martians": "1" - }, - "prevResult": { - "interfaces": [ - {"name": "dummy0", "sandbox":"netns"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.2/24", - "gateway": "10.0.0.1", - "interface": 0 + for _, ver := range []string{"0.3.0", "0.3.1", "0.4.0", "1.0.0"} { + // Redefine ver inside for scope so real value is picked up by each dynamically defined It() + // See Gingkgo's "Patterns for dynamically generating tests" documentation. + ver := ver + + It(fmt.Sprintf("[%s] passes prevResult through unchanged", ver), func() { + conf := []byte(fmt.Sprintf(`{ + "name": "test", + "type": "tuning", + "cniVersion": "%s", + "sysctl": { + "net.ipv4.conf.all.log_martians": "1" + }, + "prevResult": { + "interfaces": [ + {"name": "dummy0", "sandbox":"netns"} + ], + "ips": [ + { + "version": "4", + "address": "10.0.0.2/24", + "gateway": "10.0.0.1", + "interface": 0 + } + ] + } + }`, ver)) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: targetNS.Path(), + IfName: IFNAME, + StdinData: conf, } - ] - } -}`) - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() + beforeConf = configToRestore{} - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: targetNs.Path(), - IfName: IFNAME, - StdinData: conf, - } + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - beforeConf = configToRestore{} + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + Expect(len(result.Interfaces)).To(Equal(1)) + Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) + Expect(len(result.IPs)).To(Equal(1)) + Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) + + Expect("/tmp/tuning-test/dummy_dummy0.json").ShouldNot(BeAnExistingFile()) + + err = testutils.CmdDel(originalNS.Path(), + args.ContainerID, "", func() error { return cmdDel(args) }) + Expect(err).NotTo(HaveOccurred()) + + return nil }) Expect(err).NotTo(HaveOccurred()) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(1)) - Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) - - Expect("/tmp/tuning-test/dummy_dummy0.json").ShouldNot(BeAnExistingFile()) - - err = testutils.CmdDel(originalNS.Path(), - args.ContainerID, "", func() error { return cmdDel(args) }) - Expect(err).NotTo(HaveOccurred()) - - return nil }) - Expect(err).NotTo(HaveOccurred()) - }) - It("configures and deconfigures promiscuous mode with ADD/DEL", func() { - conf := []byte(`{ - "name": "test", - "type": "iplink", - "cniVersion": "0.3.1", - "promisc": true, - "prevResult": { - "interfaces": [ - {"name": "dummy0", "sandbox":"netns"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.2/24", - "gateway": "10.0.0.1", - "interface": 0 + It(fmt.Sprintf("[%s] configures and deconfigures promiscuous mode with ADD/DEL", ver), func() { + conf := []byte(fmt.Sprintf(`{ + "name": "test", + "type": "iplink", + "cniVersion": "%s", + "promisc": true, + "prevResult": { + "interfaces": [ + {"name": "dummy0", "sandbox":"netns"} + ], + "ips": [ + { + "version": "4", + "address": "10.0.0.2/24", + "gateway": "10.0.0.1", + "interface": 0 + } + ] + } + }`, ver)) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: originalNS.Path(), + IfName: IFNAME, + StdinData: conf, } - ] - } -}`) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: originalNS.Path(), - IfName: IFNAME, - StdinData: conf, - } + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + Expect(len(result.Interfaces)).To(Equal(1)) + Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) + Expect(len(result.IPs)).To(Equal(1)) + Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Promisc).To(Equal(1)) + + if testutils.SpecVersionHasCHECK(ver) { + n := &TuningConf{} + err = json.Unmarshal([]byte(conf), &n) + Expect(err).NotTo(HaveOccurred()) + + _, confString, err := buildOneConfig("testConfig", ver, n, r) + Expect(err).NotTo(HaveOccurred()) + + args.StdinData = confString + + err = testutils.CmdCheckWithArgs(args, func() error { + return cmdCheck(args) + }) + Expect(err).NotTo(HaveOccurred()) + } + + err = testutils.CmdDel(originalNS.Path(), + args.ContainerID, "", func() error { return cmdDel(args) }) + Expect(err).NotTo(HaveOccurred()) + + link, err = netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Promisc != 0).To(Equal(*beforeConf.Promisc)) + + return nil }) Expect(err).NotTo(HaveOccurred()) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(1)) - Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Promisc).To(Equal(1)) - - err = testutils.CmdDel(originalNS.Path(), - args.ContainerID, "", func() error { return cmdDel(args) }) - Expect(err).NotTo(HaveOccurred()) - - link, err = netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Promisc != 0).To(Equal(*beforeConf.Promisc)) - - return nil }) - Expect(err).NotTo(HaveOccurred()) - }) - It("configures and deconfigures promiscuous mode from args with ADD/DEL", func() { - conf := []byte(`{ - "name": "test", - "type": "iplink", - "cniVersion": "0.3.1", - "args": { - "cni": { - "promisc": true - } - }, - "prevResult": { - "interfaces": [ - {"name": "dummy0", "sandbox":"netns"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.2/24", - "gateway": "10.0.0.1", - "interface": 0 + It(fmt.Sprintf("[%s] configures and deconfigures promiscuous mode from args with ADD/DEL", ver), func() { + conf := []byte(fmt.Sprintf(`{ + "name": "test", + "type": "iplink", + "cniVersion": "%s", + "args": { + "cni": { + "promisc": true + } + }, + "prevResult": { + "interfaces": [ + {"name": "dummy0", "sandbox":"netns"} + ], + "ips": [ + { + "version": "4", + "address": "10.0.0.2/24", + "gateway": "10.0.0.1", + "interface": 0 + } + ] + } + }`, ver)) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: originalNS.Path(), + IfName: IFNAME, + StdinData: conf, } - ] - } -}`) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: originalNS.Path(), - IfName: IFNAME, - StdinData: conf, - } + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + Expect(len(result.Interfaces)).To(Equal(1)) + Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) + Expect(len(result.IPs)).To(Equal(1)) + Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Promisc).To(Equal(1)) + + err = testutils.CmdDel(originalNS.Path(), + args.ContainerID, "", func() error { return cmdDel(args) }) + Expect(err).NotTo(HaveOccurred()) + + link, err = netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().Promisc != 0).To(Equal(*beforeConf.Promisc)) + + return nil }) Expect(err).NotTo(HaveOccurred()) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(1)) - Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Promisc).To(Equal(1)) - - err = testutils.CmdDel(originalNS.Path(), - args.ContainerID, "", func() error { return cmdDel(args) }) - Expect(err).NotTo(HaveOccurred()) - - link, err = netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Promisc != 0).To(Equal(*beforeConf.Promisc)) - - return nil }) - Expect(err).NotTo(HaveOccurred()) - }) - It("configures and deconfigures mtu with ADD/DEL", func() { - conf := []byte(`{ - "name": "test", - "type": "iplink", - "cniVersion": "0.3.1", - "mtu": 1454, - "prevResult": { - "interfaces": [ - {"name": "dummy0", "sandbox":"netns"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.2/24", - "gateway": "10.0.0.1", - "interface": 0 + It(fmt.Sprintf("[%s] configures and deconfigures mtu with ADD/DEL", ver), func() { + conf := []byte(fmt.Sprintf(`{ + "name": "test", + "type": "iplink", + "cniVersion": "%s", + "mtu": 1454, + "prevResult": { + "interfaces": [ + {"name": "dummy0", "sandbox":"netns"} + ], + "ips": [ + { + "version": "4", + "address": "10.0.0.2/24", + "gateway": "10.0.0.1", + "interface": 0 + } + ] + } + }`, ver)) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: originalNS.Path(), + IfName: IFNAME, + StdinData: conf, } - ] - } -}`) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: originalNS.Path(), - IfName: IFNAME, - StdinData: conf, - } + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + Expect(len(result.Interfaces)).To(Equal(1)) + Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) + Expect(len(result.IPs)).To(Equal(1)) + Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().MTU).To(Equal(1454)) + + if testutils.SpecVersionHasCHECK(ver) { + n := &TuningConf{} + err = json.Unmarshal([]byte(conf), &n) + Expect(err).NotTo(HaveOccurred()) + + _, confString, err := buildOneConfig("testConfig", ver, n, r) + Expect(err).NotTo(HaveOccurred()) + + args.StdinData = confString + + err = testutils.CmdCheckWithArgs(args, func() error { + return cmdCheck(args) + }) + Expect(err).NotTo(HaveOccurred()) + } + + err = testutils.CmdDel(originalNS.Path(), + args.ContainerID, "", func() error { return cmdDel(args) }) + Expect(err).NotTo(HaveOccurred()) + + link, err = netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().MTU).To(Equal(beforeConf.Mtu)) + + return nil }) Expect(err).NotTo(HaveOccurred()) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(1)) - Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().MTU).To(Equal(1454)) - - err = testutils.CmdDel(originalNS.Path(), - args.ContainerID, "", func() error { return cmdDel(args) }) - Expect(err).NotTo(HaveOccurred()) - - link, err = netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().MTU).To(Equal(beforeConf.Mtu)) - - return nil }) - Expect(err).NotTo(HaveOccurred()) - }) - It("configures and deconfigures mtu from args with ADD/DEL", func() { - conf := []byte(`{ - "name": "test", - "type": "iplink", - "cniVersion": "0.3.1", - "args": { - "cni": { - "mtu": 1454 - } - }, - "prevResult": { - "interfaces": [ - {"name": "dummy0", "sandbox":"netns"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.2/24", - "gateway": "10.0.0.1", - "interface": 0 + It(fmt.Sprintf("[%s] configures and deconfigures mtu from args with ADD/DEL", ver), func() { + conf := []byte(fmt.Sprintf(`{ + "name": "test", + "type": "iplink", + "cniVersion": "%s", + "args": { + "cni": { + "mtu": 1454 + } + }, + "prevResult": { + "interfaces": [ + {"name": "dummy0", "sandbox":"netns"} + ], + "ips": [ + { + "version": "4", + "address": "10.0.0.2/24", + "gateway": "10.0.0.1", + "interface": 0 + } + ] + } + }`, ver)) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: originalNS.Path(), + IfName: IFNAME, + StdinData: conf, } - ] - } -}`) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: originalNS.Path(), - IfName: IFNAME, - StdinData: conf, - } + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + Expect(len(result.Interfaces)).To(Equal(1)) + Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) + Expect(len(result.IPs)).To(Equal(1)) + Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().MTU).To(Equal(1454)) + + err = testutils.CmdDel(originalNS.Path(), + args.ContainerID, "", func() error { return cmdDel(args) }) + Expect(err).NotTo(HaveOccurred()) + + link, err = netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().MTU).To(Equal(beforeConf.Mtu)) + + return nil }) Expect(err).NotTo(HaveOccurred()) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(1)) - Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().MTU).To(Equal(1454)) - - err = testutils.CmdDel(originalNS.Path(), - args.ContainerID, "", func() error { return cmdDel(args) }) - Expect(err).NotTo(HaveOccurred()) - - link, err = netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().MTU).To(Equal(beforeConf.Mtu)) - - return nil }) - Expect(err).NotTo(HaveOccurred()) - }) - It("configures and deconfigures mac address (from conf file) with ADD/DEL", func() { - conf := []byte(`{ - "name": "test", - "type": "iplink", - "cniVersion": "0.3.1", - "mac": "c2:11:22:33:44:55", - "prevResult": { - "interfaces": [ - {"name": "dummy0", "sandbox":"netns"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.2/24", - "gateway": "10.0.0.1", - "interface": 0 + It(fmt.Sprintf("[%s] configures and deconfigures mac address (from conf file) with ADD/DEL", ver), func() { + conf := []byte(fmt.Sprintf(`{ + "name": "test", + "type": "iplink", + "cniVersion": "%s", + "mac": "c2:11:22:33:44:55", + "prevResult": { + "interfaces": [ + {"name": "dummy0", "sandbox":"netns"} + ], + "ips": [ + { + "version": "4", + "address": "10.0.0.2/24", + "gateway": "10.0.0.1", + "interface": 0 + } + ] + } + }`, ver)) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: originalNS.Path(), + IfName: IFNAME, + StdinData: conf, } - ] - } -}`) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: originalNS.Path(), - IfName: IFNAME, - StdinData: conf, - } + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + Expect(len(result.Interfaces)).To(Equal(1)) + Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) + Expect(len(result.IPs)).To(Equal(1)) + Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + hw, err := net.ParseMAC("c2:11:22:33:44:55") + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(hw)) + + if testutils.SpecVersionHasCHECK(ver) { + n := &TuningConf{} + err = json.Unmarshal([]byte(conf), &n) + Expect(err).NotTo(HaveOccurred()) + + _, confString, err := buildOneConfig("testConfig", ver, n, r) + Expect(err).NotTo(HaveOccurred()) + + args.StdinData = confString + + err = testutils.CmdCheckWithArgs(args, func() error { + return cmdCheck(args) + }) + Expect(err).NotTo(HaveOccurred()) + } + + err = testutils.CmdDel(originalNS.Path(), + args.ContainerID, "", func() error { return cmdDel(args) }) + Expect(err).NotTo(HaveOccurred()) + + link, err = netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr.String()).To(Equal(beforeConf.Mac)) + + return nil }) Expect(err).NotTo(HaveOccurred()) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(1)) - Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - hw, err := net.ParseMAC("c2:11:22:33:44:55") - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hw)) - - err = testutils.CmdDel(originalNS.Path(), - args.ContainerID, "", func() error { return cmdDel(args) }) - Expect(err).NotTo(HaveOccurred()) - - link, err = netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr.String()).To(Equal(beforeConf.Mac)) - - return nil }) - Expect(err).NotTo(HaveOccurred()) - }) - It("configures and deconfigures mac address (from args) with ADD/DEL", func() { - conf := []byte(`{ - "name": "test", - "type": "iplink", - "cniVersion": "0.3.1", - "args": { - "cni": { - "mac": "c2:11:22:33:44:55" - } - }, - "prevResult": { - "interfaces": [ - {"name": "dummy0", "sandbox":"netns"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.2/24", - "gateway": "10.0.0.1", - "interface": 0 + It(fmt.Sprintf("[%s] configures and deconfigures mac address (from args) with ADD/DEL", ver), func() { + conf := []byte(fmt.Sprintf(`{ + "name": "test", + "type": "iplink", + "cniVersion": "%s", + "args": { + "cni": { + "mac": "c2:11:22:33:44:55" + } + }, + "prevResult": { + "interfaces": [ + {"name": "dummy0", "sandbox":"netns"} + ], + "ips": [ + { + "version": "4", + "address": "10.0.0.2/24", + "gateway": "10.0.0.1", + "interface": 0 + } + ] + } + }`, ver)) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: originalNS.Path(), + IfName: IFNAME, + StdinData: conf, } - ] - } -}`) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: originalNS.Path(), - IfName: IFNAME, - StdinData: conf, - } + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + Expect(len(result.Interfaces)).To(Equal(1)) + Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) + Expect(len(result.IPs)).To(Equal(1)) + Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + hw, err := net.ParseMAC("c2:11:22:33:44:55") + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(hw)) + + err = testutils.CmdDel(originalNS.Path(), + args.ContainerID, "", func() error { return cmdDel(args) }) + Expect(err).NotTo(HaveOccurred()) + + link, err = netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr.String()).To(Equal(beforeConf.Mac)) + + return nil }) Expect(err).NotTo(HaveOccurred()) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(1)) - Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - hw, err := net.ParseMAC("c2:11:22:33:44:55") - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hw)) - - err = testutils.CmdDel(originalNS.Path(), - args.ContainerID, "", func() error { return cmdDel(args) }) - Expect(err).NotTo(HaveOccurred()) - - link, err = netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr.String()).To(Equal(beforeConf.Mac)) - - return nil }) - Expect(err).NotTo(HaveOccurred()) - }) - It("configures and deconfigures mac address (from CNI_ARGS) with ADD/DEL", func() { - conf := []byte(`{ - "name": "test", - "type": "iplink", - "cniVersion": "0.3.1", - "prevResult": { - "interfaces": [ - {"name": "dummy0", "sandbox":"netns"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.2/24", - "gateway": "10.0.0.1", - "interface": 0 + It(fmt.Sprintf("[%s] configures and deconfigures mac address (from CNI_ARGS) with ADD/DEL", ver), func() { + conf := []byte(fmt.Sprintf(`{ + "name": "test", + "type": "iplink", + "cniVersion": "%s", + "prevResult": { + "interfaces": [ + {"name": "dummy0", "sandbox":"netns"} + ], + "ips": [ + { + "version": "4", + "address": "10.0.0.2/24", + "gateway": "10.0.0.1", + "interface": 0 + } + ] + } + }`, ver)) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: originalNS.Path(), + IfName: IFNAME, + StdinData: conf, + Args: "IgnoreUnknown=true;MAC=c2:11:22:33:44:66", } - ] - } -}`) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: originalNS.Path(), - IfName: IFNAME, - StdinData: conf, - Args: "IgnoreUnknown=true;MAC=c2:11:22:33:44:66", - } + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + Expect(len(result.Interfaces)).To(Equal(1)) + Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) + Expect(len(result.IPs)).To(Equal(1)) + Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + hw, err := net.ParseMAC("c2:11:22:33:44:66") + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(hw)) + + if testutils.SpecVersionHasCHECK(ver) { + n := &TuningConf{} + err = json.Unmarshal([]byte(conf), &n) + Expect(err).NotTo(HaveOccurred()) + + _, confString, err := buildOneConfig("testConfig", ver, n, r) + Expect(err).NotTo(HaveOccurred()) + + args.StdinData = confString + + err = testutils.CmdCheckWithArgs(args, func() error { + return cmdCheck(args) + }) + Expect(err).NotTo(HaveOccurred()) + } + + err = testutils.CmdDel(originalNS.Path(), + args.ContainerID, "", func() error { return cmdDel(args) }) + Expect(err).NotTo(HaveOccurred()) + + link, err = netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr.String()).To(Equal(beforeConf.Mac)) + + return nil }) Expect(err).NotTo(HaveOccurred()) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(1)) - Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - hw, err := net.ParseMAC("c2:11:22:33:44:66") - Expect(err).NotTo(HaveOccurred()) - fmt.Printf("%v, %v\n", link.Attrs().HardwareAddr, hw) - Expect(link.Attrs().HardwareAddr).To(Equal(hw)) - - err = testutils.CmdDel(originalNS.Path(), - args.ContainerID, "", func() error { return cmdDel(args) }) - Expect(err).NotTo(HaveOccurred()) - - link, err = netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr.String()).To(Equal(beforeConf.Mac)) - - return nil }) - Expect(err).NotTo(HaveOccurred()) - }) - It("configures and deconfigures promiscuous mode with CNI 0.4.0 ADD/DEL", func() { - conf := []byte(`{ - "name": "test", - "type": "iplink", - "cniVersion": "0.4.0", - "promisc": true, - "prevResult": { - "interfaces": [ - {"name": "dummy0", "sandbox":"netns"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.2/24", - "gateway": "10.0.0.1", - "interface": 0 + It(fmt.Sprintf("[%s] configures and deconfigures mac address (from RuntimeConfig) with ADD/DEL", ver), func() { + conf := []byte(fmt.Sprintf(`{ + "name": "test", + "type": "iplink", + "cniVersion": "%s", + "capabilities": {"mac": true}, + "RuntimeConfig": { + "mac": "c2:11:22:33:44:55" + }, + "prevResult": { + "interfaces": [ + {"name": "dummy0", "sandbox":"netns"} + ], + "ips": [ + { + "version": "4", + "address": "10.0.0.2/24", + "gateway": "10.0.0.1", + "interface": 0 + } + ] + } + }`, ver)) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: originalNS.Path(), + IfName: IFNAME, + StdinData: conf, } - ] - } -}`) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: originalNS.Path(), - IfName: IFNAME, - StdinData: conf, - } + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + Expect(len(result.Interfaces)).To(Equal(1)) + Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) + Expect(len(result.IPs)).To(Equal(1)) + Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + hw, err := net.ParseMAC("c2:11:22:33:44:55") + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(hw)) + + err = testutils.CmdDel(originalNS.Path(), + args.ContainerID, "", func() error { return cmdDel(args) }) + Expect(err).NotTo(HaveOccurred()) + + link, err = netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr.String()).To(Equal(beforeConf.Mac)) + + return nil }) Expect(err).NotTo(HaveOccurred()) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(1)) - Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Promisc).To(Equal(1)) - - n := &TuningConf{} - err = json.Unmarshal([]byte(conf), &n) - Expect(err).NotTo(HaveOccurred()) - - cniVersion := "0.4.0" - _, confString, err := buildOneConfig("testConfig", cniVersion, n, r) - Expect(err).NotTo(HaveOccurred()) - - args.StdinData = confString - - err = testutils.CmdCheckWithArgs(args, func() error { - return cmdCheck(args) - }) - Expect(err).NotTo(HaveOccurred()) - - err = testutils.CmdDel(originalNS.Path(), - args.ContainerID, "", func() error { return cmdDel(args) }) - Expect(err).NotTo(HaveOccurred()) - - link, err = netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().Promisc != 0).To(Equal(*beforeConf.Promisc)) - - return nil }) - Expect(err).NotTo(HaveOccurred()) - }) - It("configures and deconfigures mtu with CNI 0.4.0 ADD/DEL", func() { - conf := []byte(`{ - "name": "test", - "type": "iplink", - "cniVersion": "0.4.0", - "mtu": 1454, - "prevResult": { - "interfaces": [ - {"name": "dummy0", "sandbox":"netns"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.2/24", - "gateway": "10.0.0.1", - "interface": 0 + It(fmt.Sprintf("[%s] configures and deconfigures mac address, promisc mode and MTU (from conf file) with custom dataDir", ver), func() { + conf := []byte(fmt.Sprintf(`{ + "name": "test", + "type": "iplink", + "cniVersion": "%s", + "mac": "c2:11:22:33:44:77", + "promisc": true, + "mtu": 4000, + "dataDir": "/tmp/tuning-test", + "prevResult": { + "interfaces": [ + {"name": "dummy0", "sandbox":"netns"} + ], + "ips": [ + { + "version": "4", + "address": "10.0.0.2/24", + "gateway": "10.0.0.1", + "interface": 0 + } + ] + } + }`, ver)) + + args := &skel.CmdArgs{ + ContainerID: "dummy", + Netns: originalNS.Path(), + IfName: IFNAME, + StdinData: conf, } - ] - } -}`) - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: originalNS.Path(), - IfName: IFNAME, - StdinData: conf, - } + err := originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() + r, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + Expect(err).NotTo(HaveOccurred()) - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + result, err := types100.GetResult(r) + Expect(err).NotTo(HaveOccurred()) + + Expect(len(result.Interfaces)).To(Equal(1)) + Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) + Expect(len(result.IPs)).To(Equal(1)) + Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) + + link, err := netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + hw, err := net.ParseMAC("c2:11:22:33:44:77") + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr).To(Equal(hw)) + Expect(link.Attrs().Promisc).To(Equal(1)) + Expect(link.Attrs().MTU).To(Equal(4000)) + + Expect("/tmp/tuning-test/dummy_dummy0.json").Should(BeAnExistingFile()) + + if testutils.SpecVersionHasCHECK(ver) { + n := &TuningConf{} + err = json.Unmarshal([]byte(conf), &n) + Expect(err).NotTo(HaveOccurred()) + + _, confString, err := buildOneConfig("testConfig", ver, n, r) + Expect(err).NotTo(HaveOccurred()) + + args.StdinData = confString + + err = testutils.CmdCheckWithArgs(args, func() error { + return cmdCheck(args) + }) + Expect(err).NotTo(HaveOccurred()) + } + + err = testutils.CmdDel(originalNS.Path(), + args.ContainerID, "", func() error { return cmdDel(args) }) + Expect(err).NotTo(HaveOccurred()) + + link, err = netlink.LinkByName(IFNAME) + Expect(err).NotTo(HaveOccurred()) + Expect(link.Attrs().HardwareAddr.String()).To(Equal(beforeConf.Mac)) + Expect(link.Attrs().MTU).To(Equal(beforeConf.Mtu)) + Expect(link.Attrs().Promisc != 0).To(Equal(*beforeConf.Promisc)) + + return nil }) Expect(err).NotTo(HaveOccurred()) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(1)) - Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().MTU).To(Equal(1454)) - - n := &TuningConf{} - err = json.Unmarshal([]byte(conf), &n) - Expect(err).NotTo(HaveOccurred()) - - cniVersion := "0.4.0" - _, confString, err := buildOneConfig("testConfig", cniVersion, n, r) - Expect(err).NotTo(HaveOccurred()) - - args.StdinData = confString - - err = testutils.CmdCheckWithArgs(args, func() error { - return cmdCheck(args) - }) - Expect(err).NotTo(HaveOccurred()) - - err = testutils.CmdDel(originalNS.Path(), - args.ContainerID, "", func() error { return cmdDel(args) }) - Expect(err).NotTo(HaveOccurred()) - - link, err = netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().MTU).To(Equal(beforeConf.Mtu)) - - return nil }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("configures and deconfigures mac address (from conf file) with CNI v4.0 ADD/DEL", func() { - conf := []byte(`{ - "name": "test", - "type": "iplink", - "cniVersion": "0.4.0", - "mac": "c2:11:22:33:44:55", - "prevResult": { - "interfaces": [ - {"name": "dummy0", "sandbox":"netns"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.2/24", - "gateway": "10.0.0.1", - "interface": 0 - } - ] } -}`) - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: originalNS.Path(), - IfName: IFNAME, - StdinData: conf, - } - - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(1)) - Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - hw, err := net.ParseMAC("c2:11:22:33:44:55") - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hw)) - - n := &TuningConf{} - err = json.Unmarshal([]byte(conf), &n) - Expect(err).NotTo(HaveOccurred()) - - cniVersion := "0.4.0" - _, confString, err := buildOneConfig("testConfig", cniVersion, n, r) - Expect(err).NotTo(HaveOccurred()) - - args.StdinData = confString - - err = testutils.CmdCheckWithArgs(args, func() error { - return cmdCheck(args) - }) - Expect(err).NotTo(HaveOccurred()) - - err = testutils.CmdDel(originalNS.Path(), - args.ContainerID, "", func() error { return cmdDel(args) }) - Expect(err).NotTo(HaveOccurred()) - - link, err = netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr.String()).To(Equal(beforeConf.Mac)) - - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("configures and deconfigures mac address (from CNI_ARGS) with CNI v4 ADD/DEL", func() { - conf := []byte(`{ - "name": "test", - "type": "iplink", - "cniVersion": "0.4.0", - "prevResult": { - "interfaces": [ - {"name": "dummy0", "sandbox":"netns"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.2/24", - "gateway": "10.0.0.1", - "interface": 0 - } - ] - } -}`) - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: originalNS.Path(), - IfName: IFNAME, - StdinData: conf, - Args: "IgnoreUnknown=true;MAC=c2:11:22:33:44:66", - } - - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(1)) - Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - hw, err := net.ParseMAC("c2:11:22:33:44:66") - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hw)) - - n := &TuningConf{} - err = json.Unmarshal([]byte(conf), &n) - Expect(err).NotTo(HaveOccurred()) - - cniVersion := "0.4.0" - _, confString, err := buildOneConfig("testConfig", cniVersion, n, r) - Expect(err).NotTo(HaveOccurred()) - - args.StdinData = confString - - err = testutils.CmdCheckWithArgs(args, func() error { - return cmdCheck(args) - }) - Expect(err).NotTo(HaveOccurred()) - - err = testutils.CmdDel(originalNS.Path(), - args.ContainerID, "", func() error { return cmdDel(args) }) - Expect(err).NotTo(HaveOccurred()) - - link, err = netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr.String()).To(Equal(beforeConf.Mac)) - - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("configures and deconfigures mac address (from RuntimeConfig) with ADD/DEL", func() { - conf := []byte(`{ - "name": "test", - "type": "iplink", - "cniVersion": "0.3.1", - "capabilities": {"mac": true}, - "RuntimeConfig": { - "mac": "c2:11:22:33:44:55" - }, - "prevResult": { - "interfaces": [ - {"name": "dummy0", "sandbox":"netns"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.2/24", - "gateway": "10.0.0.1", - "interface": 0 - } - ] - } -}`) - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: originalNS.Path(), - IfName: IFNAME, - StdinData: conf, - } - - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(1)) - Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - hw, err := net.ParseMAC("c2:11:22:33:44:55") - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hw)) - - err = testutils.CmdDel(originalNS.Path(), - args.ContainerID, "", func() error { return cmdDel(args) }) - Expect(err).NotTo(HaveOccurred()) - - link, err = netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr.String()).To(Equal(beforeConf.Mac)) - - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - - It("configures and deconfigures mac address, promisc mode and MTU (from conf file) with custom dataDir", func() { - conf := []byte(`{ - "name": "test", - "type": "iplink", - "cniVersion": "0.4.0", - "mac": "c2:11:22:33:44:77", - "promisc": true, - "mtu": 4000, - "dataDir": "/tmp/tuning-test", - "prevResult": { - "interfaces": [ - {"name": "dummy0", "sandbox":"netns"} - ], - "ips": [ - { - "version": "4", - "address": "10.0.0.2/24", - "gateway": "10.0.0.1", - "interface": 0 - } - ] - } -}`) - - args := &skel.CmdArgs{ - ContainerID: "dummy", - Netns: originalNS.Path(), - IfName: IFNAME, - StdinData: conf, - } - - err := originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - r, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) - }) - Expect(err).NotTo(HaveOccurred()) - - result, err := current.GetResult(r) - Expect(err).NotTo(HaveOccurred()) - - Expect(len(result.Interfaces)).To(Equal(1)) - Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) - Expect(len(result.IPs)).To(Equal(1)) - Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24")) - - link, err := netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - hw, err := net.ParseMAC("c2:11:22:33:44:77") - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr).To(Equal(hw)) - Expect(link.Attrs().Promisc).To(Equal(1)) - Expect(link.Attrs().MTU).To(Equal(4000)) - - Expect("/tmp/tuning-test/dummy_dummy0.json").Should(BeAnExistingFile()) - - n := &TuningConf{} - err = json.Unmarshal([]byte(conf), &n) - Expect(err).NotTo(HaveOccurred()) - - cniVersion := "0.4.0" - _, confString, err := buildOneConfig("testConfig", cniVersion, n, r) - Expect(err).NotTo(HaveOccurred()) - - args.StdinData = confString - - err = testutils.CmdCheckWithArgs(args, func() error { - return cmdCheck(args) - }) - Expect(err).NotTo(HaveOccurred()) - - err = testutils.CmdDel(originalNS.Path(), - args.ContainerID, "", func() error { return cmdDel(args) }) - Expect(err).NotTo(HaveOccurred()) - - link, err = netlink.LinkByName(IFNAME) - Expect(err).NotTo(HaveOccurred()) - Expect(link.Attrs().HardwareAddr.String()).To(Equal(beforeConf.Mac)) - Expect(link.Attrs().MTU).To(Equal(beforeConf.Mtu)) - Expect(link.Attrs().Promisc != 0).To(Equal(*beforeConf.Promisc)) - - return nil - }) - Expect(err).NotTo(HaveOccurred()) - }) - }) diff --git a/plugins/meta/vrf/main.go b/plugins/meta/vrf/main.go index b47a5cb2..3d5a002a 100644 --- a/plugins/meta/vrf/main.go +++ b/plugins/meta/vrf/main.go @@ -22,7 +22,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/plugins/pkg/ns" diff --git a/plugins/meta/vrf/vrf_test.go b/plugins/meta/vrf/vrf_test.go index 4b07886f..c7523bc7 100644 --- a/plugins/meta/vrf/vrf_test.go +++ b/plugins/meta/vrf/vrf_test.go @@ -20,7 +20,7 @@ import ( "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/testutils" diff --git a/plugins/sample/main.go b/plugins/sample/main.go index 2fcef412..67b03217 100644 --- a/plugins/sample/main.go +++ b/plugins/sample/main.go @@ -19,11 +19,10 @@ package main import ( "encoding/json" "fmt" - "net" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/current" + current "github.com/containernetworking/cni/pkg/types/100" "github.com/containernetworking/cni/pkg/version" bv "github.com/containernetworking/plugins/pkg/utils/buildversion" @@ -33,21 +32,15 @@ import ( // is passed in on stdin. Your plugin may wish to expose its functionality via // runtime args, see CONVENTIONS.md in the CNI spec. type PluginConf struct { - types.NetConf // You may wish to not nest this type + // This embeds the standard NetConf structure which allows your plugin + // to more easily parse standard fields like Name, Type, CNIVersion, + // and PrevResult. + types.NetConf + RuntimeConfig *struct { SampleConfig map[string]interface{} `json:"sample"` } `json:"runtimeConfig"` - // This is the previous result, when called in the context of a chained - // plugin. Because this plugin supports multiple versions, we'll have to - // parse this in two passes. If your plugin is not chained, this can be - // removed (though you may wish to error if a non-chainable plugin is - // chained. - // If you need to modify the result before returning it, you will need - // to actually convert it to a concrete versioned struct. - RawPrevResult *map[string]interface{} `json:"prevResult"` - PrevResult *current.Result `json:"-"` - // Add plugin-specifc flags here MyAwesomeFlag bool `json:"myAwesomeFlag"` AnotherAwesomeArg string `json:"anotherAwesomeArg"` @@ -61,21 +54,12 @@ func parseConfig(stdin []byte) (*PluginConf, error) { return nil, fmt.Errorf("failed to parse network configuration: %v", err) } - // Parse previous result. Remove this if your plugin is not chained. - if conf.RawPrevResult != nil { - resultBytes, err := json.Marshal(conf.RawPrevResult) - if err != nil { - return nil, fmt.Errorf("could not serialize prevResult: %v", err) - } - res, err := version.NewResult(conf.CNIVersion, resultBytes) - if err != nil { - return nil, fmt.Errorf("could not parse prevResult: %v", err) - } - conf.RawPrevResult = nil - conf.PrevResult, err = current.NewResultFromResult(res) - if err != nil { - return nil, fmt.Errorf("could not convert result to current version: %v", err) - } + // Parse previous result. This will parse, validate, and place the + // previous result object into conf.PrevResult. If you need to modify + // or inspect the PrevResult you will need to convert it to a concrete + // versioned Result struct. + if err := version.ParsePrevResult(&conf.NetConf); err != nil { + return nil, fmt.Errorf("could not parse prevResult: %v", err) } // End previous result parsing @@ -94,50 +78,62 @@ func cmdAdd(args *skel.CmdArgs) error { return err } - // Remove this if this is an "originating" plugin + // A plugin can be either an "originating" plugin or a "chained" plugin. + // Originating plugins perform initial sandbox setup and do not require + // any result from a previous plugin in the chain. A chained plugin + // modifies sandbox configuration that was previously set up by an + // originating plugin and may optionally require a PrevResult from + // earlier plugins in the chain. + + // START chained plugin code if conf.PrevResult == nil { return fmt.Errorf("must be called as chained plugin") } - // Uncomment if this is an "originating" plugin - - //if conf.PrevResult != nil { - // return fmt.Errorf("must be called as the first plugin") - // } - - // This is some sample code to generate the list of container-side IPs. - // We're casting the prevResult to a 0.3.0 response, which can also include - // host-side IPs (but doesn't when converted from a 0.2.0 response). - // - // You don't need this if you are writing an "originating" plugin. - containerIPs := make([]net.IP, 0, len(conf.PrevResult.IPs)) - if conf.CNIVersion != "0.3.0" { - for _, ip := range conf.PrevResult.IPs { - containerIPs = append(containerIPs, ip.Address.IP) - } - } else { - for _, ip := range conf.PrevResult.IPs { - if ip.Interface == nil { - continue - } - intIdx := *ip.Interface - // Every IP is indexed in to the interfaces array, with "-1" standing - // for an unknown interface (which we'll assume to be Container-side - // Skip all IPs we know belong to an interface with the wrong name. - if intIdx >= 0 && intIdx < len(conf.PrevResult.Interfaces) && conf.PrevResult.Interfaces[intIdx].Name != args.IfName { - continue - } - containerIPs = append(containerIPs, ip.Address.IP) - } + // Convert the PrevResult to a concrete Result type that can be modified. + prevResult, err := current.GetResult(conf.PrevResult) + if err != nil { + return fmt.Errorf("failed to convert prevResult: %v", err) } - if len(containerIPs) == 0 { + + if len(prevResult.IPs) == 0 { return fmt.Errorf("got no container IPs") } + // Pass the prevResult through this plugin to the next one + result := prevResult + + // END chained plugin code + + // START originating plugin code + // if conf.PrevResult != nil { + // return fmt.Errorf("must be called as the first plugin") + // } + + // Generate some fake container IPs and add to the result + // result := ¤t.Result{CNIVersion: current.ImplementedSpecVersion} + // result.Interfaces = []*current.Interface{ + // { + // Name: "intf0", + // Sandbox: args.Netns, + // Mac: "00:11:22:33:44:55", + // }, + // } + // result.IPs = []*current.IPConfig{ + // { + // Address: "1.2.3.4/24", + // Gateway: "1.2.3.1", + // // Interface is an index into the Interfaces array + // // of the Interface element this IP applies to + // Interface: current.Int(0), + // } + // } + // END originating plugin code + // Implement your plugin here // Pass through the result for the next plugin - return types.PrintResult(conf.PrevResult, conf.CNIVersion) + return types.PrintResult(result, conf.CNIVersion) } // cmdDel is called for DELETE requests diff --git a/plugins/sample/sample_linux_test.go b/plugins/sample/sample_linux_test.go index 071bf924..1c4949ae 100644 --- a/plugins/sample/sample_linux_test.go +++ b/plugins/sample/sample_linux_test.go @@ -45,6 +45,7 @@ var _ = Describe("sample test", func() { "type": "sample", "anotherAwesomeArg": "awesome", "prevResult": { + "cniVersion": "0.3.0", "interfaces": [ { "name": "%s", @@ -71,7 +72,6 @@ var _ = Describe("sample test", func() { } _, _, err := testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) Expect(err).NotTo(HaveOccurred()) - }) It("fails an invalid config", func() { @@ -106,22 +106,14 @@ var _ = Describe("sample test", func() { } _, _, err := testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) Expect(err).To(MatchError("anotherAwesomeArg must be specified")) - }) - It("works with a 0.2.0 config", func() { + It("fails with CNI spec versions that don't support plugin chaining", func() { conf := `{ "cniVersion": "0.2.0", "name": "cni-plugin-sample-test", "type": "sample", - "anotherAwesomeArg": "foo", - "prevResult": { - "ip4": { - "ip": "10.0.0.2/24", - "gateway": "10.0.0.1", - "routes": [] - } - } + "anotherAwesomeArg": "foo" }` args := &skel.CmdArgs{ @@ -131,8 +123,7 @@ var _ = Describe("sample test", func() { StdinData: []byte(conf), } _, _, err := testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) - Expect(err).NotTo(HaveOccurred()) - + Expect(err).To(MatchError("must be called as chained plugin")) }) }) diff --git a/vendor/github.com/containernetworking/cni/pkg/types/020/types.go b/vendor/github.com/containernetworking/cni/pkg/types/020/types.go index 36f31678..6d4878ca 100644 --- a/vendor/github.com/containernetworking/cni/pkg/types/020/types.go +++ b/vendor/github.com/containernetworking/cni/pkg/types/020/types.go @@ -22,25 +22,44 @@ import ( "os" "github.com/containernetworking/cni/pkg/types" + convert "github.com/containernetworking/cni/pkg/types/internal" ) const ImplementedSpecVersion string = "0.2.0" -var SupportedVersions = []string{"", "0.1.0", ImplementedSpecVersion} +var supportedVersions = []string{"", "0.1.0", ImplementedSpecVersion} + +// Register converters for all versions less than the implemented spec version +func init() { + convert.RegisterConverter("0.1.0", []string{ImplementedSpecVersion}, convertFrom010) + convert.RegisterConverter(ImplementedSpecVersion, []string{"0.1.0"}, convertTo010) + + // Creator + convert.RegisterCreator(supportedVersions, NewResult) +} // Compatibility types for CNI version 0.1.0 and 0.2.0 +// NewResult creates a new Result object from JSON data. The JSON data +// must be compatible with the CNI versions implemented by this type. func NewResult(data []byte) (types.Result, error) { result := &Result{} if err := json.Unmarshal(data, result); err != nil { return nil, err } - return result, nil + for _, v := range supportedVersions { + if result.CNIVersion == v { + return result, nil + } + } + return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q", + supportedVersions, result.CNIVersion) } +// GetResult converts the given Result object to the ImplementedSpecVersion +// and returns the concrete type or an error func GetResult(r types.Result) (*Result, error) { - // We expect version 0.1.0/0.2.0 results - result020, err := r.GetAsVersion(ImplementedSpecVersion) + result020, err := convert.Convert(r, ImplementedSpecVersion) if err != nil { return nil, err } @@ -51,6 +70,32 @@ func GetResult(r types.Result) (*Result, error) { return result, nil } +func convertFrom010(from types.Result, toVersion string) (types.Result, error) { + if toVersion != "0.2.0" { + panic("only converts to version 0.2.0") + } + fromResult := from.(*Result) + return &Result{ + CNIVersion: ImplementedSpecVersion, + IP4: fromResult.IP4.Copy(), + IP6: fromResult.IP6.Copy(), + DNS: *fromResult.DNS.Copy(), + }, nil +} + +func convertTo010(from types.Result, toVersion string) (types.Result, error) { + if toVersion != "0.1.0" { + panic("only converts to version 0.1.0") + } + fromResult := from.(*Result) + return &Result{ + CNIVersion: "0.1.0", + IP4: fromResult.IP4.Copy(), + IP6: fromResult.IP6.Copy(), + DNS: *fromResult.DNS.Copy(), + }, nil +} + // Result is what gets returned from the plugin (via stdout) to the caller type Result struct { CNIVersion string `json:"cniVersion,omitempty"` @@ -60,17 +105,16 @@ type Result struct { } func (r *Result) Version() string { - return ImplementedSpecVersion + return r.CNIVersion } func (r *Result) GetAsVersion(version string) (types.Result, error) { - for _, supportedVersion := range SupportedVersions { - if version == supportedVersion { - r.CNIVersion = version - return r, nil - } + // If the creator of the result did not set the CNIVersion, assume it + // should be the highest spec version implemented by this Result + if r.CNIVersion == "" { + r.CNIVersion = ImplementedSpecVersion } - return nil, fmt.Errorf("cannot convert version %q to %s", SupportedVersions, version) + return convert.Convert(r, version) } func (r *Result) Print() error { @@ -93,6 +137,22 @@ type IPConfig struct { Routes []types.Route } +func (i *IPConfig) Copy() *IPConfig { + if i == nil { + return nil + } + + var routes []types.Route + for _, fromRoute := range i.Routes { + routes = append(routes, *fromRoute.Copy()) + } + return &IPConfig{ + IP: i.IP, + Gateway: i.Gateway, + Routes: routes, + } +} + // net.IPNet is not JSON (un)marshallable so this duality is needed // for our custom IPNet type diff --git a/vendor/github.com/containernetworking/cni/pkg/types/040/types.go b/vendor/github.com/containernetworking/cni/pkg/types/040/types.go new file mode 100644 index 00000000..c1986adc --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/types/040/types.go @@ -0,0 +1,305 @@ +// Copyright 2016 CNI authors +// +// 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 types040 + +import ( + "encoding/json" + "fmt" + "io" + "net" + "os" + + "github.com/containernetworking/cni/pkg/types" + types020 "github.com/containernetworking/cni/pkg/types/020" + convert "github.com/containernetworking/cni/pkg/types/internal" +) + +const ImplementedSpecVersion string = "0.4.0" + +var supportedVersions = []string{"0.3.0", "0.3.1", ImplementedSpecVersion} + +// Register converters for all versions less than the implemented spec version +func init() { + // Up-converters + convert.RegisterConverter("0.1.0", supportedVersions, convertFrom02x) + convert.RegisterConverter("0.2.0", supportedVersions, convertFrom02x) + convert.RegisterConverter("0.3.0", supportedVersions, convertInternal) + convert.RegisterConverter("0.3.1", supportedVersions, convertInternal) + + // Down-converters + convert.RegisterConverter("0.4.0", []string{"0.3.0", "0.3.1"}, convertInternal) + convert.RegisterConverter("0.4.0", []string{"0.1.0", "0.2.0"}, convertTo02x) + convert.RegisterConverter("0.3.1", []string{"0.1.0", "0.2.0"}, convertTo02x) + convert.RegisterConverter("0.3.0", []string{"0.1.0", "0.2.0"}, convertTo02x) + + // Creator + convert.RegisterCreator(supportedVersions, NewResult) +} + +func NewResult(data []byte) (types.Result, error) { + result := &Result{} + if err := json.Unmarshal(data, result); err != nil { + return nil, err + } + for _, v := range supportedVersions { + if result.CNIVersion == v { + return result, nil + } + } + return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q", + supportedVersions, result.CNIVersion) +} + +func GetResult(r types.Result) (*Result, error) { + resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion) + if err != nil { + return nil, err + } + result, ok := resultCurrent.(*Result) + if !ok { + return nil, fmt.Errorf("failed to convert result") + } + return result, nil +} + +func NewResultFromResult(result types.Result) (*Result, error) { + newResult, err := convert.Convert(result, ImplementedSpecVersion) + if err != nil { + return nil, err + } + return newResult.(*Result), nil +} + +// Result is what gets returned from the plugin (via stdout) to the caller +type Result struct { + CNIVersion string `json:"cniVersion,omitempty"` + Interfaces []*Interface `json:"interfaces,omitempty"` + IPs []*IPConfig `json:"ips,omitempty"` + Routes []*types.Route `json:"routes,omitempty"` + DNS types.DNS `json:"dns,omitempty"` +} + +func convert020IPConfig(from *types020.IPConfig, ipVersion string) *IPConfig { + return &IPConfig{ + Version: ipVersion, + Address: from.IP, + Gateway: from.Gateway, + } +} + +func convertFrom02x(from types.Result, toVersion string) (types.Result, error) { + fromResult := from.(*types020.Result) + toResult := &Result{ + CNIVersion: toVersion, + DNS: *fromResult.DNS.Copy(), + Routes: []*types.Route{}, + } + if fromResult.IP4 != nil { + toResult.IPs = append(toResult.IPs, convert020IPConfig(fromResult.IP4, "4")) + for _, fromRoute := range fromResult.IP4.Routes { + toResult.Routes = append(toResult.Routes, fromRoute.Copy()) + } + } + + if fromResult.IP6 != nil { + toResult.IPs = append(toResult.IPs, convert020IPConfig(fromResult.IP6, "6")) + for _, fromRoute := range fromResult.IP6.Routes { + toResult.Routes = append(toResult.Routes, fromRoute.Copy()) + } + } + + return toResult, nil +} + +func convertInternal(from types.Result, toVersion string) (types.Result, error) { + fromResult := from.(*Result) + toResult := &Result{ + CNIVersion: toVersion, + DNS: *fromResult.DNS.Copy(), + Routes: []*types.Route{}, + } + for _, fromIntf := range fromResult.Interfaces { + toResult.Interfaces = append(toResult.Interfaces, fromIntf.Copy()) + } + for _, fromIPC := range fromResult.IPs { + toResult.IPs = append(toResult.IPs, fromIPC.Copy()) + } + for _, fromRoute := range fromResult.Routes { + toResult.Routes = append(toResult.Routes, fromRoute.Copy()) + } + return toResult, nil +} + +func convertTo02x(from types.Result, toVersion string) (types.Result, error) { + fromResult := from.(*Result) + toResult := &types020.Result{ + CNIVersion: toVersion, + DNS: *fromResult.DNS.Copy(), + } + + for _, fromIP := range fromResult.IPs { + // Only convert the first IP address of each version as 0.2.0 + // and earlier cannot handle multiple IP addresses + if fromIP.Version == "4" && toResult.IP4 == nil { + toResult.IP4 = &types020.IPConfig{ + IP: fromIP.Address, + Gateway: fromIP.Gateway, + } + } else if fromIP.Version == "6" && toResult.IP6 == nil { + toResult.IP6 = &types020.IPConfig{ + IP: fromIP.Address, + Gateway: fromIP.Gateway, + } + } + if toResult.IP4 != nil && toResult.IP6 != nil { + break + } + } + + for _, fromRoute := range fromResult.Routes { + is4 := fromRoute.Dst.IP.To4() != nil + if is4 && toResult.IP4 != nil { + toResult.IP4.Routes = append(toResult.IP4.Routes, types.Route{ + Dst: fromRoute.Dst, + GW: fromRoute.GW, + }) + } else if !is4 && toResult.IP6 != nil { + toResult.IP6.Routes = append(toResult.IP6.Routes, types.Route{ + Dst: fromRoute.Dst, + GW: fromRoute.GW, + }) + } + } + + if toResult.IP4 == nil && toResult.IP6 == nil { + return nil, fmt.Errorf("cannot convert: no valid IP addresses") + } + + return toResult, nil +} + +func (r *Result) Version() string { + return r.CNIVersion +} + +func (r *Result) GetAsVersion(version string) (types.Result, error) { + // If the creator of the result did not set the CNIVersion, assume it + // should be the highest spec version implemented by this Result + if r.CNIVersion == "" { + r.CNIVersion = ImplementedSpecVersion + } + return convert.Convert(r, version) +} + +func (r *Result) Print() error { + return r.PrintTo(os.Stdout) +} + +func (r *Result) PrintTo(writer io.Writer) error { + data, err := json.MarshalIndent(r, "", " ") + if err != nil { + return err + } + _, err = writer.Write(data) + return err +} + +// Interface contains values about the created interfaces +type Interface struct { + Name string `json:"name"` + Mac string `json:"mac,omitempty"` + Sandbox string `json:"sandbox,omitempty"` +} + +func (i *Interface) String() string { + return fmt.Sprintf("%+v", *i) +} + +func (i *Interface) Copy() *Interface { + if i == nil { + return nil + } + newIntf := *i + return &newIntf +} + +// Int returns a pointer to the int value passed in. Used to +// set the IPConfig.Interface field. +func Int(v int) *int { + return &v +} + +// IPConfig contains values necessary to configure an IP address on an interface +type IPConfig struct { + // IP version, either "4" or "6" + Version string + // Index into Result structs Interfaces list + Interface *int + Address net.IPNet + Gateway net.IP +} + +func (i *IPConfig) String() string { + return fmt.Sprintf("%+v", *i) +} + +func (i *IPConfig) Copy() *IPConfig { + if i == nil { + return nil + } + + ipc := &IPConfig{ + Version: i.Version, + Address: i.Address, + Gateway: i.Gateway, + } + if i.Interface != nil { + intf := *i.Interface + ipc.Interface = &intf + } + return ipc +} + +// JSON (un)marshallable types +type ipConfig struct { + Version string `json:"version"` + Interface *int `json:"interface,omitempty"` + Address types.IPNet `json:"address"` + Gateway net.IP `json:"gateway,omitempty"` +} + +func (c *IPConfig) MarshalJSON() ([]byte, error) { + ipc := ipConfig{ + Version: c.Version, + Interface: c.Interface, + Address: types.IPNet(c.Address), + Gateway: c.Gateway, + } + + return json.Marshal(ipc) +} + +func (c *IPConfig) UnmarshalJSON(data []byte) error { + ipc := ipConfig{} + if err := json.Unmarshal(data, &ipc); err != nil { + return err + } + + c.Version = ipc.Version + c.Interface = ipc.Interface + c.Address = net.IPNet(ipc.Address) + c.Gateway = ipc.Gateway + return nil +} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/100/types.go b/vendor/github.com/containernetworking/cni/pkg/types/100/types.go new file mode 100644 index 00000000..0e1e8b85 --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/types/100/types.go @@ -0,0 +1,307 @@ +// Copyright 2016 CNI authors +// +// 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 types100 + +import ( + "encoding/json" + "fmt" + "io" + "net" + "os" + + "github.com/containernetworking/cni/pkg/types" + types040 "github.com/containernetworking/cni/pkg/types/040" + convert "github.com/containernetworking/cni/pkg/types/internal" +) + +const ImplementedSpecVersion string = "1.0.0" + +var supportedVersions = []string{ImplementedSpecVersion} + +// Register converters for all versions less than the implemented spec version +func init() { + // Up-converters + convert.RegisterConverter("0.1.0", supportedVersions, convertFrom02x) + convert.RegisterConverter("0.2.0", supportedVersions, convertFrom02x) + convert.RegisterConverter("0.3.0", supportedVersions, convertFrom04x) + convert.RegisterConverter("0.3.1", supportedVersions, convertFrom04x) + convert.RegisterConverter("0.4.0", supportedVersions, convertFrom04x) + + // Down-converters + convert.RegisterConverter("1.0.0", []string{"0.3.0", "0.3.1", "0.4.0"}, convertTo04x) + convert.RegisterConverter("1.0.0", []string{"0.1.0", "0.2.0"}, convertTo02x) + + // Creator + convert.RegisterCreator(supportedVersions, NewResult) +} + +func NewResult(data []byte) (types.Result, error) { + result := &Result{} + if err := json.Unmarshal(data, result); err != nil { + return nil, err + } + for _, v := range supportedVersions { + if result.CNIVersion == v { + return result, nil + } + } + return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q", + supportedVersions, result.CNIVersion) +} + +func GetResult(r types.Result) (*Result, error) { + resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion) + if err != nil { + return nil, err + } + result, ok := resultCurrent.(*Result) + if !ok { + return nil, fmt.Errorf("failed to convert result") + } + return result, nil +} + +func NewResultFromResult(result types.Result) (*Result, error) { + newResult, err := convert.Convert(result, ImplementedSpecVersion) + if err != nil { + return nil, err + } + return newResult.(*Result), nil +} + +// Result is what gets returned from the plugin (via stdout) to the caller +type Result struct { + CNIVersion string `json:"cniVersion,omitempty"` + Interfaces []*Interface `json:"interfaces,omitempty"` + IPs []*IPConfig `json:"ips,omitempty"` + Routes []*types.Route `json:"routes,omitempty"` + DNS types.DNS `json:"dns,omitempty"` +} + +func convertFrom02x(from types.Result, toVersion string) (types.Result, error) { + result040, err := convert.Convert(from, "0.4.0") + if err != nil { + return nil, err + } + result100, err := convertFrom04x(result040, ImplementedSpecVersion) + if err != nil { + return nil, err + } + return result100, nil +} + +func convertIPConfigFrom040(from *types040.IPConfig) *IPConfig { + to := &IPConfig{ + Address: from.Address, + Gateway: from.Gateway, + } + if from.Interface != nil { + intf := *from.Interface + to.Interface = &intf + } + return to +} + +func convertInterfaceFrom040(from *types040.Interface) *Interface { + return &Interface{ + Name: from.Name, + Mac: from.Mac, + Sandbox: from.Sandbox, + } +} + +func convertFrom04x(from types.Result, toVersion string) (types.Result, error) { + fromResult := from.(*types040.Result) + toResult := &Result{ + CNIVersion: toVersion, + DNS: *fromResult.DNS.Copy(), + Routes: []*types.Route{}, + } + for _, fromIntf := range fromResult.Interfaces { + toResult.Interfaces = append(toResult.Interfaces, convertInterfaceFrom040(fromIntf)) + } + for _, fromIPC := range fromResult.IPs { + toResult.IPs = append(toResult.IPs, convertIPConfigFrom040(fromIPC)) + } + for _, fromRoute := range fromResult.Routes { + toResult.Routes = append(toResult.Routes, fromRoute.Copy()) + } + return toResult, nil +} + +func convertIPConfigTo040(from *IPConfig) *types040.IPConfig { + version := "6" + if from.Address.IP.To4() != nil { + version = "4" + } + to := &types040.IPConfig{ + Version: version, + Address: from.Address, + Gateway: from.Gateway, + } + if from.Interface != nil { + intf := *from.Interface + to.Interface = &intf + } + return to +} + +func convertInterfaceTo040(from *Interface) *types040.Interface { + return &types040.Interface{ + Name: from.Name, + Mac: from.Mac, + Sandbox: from.Sandbox, + } +} + +func convertTo04x(from types.Result, toVersion string) (types.Result, error) { + fromResult := from.(*Result) + toResult := &types040.Result{ + CNIVersion: toVersion, + DNS: *fromResult.DNS.Copy(), + Routes: []*types.Route{}, + } + for _, fromIntf := range fromResult.Interfaces { + toResult.Interfaces = append(toResult.Interfaces, convertInterfaceTo040(fromIntf)) + } + for _, fromIPC := range fromResult.IPs { + toResult.IPs = append(toResult.IPs, convertIPConfigTo040(fromIPC)) + } + for _, fromRoute := range fromResult.Routes { + toResult.Routes = append(toResult.Routes, fromRoute.Copy()) + } + return toResult, nil +} + +func convertTo02x(from types.Result, toVersion string) (types.Result, error) { + // First convert to 0.4.0 + result040, err := convertTo04x(from, "0.4.0") + if err != nil { + return nil, err + } + result02x, err := convert.Convert(result040, toVersion) + if err != nil { + return nil, err + } + return result02x, nil +} + +func (r *Result) Version() string { + return r.CNIVersion +} + +func (r *Result) GetAsVersion(version string) (types.Result, error) { + // If the creator of the result did not set the CNIVersion, assume it + // should be the highest spec version implemented by this Result + if r.CNIVersion == "" { + r.CNIVersion = ImplementedSpecVersion + } + return convert.Convert(r, version) +} + +func (r *Result) Print() error { + return r.PrintTo(os.Stdout) +} + +func (r *Result) PrintTo(writer io.Writer) error { + data, err := json.MarshalIndent(r, "", " ") + if err != nil { + return err + } + _, err = writer.Write(data) + return err +} + +// Interface contains values about the created interfaces +type Interface struct { + Name string `json:"name"` + Mac string `json:"mac,omitempty"` + Sandbox string `json:"sandbox,omitempty"` +} + +func (i *Interface) String() string { + return fmt.Sprintf("%+v", *i) +} + +func (i *Interface) Copy() *Interface { + if i == nil { + return nil + } + newIntf := *i + return &newIntf +} + +// Int returns a pointer to the int value passed in. Used to +// set the IPConfig.Interface field. +func Int(v int) *int { + return &v +} + +// IPConfig contains values necessary to configure an IP address on an interface +type IPConfig struct { + // Index into Result structs Interfaces list + Interface *int + Address net.IPNet + Gateway net.IP +} + +func (i *IPConfig) String() string { + return fmt.Sprintf("%+v", *i) +} + +func (i *IPConfig) Copy() *IPConfig { + if i == nil { + return nil + } + + ipc := &IPConfig{ + Address: i.Address, + Gateway: i.Gateway, + } + if i.Interface != nil { + intf := *i.Interface + ipc.Interface = &intf + } + return ipc +} + +// JSON (un)marshallable types +type ipConfig struct { + Interface *int `json:"interface,omitempty"` + Address types.IPNet `json:"address"` + Gateway net.IP `json:"gateway,omitempty"` +} + +func (c *IPConfig) MarshalJSON() ([]byte, error) { + ipc := ipConfig{ + Interface: c.Interface, + Address: types.IPNet(c.Address), + Gateway: c.Gateway, + } + + return json.Marshal(ipc) +} + +func (c *IPConfig) UnmarshalJSON(data []byte) error { + ipc := ipConfig{} + if err := json.Unmarshal(data, &ipc); err != nil { + return err + } + + c.Interface = ipc.Interface + c.Address = net.IPNet(ipc.Address) + c.Gateway = ipc.Gateway + return nil +} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/create/create.go b/vendor/github.com/containernetworking/cni/pkg/types/create/create.go new file mode 100644 index 00000000..2258b71b --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/types/create/create.go @@ -0,0 +1,26 @@ +// Copyright 2016 CNI authors +// +// 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 create + +import ( + "github.com/containernetworking/cni/pkg/types" + convert "github.com/containernetworking/cni/pkg/types/internal" +) + +// Create creates a CNI Result using the given JSON, or an error if the creation +// could not be performed +func Create(version string, bytes []byte) (types.Result, error) { + return convert.Create(version, bytes) +} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/current/types.go b/vendor/github.com/containernetworking/cni/pkg/types/current/types.go deleted file mode 100644 index 754cc6e7..00000000 --- a/vendor/github.com/containernetworking/cni/pkg/types/current/types.go +++ /dev/null @@ -1,276 +0,0 @@ -// Copyright 2016 CNI authors -// -// 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 current - -import ( - "encoding/json" - "fmt" - "io" - "net" - "os" - - "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/020" -) - -const ImplementedSpecVersion string = "0.4.0" - -var SupportedVersions = []string{"0.3.0", "0.3.1", ImplementedSpecVersion} - -func NewResult(data []byte) (types.Result, error) { - result := &Result{} - if err := json.Unmarshal(data, result); err != nil { - return nil, err - } - return result, nil -} - -func GetResult(r types.Result) (*Result, error) { - resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion) - if err != nil { - return nil, err - } - result, ok := resultCurrent.(*Result) - if !ok { - return nil, fmt.Errorf("failed to convert result") - } - return result, nil -} - -var resultConverters = []struct { - versions []string - convert func(types.Result) (*Result, error) -}{ - {types020.SupportedVersions, convertFrom020}, - {SupportedVersions, convertFrom030}, -} - -func convertFrom020(result types.Result) (*Result, error) { - oldResult, err := types020.GetResult(result) - if err != nil { - return nil, err - } - - newResult := &Result{ - CNIVersion: ImplementedSpecVersion, - DNS: oldResult.DNS, - Routes: []*types.Route{}, - } - - if oldResult.IP4 != nil { - newResult.IPs = append(newResult.IPs, &IPConfig{ - Version: "4", - Address: oldResult.IP4.IP, - Gateway: oldResult.IP4.Gateway, - }) - for _, route := range oldResult.IP4.Routes { - newResult.Routes = append(newResult.Routes, &types.Route{ - Dst: route.Dst, - GW: route.GW, - }) - } - } - - if oldResult.IP6 != nil { - newResult.IPs = append(newResult.IPs, &IPConfig{ - Version: "6", - Address: oldResult.IP6.IP, - Gateway: oldResult.IP6.Gateway, - }) - for _, route := range oldResult.IP6.Routes { - newResult.Routes = append(newResult.Routes, &types.Route{ - Dst: route.Dst, - GW: route.GW, - }) - } - } - - return newResult, nil -} - -func convertFrom030(result types.Result) (*Result, error) { - newResult, ok := result.(*Result) - if !ok { - return nil, fmt.Errorf("failed to convert result") - } - newResult.CNIVersion = ImplementedSpecVersion - return newResult, nil -} - -func NewResultFromResult(result types.Result) (*Result, error) { - version := result.Version() - for _, converter := range resultConverters { - for _, supportedVersion := range converter.versions { - if version == supportedVersion { - return converter.convert(result) - } - } - } - return nil, fmt.Errorf("unsupported CNI result22 version %q", version) -} - -// Result is what gets returned from the plugin (via stdout) to the caller -type Result struct { - CNIVersion string `json:"cniVersion,omitempty"` - Interfaces []*Interface `json:"interfaces,omitempty"` - IPs []*IPConfig `json:"ips,omitempty"` - Routes []*types.Route `json:"routes,omitempty"` - DNS types.DNS `json:"dns,omitempty"` -} - -// Convert to the older 0.2.0 CNI spec Result type -func (r *Result) convertTo020() (*types020.Result, error) { - oldResult := &types020.Result{ - CNIVersion: types020.ImplementedSpecVersion, - DNS: r.DNS, - } - - for _, ip := range r.IPs { - // Only convert the first IP address of each version as 0.2.0 - // and earlier cannot handle multiple IP addresses - if ip.Version == "4" && oldResult.IP4 == nil { - oldResult.IP4 = &types020.IPConfig{ - IP: ip.Address, - Gateway: ip.Gateway, - } - } else if ip.Version == "6" && oldResult.IP6 == nil { - oldResult.IP6 = &types020.IPConfig{ - IP: ip.Address, - Gateway: ip.Gateway, - } - } - - if oldResult.IP4 != nil && oldResult.IP6 != nil { - break - } - } - - for _, route := range r.Routes { - is4 := route.Dst.IP.To4() != nil - if is4 && oldResult.IP4 != nil { - oldResult.IP4.Routes = append(oldResult.IP4.Routes, types.Route{ - Dst: route.Dst, - GW: route.GW, - }) - } else if !is4 && oldResult.IP6 != nil { - oldResult.IP6.Routes = append(oldResult.IP6.Routes, types.Route{ - Dst: route.Dst, - GW: route.GW, - }) - } - } - - if oldResult.IP4 == nil && oldResult.IP6 == nil { - return nil, fmt.Errorf("cannot convert: no valid IP addresses") - } - - return oldResult, nil -} - -func (r *Result) Version() string { - return ImplementedSpecVersion -} - -func (r *Result) GetAsVersion(version string) (types.Result, error) { - switch version { - case "0.3.0", "0.3.1", ImplementedSpecVersion: - r.CNIVersion = version - return r, nil - case types020.SupportedVersions[0], types020.SupportedVersions[1], types020.SupportedVersions[2]: - return r.convertTo020() - } - return nil, fmt.Errorf("cannot convert version 0.3.x to %q", version) -} - -func (r *Result) Print() error { - return r.PrintTo(os.Stdout) -} - -func (r *Result) PrintTo(writer io.Writer) error { - data, err := json.MarshalIndent(r, "", " ") - if err != nil { - return err - } - _, err = writer.Write(data) - return err -} - -// Convert this old version result to the current CNI version result -func (r *Result) Convert() (*Result, error) { - return r, nil -} - -// Interface contains values about the created interfaces -type Interface struct { - Name string `json:"name"` - Mac string `json:"mac,omitempty"` - Sandbox string `json:"sandbox,omitempty"` -} - -func (i *Interface) String() string { - return fmt.Sprintf("%+v", *i) -} - -// Int returns a pointer to the int value passed in. Used to -// set the IPConfig.Interface field. -func Int(v int) *int { - return &v -} - -// IPConfig contains values necessary to configure an IP address on an interface -type IPConfig struct { - // IP version, either "4" or "6" - Version string - // Index into Result structs Interfaces list - Interface *int - Address net.IPNet - Gateway net.IP -} - -func (i *IPConfig) String() string { - return fmt.Sprintf("%+v", *i) -} - -// JSON (un)marshallable types -type ipConfig struct { - Version string `json:"version"` - Interface *int `json:"interface,omitempty"` - Address types.IPNet `json:"address"` - Gateway net.IP `json:"gateway,omitempty"` -} - -func (c *IPConfig) MarshalJSON() ([]byte, error) { - ipc := ipConfig{ - Version: c.Version, - Interface: c.Interface, - Address: types.IPNet(c.Address), - Gateway: c.Gateway, - } - - return json.Marshal(ipc) -} - -func (c *IPConfig) UnmarshalJSON(data []byte) error { - ipc := ipConfig{} - if err := json.Unmarshal(data, &ipc); err != nil { - return err - } - - c.Version = ipc.Version - c.Interface = ipc.Interface - c.Address = net.IPNet(ipc.Address) - c.Gateway = ipc.Gateway - return nil -} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/internal/convert.go b/vendor/github.com/containernetworking/cni/pkg/types/internal/convert.go new file mode 100644 index 00000000..18fb1cb6 --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/types/internal/convert.go @@ -0,0 +1,88 @@ +// Copyright 2016 CNI authors +// +// 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 convert + +import ( + "fmt" + + "github.com/containernetworking/cni/pkg/types" +) + +// ConvertFn should convert from the given arbitrary Result type into a +// Result implementing CNI specification version passed in toVersion. +// The function is guaranteed to be passed a Result type matching the +// fromVersion it was registered with, and is guaranteed to be +// passed a toVersion matching one of the toVersions it was registered with. +type ConvertFn func(from types.Result, toVersion string) (types.Result, error) + +type converter struct { + // fromVersion is the CNI Result spec version that convertFn accepts + fromVersion string + // toVersions is a list of versions that convertFn can convert to + toVersions []string + convertFn ConvertFn +} + +var converters []*converter + +func findConverter(fromVersion, toVersion string) *converter { + for _, c := range converters { + if c.fromVersion == fromVersion { + for _, v := range c.toVersions { + if v == toVersion { + return c + } + } + } + } + return nil +} + +// Convert converts a CNI Result to the requested CNI specification version, +// or returns an error if the converstion could not be performed or failed +func Convert(from types.Result, toVersion string) (types.Result, error) { + fromVersion := from.Version() + + // Shortcut for same version + if fromVersion == toVersion { + return from, nil + } + + // Otherwise find the right converter + c := findConverter(fromVersion, toVersion) + if c == nil { + return nil, fmt.Errorf("no converter for CNI result version %s to %s", + fromVersion, toVersion) + } + return c.convertFn(from, toVersion) +} + +// RegisterConverter registers a CNI Result converter. SHOULD NOT BE CALLED +// EXCEPT FROM CNI ITSELF. +func RegisterConverter(fromVersion string, toVersions []string, convertFn ConvertFn) { + // Make sure there is no converter already registered for these + // from and to versions + for _, v := range toVersions { + if findConverter(fromVersion, v) != nil { + panic(fmt.Sprintf("converter already registered for %s to %s", + fromVersion, v)) + } + } + converters = append(converters, &converter{ + fromVersion: fromVersion, + toVersions: toVersions, + convertFn: convertFn, + }) +} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/internal/create.go b/vendor/github.com/containernetworking/cni/pkg/types/internal/create.go new file mode 100644 index 00000000..96363091 --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/types/internal/create.go @@ -0,0 +1,66 @@ +// Copyright 2016 CNI authors +// +// 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 convert + +import ( + "fmt" + + "github.com/containernetworking/cni/pkg/types" +) + +type ResultFactoryFunc func([]byte) (types.Result, error) + +type creator struct { + // CNI Result spec versions that createFn can create a Result for + versions []string + createFn ResultFactoryFunc +} + +var creators []*creator + +func findCreator(version string) *creator { + for _, c := range creators { + for _, v := range c.versions { + if v == version { + return c + } + } + } + return nil +} + +// Create creates a CNI Result using the given JSON, or an error if the creation +// could not be performed +func Create(version string, bytes []byte) (types.Result, error) { + if c := findCreator(version); c != nil { + return c.createFn(bytes) + } + return nil, fmt.Errorf("unsupported CNI result version %q", version) +} + +// RegisterCreator registers a CNI Result creator. SHOULD NOT BE CALLED +// EXCEPT FROM CNI ITSELF. +func RegisterCreator(versions []string, createFn ResultFactoryFunc) { + // Make sure there is no creator already registered for these versions + for _, v := range versions { + if findCreator(v) != nil { + panic(fmt.Sprintf("creator already registered for %s", v)) + } + } + creators = append(creators, &creator{ + versions: versions, + createFn: createFn, + }) +} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/types.go b/vendor/github.com/containernetworking/cni/pkg/types/types.go index 3fa757a5..fba17dfc 100644 --- a/vendor/github.com/containernetworking/cni/pkg/types/types.go +++ b/vendor/github.com/containernetworking/cni/pkg/types/types.go @@ -83,8 +83,6 @@ type NetConfList struct { Plugins []*NetConf `json:"plugins,omitempty"` } -type ResultFactoryFunc func([]byte) (Result, error) - // Result is an interface that provides the result of plugin execution type Result interface { // The highest CNI specification result version the result supports @@ -118,6 +116,24 @@ type DNS struct { Options []string `json:"options,omitempty"` } +func (d *DNS) Copy() *DNS { + if d == nil { + return nil + } + + to := &DNS{Domain: d.Domain} + for _, ns := range d.Nameservers { + to.Nameservers = append(to.Nameservers, ns) + } + for _, s := range d.Search { + to.Search = append(to.Search, s) + } + for _, o := range d.Options { + to.Options = append(to.Options, o) + } + return to +} + type Route struct { Dst net.IPNet GW net.IP @@ -127,6 +143,17 @@ func (r *Route) String() string { return fmt.Sprintf("%+v", *r) } +func (r *Route) Copy() *Route { + if r == nil { + return nil + } + + return &Route{ + Dst: r.Dst, + GW: r.GW, + } +} + // Well known error codes // see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes const ( diff --git a/vendor/github.com/containernetworking/cni/pkg/version/version.go b/vendor/github.com/containernetworking/cni/pkg/version/version.go index 8f3508e6..140f4b78 100644 --- a/vendor/github.com/containernetworking/cni/pkg/version/version.go +++ b/vendor/github.com/containernetworking/cni/pkg/version/version.go @@ -19,13 +19,13 @@ import ( "fmt" "github.com/containernetworking/cni/pkg/types" - "github.com/containernetworking/cni/pkg/types/020" - "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/cni/pkg/types/100" + "github.com/containernetworking/cni/pkg/types/create" ) // Current reports the version of the CNI spec implemented by this library func Current() string { - return "0.4.0" + return types100.ImplementedSpecVersion } // Legacy PluginInfo describes a plugin that is backwards compatible with the @@ -36,29 +36,12 @@ func Current() string { // Any future CNI spec versions which meet this definition should be added to // this list. var Legacy = PluginSupports("0.1.0", "0.2.0") -var All = PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0") - -var resultFactories = []struct { - supportedVersions []string - newResult types.ResultFactoryFunc -}{ - {current.SupportedVersions, current.NewResult}, - {types020.SupportedVersions, types020.NewResult}, -} +var All = PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0", "1.0.0") // Finds a Result object matching the requested version (if any) and asks // that object to parse the plugin result, returning an error if parsing failed. func NewResult(version string, resultBytes []byte) (types.Result, error) { - reconciler := &Reconciler{} - for _, resultFactory := range resultFactories { - err := reconciler.CheckRaw(version, resultFactory.supportedVersions) - if err == nil { - // Result supports this version - return resultFactory.newResult(resultBytes) - } - } - - return nil, fmt.Errorf("unsupported CNI result version %q", version) + return create.Create(version, resultBytes) } // ParsePrevResult parses a prevResult in a NetConf structure and sets @@ -68,6 +51,13 @@ func ParsePrevResult(conf *types.NetConf) error { return nil } + // Prior to 1.0.0, Result types may not marshal a CNIVersion. Since the + // result version must match the config version, if the Result's version + // is empty, inject the config version. + if ver, ok := conf.RawPrevResult["CNIVersion"]; !ok || ver == "" { + conf.RawPrevResult["CNIVersion"] = conf.CNIVersion + } + resultBytes, err := json.Marshal(conf.RawPrevResult) if err != nil { return fmt.Errorf("could not serialize prevResult: %v", err) diff --git a/vendor/github.com/fsnotify/fsnotify/.editorconfig b/vendor/github.com/fsnotify/fsnotify/.editorconfig index ba49e3c2..fad89585 100644 --- a/vendor/github.com/fsnotify/fsnotify/.editorconfig +++ b/vendor/github.com/fsnotify/fsnotify/.editorconfig @@ -1,5 +1,12 @@ root = true -[*] +[*.go] indent_style = tab indent_size = 4 +insert_final_newline = true + +[*.{yml,yaml}] +indent_style = space +indent_size = 2 +insert_final_newline = true +trim_trailing_whitespace = true diff --git a/vendor/github.com/fsnotify/fsnotify/.gitattributes b/vendor/github.com/fsnotify/fsnotify/.gitattributes new file mode 100644 index 00000000..32f1001b --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/.gitattributes @@ -0,0 +1 @@ +go.sum linguist-generated diff --git a/vendor/github.com/fsnotify/fsnotify/.travis.yml b/vendor/github.com/fsnotify/fsnotify/.travis.yml index 981d1bb8..a9c30165 100644 --- a/vendor/github.com/fsnotify/fsnotify/.travis.yml +++ b/vendor/github.com/fsnotify/fsnotify/.travis.yml @@ -2,29 +2,35 @@ sudo: false language: go go: - - 1.8.x - - 1.9.x - - tip + - "stable" + - "1.11.x" + - "1.10.x" + - "1.9.x" matrix: + include: + - go: "stable" + env: GOLINT=true allow_failures: - go: tip fast_finish: true -before_script: - - go get -u github.com/golang/lint/golint + +before_install: + - if [ ! -z "${GOLINT}" ]; then go get -u golang.org/x/lint/golint; fi script: - - go test -v --race ./... + - go test --race ./... after_script: - test -z "$(gofmt -s -l -w . | tee /dev/stderr)" - - test -z "$(golint ./... | tee /dev/stderr)" + - if [ ! -z "${GOLINT}" ]; then echo running golint; golint --set_exit_status ./...; else echo skipping golint; fi - go vet ./... os: - linux - osx + - windows notifications: email: false diff --git a/vendor/github.com/fsnotify/fsnotify/LICENSE b/vendor/github.com/fsnotify/fsnotify/LICENSE index f21e5408..e180c8fb 100644 --- a/vendor/github.com/fsnotify/fsnotify/LICENSE +++ b/vendor/github.com/fsnotify/fsnotify/LICENSE @@ -1,5 +1,5 @@ Copyright (c) 2012 The Go Authors. All rights reserved. -Copyright (c) 2012 fsnotify Authors. All rights reserved. +Copyright (c) 2012-2019 fsnotify Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are diff --git a/vendor/github.com/fsnotify/fsnotify/README.md b/vendor/github.com/fsnotify/fsnotify/README.md index 39932074..b2629e52 100644 --- a/vendor/github.com/fsnotify/fsnotify/README.md +++ b/vendor/github.com/fsnotify/fsnotify/README.md @@ -10,16 +10,16 @@ go get -u golang.org/x/sys/... Cross platform: Windows, Linux, BSD and macOS. -|Adapter |OS |Status | -|----------|----------|----------| -|inotify |Linux 2.6.27 or later, Android\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)| -|kqueue |BSD, macOS, iOS\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)| -|ReadDirectoryChangesW|Windows|Supported [![Build status](https://ci.appveyor.com/api/projects/status/ivwjubaih4r0udeh/branch/master?svg=true)](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)| -|FSEvents |macOS |[Planned](https://github.com/fsnotify/fsnotify/issues/11)| -|FEN |Solaris 11 |[In Progress](https://github.com/fsnotify/fsnotify/issues/12)| -|fanotify |Linux 2.6.37+ | | -|USN Journals |Windows |[Maybe](https://github.com/fsnotify/fsnotify/issues/53)| -|Polling |*All* |[Maybe](https://github.com/fsnotify/fsnotify/issues/9)| +| Adapter | OS | Status | +| --------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | +| inotify | Linux 2.6.27 or later, Android\* | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) | +| kqueue | BSD, macOS, iOS\* | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) | +| ReadDirectoryChangesW | Windows | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) | +| FSEvents | macOS | [Planned](https://github.com/fsnotify/fsnotify/issues/11) | +| FEN | Solaris 11 | [In Progress](https://github.com/fsnotify/fsnotify/issues/12) | +| fanotify | Linux 2.6.37+ | [Planned](https://github.com/fsnotify/fsnotify/issues/114) | +| USN Journals | Windows | [Maybe](https://github.com/fsnotify/fsnotify/issues/53) | +| Polling | *All* | [Maybe](https://github.com/fsnotify/fsnotify/issues/9) | \* Android and iOS are untested. @@ -33,6 +33,53 @@ All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based o Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`. +## Usage + +```go +package main + +import ( + "log" + + "github.com/fsnotify/fsnotify" +) + +func main() { + watcher, err := fsnotify.NewWatcher() + if err != nil { + log.Fatal(err) + } + defer watcher.Close() + + done := make(chan bool) + go func() { + for { + select { + case event, ok := <-watcher.Events: + if !ok { + return + } + log.Println("event:", event) + if event.Op&fsnotify.Write == fsnotify.Write { + log.Println("modified file:", event.Name) + } + case err, ok := <-watcher.Errors: + if !ok { + return + } + log.Println("error:", err) + } + } + }() + + err = watcher.Add("/tmp/foo") + if err != nil { + log.Fatal(err) + } + <-done +} +``` + ## Contributing Please refer to [CONTRIBUTING][] before opening an issue or pull request. @@ -65,6 +112,10 @@ There are OS-specific limits as to how many watches can be created: * Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error. * BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error. +**Why don't notifications work with NFS filesystems or filesystem in userspace (FUSE)?** + +fsnotify requires support from underlying OS to work. The current NFS protocol does not provide network level support for file notifications. + [#62]: https://github.com/howeyc/fsnotify/issues/62 [#18]: https://github.com/fsnotify/fsnotify/issues/18 [#11]: https://github.com/fsnotify/fsnotify/issues/11 diff --git a/vendor/github.com/fsnotify/fsnotify/fsnotify.go b/vendor/github.com/fsnotify/fsnotify/fsnotify.go index 190bf0de..89cab046 100644 --- a/vendor/github.com/fsnotify/fsnotify/fsnotify.go +++ b/vendor/github.com/fsnotify/fsnotify/fsnotify.go @@ -63,4 +63,6 @@ func (e Event) String() string { } // Common errors that can be reported by a watcher -var ErrEventOverflow = errors.New("fsnotify queue overflow") +var ( + ErrEventOverflow = errors.New("fsnotify queue overflow") +) diff --git a/vendor/github.com/fsnotify/fsnotify/go.mod b/vendor/github.com/fsnotify/fsnotify/go.mod new file mode 100644 index 00000000..ff11e13f --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/go.mod @@ -0,0 +1,5 @@ +module github.com/fsnotify/fsnotify + +go 1.13 + +require golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 diff --git a/vendor/github.com/fsnotify/fsnotify/go.sum b/vendor/github.com/fsnotify/fsnotify/go.sum new file mode 100644 index 00000000..f60af985 --- /dev/null +++ b/vendor/github.com/fsnotify/fsnotify/go.sum @@ -0,0 +1,2 @@ +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/vendor/github.com/fsnotify/fsnotify/inotify_poller.go b/vendor/github.com/fsnotify/fsnotify/inotify_poller.go index cc7db4b2..b33f2b4d 100644 --- a/vendor/github.com/fsnotify/fsnotify/inotify_poller.go +++ b/vendor/github.com/fsnotify/fsnotify/inotify_poller.go @@ -40,12 +40,12 @@ func newFdPoller(fd int) (*fdPoller, error) { poller.fd = fd // Create epoll fd - poller.epfd, errno = unix.EpollCreate1(0) + poller.epfd, errno = unix.EpollCreate1(unix.EPOLL_CLOEXEC) if poller.epfd == -1 { return nil, errno } // Create pipe; pipe[0] is the read end, pipe[1] the write end. - errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK) + errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK|unix.O_CLOEXEC) if errno != nil { return nil, errno } diff --git a/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go b/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go index 7d8de145..2306c462 100644 --- a/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go +++ b/vendor/github.com/fsnotify/fsnotify/open_mode_bsd.go @@ -8,4 +8,4 @@ package fsnotify import "golang.org/x/sys/unix" -const openMode = unix.O_NONBLOCK | unix.O_RDONLY +const openMode = unix.O_NONBLOCK | unix.O_RDONLY | unix.O_CLOEXEC diff --git a/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go b/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go index 9139e171..870c4d6d 100644 --- a/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go +++ b/vendor/github.com/fsnotify/fsnotify/open_mode_darwin.go @@ -9,4 +9,4 @@ package fsnotify import "golang.org/x/sys/unix" // note: this constant is not defined on BSD -const openMode = unix.O_EVTONLY +const openMode = unix.O_EVTONLY | unix.O_CLOEXEC diff --git a/vendor/github.com/onsi/ginkgo/CHANGELOG.md b/vendor/github.com/onsi/ginkgo/CHANGELOG.md index 3df6db7b..03ce58a4 100644 --- a/vendor/github.com/onsi/ginkgo/CHANGELOG.md +++ b/vendor/github.com/onsi/ginkgo/CHANGELOG.md @@ -1,3 +1,26 @@ +## 1.13.0 + +### Features +- Add a version of table.Entry that allows dumping the entry parameters. (#689) [21eaef2] + +### Fixes +- Ensure integration tests pass in an environment sans GOPATH [606fba2] +- Add books package (#568) [fc0e44e] +- doc(readme): installation via "tools package" (#677) [83bb20e] +- Solve the undefined: unix.Dup2 compile error on mips64le (#680) [0624f75] +- Import package without dot (#687) [6321024] +- Fix integration tests to stop require GOPATH (#686) [a912ec5] + +## 1.12.3 + +### Fixes +- Print correct code location of failing table test (#666) [c6d7afb] + +## 1.12.2 + +### Fixes +- Update dependencies [ea4a036] + ## 1.12.1 ### Fixes diff --git a/vendor/github.com/onsi/ginkgo/README.md b/vendor/github.com/onsi/ginkgo/README.md index 2fda7b9e..fab11458 100644 --- a/vendor/github.com/onsi/ginkgo/README.md +++ b/vendor/github.com/onsi/ginkgo/README.md @@ -4,7 +4,23 @@ Jump to the [docs](https://onsi.github.io/ginkgo/) to learn more. To start rolling your Ginkgo tests *now* [keep reading](#set-me-up)! -If you have a question, comment, bug report, feature request, etc. please open a GitHub issue. +If you have a question, comment, bug report, feature request, etc. please open a GitHub issue, or visit the [Ginkgo Slack channel](https://app.slack.com/client/T029RQSE6/CQQ50BBNW). + +## TLDR +Ginkgo builds on Go's `testing` package, allowing expressive [Behavior-Driven Development](https://en.wikipedia.org/wiki/Behavior-driven_development) ("BDD") style tests. +It is typically (and optionally) paired with the [Gomega](https://github.com/onsi/gomega) matcher library. + +```go +Describe("the strings package", func() { + Context("strings.Contains()", func() { + When("the string contains the substring in the middle", func() { + It("returns `true`", func() { + Expect(strings.Contains("Ginkgo is awesome", "is")).To(BeTrue()) + }) + }) + }) +}) +``` ## Feature List @@ -59,15 +75,36 @@ Ginkgo is best paired with Gomega. Learn more about Gomega [here](https://onsi. Agouti allows you run WebDriver integration tests. Learn more about Agouti [here](https://agouti.org) -## Set Me Up! +## Getting Started -You'll need the Go command-line tools. Ginkgo is tested with Go 1.6+, but preferably you should get the latest. Follow the [installation instructions](https://golang.org/doc/install) if you don't have it installed. +You'll need the Go command-line tools. Follow the [installation instructions](https://golang.org/doc/install) if you don't have it installed. +### Global installation +To install the Ginkgo command line interface into the `$PATH` (actually to `$GOBIN`): ```bash +go get -u github.com/onsi/ginkgo/ginkgo +``` -go get -u github.com/onsi/ginkgo/ginkgo # installs the ginkgo CLI -go get -u github.com/onsi/gomega/... # fetches the matcher library +### Go module ["tools package"](https://github.com/golang/go/issues/25922): +Create (or update) a file called `tools/tools.go` with the following contents: +```go +// +build tools +package tools + +import ( + _ "github.com/onsi/ginkgo" +) + +// This file imports packages that are used when running go generate, or used +// during the development process but not otherwise depended on by built code. +``` +The Ginkgo command can then be run via `go run github.com/onsi/ginkgo/ginkgo`. +This approach allows the version of Ginkgo to be maintained under source control for reproducible results, +and is well suited to automated test pipelines. + +### Bootstrapping +```bash cd path/to/package/you/want/to/test ginkgo bootstrap # set up a new ginkgo suite diff --git a/vendor/github.com/onsi/ginkgo/config/config.go b/vendor/github.com/onsi/ginkgo/config/config.go index 9363d49b..cf22a8e4 100644 --- a/vendor/github.com/onsi/ginkgo/config/config.go +++ b/vendor/github.com/onsi/ginkgo/config/config.go @@ -20,7 +20,7 @@ import ( "fmt" ) -const VERSION = "1.12.1" +const VERSION = "1.13.0" type GinkgoConfigType struct { RandomSeed int64 diff --git a/vendor/github.com/onsi/ginkgo/extensions/table/table.go b/vendor/github.com/onsi/ginkgo/extensions/table/table.go index ae8ab7d2..4b002780 100644 --- a/vendor/github.com/onsi/ginkgo/extensions/table/table.go +++ b/vendor/github.com/onsi/ginkgo/extensions/table/table.go @@ -12,7 +12,9 @@ import ( "fmt" "reflect" - "github.com/onsi/ginkgo" + "github.com/onsi/ginkgo/internal/codelocation" + "github.com/onsi/ginkgo/internal/global" + "github.com/onsi/ginkgo/types" ) /* @@ -40,9 +42,28 @@ Under the hood, `DescribeTable` simply generates a new Ginkgo `Describe`. Each It's important to understand that the `Describe`s and `It`s are generated at evaluation time (i.e. when Ginkgo constructs the tree of tests and before the tests run). Individual Entries can be focused (with FEntry) or marked pending (with PEntry or XEntry). In addition, the entire table can be focused or marked pending with FDescribeTable and PDescribeTable/XDescribeTable. + +A description function can be passed to Entry in place of the description. The function is then fed with the entry parameters to generate the description of the It corresponding to that particular Entry. + +For example: + + describe := func(desc string) func(int, int, bool) string { + return func(x, y int, expected bool) string { + return fmt.Sprintf("%s x=%d y=%d expected:%t", desc, x, y, expected) + } + } + + DescribeTable("a simple table", + func(x int, y int, expected bool) { + Ω(x > y).Should(Equal(expected)) + }, + Entry(describe("x > y"), 1, 0, true), + Entry(describe("x == y"), 0, 0, false), + Entry(describe("x < y"), 0, 1, false), + ) */ func DescribeTable(description string, itBody interface{}, entries ...TableEntry) bool { - describeTable(description, itBody, entries, false, false) + describeTable(description, itBody, entries, types.FlagTypeNone) return true } @@ -50,7 +71,7 @@ func DescribeTable(description string, itBody interface{}, entries ...TableEntry You can focus a table with `FDescribeTable`. This is equivalent to `FDescribe`. */ func FDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool { - describeTable(description, itBody, entries, false, true) + describeTable(description, itBody, entries, types.FlagTypeFocused) return true } @@ -58,7 +79,7 @@ func FDescribeTable(description string, itBody interface{}, entries ...TableEntr You can mark a table as pending with `PDescribeTable`. This is equivalent to `PDescribe`. */ func PDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool { - describeTable(description, itBody, entries, true, false) + describeTable(description, itBody, entries, types.FlagTypePending) return true } @@ -66,33 +87,24 @@ func PDescribeTable(description string, itBody interface{}, entries ...TableEntr You can mark a table as pending with `XDescribeTable`. This is equivalent to `XDescribe`. */ func XDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool { - describeTable(description, itBody, entries, true, false) + describeTable(description, itBody, entries, types.FlagTypePending) return true } -func describeTable(description string, itBody interface{}, entries []TableEntry, pending bool, focused bool) { +func describeTable(description string, itBody interface{}, entries []TableEntry, flag types.FlagType) { itBodyValue := reflect.ValueOf(itBody) if itBodyValue.Kind() != reflect.Func { panic(fmt.Sprintf("DescribeTable expects a function, got %#v", itBody)) } - if pending { - ginkgo.PDescribe(description, func() { + global.Suite.PushContainerNode( + description, + func() { for _, entry := range entries { entry.generateIt(itBodyValue) } - }) - } else if focused { - ginkgo.FDescribe(description, func() { - for _, entry := range entries { - entry.generateIt(itBodyValue) - } - }) - } else { - ginkgo.Describe(description, func() { - for _, entry := range entries { - entry.generateIt(itBodyValue) - } - }) - } + }, + flag, + codelocation.New(2), + ) } diff --git a/vendor/github.com/onsi/ginkgo/extensions/table/table_entry.go b/vendor/github.com/onsi/ginkgo/extensions/table/table_entry.go index 93f3bc3b..783e7964 100644 --- a/vendor/github.com/onsi/ginkgo/extensions/table/table_entry.go +++ b/vendor/github.com/onsi/ginkgo/extensions/table/table_entry.go @@ -1,49 +1,82 @@ package table import ( + "fmt" "reflect" - "github.com/onsi/ginkgo" + "github.com/onsi/ginkgo/internal/codelocation" + "github.com/onsi/ginkgo/internal/global" + "github.com/onsi/ginkgo/types" ) /* TableEntry represents an entry in a table test. You generally use the `Entry` constructor. */ type TableEntry struct { - Description string - Parameters []interface{} - Pending bool - Focused bool + Description interface{} + Parameters []interface{} + Pending bool + Focused bool + codeLocation types.CodeLocation } func (t TableEntry) generateIt(itBody reflect.Value) { + if t.codeLocation == (types.CodeLocation{}) { + // The user created the TableEntry struct directly instead of having used the (F/P/X)Entry constructors. + // Therefore default to the code location of the surrounding DescribeTable. + t.codeLocation = codelocation.New(5) + } + + var description string + descriptionValue := reflect.ValueOf(t.Description) + switch descriptionValue.Kind() { + case reflect.String: + description = descriptionValue.String() + case reflect.Func: + values := castParameters(descriptionValue, t.Parameters) + res := descriptionValue.Call(values) + if len(res) != 1 { + panic(fmt.Sprintf("The describe function should return only a value, returned %d", len(res))) + } + if res[0].Kind() != reflect.String { + panic(fmt.Sprintf("The describe function should return a string, returned %#v", res[0])) + } + description = res[0].String() + default: + panic(fmt.Sprintf("Description can either be a string or a function, got %#v", descriptionValue)) + } + if t.Pending { - ginkgo.PIt(t.Description) + global.Suite.PushItNode(description, func() {}, types.FlagTypePending, t.codeLocation, 0) return } - values := make([]reflect.Value, len(t.Parameters)) - iBodyType := itBody.Type() - for i, param := range t.Parameters { - if param == nil { - inType := iBodyType.In(i) - values[i] = reflect.Zero(inType) - } else { - values[i] = reflect.ValueOf(param) - } - } - + values := castParameters(itBody, t.Parameters) body := func() { itBody.Call(values) } if t.Focused { - ginkgo.FIt(t.Description, body) + global.Suite.PushItNode(description, body, types.FlagTypeFocused, t.codeLocation, global.DefaultTimeout) } else { - ginkgo.It(t.Description, body) + global.Suite.PushItNode(description, body, types.FlagTypeNone, t.codeLocation, global.DefaultTimeout) } } +func castParameters(function reflect.Value, parameters []interface{}) []reflect.Value { + res := make([]reflect.Value, len(parameters)) + funcType := function.Type() + for i, param := range parameters { + if param == nil { + inType := funcType.In(i) + res[i] = reflect.Zero(inType) + } else { + res[i] = reflect.ValueOf(param) + } + } + return res +} + /* Entry constructs a TableEntry. @@ -52,27 +85,51 @@ Subsequent parameters are saved off and sent to the callback passed in to `Descr Each Entry ends up generating an individual Ginkgo It. */ -func Entry(description string, parameters ...interface{}) TableEntry { - return TableEntry{description, parameters, false, false} +func Entry(description interface{}, parameters ...interface{}) TableEntry { + return TableEntry{ + Description: description, + Parameters: parameters, + Pending: false, + Focused: false, + codeLocation: codelocation.New(1), + } } /* You can focus a particular entry with FEntry. This is equivalent to FIt. */ -func FEntry(description string, parameters ...interface{}) TableEntry { - return TableEntry{description, parameters, false, true} +func FEntry(description interface{}, parameters ...interface{}) TableEntry { + return TableEntry{ + Description: description, + Parameters: parameters, + Pending: false, + Focused: true, + codeLocation: codelocation.New(1), + } } /* You can mark a particular entry as pending with PEntry. This is equivalent to PIt. */ -func PEntry(description string, parameters ...interface{}) TableEntry { - return TableEntry{description, parameters, true, false} +func PEntry(description interface{}, parameters ...interface{}) TableEntry { + return TableEntry{ + Description: description, + Parameters: parameters, + Pending: true, + Focused: false, + codeLocation: codelocation.New(1), + } } /* You can mark a particular entry as pending with XEntry. This is equivalent to XIt. */ -func XEntry(description string, parameters ...interface{}) TableEntry { - return TableEntry{description, parameters, true, false} +func XEntry(description interface{}, parameters ...interface{}) TableEntry { + return TableEntry{ + Description: description, + Parameters: parameters, + Pending: true, + Focused: false, + codeLocation: codelocation.New(1), + } } diff --git a/vendor/github.com/onsi/ginkgo/ginkgo_dsl.go b/vendor/github.com/onsi/ginkgo/ginkgo_dsl.go index 3cbf89a3..30ff86f5 100644 --- a/vendor/github.com/onsi/ginkgo/ginkgo_dsl.go +++ b/vendor/github.com/onsi/ginkgo/ginkgo_dsl.go @@ -22,9 +22,8 @@ import ( "github.com/onsi/ginkgo/config" "github.com/onsi/ginkgo/internal/codelocation" - "github.com/onsi/ginkgo/internal/failer" + "github.com/onsi/ginkgo/internal/global" "github.com/onsi/ginkgo/internal/remote" - "github.com/onsi/ginkgo/internal/suite" "github.com/onsi/ginkgo/internal/testingtproxy" "github.com/onsi/ginkgo/internal/writer" "github.com/onsi/ginkgo/reporters" @@ -46,16 +45,10 @@ To circumvent this, you should call at the top of the goroutine that caused this panic. ` -const defaultTimeout = 1 - -var globalSuite *suite.Suite -var globalFailer *failer.Failer func init() { config.Flags(flag.CommandLine, "ginkgo", true) GinkgoWriter = writer.New(os.Stdout) - globalFailer = failer.New() - globalSuite = suite.New(globalFailer) } //GinkgoWriter implements an io.Writer @@ -156,7 +149,7 @@ type GinkgoTestDescription struct { //CurrentGinkgoTestDescripton returns information about the current running test. func CurrentGinkgoTestDescription() GinkgoTestDescription { - summary, ok := globalSuite.CurrentRunningSpecSummary() + summary, ok := global.Suite.CurrentRunningSpecSummary() if !ok { return GinkgoTestDescription{} } @@ -223,7 +216,7 @@ func RunSpecsWithCustomReporters(t GinkgoTestingT, description string, specRepor for i, reporter := range specReporters { reporters[i] = reporter } - passed, hasFocusedTests := globalSuite.Run(t, description, reporters, writer, config.GinkgoConfig) + passed, hasFocusedTests := global.Suite.Run(t, description, reporters, writer, config.GinkgoConfig) if passed && hasFocusedTests && strings.TrimSpace(os.Getenv("GINKGO_EDITOR_INTEGRATION")) == "" { fmt.Println("PASS | FOCUSED") os.Exit(types.GINKGO_FOCUS_EXIT_CODE) @@ -252,7 +245,7 @@ func Skip(message string, callerSkip ...int) { skip = callerSkip[0] } - globalFailer.Skip(message, codelocation.New(skip+1)) + global.Failer.Skip(message, codelocation.New(skip+1)) panic(GINKGO_PANIC) } @@ -263,7 +256,7 @@ func Fail(message string, callerSkip ...int) { skip = callerSkip[0] } - globalFailer.Fail(message, codelocation.New(skip+1)) + global.Failer.Fail(message, codelocation.New(skip+1)) panic(GINKGO_PANIC) } @@ -280,7 +273,7 @@ func Fail(message string, callerSkip ...int) { func GinkgoRecover() { e := recover() if e != nil { - globalFailer.Panic(codelocation.New(1), e) + global.Failer.Panic(codelocation.New(1), e) } } @@ -291,25 +284,25 @@ func GinkgoRecover() { //equivalent. The difference is purely semantic -- you typically Describe the behavior of an object //or method and, within that Describe, outline a number of Contexts and Whens. func Describe(text string, body func()) bool { - globalSuite.PushContainerNode(text, body, types.FlagTypeNone, codelocation.New(1)) + global.Suite.PushContainerNode(text, body, types.FlagTypeNone, codelocation.New(1)) return true } //You can focus the tests within a describe block using FDescribe func FDescribe(text string, body func()) bool { - globalSuite.PushContainerNode(text, body, types.FlagTypeFocused, codelocation.New(1)) + global.Suite.PushContainerNode(text, body, types.FlagTypeFocused, codelocation.New(1)) return true } //You can mark the tests within a describe block as pending using PDescribe func PDescribe(text string, body func()) bool { - globalSuite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1)) + global.Suite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1)) return true } //You can mark the tests within a describe block as pending using XDescribe func XDescribe(text string, body func()) bool { - globalSuite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1)) + global.Suite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1)) return true } @@ -320,25 +313,25 @@ func XDescribe(text string, body func()) bool { //equivalent. The difference is purely semantic -- you typical Describe the behavior of an object //or method and, within that Describe, outline a number of Contexts and Whens. func Context(text string, body func()) bool { - globalSuite.PushContainerNode(text, body, types.FlagTypeNone, codelocation.New(1)) + global.Suite.PushContainerNode(text, body, types.FlagTypeNone, codelocation.New(1)) return true } //You can focus the tests within a describe block using FContext func FContext(text string, body func()) bool { - globalSuite.PushContainerNode(text, body, types.FlagTypeFocused, codelocation.New(1)) + global.Suite.PushContainerNode(text, body, types.FlagTypeFocused, codelocation.New(1)) return true } //You can mark the tests within a describe block as pending using PContext func PContext(text string, body func()) bool { - globalSuite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1)) + global.Suite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1)) return true } //You can mark the tests within a describe block as pending using XContext func XContext(text string, body func()) bool { - globalSuite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1)) + global.Suite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1)) return true } @@ -349,25 +342,25 @@ func XContext(text string, body func()) bool { //equivalent. The difference is purely semantic -- you typical Describe the behavior of an object //or method and, within that Describe, outline a number of Contexts and Whens. func When(text string, body func()) bool { - globalSuite.PushContainerNode("when "+text, body, types.FlagTypeNone, codelocation.New(1)) + global.Suite.PushContainerNode("when "+text, body, types.FlagTypeNone, codelocation.New(1)) return true } //You can focus the tests within a describe block using FWhen func FWhen(text string, body func()) bool { - globalSuite.PushContainerNode("when "+text, body, types.FlagTypeFocused, codelocation.New(1)) + global.Suite.PushContainerNode("when "+text, body, types.FlagTypeFocused, codelocation.New(1)) return true } //You can mark the tests within a describe block as pending using PWhen func PWhen(text string, body func()) bool { - globalSuite.PushContainerNode("when "+text, body, types.FlagTypePending, codelocation.New(1)) + global.Suite.PushContainerNode("when "+text, body, types.FlagTypePending, codelocation.New(1)) return true } //You can mark the tests within a describe block as pending using XWhen func XWhen(text string, body func()) bool { - globalSuite.PushContainerNode("when "+text, body, types.FlagTypePending, codelocation.New(1)) + global.Suite.PushContainerNode("when "+text, body, types.FlagTypePending, codelocation.New(1)) return true } @@ -377,25 +370,25 @@ func XWhen(text string, body func()) bool { //Ginkgo will normally run It blocks synchronously. To perform asynchronous tests, pass a //function that accepts a Done channel. When you do this, you can also provide an optional timeout. func It(text string, body interface{}, timeout ...float64) bool { - globalSuite.PushItNode(text, body, types.FlagTypeNone, codelocation.New(1), parseTimeout(timeout...)) + global.Suite.PushItNode(text, body, types.FlagTypeNone, codelocation.New(1), parseTimeout(timeout...)) return true } //You can focus individual Its using FIt func FIt(text string, body interface{}, timeout ...float64) bool { - globalSuite.PushItNode(text, body, types.FlagTypeFocused, codelocation.New(1), parseTimeout(timeout...)) + global.Suite.PushItNode(text, body, types.FlagTypeFocused, codelocation.New(1), parseTimeout(timeout...)) return true } //You can mark Its as pending using PIt func PIt(text string, _ ...interface{}) bool { - globalSuite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0) + global.Suite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0) return true } //You can mark Its as pending using XIt func XIt(text string, _ ...interface{}) bool { - globalSuite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0) + global.Suite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0) return true } @@ -403,25 +396,25 @@ func XIt(text string, _ ...interface{}) bool { //which "It" does not fit into a natural sentence flow. All the same protocols apply for Specify blocks //which apply to It blocks. func Specify(text string, body interface{}, timeout ...float64) bool { - globalSuite.PushItNode(text, body, types.FlagTypeNone, codelocation.New(1), parseTimeout(timeout...)) + global.Suite.PushItNode(text, body, types.FlagTypeNone, codelocation.New(1), parseTimeout(timeout...)) return true } //You can focus individual Specifys using FSpecify func FSpecify(text string, body interface{}, timeout ...float64) bool { - globalSuite.PushItNode(text, body, types.FlagTypeFocused, codelocation.New(1), parseTimeout(timeout...)) + global.Suite.PushItNode(text, body, types.FlagTypeFocused, codelocation.New(1), parseTimeout(timeout...)) return true } //You can mark Specifys as pending using PSpecify func PSpecify(text string, is ...interface{}) bool { - globalSuite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0) + global.Suite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0) return true } //You can mark Specifys as pending using XSpecify func XSpecify(text string, is ...interface{}) bool { - globalSuite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0) + global.Suite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0) return true } @@ -452,25 +445,25 @@ func By(text string, callbacks ...func()) { //The body function must have the signature: // func(b Benchmarker) func Measure(text string, body interface{}, samples int) bool { - globalSuite.PushMeasureNode(text, body, types.FlagTypeNone, codelocation.New(1), samples) + global.Suite.PushMeasureNode(text, body, types.FlagTypeNone, codelocation.New(1), samples) return true } //You can focus individual Measures using FMeasure func FMeasure(text string, body interface{}, samples int) bool { - globalSuite.PushMeasureNode(text, body, types.FlagTypeFocused, codelocation.New(1), samples) + global.Suite.PushMeasureNode(text, body, types.FlagTypeFocused, codelocation.New(1), samples) return true } //You can mark Measurements as pending using PMeasure func PMeasure(text string, _ ...interface{}) bool { - globalSuite.PushMeasureNode(text, func(b Benchmarker) {}, types.FlagTypePending, codelocation.New(1), 0) + global.Suite.PushMeasureNode(text, func(b Benchmarker) {}, types.FlagTypePending, codelocation.New(1), 0) return true } //You can mark Measurements as pending using XMeasure func XMeasure(text string, _ ...interface{}) bool { - globalSuite.PushMeasureNode(text, func(b Benchmarker) {}, types.FlagTypePending, codelocation.New(1), 0) + global.Suite.PushMeasureNode(text, func(b Benchmarker) {}, types.FlagTypePending, codelocation.New(1), 0) return true } @@ -481,7 +474,7 @@ func XMeasure(text string, _ ...interface{}) bool { // //You may only register *one* BeforeSuite handler per test suite. You typically do so in your bootstrap file at the top level. func BeforeSuite(body interface{}, timeout ...float64) bool { - globalSuite.SetBeforeSuiteNode(body, codelocation.New(1), parseTimeout(timeout...)) + global.Suite.SetBeforeSuiteNode(body, codelocation.New(1), parseTimeout(timeout...)) return true } @@ -494,7 +487,7 @@ func BeforeSuite(body interface{}, timeout ...float64) bool { // //You may only register *one* AfterSuite handler per test suite. You typically do so in your bootstrap file at the top level. func AfterSuite(body interface{}, timeout ...float64) bool { - globalSuite.SetAfterSuiteNode(body, codelocation.New(1), parseTimeout(timeout...)) + global.Suite.SetAfterSuiteNode(body, codelocation.New(1), parseTimeout(timeout...)) return true } @@ -539,7 +532,7 @@ func AfterSuite(body interface{}, timeout ...float64) bool { // Ω(err).ShouldNot(HaveOccurred()) // }) func SynchronizedBeforeSuite(node1Body interface{}, allNodesBody interface{}, timeout ...float64) bool { - globalSuite.SetSynchronizedBeforeSuiteNode( + global.Suite.SetSynchronizedBeforeSuiteNode( node1Body, allNodesBody, codelocation.New(1), @@ -566,7 +559,7 @@ func SynchronizedBeforeSuite(node1Body interface{}, allNodesBody interface{}, ti // dbRunner.Stop() // }) func SynchronizedAfterSuite(allNodesBody interface{}, node1Body interface{}, timeout ...float64) bool { - globalSuite.SetSynchronizedAfterSuiteNode( + global.Suite.SetSynchronizedAfterSuiteNode( allNodesBody, node1Body, codelocation.New(1), @@ -581,7 +574,7 @@ func SynchronizedAfterSuite(allNodesBody interface{}, node1Body interface{}, tim //Like It blocks, BeforeEach blocks can be made asynchronous by providing a body function that accepts //a Done channel func BeforeEach(body interface{}, timeout ...float64) bool { - globalSuite.PushBeforeEachNode(body, codelocation.New(1), parseTimeout(timeout...)) + global.Suite.PushBeforeEachNode(body, codelocation.New(1), parseTimeout(timeout...)) return true } @@ -591,7 +584,7 @@ func BeforeEach(body interface{}, timeout ...float64) bool { //Like It blocks, BeforeEach blocks can be made asynchronous by providing a body function that accepts //a Done channel func JustBeforeEach(body interface{}, timeout ...float64) bool { - globalSuite.PushJustBeforeEachNode(body, codelocation.New(1), parseTimeout(timeout...)) + global.Suite.PushJustBeforeEachNode(body, codelocation.New(1), parseTimeout(timeout...)) return true } @@ -601,7 +594,7 @@ func JustBeforeEach(body interface{}, timeout ...float64) bool { //Like It blocks, JustAfterEach blocks can be made asynchronous by providing a body function that accepts //a Done channel func JustAfterEach(body interface{}, timeout ...float64) bool { - globalSuite.PushJustAfterEachNode(body, codelocation.New(1), parseTimeout(timeout...)) + global.Suite.PushJustAfterEachNode(body, codelocation.New(1), parseTimeout(timeout...)) return true } @@ -611,13 +604,13 @@ func JustAfterEach(body interface{}, timeout ...float64) bool { //Like It blocks, AfterEach blocks can be made asynchronous by providing a body function that accepts //a Done channel func AfterEach(body interface{}, timeout ...float64) bool { - globalSuite.PushAfterEachNode(body, codelocation.New(1), parseTimeout(timeout...)) + global.Suite.PushAfterEachNode(body, codelocation.New(1), parseTimeout(timeout...)) return true } func parseTimeout(timeout ...float64) time.Duration { if len(timeout) == 0 { - return time.Duration(defaultTimeout * int64(time.Second)) + return global.DefaultTimeout } else { return time.Duration(timeout[0] * float64(time.Second)) } diff --git a/vendor/github.com/onsi/ginkgo/go.mod b/vendor/github.com/onsi/ginkgo/go.mod index c5d523c5..212b2454 100644 --- a/vendor/github.com/onsi/ginkgo/go.mod +++ b/vendor/github.com/onsi/ginkgo/go.mod @@ -1,9 +1,12 @@ module github.com/onsi/ginkgo require ( + github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/nxadm/tail v1.4.4 - github.com/onsi/gomega v1.7.1 - golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e + github.com/onsi/gomega v1.10.1 + github.com/sclevine/agouti v3.0.0+incompatible // indirect + golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 + golang.org/x/text v0.3.2 // indirect ) -go 1.12 +go 1.13 diff --git a/vendor/github.com/onsi/ginkgo/go.sum b/vendor/github.com/onsi/ginkgo/go.sum index 57265044..beb29b3e 100644 --- a/vendor/github.com/onsi/ginkgo/go.sum +++ b/vendor/github.com/onsi/ginkgo/go.sum @@ -1,24 +1,62 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/sclevine/agouti v3.0.0+incompatible h1:8IBJS6PWz3uTlMP3YBIR5f+KAldcGuOeFkFbUWfBgK4= +github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4= +golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= @@ -27,3 +65,5 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/onsi/ginkgo/internal/global/init.go b/vendor/github.com/onsi/ginkgo/internal/global/init.go new file mode 100644 index 00000000..71144320 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/internal/global/init.go @@ -0,0 +1,18 @@ +package global + +import ( + "time" + + "github.com/onsi/ginkgo/internal/failer" + "github.com/onsi/ginkgo/internal/suite" +) + +const DefaultTimeout = time.Duration(1 * time.Second) + +var Suite *suite.Suite +var Failer *failer.Failer + +func init() { + Failer = failer.New() + Suite = suite.New(Failer) +} diff --git a/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_darwin.go b/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_darwin.go new file mode 100644 index 00000000..e3d09ead --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_darwin.go @@ -0,0 +1,11 @@ +// +build darwin + +package remote + +import ( + "golang.org/x/sys/unix" +) + +func interceptorDupx(oldfd int, newfd int) { + unix.Dup2(oldfd, newfd) +} diff --git a/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_dragonfly.go b/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_dragonfly.go new file mode 100644 index 00000000..72d38686 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_dragonfly.go @@ -0,0 +1,11 @@ +// +build dragonfly + +package remote + +import ( + "golang.org/x/sys/unix" +) + +func interceptorDupx(oldfd int, newfd int) { + unix.Dup2(oldfd, newfd) +} diff --git a/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_freebsd.go b/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_freebsd.go new file mode 100644 index 00000000..497d548d --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_freebsd.go @@ -0,0 +1,11 @@ +// +build freebsd + +package remote + +import ( + "golang.org/x/sys/unix" +) + +func interceptorDupx(oldfd int, newfd int) { + unix.Dup2(oldfd, newfd) +} diff --git a/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_linux.go b/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_linux.go new file mode 100644 index 00000000..29add0d3 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_linux.go @@ -0,0 +1,12 @@ +// +build linux +// +build !mips64le + +package remote + +import ( + "golang.org/x/sys/unix" +) + +func interceptorDupx(oldfd int, newfd int) { + unix.Dup2(oldfd, newfd) +} diff --git a/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_linux_mips64le.go b/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_linux_mips64le.go new file mode 100644 index 00000000..09bd0626 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_linux_mips64le.go @@ -0,0 +1,12 @@ +// +build linux +// +build mips64le + +package remote + +import ( + "golang.org/x/sys/unix" +) + +func interceptorDupx(oldfd int, newfd int) { + unix.Dup3(oldfd, newfd, 0) +} diff --git a/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_netbsd.go b/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_netbsd.go new file mode 100644 index 00000000..16ad6aeb --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_netbsd.go @@ -0,0 +1,11 @@ +// +build netbsd + +package remote + +import ( + "golang.org/x/sys/unix" +) + +func interceptorDupx(oldfd int, newfd int) { + unix.Dup2(oldfd, newfd) +} diff --git a/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_openbsd.go b/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_openbsd.go new file mode 100644 index 00000000..4275f842 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_openbsd.go @@ -0,0 +1,11 @@ +// +build openbsd + +package remote + +import ( + "golang.org/x/sys/unix" +) + +func interceptorDupx(oldfd int, newfd int) { + unix.Dup2(oldfd, newfd) +} diff --git a/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_solaris.go b/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_solaris.go new file mode 100644 index 00000000..882a38a9 --- /dev/null +++ b/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_solaris.go @@ -0,0 +1,11 @@ +// +build solaris + +package remote + +import ( + "golang.org/x/sys/unix" +) + +func interceptorDupx(oldfd int, newfd int) { + unix.Dup2(oldfd, newfd) +} diff --git a/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_unix.go b/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_unix.go index 774967db..80614d0c 100644 --- a/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_unix.go +++ b/vendor/github.com/onsi/ginkgo/internal/remote/output_interceptor_unix.go @@ -8,7 +8,6 @@ import ( "os" "github.com/nxadm/tail" - "golang.org/x/sys/unix" ) func NewOutputInterceptor() OutputInterceptor { @@ -36,10 +35,8 @@ func (interceptor *outputInterceptor) StartInterceptingOutput() error { return err } - // This might call Dup3 if the dup2 syscall is not available, e.g. on - // linux/arm64 or linux/riscv64 - unix.Dup2(int(interceptor.redirectFile.Fd()), 1) - unix.Dup2(int(interceptor.redirectFile.Fd()), 2) + interceptorDupx(int(interceptor.redirectFile.Fd()), 1) + interceptorDupx(int(interceptor.redirectFile.Fd()), 2) if interceptor.streamTarget != nil { interceptor.tailer, _ = tail.TailFile(interceptor.redirectFile.Name(), tail.Config{Follow: true}) diff --git a/vendor/modules.txt b/vendor/modules.txt index fe09cd70..bf8d51f5 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -28,14 +28,17 @@ github.com/alexflint/go-filemutex # github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44 ## explicit github.com/buger/jsonparser -# github.com/containernetworking/cni v0.8.1 +# github.com/containernetworking/cni v0.8.1-0.20201216164644-62e54113f44a ## explicit github.com/containernetworking/cni/libcni github.com/containernetworking/cni/pkg/invoke github.com/containernetworking/cni/pkg/skel github.com/containernetworking/cni/pkg/types github.com/containernetworking/cni/pkg/types/020 -github.com/containernetworking/cni/pkg/types/current +github.com/containernetworking/cni/pkg/types/040 +github.com/containernetworking/cni/pkg/types/100 +github.com/containernetworking/cni/pkg/types/create +github.com/containernetworking/cni/pkg/types/internal github.com/containernetworking/cni/pkg/utils github.com/containernetworking/cni/pkg/version # github.com/coreos/go-iptables v0.5.0 @@ -57,7 +60,7 @@ github.com/d2g/dhcp4server/leasepool github.com/d2g/dhcp4server/leasepool/memorypool # github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4 ## explicit -# github.com/fsnotify/fsnotify v1.4.7 +# github.com/fsnotify/fsnotify v1.4.9 github.com/fsnotify/fsnotify # github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c ## explicit @@ -74,7 +77,7 @@ github.com/nxadm/tail/ratelimiter github.com/nxadm/tail/util github.com/nxadm/tail/watch github.com/nxadm/tail/winfile -# github.com/onsi/ginkgo v1.12.1 +# github.com/onsi/ginkgo v1.13.0 ## explicit github.com/onsi/ginkgo github.com/onsi/ginkgo/config @@ -82,6 +85,7 @@ github.com/onsi/ginkgo/extensions/table github.com/onsi/ginkgo/internal/codelocation github.com/onsi/ginkgo/internal/containernode github.com/onsi/ginkgo/internal/failer +github.com/onsi/ginkgo/internal/global github.com/onsi/ginkgo/internal/leafnodes github.com/onsi/ginkgo/internal/remote github.com/onsi/ginkgo/internal/spec