From 61c136126f378c680173b111110404f41197fc3a Mon Sep 17 00:00:00 2001 From: Tomofumi Hayashi Date: Thu, 6 Sep 2018 15:32:50 +0900 Subject: [PATCH] Support multiple IP addresses in CNI_ARGS --- plugins/ipam/static/README.md | 2 +- plugins/ipam/static/main.go | 47 +++++++++++++++++-------- plugins/ipam/static/static_test.go | 55 ++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 16 deletions(-) diff --git a/plugins/ipam/static/README.md b/plugins/ipam/static/README.md index 0c473d91..d9ca28c5 100644 --- a/plugins/ipam/static/README.md +++ b/plugins/ipam/static/README.md @@ -48,7 +48,7 @@ static IPAM is very simple IPAM plugin that assigns IPv4 and IPv6 addresses stat The following [CNI_ARGS](https://github.com/containernetworking/cni/blob/master/SPEC.md#parameters) are supported: -* `IP`: request a specific CIDR notation IP address +* `IP`: request a specific CIDR notation IP addresses, comma separated * `GATEWAY`: request a specific gateway address (example: CNI_ARGS="IP=10.10.0.1/24;GATEWAY=10.10.0.254") diff --git a/plugins/ipam/static/main.go b/plugins/ipam/static/main.go index 3a196d6d..2a42d882 100644 --- a/plugins/ipam/static/main.go +++ b/plugins/ipam/static/main.go @@ -18,6 +18,7 @@ import ( "encoding/json" "fmt" "net" + "strings" "github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/types" @@ -46,7 +47,7 @@ type IPAMConfig struct { type IPAMEnvArgs struct { types.CommonArgs IP types.UnmarshallableString `json:"ip,omitempty"` - GATEWAY net.IP `json:"gateway,omitempty"` + GATEWAY types.UnmarshallableString `json:"gateway,omitempty"` } type Address struct { @@ -124,23 +125,39 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) { } if e.IP != "" { - ip, subnet, err := net.ParseCIDR(string(e.IP)) - if err != nil { - return nil, "", fmt.Errorf("invalid CIDR %s: %s", e.IP, err) - } + for _, item := range strings.Split(string(e.IP), ",") { + ipstr := strings.TrimSpace(item) - addr := Address{Address: net.IPNet{IP: ip, Mask: subnet.Mask}} - if e.GATEWAY != nil { - addr.Gateway = e.GATEWAY + ip, subnet, err := net.ParseCIDR(ipstr) + if err != nil { + return nil, "", fmt.Errorf("invalid CIDR %s: %s", e.IP, err) + } + + 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) } - if addr.Address.IP.To4() != nil { - addr.Version = "4" - numV4++ - } else { - addr.Version = "6" - numV6++ + } + + if e.GATEWAY != "" { + for _, item := range strings.Split(string(e.GATEWAY), ",") { + gwip := net.ParseIP(strings.TrimSpace(item)) + if gwip == nil { + return nil, "", fmt.Errorf("invalid gateway address: %s", item) + } + + for i := range n.IPAM.Addresses { + if n.IPAM.Addresses[i].Address.Contains(gwip) { + n.IPAM.Addresses[i].Gateway = gwip + } + } } - n.IPAM.Addresses = append(n.IPAM.Addresses, addr) } } diff --git a/plugins/ipam/static/static_test.go b/plugins/ipam/static/static_test.go index afc631dd..c6d9aea6 100644 --- a/plugins/ipam/static/static_test.go +++ b/plugins/ipam/static/static_test.go @@ -210,6 +210,61 @@ var _ = Describe("static Operations", func() { }) 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()) + }) }) func mustCIDR(s string) net.IPNet {