Browse Source

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
tags/1.1.0
Michał Dulko 1 year ago
parent
commit
245eb078f4
25 changed files with 2546 additions and 47 deletions
  1. +8
    -0
      cni.Dockerfile
  2. +1
    -47
      cni_ds_init
  3. +8
    -0
      cni_py3.Dockerfile
  4. +37
    -0
      kuryr_cni/Gopkg.lock
  5. +34
    -0
      kuryr_cni/Gopkg.toml
  6. +2
    -0
      kuryr_cni/README
  7. +195
    -0
      kuryr_cni/main.go
  8. +120
    -0
      kuryr_cni/ovo.go
  9. +202
    -0
      kuryr_cni/vendor/github.com/containernetworking/cni/LICENSE
  10. +307
    -0
      kuryr_cni/vendor/github.com/containernetworking/cni/pkg/skel/skel.go
  11. +140
    -0
      kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/020/types.go
  12. +112
    -0
      kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/args.go
  13. +293
    -0
      kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/current/types.go
  14. +199
    -0
      kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/types.go
  15. +37
    -0
      kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/conf.go
  16. +144
    -0
      kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/plugin.go
  17. +49
    -0
      kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go
  18. +83
    -0
      kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/version.go
  19. +24
    -0
      kuryr_cni/vendor/github.com/pkg/errors/.gitignore
  20. +15
    -0
      kuryr_cni/vendor/github.com/pkg/errors/.travis.yml
  21. +23
    -0
      kuryr_cni/vendor/github.com/pkg/errors/LICENSE
  22. +52
    -0
      kuryr_cni/vendor/github.com/pkg/errors/README.md
  23. +32
    -0
      kuryr_cni/vendor/github.com/pkg/errors/appveyor.yml
  24. +282
    -0
      kuryr_cni/vendor/github.com/pkg/errors/errors.go
  25. +147
    -0
      kuryr_cni/vendor/github.com/pkg/errors/stack.go

+ 8
- 0
cni.Dockerfile View File

@@ -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<toni@kuryr.org>, Michał Dulko<mdulko@redhat.com>"

@@ -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}


+ 1
- 47
cni_ds_init View File

@@ -6,53 +6,7 @@ function cleanup() {
}

function deploy() {
POD_NAMESPACE=$(</var/run/secrets/kubernetes.io/serviceaccount/namespace)

# Write the script to a file.
cat > /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


+ 8
- 0
cni_py3.Dockerfile View File

@@ -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<toni@kuryr.org>, Michał Dulko<mdulko@redhat.com>"

@@ -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}


+ 37
- 0
kuryr_cni/Gopkg.lock View File

@@ -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

+ 34
- 0
kuryr_cni/Gopkg.toml View File

@@ -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

+ 2
- 0
kuryr_cni/README View File

@@ -0,0 +1,2 @@
This is golang part of Kuryr, that is the CNI plugin that gets injected into
the host.

+ 195
- 0
kuryr_cni/main.go View File

@@ -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, &current.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 := &current.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")
}

+ 120
- 0
kuryr_cni/ovo.go View File

@@ -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)
}

+ 202
- 0
kuryr_cni/vendor/github.com/containernetworking/cni/LICENSE View File

@@ -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.


+ 307
- 0
kuryr_cni/vendor/github.com/containernetworking/cni/pkg/skel/skel.go View File

@@ -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 <foo> v<version>"
//
// 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)
}
}

+ 140
- 0
kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/020/types.go View File

@@ -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
}

+ 112
- 0
kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/args.go View File

@@ -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
}

+ 293
- 0
kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/current/types.go View File

@@ -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
}

+ 199
- 0
kuryr_cni/vendor/github.com/containernetworking/cni/pkg/types/types.go View File

@@ -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")

+ 37
- 0
kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/conf.go View File

@@ -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
}

+ 144
- 0
kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/plugin.go View File

@@ -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
}

+ 49
- 0
kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/reconcile.go View File

@@ -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,
}
}

+ 83
- 0
kuryr_cni/vendor/github.com/containernetworking/cni/pkg/version/version.go View File

@@ -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
}

+ 24
- 0
kuryr_cni/vendor/github.com/pkg/errors/.gitignore View File

@@ -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

+ 15
- 0
kuryr_cni/vendor/github.com/pkg/errors/.travis.yml View File

@@ -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 ./...

+ 23
- 0
kuryr_cni/vendor/github.com/pkg/errors/LICENSE View File

@@ -0,0 +1,23 @@
Copyright (c) 2015, Dave Cheney <dave@cheney.net>
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.

+ 52
- 0
kuryr_cni/vendor/github.com/pkg/errors/README.md