From 245eb078f40def87d37fb64a8101edf538200383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Dulko?= Date: Tue, 4 Jun 2019 12:33:46 +0200 Subject: [PATCH] Implement kuryr-cni in golang This commit reimplements the kuryr-cni, that is the actual CNI plugin that gets called by kubelet and passes the request to kuryr-daemon, in golang. This means that it can injected as a binary without any dependencies, instead of using a bash script that looks for ID of kuryr-daemon container and does `docker exec` to run Python kuryr-cni inside. The obvious advantage of that is removing a constraint of python, curl and docker/runc binaries being available on any K8s host that runs Kuryr. This enables integration with Magnum, where kubelet runs in such a minimal container. Besides that injecting a binary is way more elegant and less error-prone. The golang implementation should keep the compatibility with Python one. Also currently only containerized jobs are switched to use it, so Python implementation is still kept in the repo. I'm not against removing it in very near future. Please note that there is an important limitation in comparison to the Python implementation - i.e. in case of golang binary running on K8s host, we don't have an easy access to kuryr.conf, meaning that localhost:50036 is currently hardcoded as the kuryr-daemon endpoint. This should be fixed by putting the configured endpoint into 10-kuryr.conf file that gets injected onto the host by cni_ds_init script. Implements: blueprint golang-kuryr-cni Change-Id: Ia241fb5b2937c63d3ed6e3de1ac3003e370e4db6 --- cni.Dockerfile | 8 + cni_ds_init | 48 +-- cni_py3.Dockerfile | 8 + kuryr_cni/Gopkg.lock | 37 +++ kuryr_cni/Gopkg.toml | 34 ++ kuryr_cni/README | 2 + kuryr_cni/main.go | 195 +++++++++++ kuryr_cni/ovo.go | 120 +++++++ .../containernetworking/cni/LICENSE | 202 ++++++++++++ .../containernetworking/cni/pkg/skel/skel.go | 307 ++++++++++++++++++ .../cni/pkg/types/020/types.go | 140 ++++++++ .../containernetworking/cni/pkg/types/args.go | 112 +++++++ .../cni/pkg/types/current/types.go | 293 +++++++++++++++++ .../cni/pkg/types/types.go | 199 ++++++++++++ .../cni/pkg/version/conf.go | 37 +++ .../cni/pkg/version/plugin.go | 144 ++++++++ .../cni/pkg/version/reconcile.go | 49 +++ .../cni/pkg/version/version.go | 83 +++++ .../vendor/github.com/pkg/errors/.gitignore | 24 ++ .../vendor/github.com/pkg/errors/.travis.yml | 15 + .../vendor/github.com/pkg/errors/LICENSE | 23 ++ .../vendor/github.com/pkg/errors/README.md | 52 +++ .../vendor/github.com/pkg/errors/appveyor.yml | 32 ++ .../vendor/github.com/pkg/errors/errors.go | 282 ++++++++++++++++ .../vendor/github.com/pkg/errors/stack.go | 147 +++++++++ 25 files changed, 2546 insertions(+), 47 deletions(-) create mode 100644 kuryr_cni/Gopkg.lock create mode 100644 kuryr_cni/Gopkg.toml create mode 100644 kuryr_cni/README create mode 100644 kuryr_cni/main.go create mode 100644 kuryr_cni/ovo.go create mode 100644 kuryr_cni/vendor/github.com/containernetworking/cni/LICENSE create mode 100644 kuryr_cni/vendor/github.com/containernetworking/cni/pkg/skel/skel.go create mode 100644 kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/020/types.go create mode 100644 kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/args.go create mode 100644 kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/current/types.go create mode 100644 kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/types.go create mode 100644 kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/conf.go create mode 100644 kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/plugin.go create mode 100644 kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go create mode 100644 kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/version.go create mode 100644 kuryr_cni/vendor/github.com/pkg/errors/.gitignore create mode 100644 kuryr_cni/vendor/github.com/pkg/errors/.travis.yml create mode 100644 kuryr_cni/vendor/github.com/pkg/errors/LICENSE create mode 100644 kuryr_cni/vendor/github.com/pkg/errors/README.md create mode 100644 kuryr_cni/vendor/github.com/pkg/errors/appveyor.yml create mode 100644 kuryr_cni/vendor/github.com/pkg/errors/errors.go create mode 100644 kuryr_cni/vendor/github.com/pkg/errors/stack.go diff --git a/cni.Dockerfile b/cni.Dockerfile index 11f7386e9..c3dc3c6e0 100644 --- a/cni.Dockerfile +++ b/cni.Dockerfile @@ -1,3 +1,9 @@ +FROM golang:1.11 AS builder + +WORKDIR /go/src/opendev.com/kuryr-kubernetes +COPY . . +RUN go build -o /go/bin/kuryr-cni ./kuryr_cni + FROM centos:7 LABEL authors="Antoni Segura Puimedon, Michał Dulko" @@ -18,6 +24,8 @@ RUN pip install -c $UPPER_CONSTRAINTS_FILE /opt/kuryr-kubernetes \ && rm -rf /opt/kuryr-kubernetes \ && mkdir ${OSLO_LOCK_PATH} +COPY --from=builder /go/bin/kuryr-cni /kuryr-cni + ARG CNI_DAEMON=True ENV CNI_DAEMON ${CNI_DAEMON} ENV OSLO_LOCK_PATH=${OSLO_LOCK_PATH} diff --git a/cni_ds_init b/cni_ds_init index 2085253d4..517dbe3ae 100755 --- a/cni_ds_init +++ b/cni_ds_init @@ -6,53 +6,7 @@ function cleanup() { } function deploy() { - POD_NAMESPACE=$( /kuryr-cni << EOF -#!/bin/bash -x - -finder=" -import json -import sys - -mode = 'docker' if len(sys.argv) == 1 else sys.argv[1] - -if mode == 'docker': - label_key = 'Labels' - id_key = 'Id' -else: - label_key = 'annotations' - id_key = 'id' - -containers=json.load(sys.stdin) -# Looping over all the containers until we find the right one. We print it. -for container in containers: - if (label_key in container and - container[label_key].get('io.kubernetes.pod.name') == '${KURYR_CNI_POD_NAME}' and - container[label_key].get('io.kubernetes.pod.namespace') == '${POD_NAMESPACE}' and - container[label_key].get('io.kubernetes.container.name') == 'kuryr-cni'): - print(container[id_key]) - break -" - -envs=(\$(env | grep ^CNI_)) - -if command -v runc > /dev/null; then - # We have runc binary, let's see if that works. - CONTAINERID=\`runc list -f json 2> /dev/null | python -c "\${finder}" runc\` - if [[ ! -z \${CONTAINERID} ]]; then - exec runc exec \${envs[@]/#/--env } "\${CONTAINERID}" kuryr-cni --config-file /etc/kuryr/kuryr.conf - fi -fi - -# Fall back to using Docker binary. -# TODO(dulek): We might want to fetch socket path from config. -CONTAINERID=\`curl --unix-socket /var/run/docker.sock http://v1.24/containers/json 2> /dev/null | python -c "\${finder}" docker\` -docker exec \${envs[@]/#/--env } -i "\${CONTAINERID}" kuryr-cni --config-file /etc/kuryr/kuryr.conf -EOF - - # Copy the script into the designated location + # Copy the binary into the designated location cp /kuryr-cni "/opt/cni/bin/kuryr-cni" chmod +x /opt/cni/bin/kuryr-cni cp /etc/kuryr-cni/* /etc/cni/net.d diff --git a/cni_py3.Dockerfile b/cni_py3.Dockerfile index 3c87e6249..4a5fecc2c 100644 --- a/cni_py3.Dockerfile +++ b/cni_py3.Dockerfile @@ -1,3 +1,9 @@ +FROM golang:1.11 AS builder + +WORKDIR /go/src/opendev.com/kuryr-kubernetes +COPY . . +RUN go build -o /go/bin/kuryr-cni ./kuryr_cni + FROM fedora:30 LABEL authors="Antoni Segura Puimedon, Michał Dulko" @@ -19,6 +25,8 @@ RUN python3.6 -m ensurepip \ && rm -rf /opt/kuryr-kubernetes \ && mkdir ${OSLO_LOCK_PATH} +COPY --from=builder /go/bin/kuryr-cni /kuryr-cni + ARG CNI_DAEMON=True ENV CNI_DAEMON ${CNI_DAEMON} ENV OSLO_LOCK_PATH=${OSLO_LOCK_PATH} diff --git a/kuryr_cni/Gopkg.lock b/kuryr_cni/Gopkg.lock new file mode 100644 index 000000000..ea8b754b3 --- /dev/null +++ b/kuryr_cni/Gopkg.lock @@ -0,0 +1,37 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + digest = "1:217f54a01004e15731471f4b3d420c16b99ade200d9872d7c1f0d2684df60f14" + name = "github.com/containernetworking/cni" + packages = [ + "pkg/skel", + "pkg/types", + "pkg/types/020", + "pkg/types/current", + "pkg/version", + ] + pruneopts = "UT" + revision = "7d76556571b6cf1ab90d7026a73092ac8d5e0c23" + version = "v0.7.0" + +[[projects]] + digest = "1:cf31692c14422fa27c83a05292eb5cbe0fb2775972e8f1f8446a71549bd8980b" + name = "github.com/pkg/errors" + packages = ["."] + pruneopts = "UT" + revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4" + version = "v0.8.1" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + input-imports = [ + "github.com/containernetworking/cni/pkg/skel", + "github.com/containernetworking/cni/pkg/types", + "github.com/containernetworking/cni/pkg/types/current", + "github.com/containernetworking/cni/pkg/version", + "github.com/pkg/errors", + ] + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/kuryr_cni/Gopkg.toml b/kuryr_cni/Gopkg.toml new file mode 100644 index 000000000..08b91ccb9 --- /dev/null +++ b/kuryr_cni/Gopkg.toml @@ -0,0 +1,34 @@ +# Gopkg.toml example +# +# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" +# +# [prune] +# non-go = false +# go-tests = true +# unused-packages = true + + +[[constraint]] + name = "github.com/containernetworking/cni" + version = "0.7.0" + +[prune] + go-tests = true + unused-packages = true diff --git a/kuryr_cni/README b/kuryr_cni/README new file mode 100644 index 000000000..dccc35499 --- /dev/null +++ b/kuryr_cni/README @@ -0,0 +1,2 @@ +This is golang part of Kuryr, that is the CNI plugin that gets injected into +the host. \ No newline at end of file diff --git a/kuryr_cni/main.go b/kuryr_cni/main.go new file mode 100644 index 000000000..65c8584dd --- /dev/null +++ b/kuryr_cni/main.go @@ -0,0 +1,195 @@ +package main + +import ( + "bytes" + "encoding/json" + "fmt" + "github.com/containernetworking/cni/pkg/types" + "io/ioutil" + "log" + "net" + "net/http" + "runtime" + + "github.com/containernetworking/cni/pkg/skel" + "github.com/containernetworking/cni/pkg/types/current" + "github.com/containernetworking/cni/pkg/version" + "github.com/pkg/errors" +) + +const ( + // FIXME(dulek): We don't really have a good way to fetch current URL:port binding here. + // I'm hardcoding it for now, but in the future we should probably put it in + // the JSON config in 10-kuryr.conf file that we will get passed on stdin. + urlBase = "http://localhost:5036/" + addPath = "addNetwork" + delPath = "delNetwork" +) + +type KuryrDaemonData struct { + IfName string `json:"CNI_IFNAME"` + Netns string `json:"CNI_NETNS"` + Path string `json:"CNI_PATH"` + Command string `json:"CNI_COMMAND"` + ContainerID string `json:"CNI_CONTAINERID"` + Args string `json:"CNI_ARGS"` + KuryrConf interface{} `json:"config_kuryr"` +} + +func init() { + // this ensures that main runs only on main thread (thread group leader). + // since namespace ops (unshare, setns) are done for a single thread, we + // must ensure that the goroutine does not jump from OS thread to thread + runtime.LockOSThread() +} + +func transformData(args *skel.CmdArgs, command string) (KuryrDaemonData, error) { + var conf interface{} + err := json.Unmarshal(args.StdinData, &conf) + if err != nil { + return KuryrDaemonData{}, err + } + + return KuryrDaemonData{ + IfName: args.IfName, + Netns: args.Netns, + Path: args.Path, + Command: command, + ContainerID: args.ContainerID, + Args: args.Args, + KuryrConf: conf, + }, nil +} + +func makeDaemonRequest(data KuryrDaemonData, expectedCode int) ([]byte, error) { + log.Print("Calling Kuryr Daemon.") + + b, err := json.Marshal(data) + if err != nil { + return []byte{}, errors.Wrapf(err, "Error when preparing payload for kuryr-daemon") + } + + url := "" + switch data.Command { + case "ADD": + url = urlBase + addPath + case "DEL": + url = urlBase + delPath + default: + return []byte{}, errors.Errorf("Cannot handle command %s", data.Command) + } + + resp, err := http.Post(url, "application/json", bytes.NewBuffer(b)) + if err != nil { + return []byte{}, errors.Wrapf(err, "Looks like %s cannot be reached. Is kuryr-daemon running?", url) + } + defer resp.Body.Close() + + body, _ := ioutil.ReadAll(resp.Body) + if resp.StatusCode != expectedCode { + return []byte{}, errors.Errorf("CNI Daemon returned error %d %s", resp.StatusCode, body) + } + + return body, nil +} + +func cmdAdd(args *skel.CmdArgs) error { + data, err := transformData(args, "ADD") + if err != nil { + return errors.Wrap(err, "Error when reading configuration") + } + + body, err := makeDaemonRequest(data, 202) + if err != nil { + return err + } + + vif := VIF{} + err = json.Unmarshal(body, &vif) + if err != nil { + return errors.Wrapf(err, "Error when reading response from kurry-daemon: %s", string(body)) + } + + iface := current.Interface{} + iface.Name = vif.VifName + iface.Mac = vif.Address + iface.Sandbox = args.ContainerID + + var ips []*current.IPConfig + var dns types.DNS + var routes []*types.Route + for _, subnet := range vif.Network.Subnets { + addrStr := subnet.Ips[0].Address + addr := net.ParseIP(addrStr) + if addr == nil { + return errors.Errorf("Error when parsing IP address %s received from kuryr-daemon", addrStr) + + } + _, cidr, err := net.ParseCIDR(subnet.Cidr) + if err != nil { + return errors.Wrapf(err, "Error when parsing CIDR %s received from kuryr-daemon", + subnet.Cidr) + } + + ver := "4" + if addr.To4() == nil { + ver = "6" + } + + prefixSize, _ := cidr.Mask.Size() + ifaceCIDR := fmt.Sprintf("%s/%d", addr.String(), prefixSize) + _, ifaceNet, err := net.ParseCIDR(ifaceCIDR) + if err != nil { + return errors.Wrapf(err, "Error when parsing CIDR %s received from kuryr-daemon", ifaceCIDR) + } + ifaceNum := 0 + + ips = append(ips, ¤t.IPConfig{ + Version: ver, + Interface: &ifaceNum, + Gateway: net.ParseIP(subnet.Gateway), + Address: *ifaceNet, + }) + + for _, route := range subnet.Routes { + _, dst, err := net.ParseCIDR(route.Cidr) + if err != nil { + return errors.Wrapf(err, "Error when parsing CIDR %s received from kuryr-daemon", + route.Cidr) + } + + gw := net.ParseIP(route.Gateway) + if gw == nil { + return errors.Errorf("Error when parsing IP address %s received from kuryr-daemon", + route.Gateway) + } + + routes = append(routes, &types.Route{Dst: *dst, GW: gw}) + } + + dns.Nameservers = append(dns.Nameservers, subnet.DNS...) + } + + res := ¤t.Result{ + Interfaces: []*current.Interface{&iface}, + IPs: ips, + DNS: dns, + Routes: routes, + } + + return types.PrintResult(res, "0.3.1") +} + +func cmdDel(args *skel.CmdArgs) error { + data, err := transformData(args, "DEL") + _, err = makeDaemonRequest(data, 204) + return err +} + +func cmdCheck(args *skel.CmdArgs) error { + return nil +} + +func main() { + skel.PluginMain(cmdAdd, cmdCheck, cmdDel, version.All, "CNI Plugin Kuryr-Kubernetes v1.0.0") +} diff --git a/kuryr_cni/ovo.go b/kuryr_cni/ovo.go new file mode 100644 index 000000000..ac67833f0 --- /dev/null +++ b/kuryr_cni/ovo.go @@ -0,0 +1,120 @@ +package main + +import ( + "encoding/json" + "reflect" +) + +const ( + vod = "versioned_object.data" + ob = "objects" +) + +type VIF struct { + Network Network `json:"network"` + Address string `json:"address"` + VifName string `json:"vif_name"` +} + +type Network struct { + Subnets []Subnet `json:"subnets"` +} + +type Route struct { + Cidr string `json:"cidr"` + Gateway string `json:"gateway"` +} + +type IP struct { + Address string `json:"address"` +} + +type Subnet struct { + Routes []Route `json:"routes"` + Ips []IP `json:"ips"` + Cidr string `json:"cidr"` + Gateway string `json:"gateway"` + DNS []string `json:"dns"` +} + +func UnmarshalOVO(data []byte, r interface{}) error { + // Unmarshall into a generic map + var i map[string]interface{} + if err := json.Unmarshal(data, &i); err != nil { + return err + } + + // Skip versioned_object.data level + d := i[vod].(map[string]interface{}) + + p := reflect.ValueOf(r) // this will be a pointer + v := p.Elem() // dereferences pointer + t := v.Type() // gets type of the struct + + // Go over fields of the struct + for i := 0; i < t.NumField(); i++ { + // Initial info + field := t.Field(i) + fieldVal := v.Field(i) + key := field.Tag.Get("json") + + var obj interface{} + + // Main switch + switch fieldVal.Kind() { + case reflect.String: + // In case of string let's just write it and we're done (hence continue) + fieldVal.SetString(d[key].(string)) + continue + case reflect.Slice: + if reflect.ValueOf(d[key]).Kind() != reflect.Slice { + // It's a list with next level of "versioned_object.data" and then "objects" keys. Let's flatten this. + listObj := d[key].(map[string]interface{}) + listData := listObj[vod].(map[string]interface{}) + obj = listData[ob].([]interface{}) + break + } + // If we have a slice and d[key] is just a simple list, then struct's approach will work fine, that's + // why there's this fallthrough. + fallthrough + case reflect.Struct: + // Treat it as struct + obj = d[key] + } + + // For slices and structs marshall that level of JSON, and unmarshall them into the result. The weird + // approach with reflect.New is forced by how reflections work in golang. + jsonBytes, err := json.Marshal(obj) + if err != nil { + return err + } + new := reflect.New(fieldVal.Type()) + inter := new.Interface() + if err := json.Unmarshal(jsonBytes, &inter); err != nil { + return err + } + fieldVal.Set(new.Elem()) + } + + return nil +} + +func (v *VIF) UnmarshalJSON(data []byte) error { + return UnmarshalOVO(data, v) +} + +func (v *Network) UnmarshalJSON(data []byte) error { + return UnmarshalOVO(data, v) +} + +func (v *Subnet) UnmarshalJSON(data []byte) error { + return UnmarshalOVO(data, v) +} + +func (v *IP) UnmarshalJSON(data []byte) error { + return UnmarshalOVO(data, v) +} + +func (v *Route) UnmarshalJSON(data []byte) error { + return UnmarshalOVO(data, v) +} diff --git a/kuryr_cni/vendor/github.com/containernetworking/cni/LICENSE b/kuryr_cni/vendor/github.com/containernetworking/cni/LICENSE new file mode 100644 index 000000000..8f71f43fe --- /dev/null +++ b/kuryr_cni/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/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/skel/skel.go b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/skel/skel.go new file mode 100644 index 000000000..af56b8a1c --- /dev/null +++ b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/skel/skel.go @@ -0,0 +1,307 @@ +// Copyright 2014-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 skel provides skeleton code for a CNI plugin. +// In particular, it implements argument parsing and validation. +package skel + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "strings" + + "github.com/containernetworking/cni/pkg/types" + "github.com/containernetworking/cni/pkg/version" +) + +// CmdArgs captures all the arguments passed in to the plugin +// via both env vars and stdin +type CmdArgs struct { + ContainerID string + Netns string + IfName string + Args string + Path string + StdinData []byte +} + +type dispatcher struct { + Getenv func(string) string + Stdin io.Reader + Stdout io.Writer + Stderr io.Writer + + ConfVersionDecoder version.ConfigDecoder + VersionReconciler version.Reconciler +} + +type reqForCmdEntry map[string]bool + +// internal only error to indicate lack of required environment variables +type missingEnvError struct { + msg string +} + +func (e missingEnvError) Error() string { + return e.msg +} + +func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, error) { + var cmd, contID, netns, ifName, args, path string + + vars := []struct { + name string + val *string + reqForCmd reqForCmdEntry + }{ + { + "CNI_COMMAND", + &cmd, + reqForCmdEntry{ + "ADD": true, + "CHECK": true, + "DEL": true, + }, + }, + { + "CNI_CONTAINERID", + &contID, + reqForCmdEntry{ + "ADD": true, + "CHECK": true, + "DEL": true, + }, + }, + { + "CNI_NETNS", + &netns, + reqForCmdEntry{ + "ADD": true, + "CHECK": true, + "DEL": false, + }, + }, + { + "CNI_IFNAME", + &ifName, + reqForCmdEntry{ + "ADD": true, + "CHECK": true, + "DEL": true, + }, + }, + { + "CNI_ARGS", + &args, + reqForCmdEntry{ + "ADD": false, + "CHECK": false, + "DEL": false, + }, + }, + { + "CNI_PATH", + &path, + reqForCmdEntry{ + "ADD": true, + "CHECK": true, + "DEL": true, + }, + }, + } + + argsMissing := make([]string, 0) + for _, v := range vars { + *v.val = t.Getenv(v.name) + if *v.val == "" { + if v.reqForCmd[cmd] || v.name == "CNI_COMMAND" { + argsMissing = append(argsMissing, v.name) + } + } + } + + if len(argsMissing) > 0 { + joined := strings.Join(argsMissing, ",") + return "", nil, missingEnvError{fmt.Sprintf("required env variables [%s] missing", joined)} + } + + if cmd == "VERSION" { + t.Stdin = bytes.NewReader(nil) + } + + stdinData, err := ioutil.ReadAll(t.Stdin) + if err != nil { + return "", nil, fmt.Errorf("error reading from stdin: %v", err) + } + + cmdArgs := &CmdArgs{ + ContainerID: contID, + Netns: netns, + IfName: ifName, + Args: args, + Path: path, + StdinData: stdinData, + } + return cmd, cmdArgs, nil +} + +func createTypedError(f string, args ...interface{}) *types.Error { + return &types.Error{ + Code: 100, + Msg: fmt.Sprintf(f, args...), + } +} + +func (t *dispatcher) checkVersionAndCall(cmdArgs *CmdArgs, pluginVersionInfo version.PluginInfo, toCall func(*CmdArgs) error) error { + configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData) + if err != nil { + return err + } + verErr := t.VersionReconciler.Check(configVersion, pluginVersionInfo) + if verErr != nil { + return &types.Error{ + Code: types.ErrIncompatibleCNIVersion, + Msg: "incompatible CNI versions", + Details: verErr.Details(), + } + } + + return toCall(cmdArgs) +} + +func validateConfig(jsonBytes []byte) error { + var conf struct { + Name string `json:"name"` + } + if err := json.Unmarshal(jsonBytes, &conf); err != nil { + return fmt.Errorf("error reading network config: %s", err) + } + if conf.Name == "" { + return fmt.Errorf("missing network name") + } + return nil +} + +func (t *dispatcher) pluginMain(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) *types.Error { + cmd, cmdArgs, err := t.getCmdArgsFromEnv() + if err != nil { + // Print the about string to stderr when no command is set + if _, ok := err.(missingEnvError); ok && t.Getenv("CNI_COMMAND") == "" && about != "" { + fmt.Fprintln(t.Stderr, about) + return nil + } + return createTypedError(err.Error()) + } + + if cmd != "VERSION" { + err = validateConfig(cmdArgs.StdinData) + if err != nil { + return createTypedError(err.Error()) + } + } + + switch cmd { + case "ADD": + err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdAdd) + case "CHECK": + configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData) + if err != nil { + return createTypedError(err.Error()) + } + if gtet, err := version.GreaterThanOrEqualTo(configVersion, "0.4.0"); err != nil { + return createTypedError(err.Error()) + } else if !gtet { + return &types.Error{ + Code: types.ErrIncompatibleCNIVersion, + Msg: "config version does not allow CHECK", + } + } + for _, pluginVersion := range versionInfo.SupportedVersions() { + gtet, err := version.GreaterThanOrEqualTo(pluginVersion, configVersion) + if err != nil { + return createTypedError(err.Error()) + } else if gtet { + if err := t.checkVersionAndCall(cmdArgs, versionInfo, cmdCheck); err != nil { + return createTypedError(err.Error()) + } + return nil + } + } + return &types.Error{ + Code: types.ErrIncompatibleCNIVersion, + Msg: "plugin version does not allow CHECK", + } + case "DEL": + err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdDel) + case "VERSION": + err = versionInfo.Encode(t.Stdout) + default: + return createTypedError("unknown CNI_COMMAND: %v", cmd) + } + + if err != nil { + if e, ok := err.(*types.Error); ok { + // don't wrap Error in Error + return e + } + return createTypedError(err.Error()) + } + return nil +} + +// PluginMainWithError is the core "main" for a plugin. It accepts +// callback functions for add, check, and del CNI commands and returns an error. +// +// The caller must also specify what CNI spec versions the plugin supports. +// +// It is the responsibility of the caller to check for non-nil error return. +// +// For a plugin to comply with the CNI spec, it must print any error to stdout +// as JSON and then exit with nonzero status code. +// +// To let this package automatically handle errors and call os.Exit(1) for you, +// use PluginMain() instead. +func PluginMainWithError(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) *types.Error { + return (&dispatcher{ + Getenv: os.Getenv, + Stdin: os.Stdin, + Stdout: os.Stdout, + Stderr: os.Stderr, + }).pluginMain(cmdAdd, cmdCheck, cmdDel, versionInfo, about) +} + +// PluginMain is the core "main" for a plugin which includes automatic error handling. +// +// The caller must also specify what CNI spec versions the plugin supports. +// +// The caller can specify an "about" string, which is printed on stderr +// when no CNI_COMMAND is specified. The recommended output is "CNI plugin v" +// +// When an error occurs in either cmdAdd, cmdCheck, or cmdDel, PluginMain will print the error +// as JSON to stdout and call os.Exit(1). +// +// To have more control over error handling, use PluginMainWithError() instead. +func PluginMain(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo, about string) { + if e := PluginMainWithError(cmdAdd, cmdCheck, cmdDel, versionInfo, about); e != nil { + if err := e.Print(); err != nil { + log.Print("Error writing error JSON to stdout: ", err) + } + os.Exit(1) + } +} diff --git a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/020/types.go b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/020/types.go new file mode 100644 index 000000000..53256167f --- /dev/null +++ b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/020/types.go @@ -0,0 +1,140 @@ +// 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 types020 + +import ( + "encoding/json" + "fmt" + "io" + "net" + "os" + + "github.com/containernetworking/cni/pkg/types" +) + +const ImplementedSpecVersion string = "0.2.0" + +var SupportedVersions = []string{"", "0.1.0", ImplementedSpecVersion} + +// Compatibility types for CNI version 0.1.0 and 0.2.0 + +func NewResult(data []byte) (types.Result, error) { + result := &Result{} + if err := json.Unmarshal(data, result); err != nil { + return nil, err + } + return result, nil +} + +func GetResult(r types.Result) (*Result, error) { + // We expect version 0.1.0/0.2.0 results + result020, err := r.GetAsVersion(ImplementedSpecVersion) + if err != nil { + return nil, err + } + result, ok := result020.(*Result) + if !ok { + return nil, fmt.Errorf("failed to convert result") + } + return result, nil +} + +// Result is what gets returned from the plugin (via stdout) to the caller +type Result struct { + CNIVersion string `json:"cniVersion,omitempty"` + IP4 *IPConfig `json:"ip4,omitempty"` + IP6 *IPConfig `json:"ip6,omitempty"` + DNS types.DNS `json:"dns,omitempty"` +} + +func (r *Result) Version() string { + return ImplementedSpecVersion +} + +func (r *Result) GetAsVersion(version string) (types.Result, error) { + for _, supportedVersion := range SupportedVersions { + if version == supportedVersion { + r.CNIVersion = version + return r, nil + } + } + return nil, fmt.Errorf("cannot convert version %q to %s", SupportedVersions, version) +} + +func (r *Result) Print() error { + return r.PrintTo(os.Stdout) +} + +func (r *Result) PrintTo(writer io.Writer) error { + data, err := json.MarshalIndent(r, "", " ") + if err != nil { + return err + } + _, err = writer.Write(data) + return err +} + +// 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 []types.Route +} + +// 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 types.IPNet `json:"ip"` + Gateway net.IP `json:"gateway,omitempty"` + Routes []types.Route `json:"routes,omitempty"` +} + +func (c *IPConfig) MarshalJSON() ([]byte, error) { + ipc := ipConfig{ + IP: types.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 +} diff --git a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/args.go b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/args.go new file mode 100644 index 000000000..bd8640fc9 --- /dev/null +++ b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/args.go @@ -0,0 +1,112 @@ +// 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) +} + +// UnmarshalableArgsError is used to indicate error unmarshalling args +// from the args-string in the form "K=V;K2=V2;..." +type UnmarshalableArgsError struct { + error +} + +// 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 + } + keyFieldIface := keyField.Addr().Interface() + u, ok := keyFieldIface.(encoding.TextUnmarshaler) + if !ok { + return UnmarshalableArgsError{fmt.Errorf( + "ARGS: cannot unmarshal into field '%s' - type '%s' does not implement encoding.TextUnmarshaler", + keyString, reflect.TypeOf(keyFieldIface))} + } + 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/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/current/types.go b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/current/types.go new file mode 100644 index 000000000..7267a2e6d --- /dev/null +++ b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/current/types.go @@ -0,0 +1,293 @@ +// 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 current + +import ( + "encoding/json" + "fmt" + "io" + "net" + "os" + + "github.com/containernetworking/cni/pkg/types" + "github.com/containernetworking/cni/pkg/types/020" +) + +const ImplementedSpecVersion string = "0.4.0" + +var SupportedVersions = []string{"0.3.0", "0.3.1", ImplementedSpecVersion} + +func NewResult(data []byte) (types.Result, error) { + result := &Result{} + if err := json.Unmarshal(data, result); err != nil { + return nil, err + } + return result, nil +} + +func GetResult(r types.Result) (*Result, error) { + resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion) + if err != nil { + return nil, err + } + result, ok := resultCurrent.(*Result) + if !ok { + return nil, fmt.Errorf("failed to convert result") + } + return result, nil +} + +var resultConverters = []struct { + versions []string + convert func(types.Result) (*Result, error) +}{ + {types020.SupportedVersions, convertFrom020}, + {SupportedVersions, convertFrom030}, +} + +func convertFrom020(result types.Result) (*Result, error) { + oldResult, err := types020.GetResult(result) + if err != nil { + return nil, err + } + + newResult := &Result{ + CNIVersion: ImplementedSpecVersion, + DNS: oldResult.DNS, + Routes: []*types.Route{}, + } + + if oldResult.IP4 != nil { + newResult.IPs = append(newResult.IPs, &IPConfig{ + Version: "4", + Address: oldResult.IP4.IP, + Gateway: oldResult.IP4.Gateway, + }) + for _, route := range oldResult.IP4.Routes { + newResult.Routes = append(newResult.Routes, &types.Route{ + Dst: route.Dst, + GW: route.GW, + }) + } + } + + if oldResult.IP6 != nil { + newResult.IPs = append(newResult.IPs, &IPConfig{ + Version: "6", + Address: oldResult.IP6.IP, + Gateway: oldResult.IP6.Gateway, + }) + for _, route := range oldResult.IP6.Routes { + newResult.Routes = append(newResult.Routes, &types.Route{ + Dst: route.Dst, + GW: route.GW, + }) + } + } + + return newResult, nil +} + +func convertFrom030(result types.Result) (*Result, error) { + newResult, ok := result.(*Result) + if !ok { + return nil, fmt.Errorf("failed to convert result") + } + newResult.CNIVersion = ImplementedSpecVersion + return newResult, nil +} + +func NewResultFromResult(result types.Result) (*Result, error) { + version := result.Version() + for _, converter := range resultConverters { + for _, supportedVersion := range converter.versions { + if version == supportedVersion { + return converter.convert(result) + } + } + } + return nil, fmt.Errorf("unsupported CNI result22 version %q", version) +} + +// Result is what gets returned from the plugin (via stdout) to the caller +type Result struct { + CNIVersion string `json:"cniVersion,omitempty"` + Interfaces []*Interface `json:"interfaces,omitempty"` + IPs []*IPConfig `json:"ips,omitempty"` + Routes []*types.Route `json:"routes,omitempty"` + DNS types.DNS `json:"dns,omitempty"` +} + +// Convert to the older 0.2.0 CNI spec Result type +func (r *Result) convertTo020() (*types020.Result, error) { + oldResult := &types020.Result{ + CNIVersion: types020.ImplementedSpecVersion, + DNS: r.DNS, + } + + for _, ip := range r.IPs { + // Only convert the first IP address of each version as 0.2.0 + // and earlier cannot handle multiple IP addresses + if ip.Version == "4" && oldResult.IP4 == nil { + oldResult.IP4 = &types020.IPConfig{ + IP: ip.Address, + Gateway: ip.Gateway, + } + } else if ip.Version == "6" && oldResult.IP6 == nil { + oldResult.IP6 = &types020.IPConfig{ + IP: ip.Address, + Gateway: ip.Gateway, + } + } + + if oldResult.IP4 != nil && oldResult.IP6 != nil { + break + } + } + + for _, route := range r.Routes { + is4 := route.Dst.IP.To4() != nil + if is4 && oldResult.IP4 != nil { + oldResult.IP4.Routes = append(oldResult.IP4.Routes, types.Route{ + Dst: route.Dst, + GW: route.GW, + }) + } else if !is4 && oldResult.IP6 != nil { + oldResult.IP6.Routes = append(oldResult.IP6.Routes, types.Route{ + Dst: route.Dst, + GW: route.GW, + }) + } + } + + if oldResult.IP4 == nil && oldResult.IP6 == nil { + return nil, fmt.Errorf("cannot convert: no valid IP addresses") + } + + return oldResult, nil +} + +func (r *Result) Version() string { + return ImplementedSpecVersion +} + +func (r *Result) GetAsVersion(version string) (types.Result, error) { + switch version { + case "0.3.0", "0.3.1", ImplementedSpecVersion: + r.CNIVersion = version + return r, nil + case types020.SupportedVersions[0], types020.SupportedVersions[1], types020.SupportedVersions[2]: + return r.convertTo020() + } + return nil, fmt.Errorf("cannot convert version 0.3.x to %q", version) +} + +func (r *Result) Print() error { + return r.PrintTo(os.Stdout) +} + +func (r *Result) PrintTo(writer io.Writer) error { + data, err := json.MarshalIndent(r, "", " ") + if err != nil { + return err + } + _, err = writer.Write(data) + return err +} + +// String returns a formatted string in the form of "[Interfaces: $1,][ IP: $2,] DNS: $3" where +// $1 represents the receiver's Interfaces, $2 represents the receiver's IP addresses 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 len(r.Interfaces) > 0 { + str += fmt.Sprintf("Interfaces:%+v, ", r.Interfaces) + } + if len(r.IPs) > 0 { + str += fmt.Sprintf("IP:%+v, ", r.IPs) + } + if len(r.Routes) > 0 { + str += fmt.Sprintf("Routes:%+v, ", r.Routes) + } + return fmt.Sprintf("%sDNS:%+v", str, r.DNS) +} + +// Convert this old version result to the current CNI version result +func (r *Result) Convert() (*Result, error) { + return r, nil +} + +// Interface contains values about the created interfaces +type Interface struct { + Name string `json:"name"` + Mac string `json:"mac,omitempty"` + Sandbox string `json:"sandbox,omitempty"` +} + +func (i *Interface) String() string { + return fmt.Sprintf("%+v", *i) +} + +// Int returns a pointer to the int value passed in. Used to +// set the IPConfig.Interface field. +func Int(v int) *int { + return &v +} + +// IPConfig contains values necessary to configure an IP address on an interface +type IPConfig struct { + // IP version, either "4" or "6" + Version string + // Index into Result structs Interfaces list + Interface *int + Address net.IPNet + Gateway net.IP +} + +func (i *IPConfig) String() string { + return fmt.Sprintf("%+v", *i) +} + +// JSON (un)marshallable types +type ipConfig struct { + Version string `json:"version"` + Interface *int `json:"interface,omitempty"` + Address types.IPNet `json:"address"` + Gateway net.IP `json:"gateway,omitempty"` +} + +func (c *IPConfig) MarshalJSON() ([]byte, error) { + ipc := ipConfig{ + Version: c.Version, + Interface: c.Interface, + Address: types.IPNet(c.Address), + Gateway: c.Gateway, + } + + return json.Marshal(ipc) +} + +func (c *IPConfig) UnmarshalJSON(data []byte) error { + ipc := ipConfig{} + if err := json.Unmarshal(data, &ipc); err != nil { + return err + } + + c.Version = ipc.Version + c.Interface = ipc.Interface + c.Address = net.IPNet(ipc.Address) + c.Gateway = ipc.Gateway + return nil +} diff --git a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/types.go b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/types.go new file mode 100644 index 000000000..d0d11006a --- /dev/null +++ b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/types.go @@ -0,0 +1,199 @@ +// 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" + "errors" + "fmt" + "io" + "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 { + CNIVersion string `json:"cniVersion,omitempty"` + + Name string `json:"name,omitempty"` + Type string `json:"type,omitempty"` + Capabilities map[string]bool `json:"capabilities,omitempty"` + IPAM IPAM `json:"ipam,omitempty"` + DNS DNS `json:"dns"` + + RawPrevResult map[string]interface{} `json:"prevResult,omitempty"` + PrevResult Result `json:"-"` +} + +type IPAM struct { + Type string `json:"type,omitempty"` +} + +// NetConfList describes an ordered list of networks. +type NetConfList struct { + CNIVersion string `json:"cniVersion,omitempty"` + + Name string `json:"name,omitempty"` + DisableCheck bool `json:"disableCheck,omitempty"` + Plugins []*NetConf `json:"plugins,omitempty"` +} + +type ResultFactoryFunc func([]byte) (Result, error) + +// Result is an interface that provides the result of plugin execution +type Result interface { + // The highest CNI specification result version the result supports + // without having to convert + Version() string + + // Returns the result converted into the requested CNI specification + // result version, or an error if conversion failed + GetAsVersion(version string) (Result, error) + + // Prints the result in JSON format to stdout + Print() error + + // Prints the result in JSON format to provided writer + PrintTo(writer io.Writer) error + + // Returns a JSON string representation of the result + String() string +} + +func PrintResult(result Result, version string) error { + newResult, err := result.GetAsVersion(version) + if err != nil { + return err + } + return newResult.Print() +} + +// 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 +} + +func (r *Route) String() string { + return fmt.Sprintf("%+v", *r) +} + +// Well known error codes +// see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes +const ( + ErrUnknown uint = iota // 0 + ErrIncompatibleCNIVersion // 1 + ErrUnsupportedField // 2 +) + +type Error struct { + Code uint `json:"code"` + Msg string `json:"msg"` + Details string `json:"details,omitempty"` +} + +func (e *Error) Error() string { + details := "" + if e.Details != "" { + details = fmt.Sprintf("; %v", e.Details) + } + return fmt.Sprintf("%v%v", e.Msg, details) +} + +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 route struct { + Dst IPNet `json:"dst"` + GW net.IP `json:"gw,omitempty"` +} + +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 +} + +// NotImplementedError is used to indicate that a method is not implemented for the given platform +var NotImplementedError = errors.New("Not Implemented") diff --git a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/conf.go b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/conf.go new file mode 100644 index 000000000..3cca58bbe --- /dev/null +++ b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/conf.go @@ -0,0 +1,37 @@ +// 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 version + +import ( + "encoding/json" + "fmt" +) + +// ConfigDecoder can decode the CNI version available in network config data +type ConfigDecoder struct{} + +func (*ConfigDecoder) Decode(jsonBytes []byte) (string, error) { + var conf struct { + CNIVersion string `json:"cniVersion"` + } + err := json.Unmarshal(jsonBytes, &conf) + if err != nil { + return "", fmt.Errorf("decoding version from network config: %s", err) + } + if conf.CNIVersion == "" { + return "0.1.0", nil + } + return conf.CNIVersion, nil +} diff --git a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/plugin.go b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/plugin.go new file mode 100644 index 000000000..1df427243 --- /dev/null +++ b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/plugin.go @@ -0,0 +1,144 @@ +// 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 version + +import ( + "encoding/json" + "fmt" + "io" + "strconv" + "strings" +) + +// PluginInfo reports information about CNI versioning +type PluginInfo interface { + // SupportedVersions returns one or more CNI spec versions that the plugin + // supports. If input is provided in one of these versions, then the plugin + // promises to use the same CNI version in its response + SupportedVersions() []string + + // Encode writes this CNI version information as JSON to the given Writer + Encode(io.Writer) error +} + +type pluginInfo struct { + CNIVersion_ string `json:"cniVersion"` + SupportedVersions_ []string `json:"supportedVersions,omitempty"` +} + +// pluginInfo implements the PluginInfo interface +var _ PluginInfo = &pluginInfo{} + +func (p *pluginInfo) Encode(w io.Writer) error { + return json.NewEncoder(w).Encode(p) +} + +func (p *pluginInfo) SupportedVersions() []string { + return p.SupportedVersions_ +} + +// PluginSupports returns a new PluginInfo that will report the given versions +// as supported +func PluginSupports(supportedVersions ...string) PluginInfo { + if len(supportedVersions) < 1 { + panic("programmer error: you must support at least one version") + } + return &pluginInfo{ + CNIVersion_: Current(), + SupportedVersions_: supportedVersions, + } +} + +// PluginDecoder can decode the response returned by a plugin's VERSION command +type PluginDecoder struct{} + +func (*PluginDecoder) Decode(jsonBytes []byte) (PluginInfo, error) { + var info pluginInfo + err := json.Unmarshal(jsonBytes, &info) + if err != nil { + return nil, fmt.Errorf("decoding version info: %s", err) + } + if info.CNIVersion_ == "" { + return nil, fmt.Errorf("decoding version info: missing field cniVersion") + } + if len(info.SupportedVersions_) == 0 { + if info.CNIVersion_ == "0.2.0" { + return PluginSupports("0.1.0", "0.2.0"), nil + } + return nil, fmt.Errorf("decoding version info: missing field supportedVersions") + } + return &info, nil +} + +// ParseVersion parses a version string like "3.0.1" or "0.4.5" into major, +// minor, and micro numbers or returns an error +func ParseVersion(version string) (int, int, int, error) { + var major, minor, micro int + if version == "" { + return -1, -1, -1, fmt.Errorf("invalid version %q: the version is empty", version) + } + + parts := strings.Split(version, ".") + if len(parts) >= 4 { + return -1, -1, -1, fmt.Errorf("invalid version %q: too many parts", version) + } + + major, err := strconv.Atoi(parts[0]) + if err != nil { + return -1, -1, -1, fmt.Errorf("failed to convert major version part %q: %v", parts[0], err) + } + + if len(parts) >= 2 { + minor, err = strconv.Atoi(parts[1]) + if err != nil { + return -1, -1, -1, fmt.Errorf("failed to convert minor version part %q: %v", parts[1], err) + } + } + + if len(parts) >= 3 { + micro, err = strconv.Atoi(parts[2]) + if err != nil { + return -1, -1, -1, fmt.Errorf("failed to convert micro version part %q: %v", parts[2], err) + } + } + + return major, minor, micro, nil +} + +// GreaterThanOrEqualTo takes two string versions, parses them into major/minor/micro +// numbers, and compares them to determine whether the first version is greater +// than or equal to the second +func GreaterThanOrEqualTo(version, otherVersion string) (bool, error) { + firstMajor, firstMinor, firstMicro, err := ParseVersion(version) + if err != nil { + return false, err + } + + secondMajor, secondMinor, secondMicro, err := ParseVersion(otherVersion) + if err != nil { + return false, err + } + + if firstMajor > secondMajor { + return true, nil + } else if firstMajor == secondMajor { + if firstMinor > secondMinor { + return true, nil + } else if firstMinor == secondMinor && firstMicro >= secondMicro { + return true, nil + } + } + return false, nil +} diff --git a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go new file mode 100644 index 000000000..25c3810b2 --- /dev/null +++ b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go @@ -0,0 +1,49 @@ +// 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 version + +import "fmt" + +type ErrorIncompatible struct { + Config string + Supported []string +} + +func (e *ErrorIncompatible) Details() string { + return fmt.Sprintf("config is %q, plugin supports %q", e.Config, e.Supported) +} + +func (e *ErrorIncompatible) Error() string { + return fmt.Sprintf("incompatible CNI versions: %s", e.Details()) +} + +type Reconciler struct{} + +func (r *Reconciler) Check(configVersion string, pluginInfo PluginInfo) *ErrorIncompatible { + return r.CheckRaw(configVersion, pluginInfo.SupportedVersions()) +} + +func (*Reconciler) CheckRaw(configVersion string, supportedVersions []string) *ErrorIncompatible { + for _, supportedVersion := range supportedVersions { + if configVersion == supportedVersion { + return nil + } + } + + return &ErrorIncompatible{ + Config: configVersion, + Supported: supportedVersions, + } +} diff --git a/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/version.go b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/version.go new file mode 100644 index 000000000..8f3508e61 --- /dev/null +++ b/kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/version.go @@ -0,0 +1,83 @@ +// 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 version + +import ( + "encoding/json" + "fmt" + + "github.com/containernetworking/cni/pkg/types" + "github.com/containernetworking/cni/pkg/types/020" + "github.com/containernetworking/cni/pkg/types/current" +) + +// Current reports the version of the CNI spec implemented by this library +func Current() string { + return "0.4.0" +} + +// Legacy PluginInfo describes a plugin that is backwards compatible with the +// CNI spec version 0.1.0. In particular, a runtime compiled against the 0.1.0 +// library ought to work correctly with a plugin that reports support for +// Legacy versions. +// +// Any future CNI spec versions which meet this definition should be added to +// this list. +var Legacy = PluginSupports("0.1.0", "0.2.0") +var All = PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0") + +var resultFactories = []struct { + supportedVersions []string + newResult types.ResultFactoryFunc +}{ + {current.SupportedVersions, current.NewResult}, + {types020.SupportedVersions, types020.NewResult}, +} + +// Finds a Result object matching the requested version (if any) and asks +// that object to parse the plugin result, returning an error if parsing failed. +func NewResult(version string, resultBytes []byte) (types.Result, error) { + reconciler := &Reconciler{} + for _, resultFactory := range resultFactories { + err := reconciler.CheckRaw(version, resultFactory.supportedVersions) + if err == nil { + // Result supports this version + return resultFactory.newResult(resultBytes) + } + } + + return nil, fmt.Errorf("unsupported CNI result version %q", version) +} + +// ParsePrevResult parses a prevResult in a NetConf structure and sets +// the NetConf's PrevResult member to the parsed Result object. +func ParsePrevResult(conf *types.NetConf) error { + if conf.RawPrevResult == nil { + return nil + } + + resultBytes, err := json.Marshal(conf.RawPrevResult) + if err != nil { + return fmt.Errorf("could not serialize prevResult: %v", err) + } + + conf.RawPrevResult = nil + conf.PrevResult, err = NewResult(conf.CNIVersion, resultBytes) + if err != nil { + return fmt.Errorf("could not parse prevResult: %v", err) + } + + return nil +} diff --git a/kuryr_cni/vendor/github.com/pkg/errors/.gitignore b/kuryr_cni/vendor/github.com/pkg/errors/.gitignore new file mode 100644 index 000000000..daf913b1b --- /dev/null +++ b/kuryr_cni/vendor/github.com/pkg/errors/.gitignore @@ -0,0 +1,24 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe +*.test +*.prof diff --git a/kuryr_cni/vendor/github.com/pkg/errors/.travis.yml b/kuryr_cni/vendor/github.com/pkg/errors/.travis.yml new file mode 100644 index 000000000..d4b92663b --- /dev/null +++ b/kuryr_cni/vendor/github.com/pkg/errors/.travis.yml @@ -0,0 +1,15 @@ +language: go +go_import_path: github.com/pkg/errors +go: + - 1.4.x + - 1.5.x + - 1.6.x + - 1.7.x + - 1.8.x + - 1.9.x + - 1.10.x + - 1.11.x + - tip + +script: + - go test -v ./... diff --git a/kuryr_cni/vendor/github.com/pkg/errors/LICENSE b/kuryr_cni/vendor/github.com/pkg/errors/LICENSE new file mode 100644 index 000000000..835ba3e75 --- /dev/null +++ b/kuryr_cni/vendor/github.com/pkg/errors/LICENSE @@ -0,0 +1,23 @@ +Copyright (c) 2015, Dave Cheney +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/kuryr_cni/vendor/github.com/pkg/errors/README.md b/kuryr_cni/vendor/github.com/pkg/errors/README.md new file mode 100644 index 000000000..6483ba2af --- /dev/null +++ b/kuryr_cni/vendor/github.com/pkg/errors/README.md @@ -0,0 +1,52 @@ +# errors [![Travis-CI](https://travis-ci.org/pkg/errors.svg)](https://travis-ci.org/pkg/errors) [![AppVeyor](https://ci.appveyor.com/api/projects/status/b98mptawhudj53ep/branch/master?svg=true)](https://ci.appveyor.com/project/davecheney/errors/branch/master) [![GoDoc](https://godoc.org/github.com/pkg/errors?status.svg)](http://godoc.org/github.com/pkg/errors) [![Report card](https://goreportcard.com/badge/github.com/pkg/errors)](https://goreportcard.com/report/github.com/pkg/errors) [![Sourcegraph](https://sourcegraph.com/github.com/pkg/errors/-/badge.svg)](https://sourcegraph.com/github.com/pkg/errors?badge) + +Package errors provides simple error handling primitives. + +`go get github.com/pkg/errors` + +The traditional error handling idiom in Go is roughly akin to +```go +if err != nil { + return err +} +``` +which applied recursively up the call stack results in error reports without context or debugging information. The errors package allows programmers to add context to the failure path in their code in a way that does not destroy the original value of the error. + +## Adding context to an error + +The errors.Wrap function returns a new error that adds context to the original error. For example +```go +_, err := ioutil.ReadAll(r) +if err != nil { + return errors.Wrap(err, "read failed") +} +``` +## Retrieving the cause of an error + +Using `errors.Wrap` constructs a stack of errors, adding context to the preceding error. Depending on the nature of the error it may be necessary to reverse the operation of errors.Wrap to retrieve the original error for inspection. Any error value which implements this interface can be inspected by `errors.Cause`. +```go +type causer interface { + Cause() error +} +``` +`errors.Cause` will recursively retrieve the topmost error which does not implement `causer`, which is assumed to be the original cause. For example: +```go +switch err := errors.Cause(err).(type) { +case *MyError: + // handle specifically +default: + // unknown error +} +``` + +[Read the package documentation for more information](https://godoc.org/github.com/pkg/errors). + +## Contributing + +We welcome pull requests, bug fixes and issue reports. With that said, the bar for adding new symbols to this package is intentionally set high. + +Before proposing a change, please discuss your change by raising an issue. + +## License + +BSD-2-Clause diff --git a/kuryr_cni/vendor/github.com/pkg/errors/appveyor.yml b/kuryr_cni/vendor/github.com/pkg/errors/appveyor.yml new file mode 100644 index 000000000..a932eade0 --- /dev/null +++ b/kuryr_cni/vendor/github.com/pkg/errors/appveyor.yml @@ -0,0 +1,32 @@ +version: build-{build}.{branch} + +clone_folder: C:\gopath\src\github.com\pkg\errors +shallow_clone: true # for startup speed + +environment: + GOPATH: C:\gopath + +platform: + - x64 + +# http://www.appveyor.com/docs/installed-software +install: + # some helpful output for debugging builds + - go version + - go env + # pre-installed MinGW at C:\MinGW is 32bit only + # but MSYS2 at C:\msys64 has mingw64 + - set PATH=C:\msys64\mingw64\bin;%PATH% + - gcc --version + - g++ --version + +build_script: + - go install -v ./... + +test_script: + - set PATH=C:\gopath\bin;%PATH% + - go test -v ./... + +#artifacts: +# - path: '%GOPATH%\bin\*.exe' +deploy: off diff --git a/kuryr_cni/vendor/github.com/pkg/errors/errors.go b/kuryr_cni/vendor/github.com/pkg/errors/errors.go new file mode 100644 index 000000000..7421f326f --- /dev/null +++ b/kuryr_cni/vendor/github.com/pkg/errors/errors.go @@ -0,0 +1,282 @@ +// Package errors provides simple error handling primitives. +// +// The traditional error handling idiom in Go is roughly akin to +// +// if err != nil { +// return err +// } +// +// which when applied recursively up the call stack results in error reports +// without context or debugging information. The errors package allows +// programmers to add context to the failure path in their code in a way +// that does not destroy the original value of the error. +// +// Adding context to an error +// +// The errors.Wrap function returns a new error that adds context to the +// original error by recording a stack trace at the point Wrap is called, +// together with the supplied message. For example +// +// _, err := ioutil.ReadAll(r) +// if err != nil { +// return errors.Wrap(err, "read failed") +// } +// +// If additional control is required, the errors.WithStack and +// errors.WithMessage functions destructure errors.Wrap into its component +// operations: annotating an error with a stack trace and with a message, +// respectively. +// +// Retrieving the cause of an error +// +// Using errors.Wrap constructs a stack of errors, adding context to the +// preceding error. Depending on the nature of the error it may be necessary +// to reverse the operation of errors.Wrap to retrieve the original error +// for inspection. Any error value which implements this interface +// +// type causer interface { +// Cause() error +// } +// +// can be inspected by errors.Cause. errors.Cause will recursively retrieve +// the topmost error that does not implement causer, which is assumed to be +// the original cause. For example: +// +// switch err := errors.Cause(err).(type) { +// case *MyError: +// // handle specifically +// default: +// // unknown error +// } +// +// Although the causer interface is not exported by this package, it is +// considered a part of its stable public interface. +// +// Formatted printing of errors +// +// All error values returned from this package implement fmt.Formatter and can +// be formatted by the fmt package. The following verbs are supported: +// +// %s print the error. If the error has a Cause it will be +// printed recursively. +// %v see %s +// %+v extended format. Each Frame of the error's StackTrace will +// be printed in detail. +// +// Retrieving the stack trace of an error or wrapper +// +// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are +// invoked. This information can be retrieved with the following interface: +// +// type stackTracer interface { +// StackTrace() errors.StackTrace +// } +// +// The returned errors.StackTrace type is defined as +// +// type StackTrace []Frame +// +// The Frame type represents a call site in the stack trace. Frame supports +// the fmt.Formatter interface that can be used for printing information about +// the stack trace of this error. For example: +// +// if err, ok := err.(stackTracer); ok { +// for _, f := range err.StackTrace() { +// fmt.Printf("%+s:%d", f) +// } +// } +// +// Although the stackTracer interface is not exported by this package, it is +// considered a part of its stable public interface. +// +// See the documentation for Frame.Format for more details. +package errors + +import ( + "fmt" + "io" +) + +// New returns an error with the supplied message. +// New also records the stack trace at the point it was called. +func New(message string) error { + return &fundamental{ + msg: message, + stack: callers(), + } +} + +// Errorf formats according to a format specifier and returns the string +// as a value that satisfies error. +// Errorf also records the stack trace at the point it was called. +func Errorf(format string, args ...interface{}) error { + return &fundamental{ + msg: fmt.Sprintf(format, args...), + stack: callers(), + } +} + +// fundamental is an error that has a message and a stack, but no caller. +type fundamental struct { + msg string + *stack +} + +func (f *fundamental) Error() string { return f.msg } + +func (f *fundamental) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + io.WriteString(s, f.msg) + f.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, f.msg) + case 'q': + fmt.Fprintf(s, "%q", f.msg) + } +} + +// WithStack annotates err with a stack trace at the point WithStack was called. +// If err is nil, WithStack returns nil. +func WithStack(err error) error { + if err == nil { + return nil + } + return &withStack{ + err, + callers(), + } +} + +type withStack struct { + error + *stack +} + +func (w *withStack) Cause() error { return w.error } + +func (w *withStack) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v", w.Cause()) + w.stack.Format(s, verb) + return + } + fallthrough + case 's': + io.WriteString(s, w.Error()) + case 'q': + fmt.Fprintf(s, "%q", w.Error()) + } +} + +// Wrap returns an error annotating err with a stack trace +// at the point Wrap is called, and the supplied message. +// If err is nil, Wrap returns nil. +func Wrap(err error, message string) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: message, + } + return &withStack{ + err, + callers(), + } +} + +// Wrapf returns an error annotating err with a stack trace +// at the point Wrapf is called, and the format specifier. +// If err is nil, Wrapf returns nil. +func Wrapf(err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + err = &withMessage{ + cause: err, + msg: fmt.Sprintf(format, args...), + } + return &withStack{ + err, + callers(), + } +} + +// WithMessage annotates err with a new message. +// If err is nil, WithMessage returns nil. +func WithMessage(err error, message string) error { + if err == nil { + return nil + } + return &withMessage{ + cause: err, + msg: message, + } +} + +// WithMessagef annotates err with the format specifier. +// If err is nil, WithMessagef returns nil. +func WithMessagef(err error, format string, args ...interface{}) error { + if err == nil { + return nil + } + return &withMessage{ + cause: err, + msg: fmt.Sprintf(format, args...), + } +} + +type withMessage struct { + cause error + msg string +} + +func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() } +func (w *withMessage) Cause() error { return w.cause } + +func (w *withMessage) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + if s.Flag('+') { + fmt.Fprintf(s, "%+v\n", w.Cause()) + io.WriteString(s, w.msg) + return + } + fallthrough + case 's', 'q': + io.WriteString(s, w.Error()) + } +} + +// Cause returns the underlying cause of the error, if possible. +// An error value has a cause if it implements the following +// interface: +// +// type causer interface { +// Cause() error +// } +// +// If the error does not implement Cause, the original error will +// be returned. If the error is nil, nil will be returned without further +// investigation. +func Cause(err error) error { + type causer interface { + Cause() error + } + + for err != nil { + cause, ok := err.(causer) + if !ok { + break + } + err = cause.Cause() + } + return err +} diff --git a/kuryr_cni/vendor/github.com/pkg/errors/stack.go b/kuryr_cni/vendor/github.com/pkg/errors/stack.go new file mode 100644 index 000000000..2874a048c --- /dev/null +++ b/kuryr_cni/vendor/github.com/pkg/errors/stack.go @@ -0,0 +1,147 @@ +package errors + +import ( + "fmt" + "io" + "path" + "runtime" + "strings" +) + +// Frame represents a program counter inside a stack frame. +type Frame uintptr + +// pc returns the program counter for this frame; +// multiple frames may have the same PC value. +func (f Frame) pc() uintptr { return uintptr(f) - 1 } + +// file returns the full path to the file that contains the +// function for this Frame's pc. +func (f Frame) file() string { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return "unknown" + } + file, _ := fn.FileLine(f.pc()) + return file +} + +// line returns the line number of source code of the +// function for this Frame's pc. +func (f Frame) line() int { + fn := runtime.FuncForPC(f.pc()) + if fn == nil { + return 0 + } + _, line := fn.FileLine(f.pc()) + return line +} + +// Format formats the frame according to the fmt.Formatter interface. +// +// %s source file +// %d source line +// %n function name +// %v equivalent to %s:%d +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+s function name and path of source file relative to the compile time +// GOPATH separated by \n\t (\n\t) +// %+v equivalent to %+s:%d +func (f Frame) Format(s fmt.State, verb rune) { + switch verb { + case 's': + switch { + case s.Flag('+'): + pc := f.pc() + fn := runtime.FuncForPC(pc) + if fn == nil { + io.WriteString(s, "unknown") + } else { + file, _ := fn.FileLine(pc) + fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file) + } + default: + io.WriteString(s, path.Base(f.file())) + } + case 'd': + fmt.Fprintf(s, "%d", f.line()) + case 'n': + name := runtime.FuncForPC(f.pc()).Name() + io.WriteString(s, funcname(name)) + case 'v': + f.Format(s, 's') + io.WriteString(s, ":") + f.Format(s, 'd') + } +} + +// StackTrace is stack of Frames from innermost (newest) to outermost (oldest). +type StackTrace []Frame + +// Format formats the stack of Frames according to the fmt.Formatter interface. +// +// %s lists source files for each Frame in the stack +// %v lists the source file and line number for each Frame in the stack +// +// Format accepts flags that alter the printing of some verbs, as follows: +// +// %+v Prints filename, function, and line number for each Frame in the stack. +func (st StackTrace) Format(s fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case s.Flag('+'): + for _, f := range st { + fmt.Fprintf(s, "\n%+v", f) + } + case s.Flag('#'): + fmt.Fprintf(s, "%#v", []Frame(st)) + default: + fmt.Fprintf(s, "%v", []Frame(st)) + } + case 's': + fmt.Fprintf(s, "%s", []Frame(st)) + } +} + +// stack represents a stack of program counters. +type stack []uintptr + +func (s *stack) Format(st fmt.State, verb rune) { + switch verb { + case 'v': + switch { + case st.Flag('+'): + for _, pc := range *s { + f := Frame(pc) + fmt.Fprintf(st, "\n%+v", f) + } + } + } +} + +func (s *stack) StackTrace() StackTrace { + f := make([]Frame, len(*s)) + for i := 0; i < len(f); i++ { + f[i] = Frame((*s)[i]) + } + return f +} + +func callers() *stack { + const depth = 32 + var pcs [depth]uintptr + n := runtime.Callers(3, pcs[:]) + var st stack = pcs[0:n] + return &st +} + +// funcname removes the path prefix component of a function's name reported by func.Name(). +func funcname(name string) string { + i := strings.LastIndex(name, "/") + name = name[i+1:] + i = strings.Index(name, ".") + return name[i+1:] +}