Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package cni-plugins for openSUSE:Factory checked in at 2021-04-29 22:44:25 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/cni-plugins (Old) and /work/SRC/openSUSE:Factory/.cni-plugins.new.1947 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "cni-plugins" Thu Apr 29 22:44:25 2021 rev:15 rq:888696 version:0.9.1 Changes: -------- --- /work/SRC/openSUSE:Factory/cni-plugins/cni-plugins.changes 2021-01-11 17:13:10.808333356 +0100 +++ /work/SRC/openSUSE:Factory/.cni-plugins.new.1947/cni-plugins.changes 2021-04-29 22:44:31.088267774 +0200 @@ -1,0 +2,17 @@ +Mon Apr 26 17:17:41 UTC 2021 - [email protected] + +- Update to version 0.9.1: + * ipam/dhcp: Add broadcast flag + * add flannel to support dual stack ip + * bandwidth: fix panic in tests + * host-device: Add support for DPDK device + * [main/vlan] Fix error handling for delegate IPAM plugin + * dhcp: default dhcp clien timeout is 10s + * vlan: fix error message text by removing ptp references + * dhcp: daemon dhcp client timeout is configurable + * dhcp: timeout value is set in DHCP daemon + * remove unused function + * deps: go mod tidy coreos/go-iptables + * deps: bump coreos/go-iptables + +------------------------------------------------------------------- Old: ---- cni-plugins-0.9.0.tar.xz New: ---- cni-plugins-0.9.1.tar.xz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ cni-plugins.spec ++++++ --- /var/tmp/diff_new_pack.6g7zL8/_old 2021-04-29 22:44:31.640265316 +0200 +++ /var/tmp/diff_new_pack.6g7zL8/_new 2021-04-29 22:44:31.644265299 +0200 @@ -19,7 +19,7 @@ %define cni_bin_dir %{_libexecdir}/cni %define cni_doc_dir %{_docdir}/cni-plugins Name: cni-plugins -Version: 0.9.0 +Version: 0.9.1 Release: 0 Summary: Container Network Interface plugins License: Apache-2.0 @@ -66,7 +66,7 @@ # TODO: copy the READMEs #for i in plugins/main/*/README.md ; do -# cp Documentation/* %{buildroot}%{cni_doc_dir}/plugins/ +# cp Documentation/* %%{buildroot}%%{cni_doc_dir}/plugins/ #done %post @@ -79,6 +79,6 @@ %license LICENSE %dir %{cni_bin_dir} %{cni_bin_dir}/* -# %{cni_doc_dir}/plugins/* +# %%{cni_doc_dir}/plugins/* %changelog ++++++ _service ++++++ --- /var/tmp/diff_new_pack.6g7zL8/_old 2021-04-29 22:44:31.668265191 +0200 +++ /var/tmp/diff_new_pack.6g7zL8/_new 2021-04-29 22:44:31.672265174 +0200 @@ -4,8 +4,8 @@ <param name="scm">git</param> <param name="filename">cni-plugins</param> <param name="exclude">.git</param> - <param name="versionformat">0.9.0</param> - <param name="revision">v0.9.0</param> + <param name="versionformat">0.9.1</param> + <param name="revision">v0.9.1</param> <param name="changesgenerate">enable</param> </service> <service name="recompress" mode="disabled"> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.6g7zL8/_old 2021-04-29 22:44:31.688265103 +0200 +++ /var/tmp/diff_new_pack.6g7zL8/_new 2021-04-29 22:44:31.688265103 +0200 @@ -1,4 +1,4 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/containernetworking/plugins.git</param> - <param name="changesrevision">e13bab99e54b4a34375450518d7db7a3da825e44</param></service></servicedata> \ No newline at end of file + <param name="changesrevision">fa48f7515b50272b7106702a662fadbf2ead3d18</param></service></servicedata> \ No newline at end of file ++++++ cni-plugins-0.9.0.tar.xz -> cni-plugins-0.9.1.tar.xz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cni-plugins-0.9.0/go.mod new/cni-plugins-0.9.1/go.mod --- old/cni-plugins-0.9.0/go.mod 2020-12-09 18:16:52.000000000 +0100 +++ new/cni-plugins-0.9.1/go.mod 2021-02-05 16:38:32.000000000 +0100 @@ -7,8 +7,8 @@ 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.0 - github.com/coreos/go-iptables v0.4.5 + github.com/containernetworking/cni v0.8.1 + 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 github.com/d2g/dhcp4client v1.0.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cni-plugins-0.9.0/go.sum new/cni-plugins-0.9.1/go.sum --- old/cni-plugins-0.9.0/go.sum 2020-12-09 18:16:52.000000000 +0100 +++ new/cni-plugins-0.9.1/go.sum 2021-02-05 16:38:32.000000000 +0100 @@ -6,10 +6,10 @@ 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.0 h1:BT9lpgGoH4jw3lFC7Odz2prU5ruiYKcgAjMCbgybcKI= -github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= -github.com/coreos/go-iptables v0.4.5 h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38= -github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/containernetworking/cni v0.8.1 h1:7zpDnQ3T3s4ucOuJ/ZCLrYBxzkg0AELFfII3Epo9TmI= +github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +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= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c h1:Xo2rK1pzOm0jO6abTPIQwbAmqBIOj132otexc1mmzFc= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cni-plugins-0.9.0/plugins/ipam/dhcp/client.go new/cni-plugins-0.9.1/plugins/ipam/dhcp/client.go --- old/cni-plugins-0.9.0/plugins/ipam/dhcp/client.go 2020-12-09 18:16:52.000000000 +0100 +++ new/cni-plugins-0.9.1/plugins/ipam/dhcp/client.go 2021-02-05 16:38:32.000000000 +0100 @@ -1,3 +1,17 @@ +// Copyright 2021 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 main import ( diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cni-plugins-0.9.0/plugins/ipam/dhcp/daemon.go new/cni-plugins-0.9.1/plugins/ipam/dhcp/daemon.go --- old/cni-plugins-0.9.0/plugins/ipam/dhcp/daemon.go 2020-12-09 18:16:52.000000000 +0100 +++ new/cni-plugins-0.9.1/plugins/ipam/dhcp/daemon.go 2021-02-05 16:38:32.000000000 +0100 @@ -26,6 +26,7 @@ "path/filepath" "runtime" "sync" + "time" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" @@ -41,11 +42,14 @@ mux sync.Mutex leases map[string]*DHCPLease hostNetnsPrefix string + clientTimeout time.Duration + broadcast bool } -func newDHCP() *DHCP { +func newDHCP(clientTimeout time.Duration) *DHCP { return &DHCP{ - leases: make(map[string]*DHCPLease), + leases: make(map[string]*DHCPLease), + clientTimeout: clientTimeout, } } @@ -63,7 +67,7 @@ clientID := generateClientID(args.ContainerID, conf.Name, args.IfName) hostNetns := d.hostNetnsPrefix + args.Netns - l, err := AcquireLease(clientID, hostNetns, args.IfName) + l, err := AcquireLease(clientID, hostNetns, args.IfName, d.clientTimeout, d.broadcast) if err != nil { return err } @@ -156,7 +160,10 @@ } } -func runDaemon(pidfilePath string, hostPrefix string, socketPath string) error { +func runDaemon( + pidfilePath, hostPrefix, socketPath string, + dhcpClientTimeout time.Duration, broadcast bool, +) error { // since other goroutines (on separate threads) will change namespaces, // ensure the RPC server does not get scheduled onto those runtime.LockOSThread() @@ -176,8 +183,9 @@ return fmt.Errorf("Error getting listener: %v", err) } - dhcp := newDHCP() + dhcp := newDHCP(dhcpClientTimeout) dhcp.hostNetnsPrefix = hostPrefix + dhcp.broadcast = broadcast rpc.Register(dhcp) rpc.HandleHTTP() http.Serve(l, nil) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cni-plugins-0.9.0/plugins/ipam/dhcp/lease.go new/cni-plugins-0.9.1/plugins/ipam/dhcp/lease.go --- old/cni-plugins-0.9.0/plugins/ipam/dhcp/lease.go 2020-12-09 18:16:52.000000000 +0100 +++ new/cni-plugins-0.9.1/plugins/ipam/dhcp/lease.go 2021-02-05 16:38:32.000000000 +0100 @@ -56,6 +56,8 @@ renewalTime time.Time rebindingTime time.Time expireTime time.Time + timeout time.Duration + broadcast bool stopping uint32 stop chan struct{} wg sync.WaitGroup @@ -64,11 +66,16 @@ // AcquireLease gets an DHCP lease and then maintains it in the background // by periodically renewing it. The acquired lease can be released by // calling DHCPLease.Stop() -func AcquireLease(clientID, netns, ifName string) (*DHCPLease, error) { +func AcquireLease( + clientID, netns, ifName string, + timeout time.Duration, broadcast bool, +) (*DHCPLease, error) { errCh := make(chan error, 1) l := &DHCPLease{ - clientID: clientID, - stop: make(chan struct{}), + clientID: clientID, + stop: make(chan struct{}), + timeout: timeout, + broadcast: broadcast, } log.Printf("%v: acquiring lease", clientID) @@ -115,7 +122,7 @@ } func (l *DHCPLease) acquire() error { - c, err := newDHCPClient(l.link, l.clientID) + c, err := newDHCPClient(l.link, l.clientID, l.timeout, l.broadcast) if err != nil { return err } @@ -242,7 +249,7 @@ } func (l *DHCPLease) renew() error { - c, err := newDHCPClient(l.link, l.clientID) + c, err := newDHCPClient(l.link, l.clientID, l.timeout, l.broadcast) if err != nil { return err } @@ -273,7 +280,7 @@ func (l *DHCPLease) release() error { log.Printf("%v: releasing lease", l.clientID) - c, err := newDHCPClient(l.link, l.clientID) + c, err := newDHCPClient(l.link, l.clientID, l.timeout, l.broadcast) if err != nil { return err } @@ -361,7 +368,11 @@ return nil, errNoMoreTries } -func newDHCPClient(link netlink.Link, clientID string) (*dhcp4client.Client, error) { +func newDHCPClient( + link netlink.Link, clientID string, + timeout time.Duration, + broadcast bool, +) (*dhcp4client.Client, error) { pktsock, err := dhcp4client.NewPacketSock(link.Attrs().Index) if err != nil { return nil, err @@ -369,8 +380,8 @@ return dhcp4client.New( dhcp4client.HardwareAddr(link.Attrs().HardwareAddr), - dhcp4client.Timeout(5*time.Second), - dhcp4client.Broadcast(false), + dhcp4client.Timeout(timeout), + dhcp4client.Broadcast(broadcast), dhcp4client.Connection(pktsock), ) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cni-plugins-0.9.0/plugins/ipam/dhcp/main.go new/cni-plugins-0.9.1/plugins/ipam/dhcp/main.go --- old/cni-plugins-0.9.0/plugins/ipam/dhcp/main.go 2020-12-09 18:16:52.000000000 +0100 +++ new/cni-plugins-0.9.1/plugins/ipam/dhcp/main.go 2021-02-05 16:38:32.000000000 +0100 @@ -22,6 +22,7 @@ "net/rpc" "os" "path/filepath" + "time" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" @@ -37,17 +38,21 @@ var pidfilePath string var hostPrefix string var socketPath string + var broadcast bool + var timeout 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.Parse(os.Args[2:]) if socketPath == "" { socketPath = defaultSocketPath } - if err := runDaemon(pidfilePath, hostPrefix, socketPath); err != nil { + if err := runDaemon(pidfilePath, hostPrefix, socketPath, timeout, broadcast); err != nil { log.Printf(err.Error()) os.Exit(1) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cni-plugins-0.9.0/plugins/ipam/host-local/main.go new/cni-plugins-0.9.1/plugins/ipam/host-local/main.go --- old/cni-plugins-0.9.0/plugins/ipam/host-local/main.go 2020-12-09 18:16:52.000000000 +0100 +++ new/cni-plugins-0.9.1/plugins/ipam/host-local/main.go 2021-02-05 16:38:32.000000000 +0100 @@ -15,7 +15,6 @@ package main import ( - "encoding/json" "fmt" "net" "strings" @@ -34,14 +33,6 @@ skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, bv.BuildString("host-local")) } -func loadNetConf(bytes []byte) (*types.NetConf, string, error) { - n := &types.NetConf{} - if err := json.Unmarshal(bytes, n); err != nil { - return nil, "", fmt.Errorf("failed to load netconf: %v", err) - } - return n, n.CNIVersion, nil -} - func cmdCheck(args *skel.CmdArgs) error { ipamConf, _, err := allocator.LoadIPAMConfig(args.StdinData, args.Args) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cni-plugins-0.9.0/plugins/main/host-device/host-device.go new/cni-plugins-0.9.1/plugins/main/host-device/host-device.go --- old/cni-plugins-0.9.0/plugins/main/host-device/host-device.go 2020-12-09 18:16:52.000000000 +0100 +++ new/cni-plugins-0.9.1/plugins/main/host-device/host-device.go 2021-02-05 16:38:32.000000000 +0100 @@ -43,6 +43,9 @@ sysBusPCI = "/sys/bus/pci/devices" ) +// Array of different linux drivers bound to network device needed for DPDK +var userspaceDrivers = []string{"vfio-pci", "uio_pci_generic", "igb_uio"} + //NetConf for host-device config, look the README to learn how to use those parameters type NetConf struct { types.NetConf @@ -91,6 +94,16 @@ } defer containerNs.Close() + if len(cfg.PCIAddr) > 0 { + isDpdkMode, err := hasDpdkDriver(cfg.PCIAddr) + if err != nil { + return fmt.Errorf("error with host device: %v", err) + } + if isDpdkMode { + return types.PrintResult(¤t.Result{}, cfg.CNIVersion) + } + } + hostDev, err := getLink(cfg.Device, cfg.HWAddr, cfg.KernelPath, cfg.PCIAddr) if err != nil { return fmt.Errorf("failed to find host device: %v", err) @@ -168,6 +181,16 @@ } defer containerNs.Close() + if len(cfg.PCIAddr) > 0 { + isDpdkMode, err := hasDpdkDriver(cfg.PCIAddr) + if err != nil { + return fmt.Errorf("error with host device: %v", err) + } + if isDpdkMode { + return nil + } + } + if err := moveLinkOut(containerNs, args.IfName); err != nil { return err } @@ -255,6 +278,25 @@ }) } +func hasDpdkDriver(pciaddr string) (bool, error) { + driverLink := filepath.Join(sysBusPCI, pciaddr, "driver") + driverPath, err := filepath.EvalSymlinks(driverLink) + if err != nil { + return false, err + } + driverStat, err := os.Stat(driverPath) + if err != nil { + return false, err + } + driverName := driverStat.Name() + for _, drv := range userspaceDrivers { + if driverName == drv { + return true, nil + } + } + return false, nil +} + func printLink(dev netlink.Link, cniVersion string, containerNs ns.NetNS) error { result := current.Result{ CNIVersion: current.ImplementedSpecVersion, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cni-plugins-0.9.0/plugins/main/vlan/vlan.go new/cni-plugins-0.9.1/plugins/main/vlan/vlan.go --- old/cni-plugins-0.9.0/plugins/main/vlan/vlan.go 2020-12-09 18:16:52.000000000 +0100 +++ new/cni-plugins-0.9.1/plugins/main/vlan/vlan.go 2021-02-05 16:38:32.000000000 +0100 @@ -152,7 +152,7 @@ // run the IPAM plugin and get back the config to apply r, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData) if err != nil { - return err + return fmt.Errorf("failed to execute IPAM delegate: %v", err) } // Invoke ipam del if err to avoid ip leak @@ -238,7 +238,7 @@ return err } if conf.NetConf.RawPrevResult == nil { - return fmt.Errorf("ptp: Required prevResult missing") + return fmt.Errorf("vlan: Required prevResult missing") } if err := version.ParsePrevResult(&conf.NetConf); err != nil { return err @@ -308,10 +308,10 @@ } link, err = netlink.LinkByName(intf.Name) if err != nil { - return fmt.Errorf("ptp: Container Interface name in prevResult: %s not found", intf.Name) + return fmt.Errorf("vlan: Container Interface name in prevResult: %s not found", intf.Name) } if intf.Sandbox == "" { - return fmt.Errorf("ptp: Error: Container interface %s should not be in host namespace", link.Attrs().Name) + return fmt.Errorf("vlan: Error: Container interface %s should not be in host namespace", link.Attrs().Name) } vlan, isVlan := link.(*netlink.Vlan) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cni-plugins-0.9.0/plugins/meta/bandwidth/bandwidth_linux_test.go new/cni-plugins-0.9.1/plugins/meta/bandwidth/bandwidth_linux_test.go --- old/cni-plugins-0.9.0/plugins/meta/bandwidth/bandwidth_linux_test.go 2020-12-09 18:16:52.000000000 +0100 +++ new/cni-plugins-0.9.1/plugins/meta/bandwidth/bandwidth_linux_test.go 2021-02-05 16:38:32.000000000 +0100 @@ -936,6 +936,7 @@ 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 @@ -944,6 +945,7 @@ 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 @@ -1081,6 +1083,7 @@ 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 @@ -1089,6 +1092,7 @@ 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cni-plugins-0.9.0/plugins/meta/flannel/flannel.go new/cni-plugins-0.9.1/plugins/meta/flannel/flannel.go --- old/cni-plugins-0.9.0/plugins/meta/flannel/flannel.go 2020-12-09 18:16:52.000000000 +0100 +++ new/cni-plugins-0.9.1/plugins/meta/flannel/flannel.go 2021-02-05 16:38:32.000000000 +0100 @@ -57,6 +57,8 @@ type subnetEnv struct { nw *net.IPNet sn *net.IPNet + ip6Nw *net.IPNet + ip6Sn *net.IPNet mtu *uint ipmasq *bool } @@ -64,11 +66,11 @@ func (se *subnetEnv) missing() string { m := []string{} - if se.nw == nil { - m = append(m, "FLANNEL_NETWORK") + if se.nw == nil && se.ip6Nw == nil { + m = append(m, []string{"FLANNEL_NETWORK", "FLANNEL_IPV6_NETWORK"}...) } - if se.sn == nil { - m = append(m, "FLANNEL_SUBNET") + if se.sn == nil && se.ip6Sn == nil { + m = append(m, []string{"FLANNEL_SUBNET", "FLANNEL_IPV6_SUBNET"}...) } if se.mtu == nil { m = append(m, "FLANNEL_MTU") @@ -127,6 +129,18 @@ if err != nil { return nil, err } + + case "FLANNEL_IPV6_NETWORK": + _, se.ip6Nw, err = net.ParseCIDR(parts[1]) + if err != nil { + return nil, err + } + + case "FLANNEL_IPV6_SUBNET": + _, se.ip6Sn, err = net.ParseCIDR(parts[1]) + if err != nil { + return nil, err + } case "FLANNEL_MTU": mtu, err := strconv.ParseUint(parts[1], 10, 32) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cni-plugins-0.9.0/plugins/meta/flannel/flannel_linux.go new/cni-plugins-0.9.1/plugins/meta/flannel/flannel_linux.go --- old/cni-plugins-0.9.0/plugins/meta/flannel/flannel_linux.go 2020-12-09 18:16:52.000000000 +0100 +++ new/cni-plugins-0.9.1/plugins/meta/flannel/flannel_linux.go 2021-02-05 16:38:32.000000000 +0100 @@ -40,13 +40,33 @@ if !hasKey(ipam, "type") { ipam["type"] = "host-local" } - ipam["subnet"] = fenv.sn.String() + + var rangesSlice [][]map[string]interface{} + + if fenv.sn != nil && fenv.sn.String() != "" { + rangesSlice = append(rangesSlice, []map[string]interface{}{ + {"subnet": fenv.sn.String()}, + }, + ) + } + if fenv.ip6Sn != nil && fenv.ip6Sn.String() != "" { + rangesSlice = append(rangesSlice, []map[string]interface{}{ + {"subnet": fenv.ip6Sn.String()}, + }, + ) + } + ipam["ranges"] = rangesSlice rtes, err := getIPAMRoutes(n) if err != nil { return nil, fmt.Errorf("failed to read IPAM routes: %w", err) } - rtes = append(rtes, types.Route{Dst: *fenv.nw}) + if fenv.nw != nil { + rtes = append(rtes, types.Route{Dst: *fenv.nw}) + } + if fenv.ip6Nw != nil { + rtes = append(rtes, types.Route{Dst: *fenv.ip6Nw}) + } ipam["routes"] = rtes return ipam, nil diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cni-plugins-0.9.0/plugins/meta/flannel/flannel_linux_test.go new/cni-plugins-0.9.1/plugins/meta/flannel/flannel_linux_test.go --- old/cni-plugins-0.9.0/plugins/meta/flannel/flannel_linux_test.go 2020-12-09 18:16:52.000000000 +0100 +++ new/cni-plugins-0.9.1/plugins/meta/flannel/flannel_linux_test.go 2021-02-05 16:38:32.000000000 +0100 @@ -30,10 +30,14 @@ var _ = Describe("Flannel", func() { var ( - originalNS ns.NetNS - input string - subnetFile string - dataDir string + originalNS ns.NetNS + onlyIpv4Input string + onlyIpv6Input string + dualStackInput string + onlyIpv4SubnetFile string + onlyIpv6SubnetFile string + dualStackSubnetFile string + dataDir string ) BeforeEach(func() { @@ -48,10 +52,10 @@ const inputTemplate = ` { - "name": "cni-flannel", - "type": "flannel", - "subnetFile": "%s", - "dataDir": "%s"%s + "name": "cni-flannel", + "type": "flannel", + "subnetFile": "%s", + "dataDir": "%s"%s }` const inputIPAMTemplate = ` @@ -70,12 +74,26 @@ { "dst": "10.96.0.0/12" }, { "dst": "192.168.244.0/24", "gw": "10.1.17.20" }` - const flannelSubnetEnv = ` + const onlyIpv4FlannelSubnetEnv = ` FLANNEL_NETWORK=10.1.0.0/16 FLANNEL_SUBNET=10.1.17.1/24 FLANNEL_MTU=1472 FLANNEL_IPMASQ=true ` + const onlyIpv6FlannelSubnetEnv = ` +FLANNEL_IPV6_NETWORK=fc00::/48 +FLANNEL_IPV6_SUBNET=fc00::1/64 +FLANNEL_MTU=1472 +FLANNEL_IPMASQ=true +` + const dualStackFlannelSubnetEnv = ` +FLANNEL_NETWORK=10.1.0.0/16 +FLANNEL_SUBNET=10.1.17.1/24 +FLANNEL_IPV6_NETWORK=fc00::/48 +FLANNEL_IPV6_SUBNET=fc00::1/64 +FLANNEL_MTU=1472 +FLANNEL_IPMASQ=true +` var writeSubnetEnv = func(contents string) string { file, err := ioutil.TempFile("", "subnet.env") @@ -96,7 +114,7 @@ return c } - var makeInput = func(inputIPAM string) string { + var makeInput = func(inputIPAM string, subnetFile string) string { ipamPart := "" if len(inputIPAM) > 0 { ipamPart = ",\n \"ipam\":\n" + inputIPAM @@ -108,116 +126,350 @@ BeforeEach(func() { var err error // flannel subnet.env - subnetFile = writeSubnetEnv(flannelSubnetEnv) + onlyIpv4SubnetFile = writeSubnetEnv(onlyIpv4FlannelSubnetEnv) + onlyIpv6SubnetFile = writeSubnetEnv(onlyIpv6FlannelSubnetEnv) + dualStackSubnetFile = writeSubnetEnv(dualStackFlannelSubnetEnv) // flannel state dir dataDir, err = ioutil.TempDir("", "dataDir") Expect(err).NotTo(HaveOccurred()) - input = makeInput("") + onlyIpv4Input = makeInput("", onlyIpv4SubnetFile) + onlyIpv6Input = makeInput("", onlyIpv6SubnetFile) + dualStackInput = makeInput("", dualStackSubnetFile) }) AfterEach(func() { - os.Remove(subnetFile) + os.Remove(onlyIpv4SubnetFile) + os.Remove(onlyIpv6SubnetFile) + os.Remove(dualStackSubnetFile) os.Remove(dataDir) }) Describe("CNI lifecycle", func() { - It("uses dataDir for storing network configuration", func() { - const IFNAME = "eth0" + Context("when using only ipv4 stack", func() { + It("uses dataDir for storing network configuration with ipv4 stack", func() { + const IFNAME = "eth0" + + targetNs, err := testutils.NewNS() + Expect(err).NotTo(HaveOccurred()) + defer targetNs.Close() - targetNs, err := testutils.NewNS() - Expect(err).NotTo(HaveOccurred()) - defer targetNs.Close() - - args := &skel.CmdArgs{ - ContainerID: "some-container-id", - Netns: targetNs.Path(), - IfName: IFNAME, - StdinData: []byte(input), - } - - err = originalNS.Do(func(ns.NetNS) error { - defer GinkgoRecover() - - By("calling ADD") - resI, _, err := testutils.CmdAddWithArgs(args, func() error { - return cmdAdd(args) + 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() + + By("calling ADD with ipv4 stack") + resI, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + 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()) + }) + }) - By("check that plugin writes the net config to dataDir") - path := fmt.Sprintf("%s/%s", dataDir, "some-container-id") - Expect(path).Should(BeAnExistingFile()) + Context("when using only ipv6 stack", func() { + It("uses dataDir for storing network configuration with ipv6 stack", func() { + const IFNAME = "eth0" - netConfBytes, err := ioutil.ReadFile(path) + targetNs, err := testutils.NewNS() Expect(err).NotTo(HaveOccurred()) - expected := `{ - "ipMasq" : false, - "ipam" : { - "routes" : [ - { - "dst" : "10.1.0.0/16" - } - ], - "subnet" : "10.1.17.0/24", - "type" : "host-local" - }, - "isGateway": true, - "mtu" : 1472, - "name" : "cni-flannel", - "type" : "bridge" + defer targetNs.Close() + + args := &skel.CmdArgs{ + ContainerID: "some-container-id-ipv6", + Netns: targetNs.Path(), + IfName: IFNAME, + StdinData: []byte(onlyIpv6Input), + } + + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + By("calling ADD with ipv6 stack") + resI, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + 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)) + Expect(netConfBytes).Should(MatchJSON(expected)) - result, err := current.NewResultFromResult(resI) - Expect(err).NotTo(HaveOccurred()) - Expect(result.IPs).To(HaveLen(1)) + 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()) - By("calling DEL") - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) + return nil }) Expect(err).NotTo(HaveOccurred()) + }) + }) - By("check that plugin removes net config from state dir") - Expect(path).ShouldNot(BeAnExistingFile()) + Context("when using dual stack", func() { + It("uses dataDir for storing network configuration with dual stack", func() { + const IFNAME = "eth0" - By("calling DEL again") - err = testutils.CmdDelWithArgs(args, func() error { - return cmdDel(args) - }) - By("check that plugin does not fail due to missing net config") + targetNs, err := testutils.NewNS() Expect(err).NotTo(HaveOccurred()) + defer targetNs.Close() + + args := &skel.CmdArgs{ + ContainerID: "some-container-id-dual-stack", + Netns: targetNs.Path(), + IfName: IFNAME, + StdinData: []byte(dualStackInput), + } + + err = originalNS.Do(func(ns.NetNS) error { + defer GinkgoRecover() + + By("calling ADD with dual stack") + resI, _, err := testutils.CmdAddWithArgs(args, func() error { + return cmdAdd(args) + }) + 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 + return nil + }) + Expect(err).NotTo(HaveOccurred()) }) - Expect(err).NotTo(HaveOccurred()) }) }) Describe("loadFlannelNetConf", func() { - Context("when subnetFile and dataDir are specified", func() { - It("loads flannel network config", func() { - conf, err := loadFlannelNetConf([]byte(input)) + 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)) + Expect(err).ShouldNot(HaveOccurred()) + Expect(conf.Name).To(Equal("cni-flannel")) + Expect(conf.Type).To(Equal("flannel")) + Expect(conf.SubnetFile).To(Equal(onlyIpv4SubnetFile)) + Expect(conf.DataDir).To(Equal(dataDir)) + }) + }) + + Context("when subnetFile and dataDir are specified with ipv6 stack", func() { + It("loads flannel network config with ipv6 stack", func() { + conf, err := loadFlannelNetConf([]byte(onlyIpv6Input)) + Expect(err).ShouldNot(HaveOccurred()) + Expect(conf.Name).To(Equal("cni-flannel")) + Expect(conf.Type).To(Equal("flannel")) + Expect(conf.SubnetFile).To(Equal(onlyIpv6SubnetFile)) + Expect(conf.DataDir).To(Equal(dataDir)) + }) + }) + + Context("when subnetFile and dataDir are specified with dual stack", func() { + It("loads flannel network config with dual stack", func() { + conf, err := loadFlannelNetConf([]byte(dualStackInput)) Expect(err).ShouldNot(HaveOccurred()) Expect(conf.Name).To(Equal("cni-flannel")) Expect(conf.Type).To(Equal("flannel")) - Expect(conf.SubnetFile).To(Equal(subnetFile)) + Expect(conf.SubnetFile).To(Equal(dualStackSubnetFile)) Expect(conf.DataDir).To(Equal(dataDir)) }) }) - Context("when defaulting subnetFile and dataDir", func() { + Context("when defaulting subnetFile and dataDir with ipv4 stack", func() { + BeforeEach(func() { + onlyIpv4Input = `{ +"name": "cni-flannel", +"type": "flannel" +}` + }) + + It("loads flannel network config with defaults with ipv4 stack", func() { + conf, err := loadFlannelNetConf([]byte(onlyIpv4Input)) + Expect(err).ShouldNot(HaveOccurred()) + Expect(conf.Name).To(Equal("cni-flannel")) + Expect(conf.Type).To(Equal("flannel")) + Expect(conf.SubnetFile).To(Equal(defaultSubnetFile)) + Expect(conf.DataDir).To(Equal(defaultDataDir)) + }) + }) + + Context("when defaulting subnetFile and dataDir with ipv6 stack", func() { BeforeEach(func() { - input = `{ + onlyIpv6Input = `{ "name": "cni-flannel", "type": "flannel" }` }) - It("loads flannel network config with defaults", func() { - conf, err := loadFlannelNetConf([]byte(input)) + It("loads flannel network config with defaults with ipv6 stack", func() { + conf, err := loadFlannelNetConf([]byte(onlyIpv6Input)) + Expect(err).ShouldNot(HaveOccurred()) + Expect(conf.Name).To(Equal("cni-flannel")) + Expect(conf.Type).To(Equal("flannel")) + Expect(conf.SubnetFile).To(Equal(defaultSubnetFile)) + Expect(conf.DataDir).To(Equal(defaultDataDir)) + }) + }) + + Context("when defaulting subnetFile and dataDir with dual stack", func() { + BeforeEach(func() { + dualStackInput = `{ +"name": "cni-flannel", +"type": "flannel" +}` + }) + + It("loads flannel network config with defaults with dual stack", func() { + conf, err := loadFlannelNetConf([]byte(dualStackInput)) Expect(err).ShouldNot(HaveOccurred()) Expect(conf.Name).To(Equal("cni-flannel")) Expect(conf.Type).To(Equal("flannel")) @@ -227,9 +479,9 @@ }) Describe("loadFlannelSubnetEnv", func() { - Context("when flannel subnet env is valid", func() { - It("loads flannel subnet config", func() { - conf, err := loadFlannelSubnetEnv(subnetFile) + Context("when flannel subnet env is valid with ipv4 stack", func() { + It("loads flannel subnet config with ipv4 stack", func() { + conf, err := loadFlannelSubnetEnv(onlyIpv4SubnetFile) Expect(err).ShouldNot(HaveOccurred()) Expect(conf.nw.String()).To(Equal("10.1.0.0/16")) Expect(conf.sn.String()).To(Equal("10.1.17.0/24")) @@ -239,56 +491,188 @@ }) }) - Context("when flannel subnet env is invalid", func() { + Context("when flannel subnet env is valid with ipv6 stack", func() { + It("loads flannel subnet config with ipv6 stack", func() { + conf, err := loadFlannelSubnetEnv(onlyIpv6SubnetFile) + Expect(err).ShouldNot(HaveOccurred()) + Expect(conf.ip6Nw.String()).To(Equal("fc00::/48")) + Expect(conf.ip6Sn.String()).To(Equal("fc00::/64")) + var mtu uint = 1472 + Expect(*conf.mtu).To(Equal(mtu)) + Expect(*conf.ipmasq).To(BeTrue()) + }) + }) + + Context("when flannel subnet env is valid with dual stack", func() { + It("loads flannel subnet config with dual stack", func() { + conf, err := loadFlannelSubnetEnv(dualStackSubnetFile) + Expect(err).ShouldNot(HaveOccurred()) + Expect(conf.nw.String()).To(Equal("10.1.0.0/16")) + Expect(conf.sn.String()).To(Equal("10.1.17.0/24")) + Expect(conf.ip6Nw.String()).To(Equal("fc00::/48")) + Expect(conf.ip6Sn.String()).To(Equal("fc00::/64")) + var mtu uint = 1472 + Expect(*conf.mtu).To(Equal(mtu)) + Expect(*conf.ipmasq).To(BeTrue()) + }) + }) + + Context("when flannel subnet env is invalid with ipv4 stack", func() { + BeforeEach(func() { + onlyIpv4SubnetFile = writeSubnetEnv("foo=bar") + }) + It("returns an error", func() { + _, err := loadFlannelSubnetEnv(onlyIpv4SubnetFile) + Expect(err).To(MatchError(ContainSubstring("missing FLANNEL_NETWORK, FLANNEL_IPV6_NETWORK, FLANNEL_SUBNET, FLANNEL_IPV6_SUBNET, FLANNEL_MTU, FLANNEL_IPMASQ"))) + }) + }) + + Context("when flannel subnet env is invalid with ipv6 stack", func() { + BeforeEach(func() { + onlyIpv6SubnetFile = writeSubnetEnv("foo=bar") + }) + It("returns an error", func() { + _, err := loadFlannelSubnetEnv(onlyIpv6SubnetFile) + Expect(err).To(MatchError(ContainSubstring("missing FLANNEL_NETWORK, FLANNEL_IPV6_NETWORK, FLANNEL_SUBNET, FLANNEL_IPV6_SUBNET, FLANNEL_MTU, FLANNEL_IPMASQ"))) + }) + }) + + Context("when flannel subnet env is invalid with dual stack", func() { BeforeEach(func() { - subnetFile = writeSubnetEnv("foo=bar") + dualStackSubnetFile = writeSubnetEnv("foo=bar") }) It("returns an error", func() { - _, err := loadFlannelSubnetEnv(subnetFile) - Expect(err).To(MatchError(ContainSubstring("missing FLANNEL_NETWORK, FLANNEL_SUBNET, FLANNEL_MTU, FLANNEL_IPMASQ"))) + _, err := loadFlannelSubnetEnv(dualStackSubnetFile) + Expect(err).To(MatchError(ContainSubstring("missing FLANNEL_NETWORK, FLANNEL_IPV6_NETWORK, FLANNEL_SUBNET, FLANNEL_IPV6_SUBNET, FLANNEL_MTU, FLANNEL_IPMASQ"))) }) }) }) }) Describe("getDelegateIPAM", func() { - Context("when input IPAM is provided", func() { + Context("when input IPAM is provided with ipv4 stack", func() { BeforeEach(func() { inputIPAM := makeInputIPAM(inputIPAMType, inputIPAMRoutes, "") - input = makeInput(inputIPAM) + onlyIpv4Input = makeInput(inputIPAM, onlyIpv4SubnetFile) }) - It("configures Delegate IPAM accordingly", func() { - conf, err := loadFlannelNetConf([]byte(input)) + It("configures Delegate IPAM accordingly with ipv4 stack", func() { + conf, err := loadFlannelNetConf([]byte(onlyIpv4Input)) Expect(err).ShouldNot(HaveOccurred()) - fenv, err := loadFlannelSubnetEnv(subnetFile) + fenv, err := loadFlannelSubnetEnv(onlyIpv4SubnetFile) Expect(err).ShouldNot(HaveOccurred()) ipam, err := getDelegateIPAM(conf, fenv) Expect(err).ShouldNot(HaveOccurred()) podsRoute := "{ \"dst\": \"10.1.0.0/16\" }\n" - subnet := "\"subnet\": \"10.1.17.0/24\"" + subnet := "\"ranges\": [[{\"subnet\": \"10.1.17.0/24\"}]]" expected := makeInputIPAM(inputIPAMType, inputIPAMRoutes+",\n"+podsRoute, ",\n"+subnet) buf, _ := json.Marshal(ipam) Expect(buf).Should(MatchJSON(expected)) }) }) - Context("when input IPAM is provided without 'type'", func() { + Context("when input IPAM is provided with ipv6 stack", func() { + BeforeEach(func() { + inputIPAM := makeInputIPAM(inputIPAMType, inputIPAMRoutes, "") + onlyIpv6Input = makeInput(inputIPAM, onlyIpv6SubnetFile) + }) + It("configures Delegate IPAM accordingly with ipv6 stack", func() { + conf, err := loadFlannelNetConf([]byte(onlyIpv6Input)) + Expect(err).ShouldNot(HaveOccurred()) + fenv, err := loadFlannelSubnetEnv(onlyIpv6SubnetFile) + Expect(err).ShouldNot(HaveOccurred()) + + ipam, err := getDelegateIPAM(conf, fenv) + Expect(err).ShouldNot(HaveOccurred()) + + podsRoute := "{ \"dst\": \"fc00::/48\" }\n" + subnet := "\"ranges\": [[{ \"subnet\": \"fc00::/64\" }]]" + expected := makeInputIPAM(inputIPAMType, inputIPAMRoutes+",\n"+podsRoute, ",\n"+subnet) + buf, _ := json.Marshal(ipam) + Expect(buf).Should(MatchJSON(expected)) + }) + }) + + Context("when input IPAM is provided with dual stack", func() { + BeforeEach(func() { + inputIPAM := makeInputIPAM(inputIPAMType, inputIPAMRoutes, "") + dualStackInput = makeInput(inputIPAM, dualStackSubnetFile) + }) + It("configures Delegate IPAM accordingly with dual stack", func() { + conf, err := loadFlannelNetConf([]byte(dualStackInput)) + Expect(err).ShouldNot(HaveOccurred()) + fenv, err := loadFlannelSubnetEnv(dualStackSubnetFile) + Expect(err).ShouldNot(HaveOccurred()) + + ipam, err := getDelegateIPAM(conf, fenv) + Expect(err).ShouldNot(HaveOccurred()) + + podsRoute := "{ \"dst\": \"10.1.0.0/16\" }" + ",\n" + "{ \"dst\": \"fc00::/48\" }\n" + subnet := "\"ranges\": [[{ \"subnet\": \"10.1.17.0/24\" }],\n[{ \"subnet\": \"fc00::/64\" }]]" + expected := makeInputIPAM(inputIPAMType, inputIPAMRoutes+",\n"+podsRoute, ",\n"+subnet) + buf, _ := json.Marshal(ipam) + Expect(buf).Should(MatchJSON(expected)) + }) + }) + + Context("when input IPAM is provided without 'type' with ipv4 stack", func() { BeforeEach(func() { inputIPAM := makeInputIPAM("", inputIPAMRoutes, "") - input = makeInput(inputIPAM) + onlyIpv4Input = makeInput(inputIPAM, onlyIpv4SubnetFile) }) - It("configures Delegate IPAM with 'host-local' ipam", func() { - conf, err := loadFlannelNetConf([]byte(input)) + It("configures Delegate IPAM with 'host-local' ipam with ipv4 stack", func() { + conf, err := loadFlannelNetConf([]byte(onlyIpv4Input)) Expect(err).ShouldNot(HaveOccurred()) - fenv, err := loadFlannelSubnetEnv(subnetFile) + fenv, err := loadFlannelSubnetEnv(onlyIpv4SubnetFile) Expect(err).ShouldNot(HaveOccurred()) ipam, err := getDelegateIPAM(conf, fenv) Expect(err).ShouldNot(HaveOccurred()) podsRoute := "{ \"dst\": \"10.1.0.0/16\" }\n" - subnet := "\"subnet\": \"10.1.17.0/24\"" + subnet := "\"ranges\": [[{\"subnet\": \"10.1.17.0/24\"}]]" + expected := makeInputIPAM("host-local", inputIPAMRoutes+",\n"+podsRoute, ",\n"+subnet) + buf, _ := json.Marshal(ipam) + Expect(buf).Should(MatchJSON(expected)) + }) + }) + + Context("when input IPAM is provided without 'type' with ipv6 stack", func() { + BeforeEach(func() { + inputIPAM := makeInputIPAM("", inputIPAMRoutes, "") + onlyIpv6Input = makeInput(inputIPAM, onlyIpv6SubnetFile) + }) + It("configures Delegate IPAM with 'host-local' ipam with ipv6 stack", func() { + conf, err := loadFlannelNetConf([]byte(onlyIpv6Input)) + Expect(err).ShouldNot(HaveOccurred()) + fenv, err := loadFlannelSubnetEnv(onlyIpv6SubnetFile) + Expect(err).ShouldNot(HaveOccurred()) + ipam, err := getDelegateIPAM(conf, fenv) + Expect(err).ShouldNot(HaveOccurred()) + + podsRoute := "{ \"dst\": \"fc00::/48\" }\n" + subnet := "\"ranges\": [[{ \"subnet\": \"fc00::/64\" }]]" + expected := makeInputIPAM("host-local", inputIPAMRoutes+",\n"+podsRoute, ",\n"+subnet) + buf, _ := json.Marshal(ipam) + Expect(buf).Should(MatchJSON(expected)) + }) + }) + + Context("when input IPAM is provided without 'type' with dual stack", func() { + BeforeEach(func() { + inputIPAM := makeInputIPAM("", inputIPAMRoutes, "") + dualStackInput = makeInput(inputIPAM, dualStackSubnetFile) + }) + It("configures Delegate IPAM with 'host-local' ipam with dual stack", func() { + conf, err := loadFlannelNetConf([]byte(dualStackInput)) + Expect(err).ShouldNot(HaveOccurred()) + fenv, err := loadFlannelSubnetEnv(dualStackSubnetFile) + Expect(err).ShouldNot(HaveOccurred()) + ipam, err := getDelegateIPAM(conf, fenv) + Expect(err).ShouldNot(HaveOccurred()) + + podsRoute := "{ \"dst\": \"10.1.0.0/16\" }" + ",\n" + "{ \"dst\": \"fc00::/48\" }\n" + subnet := "\"ranges\": [[{ \"subnet\": \"10.1.17.0/24\" }],\n[{ \"subnet\": \"fc00::/64\" }]]" expected := makeInputIPAM("host-local", inputIPAMRoutes+",\n"+podsRoute, ",\n"+subnet) buf, _ := json.Marshal(ipam) Expect(buf).Should(MatchJSON(expected)) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cni-plugins-0.9.0/vendor/github.com/containernetworking/cni/pkg/invoke/find.go new/cni-plugins-0.9.1/vendor/github.com/containernetworking/cni/pkg/invoke/find.go --- old/cni-plugins-0.9.0/vendor/github.com/containernetworking/cni/pkg/invoke/find.go 2020-12-09 18:16:52.000000000 +0100 +++ new/cni-plugins-0.9.1/vendor/github.com/containernetworking/cni/pkg/invoke/find.go 2021-02-05 16:38:32.000000000 +0100 @@ -18,6 +18,7 @@ "fmt" "os" "path/filepath" + "strings" ) // FindInPath returns the full path of the plugin by searching in the provided path @@ -26,6 +27,10 @@ return "", fmt.Errorf("no plugin name provided") } + if strings.ContainsRune(plugin, os.PathSeparator) { + return "", fmt.Errorf("invalid plugin name: %s", plugin) + } + if len(paths) == 0 { return "", fmt.Errorf("no paths provided") } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cni-plugins-0.9.0/vendor/github.com/coreos/go-iptables/iptables/iptables.go new/cni-plugins-0.9.1/vendor/github.com/coreos/go-iptables/iptables/iptables.go --- old/cni-plugins-0.9.0/vendor/github.com/coreos/go-iptables/iptables/iptables.go 2020-12-09 18:16:52.000000000 +0100 +++ new/cni-plugins-0.9.1/vendor/github.com/coreos/go-iptables/iptables/iptables.go 2021-02-05 16:38:32.000000000 +0100 @@ -31,7 +31,6 @@ exec.ExitError cmd exec.Cmd msg string - proto Protocol exitStatus *int //for overriding } @@ -51,9 +50,8 @@ if e.ExitStatus() != 1 { return false } - cmdIptables := getIptablesCommand(e.proto) - msgNoRuleExist := fmt.Sprintf("%s: Bad rule (does a matching rule exist in that chain?).\n", cmdIptables) - msgNoChainExist := fmt.Sprintf("%s: No chain/target/match by that name.\n", cmdIptables) + msgNoRuleExist := "Bad rule (does a matching rule exist in that chain?).\n" + msgNoChainExist := "No chain/target/match by that name.\n" return strings.Contains(e.msg, msgNoRuleExist) || strings.Contains(e.msg, msgNoChainExist) } @@ -75,6 +73,7 @@ v2 int v3 int mode string // the underlying iptables operating mode, e.g. nf_tables + timeout int // time to wait for the iptables lock, default waits forever } // Stat represents a structured statistic entry. @@ -91,19 +90,42 @@ Options string `json:"options"` } -// New creates a new IPTables. -// For backwards compatibility, this always uses IPv4, i.e. "iptables". -func New() (*IPTables, error) { - return NewWithProtocol(ProtocolIPv4) +type option func(*IPTables) + +func IPFamily(proto Protocol) option { + return func(ipt *IPTables) { + ipt.proto = proto + } } -// New creates a new IPTables for the given proto. -// The proto will determine which command is used, either "iptables" or "ip6tables". -func NewWithProtocol(proto Protocol) (*IPTables, error) { - path, err := exec.LookPath(getIptablesCommand(proto)) +func Timeout(timeout int) option { + return func(ipt *IPTables) { + ipt.timeout = timeout + } +} + +// New creates a new IPTables configured with the options passed as parameter. +// For backwards compatibility, by default always uses IPv4 and timeout 0. +// i.e. you can create an IPv6 IPTables using a timeout of 5 seconds passing +// the IPFamily and Timeout options as follow: +// ip6t := New(IPFamily(ProtocolIPv6), Timeout(5)) +func New(opts ...option) (*IPTables, error) { + + ipt := &IPTables{ + proto: ProtocolIPv4, + timeout: 0, + } + + for _, opt := range opts { + opt(ipt) + } + + path, err := exec.LookPath(getIptablesCommand(ipt.proto)) if err != nil { return nil, err } + ipt.path = path + vstring, err := getIptablesVersionString(path) if err != nil { return nil, fmt.Errorf("could not get iptables version: %v", err) @@ -112,21 +134,23 @@ if err != nil { return nil, fmt.Errorf("failed to extract iptables version from [%s]: %v", vstring, err) } + ipt.v1 = v1 + ipt.v2 = v2 + ipt.v3 = v3 + ipt.mode = mode checkPresent, waitPresent, randomFullyPresent := getIptablesCommandSupport(v1, v2, v3) + ipt.hasCheck = checkPresent + ipt.hasWait = waitPresent + ipt.hasRandomFully = randomFullyPresent - ipt := IPTables{ - path: path, - proto: proto, - hasCheck: checkPresent, - hasWait: waitPresent, - hasRandomFully: randomFullyPresent, - v1: v1, - v2: v2, - v3: v3, - mode: mode, - } - return &ipt, nil + return ipt, nil +} + +// New creates a new IPTables for the given proto. +// The proto will determine which command is used, either "iptables" or "ip6tables". +func NewWithProtocol(proto Protocol) (*IPTables, error) { + return New(IPFamily(proto), Timeout(0)) } // Proto returns the protocol used by this IPTables. @@ -185,6 +209,14 @@ return ipt.run(cmd...) } +func (ipt *IPTables) DeleteIfExists(table, chain string, rulespec ...string) error { + exists, err := ipt.Exists(table, chain, rulespec...) + if err == nil && exists { + err = ipt.Delete(table, chain, rulespec...) + } + return err +} + // List rules in specified table/chain func (ipt *IPTables) List(table, chain string) ([]string, error) { args := []string{"-t", table, "-S", chain} @@ -222,6 +254,21 @@ return chains, nil } +// '-S' is fine with non existing rule index as long as the chain exists +// therefore pass index 1 to reduce overhead for large chains +func (ipt *IPTables) ChainExists(table, chain string) (bool, error) { + err := ipt.run("-t", table, "-S", chain, "1") + eerr, eok := err.(*Error) + switch { + case err == nil: + return true, nil + case eok && eerr.ExitStatus() == 1: + return false, nil + default: + return false, err + } +} + // Stats lists rules including the byte and packet counts func (ipt *IPTables) Stats(table, chain string) ([][]string, error) { args := []string{"-t", table, "-L", chain, "-n", "-v", "-x"} @@ -401,6 +448,18 @@ return ipt.run("-t", table, "-X", chain) } +func (ipt *IPTables) ClearAndDeleteChain(table, chain string) error { + exists, err := ipt.ChainExists(table, chain) + if err != nil || !exists { + return err + } + err = ipt.run("-t", table, "-F", chain) + if err == nil { + err = ipt.run("-t", table, "-X", chain) + } + return err +} + // ChangePolicy changes policy on chain to target func (ipt *IPTables) ChangePolicy(table, chain, target string) error { return ipt.run("-t", table, "-P", chain, target) @@ -428,6 +487,9 @@ args = append([]string{ipt.path}, args...) if ipt.hasWait { args = append(args, "--wait") + if ipt.timeout != 0 { + args = append(args, strconv.Itoa(ipt.timeout)) + } } else { fmu, err := newXtablesFileLock() if err != nil { @@ -452,7 +514,7 @@ if err := cmd.Run(); err != nil { switch e := err.(type) { case *exec.ExitError: - return &Error{*e, cmd, stderr.String(), ipt.proto, nil} + return &Error{*e, cmd, stderr.String(), nil} default: return err } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/cni-plugins-0.9.0/vendor/modules.txt new/cni-plugins-0.9.1/vendor/modules.txt --- old/cni-plugins-0.9.0/vendor/modules.txt 2020-12-09 18:16:52.000000000 +0100 +++ new/cni-plugins-0.9.1/vendor/modules.txt 2021-02-05 16:38:32.000000000 +0100 @@ -28,7 +28,7 @@ # github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44 ## explicit github.com/buger/jsonparser -# github.com/containernetworking/cni v0.8.0 +# github.com/containernetworking/cni v0.8.1 ## explicit github.com/containernetworking/cni/libcni github.com/containernetworking/cni/pkg/invoke @@ -38,7 +38,7 @@ github.com/containernetworking/cni/pkg/types/current github.com/containernetworking/cni/pkg/utils github.com/containernetworking/cni/pkg/version -# github.com/coreos/go-iptables v0.4.5 +# github.com/coreos/go-iptables v0.5.0 ## explicit github.com/coreos/go-iptables/iptables # github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7
