Merge pull request #42 from steveeJ/plugins-args

host-local: allow ip request via CNI_ARGS
This commit is contained in:
Stefan Junker 2015-09-04 01:42:11 +02:00
commit fbd828cf60
5 changed files with 98 additions and 4 deletions

View File

@ -31,6 +31,11 @@ It stores the state locally on the host filesystem, therefore ensuring uniquenes
* `gateway` (string, optional): IP inside of "subnet" to designate as the gateway. Defaults to ".1" IP inside of the "subnet" block.
* `routes` (string, optional): list of routes to 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.
## Supported arguments
The following [CNI_ARGS](https://github.com/appc/cni/blob/master/SPEC.md#parameters) are supported:
* `ip`: request a specific IP address from the subnet. If it's not available, the plugin will exit with an error
## Files
Allocated IP addresses are stored as files in /var/lib/cni/networks/$NETWORK_NAME.

36
pkg/plugin/args.go Normal file
View File

@ -0,0 +1,36 @@
package plugin
import (
"encoding"
"fmt"
"reflect"
"strings"
)
func LoadArgs(args string, container interface{}) error {
if args == "" {
return nil
}
containerValue := reflect.ValueOf(container)
pairs := strings.Split(args, ",")
for _, pair := range pairs {
kv := strings.Split(pair, "=")
if len(kv) != 2 {
return fmt.Errorf("ARGS: invalid pair %q", pair)
}
keyString := kv[0]
valueString := kv[1]
keyField := containerValue.Elem().FieldByName(keyString)
if !keyField.IsValid() {
return fmt.Errorf("ARGS: invalid key %q", keyString)
}
u := keyField.Addr().Interface().(encoding.TextUnmarshaler)
err := u.UnmarshalText([]byte(valueString))
if err != nil {
return fmt.Errorf("ARGS: error parsing value of pair %q: %v)", pair, err)
}
}
return nil
}

View File

@ -78,6 +78,40 @@ func (a *IPAllocator) Get(id string) (*plugin.IPConfig, error) {
gw = ip.NextIP(a.conf.Subnet.IP)
}
var requestedIP net.IP
if a.conf.Args != nil {
requestedIP = a.conf.Args.IP
}
if requestedIP != nil {
if gw != nil && gw.Equal(a.conf.Args.IP) {
return nil, fmt.Errorf("requested IP must differ gateway IP")
}
subnet := net.IPNet{
IP: a.conf.Subnet.IP,
Mask: a.conf.Subnet.Mask,
}
err := validateRangeIP(requestedIP, &subnet)
if err != nil {
return nil, err
}
reserved, err := a.store.Reserve(id, requestedIP)
if err != nil {
return nil, err
}
if reserved {
return &plugin.IPConfig{
IP: net.IPNet{requestedIP, a.conf.Subnet.Mask},
Gateway: gw,
Routes: a.conf.Routes,
}, nil
}
return nil, fmt.Errorf("requested IP address %q is not available in network: %s", requestedIP, a.conf.Name)
}
for cur := a.start; !cur.Equal(a.end); cur = ip.NextIP(cur) {
// don't allocate gateway IP
if gw != nil && cur.Equal(gw) {
@ -96,7 +130,6 @@ func (a *IPAllocator) Get(id string) (*plugin.IPConfig, error) {
}, nil
}
}
return nil, fmt.Errorf("no IP addresses available in network: %s", a.conf.Name)
}

View File

@ -32,6 +32,11 @@ type IPAMConfig struct {
Subnet ip.IPNet `json:"subnet"`
Gateway net.IP `json:"gateway"`
Routes []plugin.Route `json:"routes"`
Args *IPAMArgs `json:"-"`
}
type IPAMArgs struct {
IP net.IP `json:"ip",omitempty`
}
type Net struct {
@ -40,12 +45,20 @@ type Net struct {
}
// NewIPAMConfig creates a NetworkConfig from the given network name.
func LoadIPAMConfig(bytes []byte) (*IPAMConfig, error) {
func LoadIPAMConfig(bytes []byte, args string) (*IPAMConfig, error) {
n := Net{}
if err := json.Unmarshal(bytes, &n); err != nil {
return nil, err
}
if args != "" {
ipamArgs := IPAMArgs{}
err := plugin.LoadArgs(args, &ipamArgs)
if err != nil {
return nil, err
}
}
if n.IPAM == nil {
return nil, fmt.Errorf("%q missing 'ipam' key")
}

View File

@ -28,7 +28,7 @@ func main() {
}
func cmdAdd(args *skel.CmdArgs) error {
ipamConf, err := LoadIPAMConfig(args.StdinData)
ipamConf, err := LoadIPAMConfig(args.StdinData, args.Args)
if err != nil {
return err
}
@ -39,6 +39,13 @@ func cmdAdd(args *skel.CmdArgs) error {
}
defer store.Close()
ipamArgs := IPAMArgs{}
err = plugin.LoadArgs(args.Args, &ipamArgs)
if err != nil {
return err
}
ipamConf.Args = &ipamArgs
allocator, err := NewIPAllocator(ipamConf, store)
if err != nil {
return err
@ -66,7 +73,7 @@ func cmdAdd(args *skel.CmdArgs) error {
}
func cmdDel(args *skel.CmdArgs) error {
ipamConf, err := LoadIPAMConfig(args.StdinData)
ipamConf, err := LoadIPAMConfig(args.StdinData, args.Args)
if err != nil {
return err
}