Support CNI_ARGS in static IPAM plugin
This change is to add CNI_ARGS support in static IPAM plugin. When IP/SUBNET/GATEWAY are given in CNI_ARGS, static IPAM adds these info in addition to config files. To configure ip address only from CNI_ARGS, 'address' field in config is changed to optional from required.
This commit is contained in:
parent
1d973f59d2
commit
6da1cb7876
@ -38,8 +38,17 @@ static IPAM is very simple IPAM plugin that assigns IPv4 and IPv6 addresses stat
|
|||||||
## Network configuration reference
|
## Network configuration reference
|
||||||
|
|
||||||
* `type` (string, required): "static"
|
* `type` (string, required): "static"
|
||||||
* `addresses` (array, required): an array of arrays of ip address objects:
|
* `addresses` (array, optional): an array of arrays of ip address objects:
|
||||||
* `address` (string, required): CIDR notation IP address.
|
* `address` (string, required): CIDR notation IP address.
|
||||||
* `gateway` (string, optional): IP inside of "subnet" to designate as the gateway.
|
* `gateway` (string, optional): IP inside of "subnet" to designate as the gateway.
|
||||||
* `routes` (string, optional): list of routes add to the container namespace. Each route is a dictionary with "dst" and optional "gw" fields. If "gw" is omitted, value of "gateway" will be used.
|
* `routes` (string, optional): list of routes add to the container namespace. Each route is a dictionary with "dst" and optional "gw" fields. If "gw" is omitted, value of "gateway" will be used.
|
||||||
* `dns` (string, optional): the dictionary with "nameservers", "domain" and "search".
|
* `dns` (string, optional): the dictionary with "nameservers", "domain" and "search".
|
||||||
|
|
||||||
|
## Supported arguments
|
||||||
|
The following [CNI_ARGS](https://github.com/containernetworking/cni/blob/master/SPEC.md#parameters) are supported:
|
||||||
|
|
||||||
|
* `IP`: request a specific IP address from a subnet
|
||||||
|
* `SUBNET`: request a specific subnet
|
||||||
|
* `GATEWAY`: request a specific gateway address
|
||||||
|
|
||||||
|
(example: CNI_ARGS="IP=10.10.0.1;SUBNET=10.10.0.0/24;GATEWAY=10.10.0.254")
|
||||||
|
@ -30,19 +30,29 @@ import (
|
|||||||
// The top-level network config - IPAM plugins are passed the full configuration
|
// The top-level network config - IPAM plugins are passed the full configuration
|
||||||
// of the calling plugin, not just the IPAM section.
|
// of the calling plugin, not just the IPAM section.
|
||||||
type Net struct {
|
type Net struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
CNIVersion string `json:"cniVersion"`
|
CNIVersion string `json:"cniVersion"`
|
||||||
IPAM *IPAMConfig `json:"ipam"`
|
IPAM *IPAMConfig `json:"ipam"`
|
||||||
|
RuntimeConfig struct {
|
||||||
|
Addresses []Address `json:"addresses,omitempty"`
|
||||||
|
} `json:"runtimeConfig,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type IPAMConfig struct {
|
type IPAMConfig struct {
|
||||||
Name string
|
Name string
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Routes []*types.Route `json:"routes"`
|
Routes []*types.Route `json:"routes"`
|
||||||
Addresses []Address `json:"addresses"`
|
Addresses []Address `json:"addresses,omitempty"`
|
||||||
DNS types.DNS `json:"dns"`
|
DNS types.DNS `json:"dns"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IPAMEnvArgs struct {
|
||||||
|
types.CommonArgs
|
||||||
|
IP net.IP `json:"ip,omitempty"`
|
||||||
|
SUBNET types.UnmarshallableString `json:"subnet,omitempty"`
|
||||||
|
GATEWAY net.IP `json:"gateway,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
type Address struct {
|
type Address struct {
|
||||||
AddressStr string `json:"address"`
|
AddressStr string `json:"address"`
|
||||||
Gateway net.IP `json:"gateway,omitempty"`
|
Gateway net.IP `json:"gateway,omitempty"`
|
||||||
@ -77,9 +87,14 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) {
|
|||||||
return nil, "", fmt.Errorf("IPAM config missing 'ipam' key")
|
return nil, "", fmt.Errorf("IPAM config missing 'ipam' key")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(n.RuntimeConfig.Addresses) != 0 {
|
||||||
|
n.IPAM.Addresses = append(n.RuntimeConfig.Addresses, n.IPAM.Addresses...)
|
||||||
|
}
|
||||||
|
|
||||||
// Validate all ranges
|
// Validate all ranges
|
||||||
numV4 := 0
|
numV4 := 0
|
||||||
numV6 := 0
|
numV6 := 0
|
||||||
|
|
||||||
for i := range n.IPAM.Addresses {
|
for i := range n.IPAM.Addresses {
|
||||||
ip, addr, err := net.ParseCIDR(n.IPAM.Addresses[i].AddressStr)
|
ip, addr, err := net.ParseCIDR(n.IPAM.Addresses[i].AddressStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -101,6 +116,34 @@ 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 != nil && e.SUBNET != "" {
|
||||||
|
_, subnet, err := net.ParseCIDR(string(e.SUBNET))
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", fmt.Errorf("invalid CIDR %s: %s", e.SUBNET, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := Address{Address: net.IPNet{IP: e.IP, Mask: subnet.Mask}}
|
||||||
|
if e.GATEWAY != nil {
|
||||||
|
addr.Gateway = e.GATEWAY
|
||||||
|
}
|
||||||
|
if addr.Address.IP.To4() != nil {
|
||||||
|
addr.Version = "4"
|
||||||
|
numV4++
|
||||||
|
} else {
|
||||||
|
addr.Version = "6"
|
||||||
|
numV6++
|
||||||
|
}
|
||||||
|
n.IPAM.Addresses = append(n.IPAM.Addresses, addr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// CNI spec 0.2.0 and below supported only one v4 and v6 address
|
// CNI spec 0.2.0 and below supported only one v4 and v6 address
|
||||||
if numV4 > 1 || numV6 > 1 {
|
if numV4 > 1 || numV6 > 1 {
|
||||||
for _, v := range types020.SupportedVersions {
|
for _, v := range types020.SupportedVersions {
|
||||||
|
@ -148,6 +148,68 @@ var _ = Describe("static Operations", func() {
|
|||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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;SUBNET=10.10.0.0/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())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
func mustCIDR(s string) net.IPNet {
|
func mustCIDR(s string) net.IPNet {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user