diff --git a/README.md b/README.md index 7b5aad78..5c9b641f 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,8 @@ ## What is CNI? CNI, the _Container Network Interface_, is a proposed standard for configuring network interfaces for Linux application containers. -The standard consists of a simple specification for how executable plugins can be used to configure network namespaces. +The standard consists of a simple specification for how executable plugins can be used to configure network namespaces; this repository also contains a go library implementing that specification. + The specification itself is contained in [SPEC.md](SPEC.md) ## Why develop CNI? @@ -47,6 +48,8 @@ $ cat >/etc/cni/net.d/10-mynet.conf < \n", exe, CmdAdd) + fmt.Fprintf(os.Stderr, " %s %s \n", exe, CmdDel) + os.Exit(1) +} + +func exit(err error) { + if err != nil { + fmt.Fprintf(os.Stderr, "%s\n", err) + os.Exit(1) + } + os.Exit(0) +} diff --git a/libcni/api.go b/libcni/api.go new file mode 100644 index 00000000..feaed8d6 --- /dev/null +++ b/libcni/api.go @@ -0,0 +1,68 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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 libcni + +import ( + "strings" + + "github.com/appc/cni/pkg/invoke" + "github.com/appc/cni/pkg/types" +) + +type RuntimeConf struct { + ContainerID string + NetNS string + IfName string + Args [][2]string +} + +type NetworkConfig struct { + Network *types.NetConf + Bytes []byte +} + +type CNI interface { + AddNetwork(net *NetworkConfig, rt *RuntimeConf) (*types.Result, error) + DelNetwork(net *NetworkConfig, rt *RuntimeConf) error +} + +type CNIConfig struct { + Path []string +} + +func (c *CNIConfig) AddNetwork(net *NetworkConfig, rt *RuntimeConf) (*types.Result, error) { + return c.execPlugin("ADD", net, rt) +} + +func (c *CNIConfig) DelNetwork(net *NetworkConfig, rt *RuntimeConf) error { + _, err := c.execPlugin("DEL", net, rt) + return err +} + +// ===== + +func (c *CNIConfig) execPlugin(action string, conf *NetworkConfig, rt *RuntimeConf) (*types.Result, error) { + pluginPath := invoke.FindInPath(conf.Network.Type, c.Path) + + args := &invoke.Args{ + Command: action, + ContainerID: rt.ContainerID, + NetNS: rt.NetNS, + PluginArgs: rt.Args, + IfName: rt.IfName, + Path: strings.Join(c.Path, ":"), + } + return invoke.ExecPlugin(pluginPath, conf.Bytes, args) +} diff --git a/libcni/conf.go b/libcni/conf.go new file mode 100644 index 00000000..47babeb4 --- /dev/null +++ b/libcni/conf.go @@ -0,0 +1,85 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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 libcni + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "sort" +) + +func ConfFromBytes(bytes []byte) (*NetworkConfig, error) { + conf := &NetworkConfig{Bytes: bytes} + if err := json.Unmarshal(bytes, &conf.Network); err != nil { + return nil, fmt.Errorf("error parsing configuration: %s", err) + } + return conf, nil +} + +func ConfFromFile(filename string) (*NetworkConfig, error) { + bytes, err := ioutil.ReadFile(filename) + if err != nil { + return nil, fmt.Errorf("error reading %s: %s", filename, err) + } + return ConfFromBytes(bytes) +} + +func ConfFiles(dir string) ([]string, error) { + // In part, adapted from rkt/networking/podenv.go#listFiles + files, err := ioutil.ReadDir(dir) + switch { + case err == nil: // break + case os.IsNotExist(err): + return nil, nil + default: + return nil, err + } + + confFiles := []string{} + for _, f := range files { + if f.IsDir() { + continue + } + if filepath.Ext(f.Name()) == ".conf" { + confFiles = append(confFiles, filepath.Join(dir, f.Name())) + } + } + return confFiles, nil +} + +func LoadConf(dir, name string) (*NetworkConfig, error) { + files, err := ConfFiles(dir) + switch { + case err != nil: + return nil, err + case len(files) == 0: + return nil, fmt.Errorf("no net configurations found") + } + sort.Strings(files) + + for _, confFile := range files { + conf, err := ConfFromFile(confFile) + if err != nil { + return nil, err + } + if conf.Network.Name == name { + return conf, nil + } + } + return nil, fmt.Errorf(`no net configuration with name "%s" in %s`, name, dir) +} diff --git a/pkg/invoke/args.go b/pkg/invoke/args.go new file mode 100644 index 00000000..6f0a813a --- /dev/null +++ b/pkg/invoke/args.go @@ -0,0 +1,76 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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 invoke + +import ( + "os" + "strings" +) + +type CNIArgs interface { + // For use with os/exec; i.e., return nil to inherit the + // environment from this process + AsEnv() []string +} + +type inherited struct{} + +var inheritArgsFromEnv inherited + +func (_ *inherited) AsEnv() []string { + return nil +} + +func ArgsFromEnv() CNIArgs { + return &inheritArgsFromEnv +} + +type Args struct { + Command string + ContainerID string + NetNS string + PluginArgs [][2]string + PluginArgsStr string + IfName string + Path string +} + +func (args *Args) AsEnv() []string { + env := os.Environ() + pluginArgsStr := args.PluginArgsStr + if pluginArgsStr == "" { + pluginArgsStr = stringify(args.PluginArgs) + } + + env = append(env, + "CNI_COMMAND="+args.Command, + "CNI_CONTAINERID="+args.ContainerID, + "CNI_NETNS="+args.NetNS, + "CNI_ARGS="+pluginArgsStr, + "CNI_IFNAME="+args.IfName, + "CNI_PATH="+args.Path) + return env +} + +// taken from rkt/networking/net_plugin.go +func stringify(pluginArgs [][2]string) string { + entries := make([]string, len(pluginArgs)) + + for i, kv := range pluginArgs { + entries[i] = strings.Join(kv[:], "=") + } + + return strings.Join(entries, ";") +} diff --git a/pkg/invoke/exec.go b/pkg/invoke/exec.go new file mode 100644 index 00000000..d7c5b7ab --- /dev/null +++ b/pkg/invoke/exec.go @@ -0,0 +1,66 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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 invoke + +import ( + "bytes" + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + + "github.com/appc/cni/pkg/types" +) + +func pluginErr(err error, output []byte) error { + if _, ok := err.(*exec.ExitError); ok { + emsg := types.Error{} + if perr := json.Unmarshal(output, &emsg); perr != nil { + return fmt.Errorf("netplugin failed but error parsing its diagnostic message %q: %v", string(output), perr) + } + details := "" + if emsg.Details != "" { + details = fmt.Sprintf("; %v", emsg.Details) + } + return fmt.Errorf("%v%v", emsg.Msg, details) + } + + return err +} + +func ExecPlugin(pluginPath string, netconf []byte, args CNIArgs) (*types.Result, error) { + if pluginPath == "" { + return nil, fmt.Errorf("could not find %q plugin", filepath.Base(pluginPath)) + } + + stdout := &bytes.Buffer{} + + c := exec.Cmd{ + Env: args.AsEnv(), + Path: pluginPath, + Args: []string{pluginPath}, + Stdin: bytes.NewBuffer(netconf), + Stdout: stdout, + Stderr: os.Stderr, + } + if err := c.Run(); err != nil { + return nil, pluginErr(err, stdout.Bytes()) + } + + res := &types.Result{} + err := json.Unmarshal(stdout.Bytes(), res) + return res, err +} diff --git a/pkg/invoke/find.go b/pkg/invoke/find.go new file mode 100644 index 00000000..dfad12bc --- /dev/null +++ b/pkg/invoke/find.go @@ -0,0 +1,37 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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 invoke + +import ( + "os" + "path/filepath" + "strings" +) + +func FindInPath(plugin string, path []string) string { + for _, p := range path { + fullname := filepath.Join(p, plugin) + if fi, err := os.Stat(fullname); err == nil && fi.Mode().IsRegular() { + return fullname + } + } + return "" +} + +// Find returns the full path of the plugin by searching in CNI_PATH +func Find(plugin string) string { + paths := strings.Split(os.Getenv("CNI_PATH"), ":") + return FindInPath(plugin, paths) +} diff --git a/pkg/ip/cidr.go b/pkg/ip/cidr.go index c9633988..723a1f74 100644 --- a/pkg/ip/cidr.go +++ b/pkg/ip/cidr.go @@ -15,23 +15,10 @@ package ip import ( - "encoding/json" "math/big" "net" ) -// ParseCIDR takes a string like "10.2.3.1/24" and -// return IPNet with "10.2.3.1" and /24 mask -func ParseCIDR(s string) (*net.IPNet, error) { - ip, ipn, err := net.ParseCIDR(s) - if err != nil { - return nil, err - } - - ipn.IP = ip - return ipn, nil -} - // NextIP returns IP incremented by 1 func NextIP(ip net.IP) net.IP { i := ipToInt(ip) @@ -62,25 +49,3 @@ func Network(ipn *net.IPNet) *net.IPNet { Mask: ipn.Mask, } } - -// like net.IPNet but adds JSON marshalling and unmarshalling -type IPNet net.IPNet - -func (n IPNet) MarshalJSON() ([]byte, error) { - return json.Marshal((*net.IPNet)(&n).String()) -} - -func (n *IPNet) UnmarshalJSON(data []byte) error { - var s string - if err := json.Unmarshal(data, &s); err != nil { - return err - } - - tmp, err := ParseCIDR(s) - if err != nil { - return err - } - - *n = IPNet(*tmp) - return nil -} diff --git a/pkg/ipam/ipam.go b/pkg/ipam/ipam.go new file mode 100644 index 00000000..a76299d7 --- /dev/null +++ b/pkg/ipam/ipam.go @@ -0,0 +1,75 @@ +// Copyright 2015 CoreOS, Inc. +// +// 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 ipam + +import ( + "fmt" + "os" + + "github.com/appc/cni/pkg/invoke" + "github.com/appc/cni/pkg/ip" + "github.com/appc/cni/pkg/types" + + "github.com/vishvananda/netlink" +) + +func ExecAdd(plugin string, netconf []byte) (*types.Result, error) { + if os.Getenv("CNI_COMMAND") != "ADD" { + return nil, fmt.Errorf("CNI_COMMAND is not ADD") + } + return invoke.ExecPlugin(invoke.Find(plugin), netconf, invoke.ArgsFromEnv()) +} + +func ExecDel(plugin string, netconf []byte) error { + if os.Getenv("CNI_COMMAND") != "DEL" { + return fmt.Errorf("CNI_COMMAND is not DEL") + } + _, err := invoke.ExecPlugin(invoke.Find(plugin), netconf, invoke.ArgsFromEnv()) + return err +} + +// ConfigureIface takes the result of IPAM plugin and +// applies to the ifName interface +func ConfigureIface(ifName string, res *types.Result) error { + link, err := netlink.LinkByName(ifName) + if err != nil { + return fmt.Errorf("failed to lookup %q: %v", ifName, err) + } + + if err := netlink.LinkSetUp(link); err != nil { + return fmt.Errorf("failed to set %q UP: %v", ifName, err) + } + + // TODO(eyakubovich): IPv6 + addr := &netlink.Addr{IPNet: &res.IP4.IP, Label: ""} + if err = netlink.AddrAdd(link, addr); err != nil { + return fmt.Errorf("failed to add IP addr to %q: %v", ifName, err) + } + + for _, r := range res.IP4.Routes { + gw := r.GW + if gw == nil { + gw = res.IP4.Gateway + } + if err = ip.AddRoute(&r.Dst, gw, link); err != nil { + // we skip over duplicate routes as we assume the first one wins + if !os.IsExist(err) { + return fmt.Errorf("failed to add route '%v via %v dev %v': %v", r.Dst, gw, ifName, err) + } + } + } + + return nil +} diff --git a/pkg/plugin/ipam.go b/pkg/plugin/ipam.go deleted file mode 100644 index f304301f..00000000 --- a/pkg/plugin/ipam.go +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2015 CoreOS, Inc. -// -// 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 plugin - -import ( - "bytes" - "encoding/json" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - - "github.com/appc/cni/pkg/ip" - "github.com/vishvananda/netlink" -) - -// Find returns the full path of the plugin by searching in CNI_PATH -func Find(plugin string) string { - paths := strings.Split(os.Getenv("CNI_PATH"), ":") - - for _, p := range paths { - fullname := filepath.Join(p, plugin) - if fi, err := os.Stat(fullname); err == nil && fi.Mode().IsRegular() { - return fullname - } - } - - return "" -} - -func pluginErr(err error, output []byte) error { - if _, ok := err.(*exec.ExitError); ok { - emsg := Error{} - if perr := json.Unmarshal(output, &emsg); perr != nil { - return fmt.Errorf("netplugin failed but error parsing its diagnostic message %q: %v", string(output), perr) - } - details := "" - if emsg.Details != "" { - details = fmt.Sprintf("; %v", emsg.Details) - } - return fmt.Errorf("%v%v", emsg.Msg, details) - } - - return err -} - -// ExecAdd executes IPAM plugin, assuming CNI_COMMAND == ADD. -// Parses and returns resulting IPConfig -func ExecAdd(plugin string, netconf []byte) (*Result, error) { - if os.Getenv("CNI_COMMAND") != "ADD" { - return nil, fmt.Errorf("CNI_COMMAND is not ADD") - } - if plugin == "" { - return nil, fmt.Errorf(`name of IPAM plugin is missing. Please specify a "type" field in the "ipam" section`) - } - - pluginPath := Find(plugin) - if pluginPath == "" { - return nil, fmt.Errorf("could not find %q IPAM plugin", plugin) - } - - stdout := &bytes.Buffer{} - - c := exec.Cmd{ - Path: pluginPath, - Args: []string{pluginPath}, - Stdin: bytes.NewBuffer(netconf), - Stdout: stdout, - Stderr: os.Stderr, - } - if err := c.Run(); err != nil { - return nil, pluginErr(err, stdout.Bytes()) - } - - res := &Result{} - err := json.Unmarshal(stdout.Bytes(), res) - return res, err -} - -// ExecDel executes IPAM plugin, assuming CNI_COMMAND == DEL. -func ExecDel(plugin string, netconf []byte) error { - if os.Getenv("CNI_COMMAND") != "DEL" { - return fmt.Errorf("CNI_COMMAND is not DEL") - } - - pluginPath := Find(plugin) - if pluginPath == "" { - return fmt.Errorf("could not find %q plugin", plugin) - } - - stdout := &bytes.Buffer{} - - c := exec.Cmd{ - Path: pluginPath, - Args: []string{pluginPath}, - Stdin: bytes.NewBuffer(netconf), - Stdout: stdout, - Stderr: os.Stderr, - } - if err := c.Run(); err != nil { - return pluginErr(err, stdout.Bytes()) - } - return nil -} - -// ConfigureIface takes the result of IPAM plugin and -// applies to the ifName interface -func ConfigureIface(ifName string, res *Result) error { - link, err := netlink.LinkByName(ifName) - if err != nil { - return fmt.Errorf("failed to lookup %q: %v", ifName, err) - } - - if err := netlink.LinkSetUp(link); err != nil { - return fmt.Errorf("failed to set %q UP: %v", ifName, err) - } - - // TODO(eyakubovich): IPv6 - addr := &netlink.Addr{IPNet: &res.IP4.IP, Label: ""} - if err = netlink.AddrAdd(link, addr); err != nil { - return fmt.Errorf("failed to add IP addr to %q: %v", ifName, err) - } - - for _, r := range res.IP4.Routes { - gw := r.GW - if gw == nil { - gw = res.IP4.Gateway - } - if err = ip.AddRoute(&r.Dst, gw, link); err != nil { - // we skip over duplicate routes as we assume the first one wins - if !os.IsExist(err) { - return fmt.Errorf("failed to add route '%v via %v dev %v': %v", r.Dst, gw, ifName, err) - } - } - } - - return nil -} diff --git a/pkg/skel/skel.go b/pkg/skel/skel.go index bf79b91d..d6204dd4 100644 --- a/pkg/skel/skel.go +++ b/pkg/skel/skel.go @@ -22,7 +22,7 @@ import ( "log" "os" - "github.com/appc/cni/pkg/plugin" + "github.com/appc/cni/pkg/types" ) // CmdArgs captures all the arguments passed in to the plugin @@ -93,7 +93,7 @@ func PluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error) { } if err != nil { - if e, ok := err.(*plugin.Error); ok { + if e, ok := err.(*types.Error); ok { // don't wrap Error in Error dieErr(e) } @@ -102,14 +102,14 @@ func PluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error) { } func dieMsg(f string, args ...interface{}) { - e := &plugin.Error{ + e := &types.Error{ Code: 100, Msg: fmt.Sprintf(f, args...), } dieErr(e) } -func dieErr(e *plugin.Error) { +func dieErr(e *types.Error) { if err := e.Print(); err != nil { log.Print("Error writing error JSON to stdout: ", err) } diff --git a/pkg/plugin/args.go b/pkg/types/args.go similarity index 56% rename from pkg/plugin/args.go rename to pkg/types/args.go index 274ec66b..68162435 100644 --- a/pkg/plugin/args.go +++ b/pkg/types/args.go @@ -1,4 +1,18 @@ -package plugin +// Copyright 2015 CoreOS, Inc. +// +// 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 types import ( "encoding" diff --git a/pkg/plugin/types.go b/pkg/types/types.go similarity index 73% rename from pkg/plugin/types.go rename to pkg/types/types.go index d5952dde..21ba32d6 100644 --- a/pkg/plugin/types.go +++ b/pkg/types/types.go @@ -12,16 +12,48 @@ // See the License for the specific language governing permissions and // limitations under the License. -package plugin +package types import ( "encoding/json" "net" "os" - - "github.com/appc/cni/pkg/ip" ) +// like net.IPNet but adds JSON marshalling and unmarshalling +type IPNet net.IPNet + +// ParseCIDR takes a string like "10.2.3.1/24" and +// return IPNet with "10.2.3.1" and /24 mask +func ParseCIDR(s string) (*net.IPNet, error) { + ip, ipn, err := net.ParseCIDR(s) + if err != nil { + return nil, err + } + + ipn.IP = ip + return ipn, nil +} + +func (n IPNet) MarshalJSON() ([]byte, error) { + return json.Marshal((*net.IPNet)(&n).String()) +} + +func (n *IPNet) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + + tmp, err := ParseCIDR(s) + if err != nil { + return err + } + + *n = IPNet(*tmp) + return nil +} + // NetConf describes a network. type NetConf struct { Name string `json:"name,omitempty"` @@ -68,23 +100,23 @@ func (e *Error) Print() error { } // net.IPNet is not JSON (un)marshallable so this duality is needed -// for our custom ip.IPNet type +// for our custom IPNet type // JSON (un)marshallable types type ipConfig struct { - IP ip.IPNet `json:"ip"` - Gateway net.IP `json:"gateway,omitempty"` - Routes []Route `json:"routes,omitempty"` + IP IPNet `json:"ip"` + Gateway net.IP `json:"gateway,omitempty"` + Routes []Route `json:"routes,omitempty"` } type route struct { - Dst ip.IPNet `json:"dst"` - GW net.IP `json:"gw,omitempty"` + Dst IPNet `json:"dst"` + GW net.IP `json:"gw,omitempty"` } func (c *IPConfig) MarshalJSON() ([]byte, error) { ipc := ipConfig{ - IP: ip.IPNet(c.IP), + IP: IPNet(c.IP), Gateway: c.Gateway, Routes: c.Routes, } @@ -117,7 +149,7 @@ func (r *Route) UnmarshalJSON(data []byte) error { func (r *Route) MarshalJSON() ([]byte, error) { rt := route{ - Dst: ip.IPNet(r.Dst), + Dst: IPNet(r.Dst), GW: r.GW, } diff --git a/plugins/ipam/dhcp/daemon.go b/plugins/ipam/dhcp/daemon.go index 7b5ecfb1..1fc91f20 100644 --- a/plugins/ipam/dhcp/daemon.go +++ b/plugins/ipam/dhcp/daemon.go @@ -27,8 +27,8 @@ import ( "runtime" "sync" - "github.com/appc/cni/pkg/plugin" "github.com/appc/cni/pkg/skel" + "github.com/appc/cni/pkg/types" "github.com/coreos/go-systemd/activation" ) @@ -50,8 +50,8 @@ func newDHCP() *DHCP { // Allocate acquires an IP from a DHCP server for a specified container. // The acquired lease will be maintained until Release() is called. -func (d *DHCP) Allocate(args *skel.CmdArgs, result *plugin.Result) error { - conf := plugin.NetConf{} +func (d *DHCP) Allocate(args *skel.CmdArgs, result *types.Result) error { + conf := types.NetConf{} if err := json.Unmarshal(args.StdinData, &conf); err != nil { return fmt.Errorf("error parsing netconf: %v", err) } @@ -70,7 +70,7 @@ func (d *DHCP) Allocate(args *skel.CmdArgs, result *plugin.Result) error { d.setLease(args.ContainerID, conf.Name, l) - result.IP4 = &plugin.IPConfig{ + result.IP4 = &types.IPConfig{ IP: *ipn, Gateway: l.Gateway(), Routes: l.Routes(), @@ -82,7 +82,7 @@ func (d *DHCP) Allocate(args *skel.CmdArgs, result *plugin.Result) error { // Release stops maintenance of the lease acquired in Allocate() // and sends a release msg to the DHCP server. func (d *DHCP) Release(args *skel.CmdArgs, reply *struct{}) error { - conf := plugin.NetConf{} + conf := types.NetConf{} if err := json.Unmarshal(args.StdinData, &conf); err != nil { return fmt.Errorf("error parsing netconf: %v", err) } diff --git a/plugins/ipam/dhcp/lease.go b/plugins/ipam/dhcp/lease.go index 0c8aa7ad..42a2d86a 100644 --- a/plugins/ipam/dhcp/lease.go +++ b/plugins/ipam/dhcp/lease.go @@ -28,7 +28,7 @@ import ( "github.com/vishvananda/netlink" "github.com/appc/cni/pkg/ns" - "github.com/appc/cni/pkg/plugin" + "github.com/appc/cni/pkg/types" ) // RFC 2131 suggests using exponential backoff, starting with 4sec @@ -285,7 +285,7 @@ func (l *DHCPLease) Gateway() net.IP { return parseRouter(l.opts) } -func (l *DHCPLease) Routes() []plugin.Route { +func (l *DHCPLease) Routes() []types.Route { routes := parseRoutes(l.opts) return append(routes, parseCIDRRoutes(l.opts)...) } diff --git a/plugins/ipam/dhcp/main.go b/plugins/ipam/dhcp/main.go index ca802719..c35bdf4a 100644 --- a/plugins/ipam/dhcp/main.go +++ b/plugins/ipam/dhcp/main.go @@ -20,8 +20,8 @@ import ( "os" "path/filepath" - "github.com/appc/cni/pkg/plugin" "github.com/appc/cni/pkg/skel" + "github.com/appc/cni/pkg/types" ) const socketPath = "/run/cni/dhcp.sock" @@ -35,7 +35,7 @@ func main() { } func cmdAdd(args *skel.CmdArgs) error { - result := plugin.Result{} + result := types.Result{} if err := rpcCall("DHCP.Allocate", args, &result); err != nil { return err } diff --git a/plugins/ipam/dhcp/options.go b/plugins/ipam/dhcp/options.go index 37cfc613..9189e5be 100644 --- a/plugins/ipam/dhcp/options.go +++ b/plugins/ipam/dhcp/options.go @@ -20,7 +20,7 @@ import ( "net" "time" - "github.com/appc/cni/pkg/plugin" + "github.com/appc/cni/pkg/types" "github.com/d2g/dhcp4" ) @@ -40,17 +40,17 @@ func classfulSubnet(sn net.IP) net.IPNet { } } -func parseRoutes(opts dhcp4.Options) []plugin.Route { +func parseRoutes(opts dhcp4.Options) []types.Route { // StaticRoutes format: pairs of: // Dest = 4 bytes; Classful IP subnet // Router = 4 bytes; IP address of router - routes := []plugin.Route{} + routes := []types.Route{} if opt, ok := opts[dhcp4.OptionStaticRoute]; ok { for len(opt) >= 8 { sn := opt[0:4] r := opt[4:8] - rt := plugin.Route{ + rt := types.Route{ Dst: classfulSubnet(sn), GW: r, } @@ -62,10 +62,10 @@ func parseRoutes(opts dhcp4.Options) []plugin.Route { return routes } -func parseCIDRRoutes(opts dhcp4.Options) []plugin.Route { +func parseCIDRRoutes(opts dhcp4.Options) []types.Route { // See RFC4332 for format (http://tools.ietf.org/html/rfc3442) - routes := []plugin.Route{} + routes := []types.Route{} if opt, ok := opts[dhcp4.OptionClasslessRouteFormat]; ok { for len(opt) >= 5 { width := int(opt[0]) @@ -89,7 +89,7 @@ func parseCIDRRoutes(opts dhcp4.Options) []plugin.Route { gw := net.IP(opt[octets+1 : octets+5]) - rt := plugin.Route{ + rt := types.Route{ Dst: net.IPNet{ IP: net.IP(sn), Mask: net.CIDRMask(width, 32), diff --git a/plugins/ipam/dhcp/options_test.go b/plugins/ipam/dhcp/options_test.go index 52b27304..5aa129d7 100644 --- a/plugins/ipam/dhcp/options_test.go +++ b/plugins/ipam/dhcp/options_test.go @@ -18,20 +18,20 @@ import ( "net" "testing" - "github.com/appc/cni/pkg/plugin" + "github.com/appc/cni/pkg/types" "github.com/d2g/dhcp4" ) -func validateRoutes(t *testing.T, routes []plugin.Route) { - expected := []plugin.Route{ - plugin.Route{ +func validateRoutes(t *testing.T, routes []types.Route) { + expected := []types.Route{ + types.Route{ Dst: net.IPNet{ IP: net.IPv4(10, 0, 0, 0), Mask: net.CIDRMask(8, 32), }, GW: net.IPv4(10, 1, 2, 3), }, - plugin.Route{ + types.Route{ Dst: net.IPNet{ IP: net.IPv4(192, 168, 1, 0), Mask: net.CIDRMask(24, 32), diff --git a/plugins/ipam/host-local/allocator.go b/plugins/ipam/host-local/allocator.go index 8d20344a..56c79479 100644 --- a/plugins/ipam/host-local/allocator.go +++ b/plugins/ipam/host-local/allocator.go @@ -19,7 +19,7 @@ import ( "net" "github.com/appc/cni/pkg/ip" - "github.com/appc/cni/pkg/plugin" + "github.com/appc/cni/pkg/types" "github.com/appc/cni/plugins/ipam/host-local/backend" ) @@ -69,7 +69,7 @@ func validateRangeIP(ip net.IP, ipnet *net.IPNet) error { } // Returns newly allocated IP along with its config -func (a *IPAllocator) Get(id string) (*plugin.IPConfig, error) { +func (a *IPAllocator) Get(id string) (*types.IPConfig, error) { a.store.Lock() defer a.store.Unlock() @@ -103,7 +103,7 @@ func (a *IPAllocator) Get(id string) (*plugin.IPConfig, error) { } if reserved { - return &plugin.IPConfig{ + return &types.IPConfig{ IP: net.IPNet{requestedIP, a.conf.Subnet.Mask}, Gateway: gw, Routes: a.conf.Routes, @@ -123,7 +123,7 @@ func (a *IPAllocator) Get(id string) (*plugin.IPConfig, error) { return nil, err } if reserved { - return &plugin.IPConfig{ + return &types.IPConfig{ IP: net.IPNet{cur, a.conf.Subnet.Mask}, Gateway: gw, Routes: a.conf.Routes, @@ -135,7 +135,7 @@ func (a *IPAllocator) Get(id string) (*plugin.IPConfig, error) { // Allocates both an IP and the Gateway IP, i.e. a /31 // This is used for Point-to-Point links -func (a *IPAllocator) GetPtP(id string) (*plugin.IPConfig, error) { +func (a *IPAllocator) GetPtP(id string) (*types.IPConfig, error) { a.store.Lock() defer a.store.Unlock() @@ -165,7 +165,7 @@ func (a *IPAllocator) GetPtP(id string) (*plugin.IPConfig, error) { _, bits := a.conf.Subnet.Mask.Size() mask := net.CIDRMask(bits-1, bits) - return &plugin.IPConfig{ + return &types.IPConfig{ IP: net.IPNet{cur, mask}, Gateway: gw, Routes: a.conf.Routes, diff --git a/plugins/ipam/host-local/config.go b/plugins/ipam/host-local/config.go index 828b123d..8d219557 100644 --- a/plugins/ipam/host-local/config.go +++ b/plugins/ipam/host-local/config.go @@ -19,20 +19,19 @@ import ( "fmt" "net" - "github.com/appc/cni/pkg/ip" - "github.com/appc/cni/pkg/plugin" + "github.com/appc/cni/pkg/types" ) // IPAMConfig represents the IP related network configuration. type IPAMConfig struct { Name string - Type string `json:"type"` - RangeStart net.IP `json:"rangeStart"` - RangeEnd net.IP `json:"rangeEnd"` - Subnet ip.IPNet `json:"subnet"` - Gateway net.IP `json:"gateway"` - Routes []plugin.Route `json:"routes"` - Args *IPAMArgs `json:"-"` + Type string `json:"type"` + RangeStart net.IP `json:"rangeStart"` + RangeEnd net.IP `json:"rangeEnd"` + Subnet types.IPNet `json:"subnet"` + Gateway net.IP `json:"gateway"` + Routes []types.Route `json:"routes"` + Args *IPAMArgs `json:"-"` } type IPAMArgs struct { @@ -53,7 +52,7 @@ func LoadIPAMConfig(bytes []byte, args string) (*IPAMConfig, error) { if args != "" { ipamArgs := IPAMArgs{} - err := plugin.LoadArgs(args, &ipamArgs) + err := types.LoadArgs(args, &ipamArgs) if err != nil { return nil, err } diff --git a/plugins/ipam/host-local/main.go b/plugins/ipam/host-local/main.go index 76b9e847..18c97561 100644 --- a/plugins/ipam/host-local/main.go +++ b/plugins/ipam/host-local/main.go @@ -19,8 +19,8 @@ import ( "github.com/appc/cni/plugins/ipam/host-local/backend/disk" - "github.com/appc/cni/pkg/plugin" "github.com/appc/cni/pkg/skel" + "github.com/appc/cni/pkg/types" ) func main() { @@ -40,7 +40,7 @@ func cmdAdd(args *skel.CmdArgs) error { defer store.Close() ipamArgs := IPAMArgs{} - err = plugin.LoadArgs(args.Args, &ipamArgs) + err = types.LoadArgs(args.Args, &ipamArgs) if err != nil { return err } @@ -51,7 +51,7 @@ func cmdAdd(args *skel.CmdArgs) error { return err } - var ipConf *plugin.IPConfig + var ipConf *types.IPConfig switch ipamConf.Type { case "host-local": @@ -66,7 +66,7 @@ func cmdAdd(args *skel.CmdArgs) error { return err } - r := &plugin.Result{ + r := &types.Result{ IP4: ipConf, } return r.Print() diff --git a/plugins/main/bridge/bridge.go b/plugins/main/bridge/bridge.go index e65e08e4..24549770 100644 --- a/plugins/main/bridge/bridge.go +++ b/plugins/main/bridge/bridge.go @@ -24,16 +24,17 @@ import ( "syscall" "github.com/appc/cni/pkg/ip" + "github.com/appc/cni/pkg/ipam" "github.com/appc/cni/pkg/ns" - "github.com/appc/cni/pkg/plugin" "github.com/appc/cni/pkg/skel" + "github.com/appc/cni/pkg/types" "github.com/vishvananda/netlink" ) const defaultBrName = "cni0" type NetConf struct { - plugin.NetConf + types.NetConf BrName string `json:"bridge"` IsGW bool `json:"isGateway"` IPMasq bool `json:"ipMasq"` @@ -183,7 +184,7 @@ func cmdAdd(args *skel.CmdArgs) error { } // run the IPAM plugin and get back the config to apply - result, err := plugin.ExecAdd(n.IPAM.Type, args.StdinData) + result, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData) if err != nil { return err } @@ -197,7 +198,7 @@ func cmdAdd(args *skel.CmdArgs) error { } err = ns.WithNetNSPath(args.Netns, false, func(hostNS *os.File) error { - return plugin.ConfigureIface(args.IfName, result) + return ipam.ConfigureIface(args.IfName, result) }) if err != nil { return err @@ -234,7 +235,7 @@ func cmdDel(args *skel.CmdArgs) error { return err } - err = plugin.ExecDel(n.IPAM.Type, args.StdinData) + err = ipam.ExecDel(n.IPAM.Type, args.StdinData) if err != nil { return err } diff --git a/plugins/main/ipvlan/ipvlan.go b/plugins/main/ipvlan/ipvlan.go index 65e37b42..31a21d52 100644 --- a/plugins/main/ipvlan/ipvlan.go +++ b/plugins/main/ipvlan/ipvlan.go @@ -22,14 +22,15 @@ import ( "runtime" "github.com/appc/cni/pkg/ip" + "github.com/appc/cni/pkg/ipam" "github.com/appc/cni/pkg/ns" - "github.com/appc/cni/pkg/plugin" "github.com/appc/cni/pkg/skel" + "github.com/appc/cni/pkg/types" "github.com/vishvananda/netlink" ) type NetConf struct { - plugin.NetConf + types.NetConf Master string `json:"master"` Mode string `json:"mode"` MTU int `json:"mtu"` @@ -122,7 +123,7 @@ func cmdAdd(args *skel.CmdArgs) error { } // run the IPAM plugin and get back the config to apply - result, err := plugin.ExecAdd(n.IPAM.Type, args.StdinData) + result, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData) if err != nil { return err } @@ -131,7 +132,7 @@ func cmdAdd(args *skel.CmdArgs) error { } err = ns.WithNetNS(netns, false, func(_ *os.File) error { - return plugin.ConfigureIface(args.IfName, result) + return ipam.ConfigureIface(args.IfName, result) }) if err != nil { return err @@ -146,7 +147,7 @@ func cmdDel(args *skel.CmdArgs) error { return err } - err = plugin.ExecDel(n.IPAM.Type, args.StdinData) + err = ipam.ExecDel(n.IPAM.Type, args.StdinData) if err != nil { return err } diff --git a/plugins/main/macvlan/macvlan.go b/plugins/main/macvlan/macvlan.go index 236693e0..c92de10b 100644 --- a/plugins/main/macvlan/macvlan.go +++ b/plugins/main/macvlan/macvlan.go @@ -22,14 +22,15 @@ import ( "runtime" "github.com/appc/cni/pkg/ip" + "github.com/appc/cni/pkg/ipam" "github.com/appc/cni/pkg/ns" - "github.com/appc/cni/pkg/plugin" "github.com/appc/cni/pkg/skel" + "github.com/appc/cni/pkg/types" "github.com/vishvananda/netlink" ) type NetConf struct { - plugin.NetConf + types.NetConf Master string `json:"master"` Mode string `json:"mode"` MTU int `json:"mtu"` @@ -126,7 +127,7 @@ func cmdAdd(args *skel.CmdArgs) error { } // run the IPAM plugin and get back the config to apply - result, err := plugin.ExecAdd(n.IPAM.Type, args.StdinData) + result, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData) if err != nil { return err } @@ -135,7 +136,7 @@ func cmdAdd(args *skel.CmdArgs) error { } err = ns.WithNetNS(netns, false, func(_ *os.File) error { - return plugin.ConfigureIface(args.IfName, result) + return ipam.ConfigureIface(args.IfName, result) }) if err != nil { return err @@ -150,7 +151,7 @@ func cmdDel(args *skel.CmdArgs) error { return err } - err = plugin.ExecDel(n.IPAM.Type, args.StdinData) + err = ipam.ExecDel(n.IPAM.Type, args.StdinData) if err != nil { return err } diff --git a/plugins/main/ptp/ptp.go b/plugins/main/ptp/ptp.go index 58a0396e..f14b55ea 100644 --- a/plugins/main/ptp/ptp.go +++ b/plugins/main/ptp/ptp.go @@ -26,9 +26,10 @@ import ( "github.com/vishvananda/netlink" "github.com/appc/cni/pkg/ip" + "github.com/appc/cni/pkg/ipam" "github.com/appc/cni/pkg/ns" - "github.com/appc/cni/pkg/plugin" "github.com/appc/cni/pkg/skel" + "github.com/appc/cni/pkg/types" ) func init() { @@ -39,12 +40,12 @@ func init() { } type NetConf struct { - plugin.NetConf + types.NetConf IPMasq bool `json:"ipMasq"` MTU int `json:"mtu"` } -func setupContainerVeth(netns, ifName string, mtu int, pr *plugin.Result) (string, error) { +func setupContainerVeth(netns, ifName string, mtu int, pr *types.Result) (string, error) { var hostVethName string err := ns.WithNetNSPath(netns, false, func(hostNS *os.File) error { hostVeth, _, err := ip.SetupVeth(ifName, mtu, hostNS) @@ -52,7 +53,7 @@ func setupContainerVeth(netns, ifName string, mtu int, pr *plugin.Result) (strin return err } - err = plugin.ConfigureIface(ifName, pr) + err = ipam.ConfigureIface(ifName, pr) if err != nil { return err } @@ -64,7 +65,7 @@ func setupContainerVeth(netns, ifName string, mtu int, pr *plugin.Result) (strin return hostVethName, err } -func setupHostVeth(vethName string, ipConf *plugin.IPConfig) error { +func setupHostVeth(vethName string, ipConf *types.IPConfig) error { // hostVeth moved namespaces and may have a new ifindex veth, err := netlink.LinkByName(vethName) if err != nil { @@ -100,7 +101,7 @@ func cmdAdd(args *skel.CmdArgs) error { } // run the IPAM plugin and get back the config to apply - result, err := plugin.ExecAdd(conf.IPAM.Type, args.StdinData) + result, err := ipam.ExecAdd(conf.IPAM.Type, args.StdinData) if err != nil { return err } @@ -152,7 +153,7 @@ func cmdDel(args *skel.CmdArgs) error { } } - return plugin.ExecDel(conf.IPAM.Type, args.StdinData) + return ipam.ExecDel(conf.IPAM.Type, args.StdinData) } func main() { diff --git a/plugins/meta/flannel/flannel.go b/plugins/meta/flannel/flannel.go index 059b2bba..2dd811d7 100644 --- a/plugins/meta/flannel/flannel.go +++ b/plugins/meta/flannel/flannel.go @@ -29,8 +29,9 @@ import ( "strconv" "strings" - "github.com/appc/cni/pkg/plugin" + "github.com/appc/cni/pkg/ipam" "github.com/appc/cni/pkg/skel" + "github.com/appc/cni/pkg/types" ) const ( @@ -39,7 +40,7 @@ const ( ) type NetConf struct { - plugin.NetConf + types.NetConf SubnetFile string `json:"subnetFile"` Delegate map[string]interface{} `json:"delegate"` } @@ -130,7 +131,7 @@ func delegateAdd(cid string, netconf map[string]interface{}) error { return err } - result, err := plugin.ExecAdd(netconf["type"].(string), netconfBytes) + result, err := ipam.ExecAdd(netconf["type"].(string), netconfBytes) if err != nil { return err } @@ -199,8 +200,8 @@ func cmdAdd(args *skel.CmdArgs) error { n.Delegate["ipam"] = map[string]interface{}{ "type": "host-local", "subnet": fenv.sn.String(), - "routes": []plugin.Route{ - plugin.Route{ + "routes": []types.Route{ + types.Route{ Dst: *fenv.nw, }, }, @@ -215,12 +216,12 @@ func cmdDel(args *skel.CmdArgs) error { return err } - n := &plugin.NetConf{} + n := &types.NetConf{} if err = json.Unmarshal(netconfBytes, n); err != nil { return fmt.Errorf("failed to parse netconf: %v", err) } - return plugin.ExecDel(n.Type, netconfBytes) + return ipam.ExecDel(n.Type, netconfBytes) } func main() { diff --git a/test b/test index 838880ed..0e315149 100755 --- a/test +++ b/test @@ -11,7 +11,7 @@ source ./build TESTABLE="plugins/ipam/dhcp" -FORMATTABLE="$TESTABLE pkg/ip pkg/ns pkg/plugin pkg/skel plugins/ipam/host-local plugins/main/bridge plugins/meta/flannel" +FORMATTABLE="$TESTABLE libcni pkg/ip pkg/ns pkg/invoke pkg/types pkg/ipam pkg/skel plugins/ipam/host-local plugins/main/bridge plugins/meta/flannel" # user has not provided PKG override if [ -z "$PKG" ]; then