diff --git a/plugins/ipam/static/README.md b/plugins/ipam/static/README.md index 000fb486..1dfd029d 100644 --- a/plugins/ipam/static/README.md +++ b/plugins/ipam/static/README.md @@ -60,3 +60,9 @@ The plugin also support following [capability argument](https://github.com/conta The following [args conventions](https://github.com/containernetworking/cni/blob/master/CONVENTIONS.md#args-in-network-config) are supported: * `ips` (array of strings): A list of custom IPs to attempt to allocate, with prefix (e.g. '10.10.0.1/24') + +Notice: If some of above are used at same time, only one will work according to the priorities below + +1. [capability argument](https://github.com/containernetworking/cni/blob/master/CONVENTIONS.md) +1. [args conventions](https://github.com/containernetworking/cni/blob/master/CONVENTIONS.md#args-in-network-config) +1. [CNI_ARGS](https://github.com/containernetworking/cni/blob/master/SPEC.md#parameters) diff --git a/plugins/ipam/static/main.go b/plugins/ipam/static/main.go index 41a46e69..e83f5adf 100644 --- a/plugins/ipam/static/main.go +++ b/plugins/ipam/static/main.go @@ -145,11 +145,44 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) { return nil, "", err } - if len(n.RuntimeConfig.IPs) != 0 { - // args IP overwrites IP, so clear IPAM Config - n.IPAM.Addresses = make([]Address, 0, len(n.RuntimeConfig.IPs)) - for _, addr := range n.RuntimeConfig.IPs { - n.IPAM.Addresses = append(n.IPAM.Addresses, Address{AddressStr: addr}) + // load IP from CNI_ARGS + if envArgs != "" { + e := IPAMEnvArgs{} + err := types.LoadArgs(envArgs, &e) + if err != nil { + return nil, "", err + } + + if e.IP != "" { + for _, item := range strings.Split(string(e.IP), ",") { + ipstr := strings.TrimSpace(item) + + ip, subnet, err := net.ParseCIDR(ipstr) + if err != nil { + return nil, "", fmt.Errorf("invalid CIDR %s: %s", ipstr, err) + } + + addr := Address{ + Address: net.IPNet{IP: ip, Mask: subnet.Mask}, + AddressStr: ipstr, + } + n.IPAM.Addresses = append(n.IPAM.Addresses, addr) + } + } + + 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 + } + } + } } } @@ -162,6 +195,15 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) { } } + // import address from runtimeConfig + if len(n.RuntimeConfig.IPs) != 0 { + // runtimeConfig IP overwrites IP, so clear IPAM Config + n.IPAM.Addresses = make([]Address, 0, len(n.RuntimeConfig.IPs)) + for _, addr := range n.RuntimeConfig.IPs { + n.IPAM.Addresses = append(n.IPAM.Addresses, Address{AddressStr: addr}) + } + } + if n.IPAM == nil { return nil, "", fmt.Errorf("IPAM config missing 'ipam' key") } @@ -191,50 +233,6 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) { } } - if envArgs != "" { - e := IPAMEnvArgs{} - err := types.LoadArgs(envArgs, &e) - if err != nil { - return nil, "", err - } - - if e.IP != "" { - for _, item := range strings.Split(string(e.IP), ",") { - ipstr := strings.TrimSpace(item) - - ip, subnet, err := net.ParseCIDR(ipstr) - if err != nil { - return nil, "", fmt.Errorf("invalid CIDR %s: %s", ipstr, 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 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 - } - } - } - } - } - // CNI spec 0.2.0 and below supported only one v4 and v6 address if numV4 > 1 || numV6 > 1 { for _, v := range types020.SupportedVersions { diff --git a/plugins/ipam/static/static_test.go b/plugins/ipam/static/static_test.go index 8239b74f..54272565 100644 --- a/plugins/ipam/static/static_test.go +++ b/plugins/ipam/static/static_test.go @@ -404,6 +404,82 @@ var _ = Describe("static Operations", func() { }) 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"] + } + } + }` + + 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()) + 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"), + })) + 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()) + }) + }) func mustCIDR(s string) net.IPNet {