diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index e987e60a..4471f633 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -20,6 +20,21 @@ "ImportPath": "github.com/cloudfoundry-incubator/candiedyaml", "Rev": "99c3df83b51532e3615f851d8c2dbb638f5313bf" }, + { + "ImportPath": "github.com/containernetworking/cni/libcni", + "Comment": "v0.3.0-67-gc5e39a8", + "Rev": "c5e39a87f702f757a1e98f701d873992bd4d37b1" + }, + { + "ImportPath": "github.com/containernetworking/cni/pkg/invoke", + "Comment": "v0.3.0-67-gc5e39a8", + "Rev": "c5e39a87f702f757a1e98f701d873992bd4d37b1" + }, + { + "ImportPath": "github.com/containernetworking/cni/pkg/types", + "Comment": "v0.3.0-67-gc5e39a8", + "Rev": "c5e39a87f702f757a1e98f701d873992bd4d37b1" + }, { "ImportPath": "github.com/containers/image/directory", "Rev": "0c795ea71fce8641e408312e1628d20b38903fe6" @@ -289,6 +304,10 @@ "Comment": "v1.0.0-rc1-29-gbbaf29e", "Rev": "bbaf29e6173a1ef017d75db11e4d698ac4de0f1e" }, + { + "ImportPath": "github.com/rajatchopra/ocicni", + "Rev": "df6e1fb00fd0763d67f8f6f6ebaf3496c3eea451" + }, { "ImportPath": "github.com/syndtr/gocapability/capability", "Rev": "2c00daeb6c3b45114c80ac44119e7b8801fdd852" @@ -369,6 +388,11 @@ "Comment": "v1.0.1-GA-29-g79b7c34", "Rev": "79b7c349179cdd6efd8bac4a1ce7f01b98c16e9b" }, + { + "ImportPath": "google.golang.org/grpc/internal", + "Comment": "v1.0.1-GA-29-g79b7c34", + "Rev": "79b7c349179cdd6efd8bac4a1ce7f01b98c16e9b" + }, { "ImportPath": "google.golang.org/grpc/metadata", "Comment": "v1.0.1-GA-29-g79b7c34", @@ -379,6 +403,11 @@ "Comment": "v1.0.1-GA-29-g79b7c34", "Rev": "79b7c349179cdd6efd8bac4a1ce7f01b98c16e9b" }, + { + "ImportPath": "google.golang.org/grpc/peer", + "Comment": "v1.0.1-GA-29-g79b7c34", + "Rev": "79b7c349179cdd6efd8bac4a1ce7f01b98c16e9b" + }, { "ImportPath": "google.golang.org/grpc/transport", "Comment": "v1.0.1-GA-29-g79b7c34", @@ -403,16 +432,6 @@ "ImportPath": "k8s.io/kubernetes/pkg/util/sets", "Comment": "v1.4.0-alpha.1-489-g976ca09", "Rev": "976ca09d714cf114fb7a9e681bc0b170760cbdab" - }, - { - "ImportPath": "google.golang.org/grpc/internal", - "Comment": "v1.0.1-GA-29-g79b7c34", - "Rev": "79b7c349179cdd6efd8bac4a1ce7f01b98c16e9b" - }, - { - "ImportPath": "google.golang.org/grpc/peer", - "Comment": "v1.0.1-GA-29-g79b7c34", - "Rev": "79b7c349179cdd6efd8bac4a1ce7f01b98c16e9b" } ] } diff --git a/vendor/github.com/containernetworking/cni/LICENSE b/vendor/github.com/containernetworking/cni/LICENSE new file mode 100644 index 00000000..8f71f43f --- /dev/null +++ b/vendor/github.com/containernetworking/cni/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + 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. + diff --git a/vendor/github.com/containernetworking/cni/libcni/api.go b/vendor/github.com/containernetworking/cni/libcni/api.go new file mode 100644 index 00000000..340a20cc --- /dev/null +++ b/vendor/github.com/containernetworking/cni/libcni/api.go @@ -0,0 +1,73 @@ +// Copyright 2015 CNI authors +// +// 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/containernetworking/cni/pkg/invoke" + "github.com/containernetworking/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) { + pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path) + if err != nil { + return nil, err + } + + return invoke.ExecPluginWithResult(pluginPath, net.Bytes, c.args("ADD", rt)) +} + +func (c *CNIConfig) DelNetwork(net *NetworkConfig, rt *RuntimeConf) error { + pluginPath, err := invoke.FindInPath(net.Network.Type, c.Path) + if err != nil { + return err + } + + return invoke.ExecPluginWithoutResult(pluginPath, net.Bytes, c.args("DEL", rt)) +} + +// ===== +func (c *CNIConfig) args(action string, rt *RuntimeConf) *invoke.Args { + return &invoke.Args{ + Command: action, + ContainerID: rt.ContainerID, + NetNS: rt.NetNS, + PluginArgs: rt.Args, + IfName: rt.IfName, + Path: strings.Join(c.Path, ":"), + } +} diff --git a/vendor/github.com/containernetworking/cni/libcni/conf.go b/vendor/github.com/containernetworking/cni/libcni/conf.go new file mode 100644 index 00000000..984b1592 --- /dev/null +++ b/vendor/github.com/containernetworking/cni/libcni/conf.go @@ -0,0 +1,110 @@ +// Copyright 2015 CNI authors +// +// 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) +} + +func InjectConf(original *NetworkConfig, key string, newValue interface{}) (*NetworkConfig, error) { + config := make(map[string]interface{}) + err := json.Unmarshal(original.Bytes, &config) + if err != nil { + return nil, fmt.Errorf("unmarshal existing network bytes: %s", err) + } + + if key == "" { + return nil, fmt.Errorf("key value can not be empty") + } + + if newValue == nil { + return nil, fmt.Errorf("newValue must be specified") + } + + config[key] = newValue + + newBytes, err := json.Marshal(config) + if err != nil { + return nil, err + } + + return ConfFromBytes(newBytes) +} diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/args.go b/vendor/github.com/containernetworking/cni/pkg/invoke/args.go new file mode 100644 index 00000000..be28ba62 --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/invoke/args.go @@ -0,0 +1,76 @@ +// Copyright 2015 CNI authors +// +// 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/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go b/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go new file mode 100644 index 00000000..ddf1d172 --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go @@ -0,0 +1,53 @@ +// Copyright 2016 CNI authors +// +// 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 ( + "fmt" + "os" + "strings" + + "github.com/containernetworking/cni/pkg/types" +) + +func DelegateAdd(delegatePlugin string, netconf []byte) (*types.Result, error) { + if os.Getenv("CNI_COMMAND") != "ADD" { + return nil, fmt.Errorf("CNI_COMMAND is not ADD") + } + + paths := strings.Split(os.Getenv("CNI_PATH"), ":") + + pluginPath, err := FindInPath(delegatePlugin, paths) + if err != nil { + return nil, err + } + + return ExecPluginWithResult(pluginPath, netconf, ArgsFromEnv()) +} + +func DelegateDel(delegatePlugin string, netconf []byte) error { + if os.Getenv("CNI_COMMAND") != "DEL" { + return fmt.Errorf("CNI_COMMAND is not DEL") + } + + paths := strings.Split(os.Getenv("CNI_PATH"), ":") + + pluginPath, err := FindInPath(delegatePlugin, paths) + if err != nil { + return err + } + + return ExecPluginWithoutResult(pluginPath, netconf, ArgsFromEnv()) +} diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/exec.go b/vendor/github.com/containernetworking/cni/pkg/invoke/exec.go new file mode 100644 index 00000000..d7e38f21 --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/invoke/exec.go @@ -0,0 +1,86 @@ +// Copyright 2015 CNI authors +// +// 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" + "io" + "os" + "os/exec" + + "github.com/containernetworking/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 ExecPluginWithResult(pluginPath string, netconf []byte, args CNIArgs) (*types.Result, error) { + stdoutBytes, err := execPlugin(pluginPath, netconf, args) + if err != nil { + return nil, err + } + + res := &types.Result{} + err = json.Unmarshal(stdoutBytes, res) + return res, err +} + +func ExecPluginWithoutResult(pluginPath string, netconf []byte, args CNIArgs) error { + _, err := execPlugin(pluginPath, netconf, args) + return err +} + +func execPlugin(pluginPath string, netconf []byte, args CNIArgs) ([]byte, error) { + return defaultRawExec.ExecPlugin(pluginPath, netconf, args.AsEnv()) +} + +var defaultRawExec = &RawExec{Stderr: os.Stderr} + +type RawExec struct { + Stderr io.Writer +} + +func (e *RawExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) { + stdout := &bytes.Buffer{} + + c := exec.Cmd{ + Env: environ, + Path: pluginPath, + Args: []string{pluginPath}, + Stdin: bytes.NewBuffer(stdinData), + Stdout: stdout, + Stderr: e.Stderr, + } + if err := c.Run(); err != nil { + return nil, pluginErr(err, stdout.Bytes()) + } + + return stdout.Bytes(), nil +} diff --git a/vendor/github.com/containernetworking/cni/pkg/invoke/find.go b/vendor/github.com/containernetworking/cni/pkg/invoke/find.go new file mode 100644 index 00000000..3b037907 --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/invoke/find.go @@ -0,0 +1,47 @@ +// Copyright 2015 CNI authors +// +// 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 ( + "fmt" + "os" + "path/filepath" +) + +// FindInPath returns the full path of the plugin by searching in the provided path +func FindInPath(plugin string, paths []string) (string, error) { + if plugin == "" { + return "", fmt.Errorf("no plugin name provided") + } + + if len(paths) == 0 { + return "", fmt.Errorf("no paths provided") + } + + var fullpath string + for _, path := range paths { + full := filepath.Join(path, plugin) + if fi, err := os.Stat(full); err == nil && fi.Mode().IsRegular() { + fullpath = full + break + } + } + + if fullpath == "" { + return "", fmt.Errorf("failed to find plugin %q in path %s", plugin, paths) + } + + return fullpath, nil +} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/args.go b/vendor/github.com/containernetworking/cni/pkg/types/args.go new file mode 100644 index 00000000..66dcf9ea --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/types/args.go @@ -0,0 +1,101 @@ +// Copyright 2015 CNI authors +// +// 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" + "fmt" + "reflect" + "strings" +) + +// UnmarshallableBool typedef for builtin bool +// because builtin type's methods can't be declared +type UnmarshallableBool bool + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// Returns boolean true if the string is "1" or "[Tt]rue" +// Returns boolean false if the string is "0" or "[Ff]alse" +func (b *UnmarshallableBool) UnmarshalText(data []byte) error { + s := strings.ToLower(string(data)) + switch s { + case "1", "true": + *b = true + case "0", "false": + *b = false + default: + return fmt.Errorf("Boolean unmarshal error: invalid input %s", s) + } + return nil +} + +// UnmarshallableString typedef for builtin string +type UnmarshallableString string + +// UnmarshalText implements the encoding.TextUnmarshaler interface. +// Returns the string +func (s *UnmarshallableString) UnmarshalText(data []byte) error { + *s = UnmarshallableString(data) + return nil +} + +// CommonArgs contains the IgnoreUnknown argument +// and must be embedded by all Arg structs +type CommonArgs struct { + IgnoreUnknown UnmarshallableBool `json:"ignoreunknown,omitempty"` +} + +// GetKeyField is a helper function to receive Values +// Values that represent a pointer to a struct +func GetKeyField(keyString string, v reflect.Value) reflect.Value { + return v.Elem().FieldByName(keyString) +} + +// LoadArgs parses args from a string in the form "K=V;K2=V2;..." +func LoadArgs(args string, container interface{}) error { + if args == "" { + return nil + } + + containerValue := reflect.ValueOf(container) + + pairs := strings.Split(args, ";") + unknownArgs := []string{} + 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 := GetKeyField(keyString, containerValue) + if !keyField.IsValid() { + unknownArgs = append(unknownArgs, pair) + continue + } + + 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) + } + } + + isIgnoreUnknown := GetKeyField("IgnoreUnknown", containerValue).Bool() + if len(unknownArgs) > 0 && !isIgnoreUnknown { + return fmt.Errorf("ARGS: unknown args %q", unknownArgs) + } + return nil +} diff --git a/vendor/github.com/containernetworking/cni/pkg/types/types.go b/vendor/github.com/containernetworking/cni/pkg/types/types.go new file mode 100644 index 00000000..6948dcb1 --- /dev/null +++ b/vendor/github.com/containernetworking/cni/pkg/types/types.go @@ -0,0 +1,191 @@ +// Copyright 2015 CNI authors +// +// 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/json" + "fmt" + "net" + "os" +) + +// 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"` + Type string `json:"type,omitempty"` + IPAM struct { + Type string `json:"type,omitempty"` + } `json:"ipam,omitempty"` + DNS DNS `json:"dns"` +} + +// Result is what gets returned from the plugin (via stdout) to the caller +type Result struct { + IP4 *IPConfig `json:"ip4,omitempty"` + IP6 *IPConfig `json:"ip6,omitempty"` + DNS DNS `json:"dns,omitempty"` +} + +func (r *Result) Print() error { + return prettyPrint(r) +} + +// String returns a formatted string in the form of "[IP4: $1,][ IP6: $2,] DNS: $3" where +// $1 represents the receiver's IPv4, $2 represents the receiver's IPv6 and $3 the +// receiver's DNS. If $1 or $2 are nil, they won't be present in the returned string. +func (r *Result) String() string { + var str string + if r.IP4 != nil { + str = fmt.Sprintf("IP4:%+v, ", *r.IP4) + } + if r.IP6 != nil { + str += fmt.Sprintf("IP6:%+v, ", *r.IP6) + } + return fmt.Sprintf("%sDNS:%+v", str, r.DNS) +} + +// IPConfig contains values necessary to configure an interface +type IPConfig struct { + IP net.IPNet + Gateway net.IP + Routes []Route +} + +// DNS contains values interesting for DNS resolvers +type DNS struct { + Nameservers []string `json:"nameservers,omitempty"` + Domain string `json:"domain,omitempty"` + Search []string `json:"search,omitempty"` + Options []string `json:"options,omitempty"` +} + +type Route struct { + Dst net.IPNet + GW net.IP +} + +type Error struct { + Code uint `json:"code"` + Msg string `json:"msg"` + Details string `json:"details,omitempty"` +} + +func (e *Error) Error() string { + return e.Msg +} + +func (e *Error) Print() error { + return prettyPrint(e) +} + +// net.IPNet is not JSON (un)marshallable so this duality is needed +// for our custom IPNet type + +// JSON (un)marshallable types +type ipConfig struct { + IP IPNet `json:"ip"` + Gateway net.IP `json:"gateway,omitempty"` + Routes []Route `json:"routes,omitempty"` +} + +type route struct { + Dst IPNet `json:"dst"` + GW net.IP `json:"gw,omitempty"` +} + +func (c *IPConfig) MarshalJSON() ([]byte, error) { + ipc := ipConfig{ + IP: IPNet(c.IP), + Gateway: c.Gateway, + Routes: c.Routes, + } + + return json.Marshal(ipc) +} + +func (c *IPConfig) UnmarshalJSON(data []byte) error { + ipc := ipConfig{} + if err := json.Unmarshal(data, &ipc); err != nil { + return err + } + + c.IP = net.IPNet(ipc.IP) + c.Gateway = ipc.Gateway + c.Routes = ipc.Routes + return nil +} + +func (r *Route) UnmarshalJSON(data []byte) error { + rt := route{} + if err := json.Unmarshal(data, &rt); err != nil { + return err + } + + r.Dst = net.IPNet(rt.Dst) + r.GW = rt.GW + return nil +} + +func (r *Route) MarshalJSON() ([]byte, error) { + rt := route{ + Dst: IPNet(r.Dst), + GW: r.GW, + } + + return json.Marshal(rt) +} + +func prettyPrint(obj interface{}) error { + data, err := json.MarshalIndent(obj, "", " ") + if err != nil { + return err + } + _, err = os.Stdout.Write(data) + return err +} diff --git a/vendor/github.com/rajatchopra/ocicni/README.md b/vendor/github.com/rajatchopra/ocicni/README.md new file mode 100644 index 00000000..99c103c8 --- /dev/null +++ b/vendor/github.com/rajatchopra/ocicni/README.md @@ -0,0 +1,3 @@ +# ocicni + +API layer to call the CNI plugins from an OCI lifecycle daemon diff --git a/vendor/github.com/rajatchopra/ocicni/noop.go b/vendor/github.com/rajatchopra/ocicni/noop.go new file mode 100644 index 00000000..fb3b4dbb --- /dev/null +++ b/vendor/github.com/rajatchopra/ocicni/noop.go @@ -0,0 +1,26 @@ + +package ocicni + +type cniNoOp struct { +} + + +func (noop *cniNoOp) Name() string { + return "CNINoOp" +} + +func (noop *cniNoOp) SetUpPod(netnsPath string, namespace string, name string, containerID string) error { + return nil +} + +func (noop *cniNoOp) TearDownPod(netnsPath string, namespace string, name string, containerID string) error { + return nil +} + +func (noop *cniNoOp) GetContainerNetworkStatus(netnsPath string, namespace string, name string, containerID string) (string, error) { + return "", nil +} + +func (noop *cniNoOp) Status() error { + return nil +} diff --git a/vendor/github.com/rajatchopra/ocicni/ocicni.go b/vendor/github.com/rajatchopra/ocicni/ocicni.go new file mode 100644 index 00000000..03f7889a --- /dev/null +++ b/vendor/github.com/rajatchopra/ocicni/ocicni.go @@ -0,0 +1,260 @@ + +package ocicni + +import ( + "errors" + "fmt" + "os/exec" + "sort" + "sync" + "time" + + "github.com/containernetworking/cni/libcni" + cnitypes "github.com/containernetworking/cni/pkg/types" + "github.com/golang/glog" +) + +type cniNetworkPlugin struct { + loNetwork *cniNetwork + + sync.RWMutex + defaultNetwork *cniNetwork + + nsenterPath string + pluginDir string + vendorCNIDirPrefix string +} + +type cniNetwork struct { + name string + NetworkConfig *libcni.NetworkConfig + CNIConfig libcni.CNI +} + +func InitCNI(pluginDir string) (CNIPlugin, error) { + plugin := probeNetworkPluginsWithVendorCNIDirPrefix(pluginDir, "") + var err error + plugin.nsenterPath, err = exec.LookPath("nsenter") + if err != nil { + return nil, err + } + + // check if a default network exists, otherwise dump the CNI search and return a noop plugin + _, err = getDefaultCNINetwork(plugin.pluginDir, plugin.vendorCNIDirPrefix) + if err != nil { + glog.Warningf("Error in finding usable CNI plugin - %v", err) + // create a noop plugin instead + return &cniNoOp{}, nil + } + + // sync network config from pluginDir periodically to detect network config updates + go func() { + t := time.NewTimer(10*time.Second) + for { + plugin.syncNetworkConfig() + select { + case <- t.C: + } + } + }() + return plugin, nil +} + +func probeNetworkPluginsWithVendorCNIDirPrefix(pluginDir, vendorCNIDirPrefix string) (*cniNetworkPlugin) { + plugin := &cniNetworkPlugin{ + defaultNetwork: nil, + loNetwork: getLoNetwork(vendorCNIDirPrefix), + pluginDir: pluginDir, + vendorCNIDirPrefix: vendorCNIDirPrefix, + } + + // sync NetworkConfig in best effort during probing. + plugin.syncNetworkConfig() + return plugin +} + +func getDefaultCNINetwork(pluginDir, vendorCNIDirPrefix string) (*cniNetwork, error) { + if pluginDir == "" { + pluginDir = DefaultNetDir + } + files, err := libcni.ConfFiles(pluginDir) + switch { + case err != nil: + return nil, err + case len(files) == 0: + return nil, fmt.Errorf("No networks found in %s", pluginDir) + } + + sort.Strings(files) + for _, confFile := range files { + conf, err := libcni.ConfFromFile(confFile) + if err != nil { + glog.Warningf("Error loading CNI config file %s: %v", confFile, err) + continue + } + // Search for vendor-specific plugins as well as default plugins in the CNI codebase. + vendorDir := vendorCNIDir(vendorCNIDirPrefix, conf.Network.Type) + cninet := &libcni.CNIConfig{ + Path: []string{DefaultCNIDir, vendorDir}, + } + network := &cniNetwork{name: conf.Network.Name, NetworkConfig: conf, CNIConfig: cninet} + return network, nil + } + return nil, fmt.Errorf("No valid networks found in %s", pluginDir) +} + +func vendorCNIDir(prefix, pluginType string) string { + return fmt.Sprintf(VendorCNIDirTemplate, prefix, pluginType) +} + +func getLoNetwork(vendorDirPrefix string) *cniNetwork { + loConfig, err := libcni.ConfFromBytes([]byte(`{ + "cniVersion": "0.1.0", + "name": "cni-loopback", + "type": "loopback" +}`)) + if err != nil { + // The hardcoded config above should always be valid and unit tests will + // catch this + panic(err) + } + cninet := &libcni.CNIConfig{ + Path: []string{vendorCNIDir(vendorDirPrefix, loConfig.Network.Type), DefaultCNIDir}, + } + loNetwork := &cniNetwork{ + name: "lo", + NetworkConfig: loConfig, + CNIConfig: cninet, + } + + return loNetwork +} + +func (plugin *cniNetworkPlugin) syncNetworkConfig() { + network, err := getDefaultCNINetwork(plugin.pluginDir, plugin.vendorCNIDirPrefix) + if err != nil { + glog.Errorf("error updating cni config: %s", err) + return + } + plugin.setDefaultNetwork(network) +} + +func (plugin *cniNetworkPlugin) getDefaultNetwork() *cniNetwork { + plugin.RLock() + defer plugin.RUnlock() + return plugin.defaultNetwork +} + +func (plugin *cniNetworkPlugin) setDefaultNetwork(n *cniNetwork) { + plugin.Lock() + defer plugin.Unlock() + plugin.defaultNetwork = n +} + +func (plugin *cniNetworkPlugin) checkInitialized() error { + if plugin.getDefaultNetwork() == nil { + return errors.New("cni config uninitialized") + } + return nil +} + +func (plugin *cniNetworkPlugin) Name() string { + return CNIPluginName +} + +func (plugin *cniNetworkPlugin) SetUpPod(netnsPath string, namespace string, name string, id string) error { + if err := plugin.checkInitialized(); err != nil { + return err + } + + _, err := plugin.loNetwork.addToNetwork(name, namespace, id, netnsPath) + if err != nil { + glog.Errorf("Error while adding to cni lo network: %s", err) + return err + } + + _, err = plugin.getDefaultNetwork().addToNetwork(name, namespace, id, netnsPath) + if err != nil { + glog.Errorf("Error while adding to cni network: %s", err) + return err + } + + return err +} + +func (plugin *cniNetworkPlugin) TearDownPod(netnsPath string, namespace string, name string, id string) error { + if err := plugin.checkInitialized(); err != nil { + return err + } + + return plugin.getDefaultNetwork().deleteFromNetwork(name, namespace, id, netnsPath) +} + +// TODO: Use the addToNetwork function to obtain the IP of the Pod. That will assume idempotent ADD call to the plugin. +// Also fix the runtime's call to Status function to be done only in the case that the IP is lost, no need to do periodic calls +func (plugin *cniNetworkPlugin) GetContainerNetworkStatus(netnsPath string, namespace string, name string, id string) (string, error) { + ip, err := getContainerIP(plugin.nsenterPath, netnsPath, DefaultInterfaceName, "-4") + if err != nil { + return "", err + } + + return ip.String(), nil +} + +func (network *cniNetwork) addToNetwork(podName string, podNamespace string, podInfraContainerID string, podNetnsPath string) (*cnitypes.Result, error) { + rt, err := buildCNIRuntimeConf(podName, podNamespace, podInfraContainerID, podNetnsPath) + if err != nil { + glog.Errorf("Error adding network: %v", err) + return nil, err + } + + netconf, cninet := network.NetworkConfig, network.CNIConfig + glog.V(4).Infof("About to run with conf.Network.Type=%v", netconf.Network.Type) + res, err := cninet.AddNetwork(netconf, rt) + if err != nil { + glog.Errorf("Error adding network: %v", err) + return nil, err + } + + return res, nil +} + +func (network *cniNetwork) deleteFromNetwork(podName string, podNamespace string, podInfraContainerID string, podNetnsPath string) error { + rt, err := buildCNIRuntimeConf(podName, podNamespace, podInfraContainerID, podNetnsPath) + if err != nil { + glog.Errorf("Error deleting network: %v", err) + return err + } + + netconf, cninet := network.NetworkConfig, network.CNIConfig + glog.V(4).Infof("About to run with conf.Network.Type=%v", netconf.Network.Type) + err = cninet.DelNetwork(netconf, rt) + if err != nil { + glog.Errorf("Error deleting network: %v", err) + return err + } + return nil +} + +func buildCNIRuntimeConf(podName string, podNs string, podInfraContainerID string, podNetnsPath string) (*libcni.RuntimeConf, error) { + glog.V(4).Infof("Got netns path %v", podNetnsPath) + glog.V(4).Infof("Using netns path %v", podNs) + + rt := &libcni.RuntimeConf{ + ContainerID: podInfraContainerID, + NetNS: podNetnsPath, + IfName: DefaultInterfaceName, + Args: [][2]string{ + {"IgnoreUnknown", "1"}, + {"K8S_POD_NAMESPACE", podNs}, + {"K8S_POD_NAME", podName}, + {"K8S_POD_INFRA_CONTAINER_ID", podInfraContainerID}, + }, + } + + return rt, nil +} + +func (plugin *cniNetworkPlugin) Status() error { + return plugin.checkInitialized() +} diff --git a/vendor/github.com/rajatchopra/ocicni/types.go b/vendor/github.com/rajatchopra/ocicni/types.go new file mode 100644 index 00000000..505c0a7d --- /dev/null +++ b/vendor/github.com/rajatchopra/ocicni/types.go @@ -0,0 +1,30 @@ + +package ocicni + +const ( + DefaultInterfaceName = "eth0" + CNIPluginName = "cni" + DefaultNetDir = "/etc/cni/net.d" + DefaultCNIDir = "/opt/cni/bin" + VendorCNIDirTemplate = "%s/opt/%s/bin" +) + +type CNIPlugin interface { + // Name returns the plugin's name. This will be used when searching + // for a plugin by name, e.g. + Name() string + + // SetUpPod is the method called after the infra container of + // the pod has been created but before the other containers of the + // pod are launched. + SetUpPod(netnsPath string, namespace string, name string, containerID string) error + + // TearDownPod is the method called before a pod's infra container will be deleted + TearDownPod(netnsPath string, namespace string, name string, containerID string) error + + // Status is the method called to obtain the ipv4 or ipv6 addresses of the container + GetContainerNetworkStatus(netnsPath string, namespace string, name string, containerID string) (string, error) + + // NetworkStatus returns error if the network plugin is in error state + Status() error +} diff --git a/vendor/github.com/rajatchopra/ocicni/util.go b/vendor/github.com/rajatchopra/ocicni/util.go new file mode 100644 index 00000000..bc5df06a --- /dev/null +++ b/vendor/github.com/rajatchopra/ocicni/util.go @@ -0,0 +1,33 @@ + +package ocicni + +import ( + "os/exec" + "strings" + "net" + "fmt" +) + +func getContainerIP(nsenterPath, netnsPath, interfaceName, addrType string) (net.IP, error) { + // Try to retrieve ip inside container network namespace + output, err := exec.Command(nsenterPath, fmt.Sprintf("--net=%s", netnsPath), "-F", "--", + "ip", "-o", addrType, "addr", "show", "dev", interfaceName, "scope", "global").CombinedOutput() + if err != nil { + return nil, fmt.Errorf("Unexpected command output %s with error: %v", output, err) + } + + lines := strings.Split(string(output), "\n") + if len(lines) < 1 { + return nil, fmt.Errorf("Unexpected command output %s", output) + } + fields := strings.Fields(lines[0]) + if len(fields) < 4 { + return nil, fmt.Errorf("Unexpected address output %s ", lines[0]) + } + ip, _, err := net.ParseCIDR(fields[3]) + if err != nil { + return nil, fmt.Errorf("CNI failed to parse ip from output %s due to %v", output, err) + } + + return ip, nil +}