Compare commits

..

49 Commits
0.2.9 ... 0.5.1

Author SHA1 Message Date
cda1fe78b1 Enable scale from zero
This must be used with HEAD of faasd after the following PR is
merged: https://github.com/alexellis/faas-containerd/pull/29

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-20 08:44:42 +00:00
a3392634a7 Enable basic-auth on faas-containerd
There was a missing basic_auth environment variable which would
have left a machine vulnerable if discovered.

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-10 18:57:24 +00:00
95e278b29a Port networking to CNI
Replaced netns utility with CNI plugins to create a bridge network
and allow communication between containers with firewall plugin.

Overwrite the CNI config in case it exists. Allow updating the config
on new versions.

Signed-off-by: Carlos de Paula <me@carlosedp.com>
2020-01-10 18:30:43 +00:00
d802ba70c1 Update README.md 2020-01-09 11:52:33 +00:00
cd76ff3ebc Update README.md 2020-01-09 11:51:08 +00:00
ed5110de30 Update faasd version
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-05 12:03:53 +00:00
2f3ba1335c Update docs for #15
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-05 12:02:50 +00:00
24e065965f Update path for secrets for e2e tests
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-05 12:00:43 +00:00
fd2ee55f9f Update e2e tests for secrets folder
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-05 12:00:43 +00:00
d135999d3b Set working directory for faasd / faas-containerd
* faasd writes secrets to wd + /secrets/*
* faas-containerd is passed a custom path to use to load the
secrets

Both services gain their work /run/ folders for temporary and
working files. Tested on RPi3 e2e with faasd install.

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-05 12:00:43 +00:00
3068d03279 Set wd to /run/faasd
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-05 12:00:43 +00:00
fd4f53fe15 Update faas-containerd.service 2020-01-05 09:23:19 +00:00
4b93ccba3f Debug statement for CI
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-05 08:56:29 +00:00
e6b814fd60 Add basic_auth env variable to faas-containerd.service
Fixes #11
Having this env variable set enables basic authentication for faas-containerd.

Signed-off-by: Mark Sharpley <mcs94@cam.ac.uk>
2020-01-05 08:32:01 +00:00
06890cddb9 Tweak e2e tests
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-03 19:41:04 +00:00
40da0a35c3 Tweak e2e tests
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-03 19:35:01 +00:00
0c0d05b2ea Tweak e2e tests
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-03 19:12:10 +00:00
af46f34003 Tweak e2e tests
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-03 19:05:17 +00:00
2f7269fc97 Tweak e2e tests
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-03 18:58:37 +00:00
1f56c39675 Tweak e2e tests
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-03 18:53:03 +00:00
7152b170bb Tweak e2e tests
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-03 18:47:25 +00:00
47955954eb Tweak e2e tests
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-03 18:43:46 +00:00
7f672f006a Tweak e2e tests
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-03 18:29:46 +00:00
20ec76bf74 Run faasd from usr local bin
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-03 18:25:27 +00:00
04d1688bfb Repurpose dist binary for e2e test
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-03 18:21:48 +00:00
a8f514f7d6 Show pwd in prepare-test step
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-03 18:13:12 +00:00
502d3fdecc Show working dir
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-03 18:10:44 +00:00
5d098c9cb7 Invoke async in e2e tests
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-03 18:06:31 +00:00
0935dc6867 Use GOPATH from env
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-03 18:04:24 +00:00
e77d05ec94 Test e2e
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-03 18:02:28 +00:00
7ab69b5317 Add e2e test prep
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-03 17:59:11 +00:00
098baba7cc Add unit test for proxy and shutdown channel
* Proxy has initial unit test and more can be added
* Shutdown channel and cancellation added for proper shutdown of
the proxy

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-03 12:06:53 +00:00
9d688b9ea6 Bump faasd version
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-02 18:40:12 +00:00
1458a2f2fb Add Caddyfile to backlog
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-01 12:35:33 +00:00
c18a038062 Generate basic-auth password in install command
* Required so that faas-containerd can start independently of
faasd.
* Extracts common mount path const for mounted secrets

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-01 12:33:11 +00:00
af0555a85b Add tutorial and give other options for Linux users
Closes: #9
Closes: #8

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-01 12:17:58 +00:00
2ff8646669 Add .github
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2020-01-01 12:10:02 +00:00
d785bebf4c Copy headers in both directions in proxy
* Issue was detected whilst testing 0.4.0 from @Waterdrips which
added basic auth, but the header was not being propagated.
* This code is tested in OpenFaaS already, but unit tests will
be added retrospectively.
* Proxy now reads the gateway URL via a channel instead of from
a file to make unit testing easier.

Basic auth now works as expected with faas-cli login / list.

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2019-12-31 18:20:43 +00:00
17845457e2 Alteration for version passing
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2019-12-31 12:30:14 +00:00
300d8b082a Pass version from main
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2019-12-31 12:30:14 +00:00
17a5e2c625 Extract file for version command
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2019-12-31 12:18:27 +00:00
f0172e618a Move basic auth note to common section
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2019-12-30 20:48:27 +00:00
61e2d16c3e Add Basic Auth to the gateway
Add and enable basic auth to the gateway. This allows users to
put their gateway on the internet and expose it to public networks
without anyone being able to control their deployments.

Added information to the README that allows users to get their
gatewau basic auth password and username

Signed-off-by: Alistair Hey <alistair@heyal.co.uk>
2019-12-30 20:45:00 +00:00
ae0753a6d9 Update faasd version
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2019-12-29 13:16:16 +00:00
19a769b7da Update proxy print message
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2019-12-29 13:02:03 +00:00
48237e0b3c Don't follow redirects
Required for functioning proxy

Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2019-12-29 12:59:39 +00:00
306313ed9a Proxy from faasd to gateway
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2019-12-29 12:46:32 +00:00
ff0cccf0dc Add proxy to faasd up
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2019-12-29 12:46:32 +00:00
52baca9d17 Update download location of Go
Signed-off-by: Alex Ellis (OpenFaaS Ltd) <alexellis2@gmail.com>
2019-12-29 12:00:54 +00:00
75 changed files with 5956 additions and 159 deletions

1
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1 @@
@alexellis

41
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,41 @@
<!--- Provide a general summary of the issue in the Title above -->
## Expected Behaviour
<!--- If you're describing a bug, tell us what should happen -->
<!--- If you're suggesting a change/improvement, tell us how it should work -->
## Current Behaviour
<!--- If describing a bug, tell us what happens instead of the expected behavior -->
<!--- If suggesting a change/improvement, explain the difference from current behavior -->
## Possible Solution
<!--- Not obligatory, but suggest a fix/reason for the bug, -->
<!--- or ideas how to implement the addition or change -->
## Steps to Reproduce (for bugs)
<!--- Provide a link to a live example, or an unambiguous set of steps to -->
<!--- reproduce this bug. Include code to reproduce, if relevant -->
1.
2.
3.
4.
## Context
<!--- How has this issue affected you? What are you trying to accomplish? -->
<!--- Providing context helps us come up with a solution that is most useful in the real world -->
## Your Environment
* OS and architecture:
* Versions:
```sh
go version
containerd -version
uname -a
cat /etc/os-release
```

40
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,40 @@
<!--- Provide a general summary of your changes in the Title above -->
## Description
<!--- Describe your changes in detail -->
## Motivation and Context
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here. -->
- [ ] I have raised an issue to propose this change **this is required**
## How Has This Been Tested?
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->
## Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
## Checklist:
Commits:
- [ ] I've read the [CONTRIBUTION](https://github.com/openfaas/faas/blob/master/CONTRIBUTING.md) guide
- [ ] My commit message has a body and describe how this was tested and why it is required.
- [ ] I have signed-off my commits with `git commit -s` for the Developer Certificate of Origin (DCO)
Code:
- [ ] My code follows the code style of this project.
- [ ] I have added tests to cover my changes.
Docs:
- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.

3
.gitignore vendored
View File

@ -2,3 +2,6 @@
hosts
/resolv.conf
.idea/
basic-auth-user
basic-auth-password

View File

@ -1,9 +1,19 @@
sudo: required
language: go
go:
- '1.12'
addons:
apt:
packages:
- runc
script:
- make dist
- make prepare-test
- make test-e2e
deploy:
provider: releases
api_key:

43
Gopkg.lock generated
View File

@ -130,6 +130,14 @@
pruneopts = "UT"
revision = "bda0ff6ed73c67bfb5e62bc9c697f146b7fd7f13"
[[projects]]
branch = "master"
digest = "1:2301a9a859e3b0946e2ddd6961ba6faf6857e6e68bc9293db758dbe3b17cc35e"
name = "github.com/containerd/go-cni"
packages = ["."]
pruneopts = "UT"
revision = "c154a49e2c754b83ebfb12ebf1362213b94d23e6"
[[projects]]
digest = "1:6d66a41dbbc6819902f1589d0550bc01c18032c0a598a7cd656731e6df73861b"
name = "github.com/containerd/ttrpc"
@ -144,6 +152,21 @@
pruneopts = "UT"
revision = "a93fcdb778cd272c6e9b3028b2f42d813e785d40"
[[projects]]
digest = "1:1a07bbfee1d0534e8dda4773948e6dcd3a061ea7ab047ce04619476946226483"
name = "github.com/containernetworking/cni"
packages = [
"libcni",
"pkg/invoke",
"pkg/types",
"pkg/types/020",
"pkg/types/current",
"pkg/version",
]
pruneopts = "UT"
revision = "4cfb7b568922a3c79a23e438dc52fe537fc9687e"
version = "v0.7.1"
[[projects]]
digest = "1:e495f9f1fb2bae55daeb76e099292054fe1f734947274b3cfc403ccda595d55a"
name = "github.com/docker/distribution"
@ -204,6 +227,14 @@
revision = "6c65a5562fc06764971b7c5d05c76c75e84bdbf7"
version = "v1.3.2"
[[projects]]
digest = "1:582b704bebaa06b48c29b0cec224a6058a09c86883aaddabde889cd1a5f73e1b"
name = "github.com/google/uuid"
packages = ["."]
pruneopts = "UT"
revision = "0cd6bf5da1e1c83f8b45653022c74f71af0538a4"
version = "v1.1.1"
[[projects]]
digest = "1:870d441fe217b8e689d7949fef6e43efbc787e50f200cb1e70dbca9204a1d6be"
name = "github.com/inconshreveable/mousetrap"
@ -273,6 +304,14 @@
revision = "ba968bfe8b2f7e042a574c888954fccecfa385b4"
version = "v0.8.1"
[[projects]]
digest = "1:044c51736e2688a3e4f28f72537f8a7b3f9c188fab4477d5334d92dfe2c07ed5"
name = "github.com/sethvargo/go-password"
packages = ["password"]
pruneopts = "UT"
revision = "07c3d521e892540e71469bb0312866130714c038"
version = "v0.1.3"
[[projects]]
digest = "1:fd61cf4ae1953d55df708acb6b91492d538f49c305b364a014049914495db426"
name = "github.com/sirupsen/logrus"
@ -464,8 +503,12 @@
"github.com/containerd/containerd/errdefs",
"github.com/containerd/containerd/namespaces",
"github.com/containerd/containerd/oci",
"github.com/containerd/go-cni",
"github.com/google/uuid",
"github.com/morikuni/aec",
"github.com/opencontainers/runtime-spec/specs-go",
"github.com/pkg/errors",
"github.com/sethvargo/go-password/password",
"github.com/spf13/cobra",
"github.com/vishvananda/netlink",
"github.com/vishvananda/netns",

View File

@ -21,3 +21,15 @@
[prune]
go-tests = true
unused-packages = true
[[constraint]]
name = "github.com/sethvargo/go-password"
version = "0.1.3"
[[constraint]]
branch = "master"
name = "github.com/containerd/go-cni"
[[constraint]]
name = "github.com/google/uuid"
version = "1.1.1"

View File

@ -1,6 +1,10 @@
Version := $(shell git describe --tags --dirty)
GitCommit := $(shell git rev-parse HEAD)
LDFLAGS := "-s -w -X pkg.Version=$(Version) -X pkg.GitCommit=$(GitCommit)"
LDFLAGS := "-s -w -X main.Version=$(Version) -X main.GitCommit=$(GitCommit)"
CONTAINERD_VER := 1.3.2
FAASC_VER := 0.4.0
CNI_VERSION := v0.8.4
ARCH := amd64
.PHONY: all
all: local
@ -13,3 +17,36 @@ dist:
CGO_ENABLED=0 GOOS=linux go build -ldflags $(LDFLAGS) -a -installsuffix cgo -o bin/faasd
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -ldflags $(LDFLAGS) -a -installsuffix cgo -o bin/faasd-armhf
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags $(LDFLAGS) -a -installsuffix cgo -o bin/faasd-arm64
.PHONY: prepare-test
prepare-test:
curl -sLSf https://github.com/containerd/containerd/releases/download/v$(CONTAINERD_VER)/containerd-$(CONTAINERD_VER).linux-amd64.tar.gz > /tmp/containerd.tar.gz && sudo tar -xvf /tmp/containerd.tar.gz -C /usr/local/bin/ --strip-components=1
curl -SLfs https://raw.githubusercontent.com/containerd/containerd/v1.3.2/containerd.service | sudo tee /etc/systemd/system/containerd.service
sudo systemctl daemon-reload && sudo systemctl start containerd
sudo curl -fSLs "https://github.com/genuinetools/netns/releases/download/v0.5.3/netns-linux-amd64" --output "/usr/local/bin/netns" && sudo chmod a+x "/usr/local/bin/netns"
sudo /sbin/sysctl -w net.ipv4.conf.all.forwarding=1
sudo mkdir -p /opt/cni/bin
curl -sSL https://github.com/containernetworking/plugins/releases/download/$(CNI_VERSION)/cni-plugins-linux-$(ARCH)-$(CNI_VERSION).tgz | sudo tar -xz -C /opt/cni/bin
sudo curl -sSLf "https://github.com/alexellis/faas-containerd/releases/download/$(FAASC_VER)/faas-containerd" --output "/usr/local/bin/faas-containerd" && sudo chmod a+x "/usr/local/bin/faas-containerd" || :
sudo cp $(GOPATH)/src/github.com/alexellis/faasd/bin/faasd /usr/local/bin/
cd $(GOPATH)/src/github.com/alexellis/faasd/ && sudo /usr/local/bin/faasd install
sudo systemctl status -l containerd --no-pager
sudo journalctl -u faas-containerd --no-pager
sudo systemctl status -l faas-containerd --no-pager
sudo systemctl status -l faasd --no-pager
curl -sSLf https://cli.openfaas.com | sudo sh
sleep 120 && sudo journalctl -u faasd --no-pager
.PHONY: test-e2e
test-e2e:
sudo cat /run/faasd/secrets/basic-auth-password | /usr/local/bin/faas-cli login --password-stdin
/usr/local/bin/faas-cli store deploy figlet --env write_timeout=1s --env read_timeout=1s
sleep 2
/usr/local/bin/faas-cli list -v
uname | /usr/local/bin/faas-cli invoke figlet
uname | /usr/local/bin/faas-cli invoke figlet --async
sleep 10
/usr/local/bin/faas-cli list -v
/usr/local/bin/faas-cli remove figlet
sleep 3
/usr/local/bin/faas-cli list

View File

@ -30,21 +30,30 @@ Other operations are pending development in the provider.
### Pre-reqs
* Linux - ideally Ubuntu, which is used for testing
* Linux
PC / Cloud - any Linux that containerd works on should be fair game, but faasd is tested with Ubuntu 18.04
For Raspberry Pi Raspbian Stretch or newer also works fine
For MacOS users try [multipass.run](https://multipass.run) or [Vagrant](https://www.vagrantup.com/)
For Windows users, install [Git Bash](https://git-scm.com/downloads) along with multipass or vagrant. You can also use WSL1 or WSL2 which provides a Linux environment.
* Installation steps as per [faas-containerd](https://github.com/alexellis/faas-containerd) for building and for development
* [netns](https://github.com/genuinetools/netns/releases) binary in `$PATH`
* [containerd v1.3.2](https://github.com/containerd/containerd)
* [CNI plugins v0.8.4](https://github.com/containernetworking/plugins)
* [faas-cli](https://github.com/openfaas/faas-cli) (optional)
## Backlog
Pending:
* [ ] Configure `basic_auth` to protect the OpenFaaS gateway and faas-containerd HTTP API
* [ ] Use CNI to create network namespaces and adapters
* [ ] Monitor and restart any of the core components at runtime if the container stops
* [ ] Bundle/package/automate installation of containerd - [see bootstrap from k3s](https://github.com/rancher/k3s)
* [ ] Provide ufw rules / example for blocking access to everything but a reverse proxy to the gateway container
* [ ] Provide [simple Caddyfile example](https://blog.alexellis.io/https-inlets-local-endpoints/) in the README showing how to expose the faasd proxy on port 80/443 with TLS
Done:
@ -55,9 +64,35 @@ Done:
* [x] Restart containers upon restart of faasd
* [x] Clear / remove containers and tasks with SIGTERM / SIGINT
* [x] Determine armhf/arm64 containers to run for gateway
* [x] Configure `basic_auth` to protect the OpenFaaS gateway and faas-containerd HTTP API
* [x] Setup custom working directory for faasd `/run/faasd/`
* [x] Use CNI to create network namespaces and adapters
## Tutorial: Get started on armhf / Raspberry Pi
You can run this tutorial on your Raspberry Pi, or adapt the steps for a regular Linux VM/VPS host.
* [faasd - lightweight Serverless for your Raspberry Pi](https://blog.alexellis.io/faasd-for-lightweight-serverless/)
## Hacking (build from source)
Install the CNI plugins:
```sh
export CNI_VERSION=v0.8.4
```
* For PC run `export ARCH=amd64`
* For RPi/armhf run `export ARCH=arm`
* For arm64 run `export ARCH=arm64`
Then run:
```sh
mkdir -p /opt/cni/bin
curl -sSL https://github.com/containernetworking/plugins/releases/download/${CNI_VERSION}/cni-plugins-linux-${ARCH}-${CNI_VERSION}.tgz | tar -xz -C /opt/cni/bin
```
First run faas-containerd
```sh
@ -86,62 +121,59 @@ go build
```sh
# For x86_64
sudo curl -fSLs "https://github.com/alexellis/faasd/releases/download/0.2.5/faasd" \
sudo curl -fSLs "https://github.com/alexellis/faasd/releases/download/0.4.4/faasd" \
-o "/usr/local/bin/faasd" \
&& sudo chmod a+x "/usr/local/bin/faasd"
# armhf
sudo curl -fSLs "https://github.com/alexellis/faasd/releases/download/0.2.5/faasd-armhf" \
sudo curl -fSLs "https://github.com/alexellis/faasd/releases/download/0.4.4/faasd-armhf" \
-o "/usr/local/bin/faasd" \
&& sudo chmod a+x "/usr/local/bin/faasd"
# arm64
sudo curl -fSLs "https://github.com/alexellis/faasd/releases/download/0.2.5/faasd-arm64" \
sudo curl -fSLs "https://github.com/alexellis/faasd/releases/download/0.4.4/faasd-arm64" \
-o "/usr/local/bin/faasd" \
&& sudo chmod a+x "/usr/local/bin/faasd"
```
### At run-time
Look in `hosts` in the current working folder to get the IP for the gateway or Prometheus
Look in `hosts` in the current working folder or in `/run/faasd/` to get the IP for the gateway or Prometheus
```sh
127.0.0.1 localhost
172.19.0.1 faas-containerd
172.19.0.2 prometheus
10.62.0.1 faas-containerd
172.19.0.3 gateway
172.19.0.4 nats
172.19.0.5 queue-worker
10.62.0.2 prometheus
10.62.0.3 gateway
10.62.0.4 nats
10.62.0.5 queue-worker
```
Since faas-containerd uses containerd heavily it is not running as a container, but as a stand-alone process. Its port is available via the bridge interface, i.e. netns0.
The IP addresses are dynamic and may change on every launch.
* Prometheus will run on the Prometheus IP plus port 8080 i.e. http://172.19.0.2:9090/targets
Since faas-containerd uses containerd heavily it is not running as a container, but as a stand-alone process. Its port is available via the bridge interface, i.e. openfaas0.
* faas-containerd runs on 172.19.0.1:8081
* Prometheus will run on the Prometheus IP plus port 8080 i.e. http://[prometheus_ip]:9090/targets
* Now go to the gateway's IP address as shown above on port 8080, i.e. http://172.19.0.3:8080 - you can also use this address to deploy OpenFaaS Functions via the `faas-cli`.
* faas-containerd runs on 10.62.0.1:8081
* Now go to the gateway's IP address as shown above on port 8080, i.e. http://[gateway_ip]:8080 - you can also use this address to deploy OpenFaaS Functions via the `faas-cli`.
* basic-auth
You will then need to get the basic-auth password, it is written to `/run/faasd/secrets/basic-auth-password` if you followed the above instructions.
The default Basic Auth username is `admin`, which is written to `/run/faasd/secrets/basic-auth-user`, if you wish to use a non-standard user then create this file and add your username (no newlines or other characters)
#### Installation with systemd
* `faasd install` - install faasd and containerd with systemd, run in `$GOPATH/src/github.com/alexellis/faasd`
* `faasd install` - install faasd and containerd with systemd, this must be run from `$GOPATH/src/github.com/alexellis/faasd`
* `journalctl -u faasd` - faasd systemd logs
* `journalctl -u faas-containerd` - faas-containerd systemd logs
### Appendix
Removing containers:
```sh
echo faas-containerd gateway prometheus | xargs sudo ctr task rm -f
echo faas-containerd gateway prometheus | xargs sudo ctr container rm
echo faas-containerd gateway prometheus | xargs sudo ctr snapshot rm
```
## Links
#### Links
https://github.com/renatofq/ctrofb/blob/31968e4b4893f3603e9998f21933c4131523bb5d/cmd/network.go

View File

@ -2,10 +2,12 @@ package cmd
import (
"fmt"
"io"
"os"
"path"
systemd "github.com/alexellis/faasd/pkg/systemd"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
@ -16,8 +18,31 @@ var installCmd = &cobra.Command{
RunE: runInstall,
}
const faasdwd = "/run/faasd"
const faasContainerdwd = "/run/faas-containerd"
func runInstall(_ *cobra.Command, _ []string) error {
if err := ensureWorkingDir(path.Join(faasdwd, "secrets")); err != nil {
return err
}
if err := ensureWorkingDir(faasContainerdwd); err != nil {
return err
}
if basicAuthErr := makeBasicAuthFiles(path.Join(faasdwd, "secrets")); basicAuthErr != nil {
return errors.Wrap(basicAuthErr, "cannot create basic-auth-* files")
}
if err := cp("prometheus.yml", faasdwd); err != nil {
return err
}
if err := cp("resolv.conf", faasdwd); err != nil {
return err
}
err := binExists("/usr/local/bin/", "faas-containerd")
if err != nil {
return err
@ -28,17 +53,15 @@ func runInstall(_ *cobra.Command, _ []string) error {
return err
}
err = binExists("/usr/local/bin/", "netns")
err = systemd.InstallUnit("faas-containerd", map[string]string{
"Cwd": faasContainerdwd,
"SecretMountPath": path.Join(faasdwd, "secrets")})
if err != nil {
return err
}
err = systemd.InstallUnit("faas-containerd")
if err != nil {
return err
}
err = systemd.InstallUnit("faasd")
err = systemd.InstallUnit("faasd", map[string]string{"Cwd": faasdwd})
if err != nil {
return err
}
@ -78,3 +101,33 @@ func binExists(folder, name string) error {
}
return nil
}
func ensureWorkingDir(folder string) error {
if _, err := os.Stat(folder); err != nil {
err = os.MkdirAll(folder, 0600)
if err != nil {
return err
}
}
return nil
}
func cp(source, destFolder string) error {
file, err := os.Open(source)
if err != nil {
return err
}
defer file.Close()
out, err := os.Create(path.Join(destFolder, source))
if err != nil {
return err
}
defer out.Close()
_, err = io.Copy(out, file)
return err
}

View File

@ -3,19 +3,10 @@ package cmd
import (
"fmt"
"github.com/alexellis/faasd/pkg"
"github.com/morikuni/aec"
"github.com/spf13/cobra"
)
var (
// Version as per git repo
Version string
// GitCommit as per git repo
GitCommit string
)
// WelcomeMessage to introduce ofc-bootstrap
const WelcomeMessage = "Welcome to faasd"
@ -25,39 +16,14 @@ func init() {
rootCommand.AddCommand(installCmd)
}
var rootCommand = &cobra.Command{
Use: "faasd",
Short: "Start faasd",
Long: `
faasd - serverless without Kubernetes
`,
RunE: runRootCommand,
SilenceUsage: true,
}
var versionCmd = &cobra.Command{
Use: "version",
Short: "Display version information.",
Run: parseBaseCommand,
}
func getVersion() string {
if len(Version) != 0 {
return Version
}
return "dev"
}
func parseBaseCommand(_ *cobra.Command, _ []string) {
printLogo()
fmt.Printf(
`faasd
Commit: %s
Version: %s
`, pkg.GitCommit, pkg.GetVersion())
}
var (
// GitCommit Git Commit SHA
GitCommit string
// Version version of the CLI
Version string
)
// Execute faasd
func Execute(version, gitCommit string) error {
// Get Version and GitCommit values from main.go.
@ -70,6 +36,16 @@ func Execute(version, gitCommit string) error {
return nil
}
var rootCommand = &cobra.Command{
Use: "faasd",
Short: "Start faasd",
Long: `
faasd - serverless without Kubernetes
`,
RunE: runRootCommand,
SilenceUsage: true,
}
func runRootCommand(cmd *cobra.Command, args []string) error {
printLogo()
@ -78,7 +54,39 @@ func runRootCommand(cmd *cobra.Command, args []string) error {
return nil
}
var versionCmd = &cobra.Command{
Use: "version",
Short: "Display version information.",
Run: parseBaseCommand,
}
func parseBaseCommand(_ *cobra.Command, _ []string) {
printLogo()
fmt.Printf(
`faasd
Commit: %s
Version: %s
`, GitCommit, GetVersion())
}
func printLogo() {
logoText := aec.WhiteF.Apply(pkg.Logo)
logoText := aec.WhiteF.Apply(Logo)
fmt.Println(logoText)
}
// GetVersion get latest version
func GetVersion() string {
if len(Version) == 0 {
return "dev"
}
return Version
}
// Logo for version and root command
const Logo = ` __ _
/ _| __ _ __ _ ___ __| |
| |_ / _` + "`" + ` |/ _` + "`" + ` / __|/ _` + "`" + ` |
| _| (_| | (_| \__ \ (_| |
|_| \__,_|\__,_|___/\__,_|
`

223
cmd/up.go
View File

@ -2,16 +2,22 @@ package cmd
import (
"fmt"
"io"
"io/ioutil"
"log"
"os"
"os/signal"
"path"
"strings"
"sync"
"syscall"
"time"
"github.com/pkg/errors"
"github.com/alexellis/faasd/pkg"
"github.com/alexellis/k3sup/pkg/env"
"github.com/sethvargo/go-password/password"
"github.com/spf13/cobra"
)
@ -21,6 +27,34 @@ var upCmd = &cobra.Command{
RunE: runUp,
}
// defaultCNIConf is a CNI configuration that enables network access to containers (docker-bridge style)
var defaultCNIConf = fmt.Sprintf(`
{
"cniVersion": "0.4.0",
"name": "%s",
"plugins": [
{
"type": "bridge",
"bridge": "%s",
"isGateway": true,
"ipMasq": true,
"ipam": {
"type": "host-local",
"subnet": "%s",
"routes": [
{ "dst": "0.0.0.0/0" }
]
}
},
{
"type": "firewall"
}
]
}
`, pkg.DefaultNetworkName, pkg.DefaultBridgeName, pkg.DefaultSubnet)
const secretMountDir = "/run/secrets"
func runUp(_ *cobra.Command, _ []string) error {
clientArch, clientOS := env.GetClientArch()
@ -42,6 +76,14 @@ func runUp(_ *cobra.Command, _ []string) error {
clientSuffix = "-arm64"
}
if basicAuthErr := makeBasicAuthFiles(path.Join(path.Join(faasdwd, "secrets"))); basicAuthErr != nil {
return errors.Wrap(basicAuthErr, "cannot create basic-auth-* files")
}
if makeNetworkErr := makeNetworkConfig(); makeNetworkErr != nil {
return errors.Wrap(makeNetworkErr, "error creating network config")
}
services := makeServiceDefinitions(clientSuffix)
start := time.Now()
@ -65,6 +107,8 @@ func runUp(_ *cobra.Command, _ []string) error {
log.Printf("Supervisor init done in: %s\n", time.Since(start).String())
shutdownTimeout := time.Second * 1
timeout := time.Second * 60
proxyDoneCh := make(chan bool)
wg := sync.WaitGroup{}
wg.Add(1)
@ -80,19 +124,107 @@ func runUp(_ *cobra.Command, _ []string) error {
if err != nil {
fmt.Println(err)
}
// Close proxy
proxyDoneCh <- true
time.AfterFunc(shutdownTimeout, func() {
wg.Done()
})
}()
gatewayURLChan := make(chan string, 1)
proxyPort := 8080
proxy := pkg.NewProxy(proxyPort, timeout)
go proxy.Start(gatewayURLChan, proxyDoneCh)
go func() {
wd, _ := os.Getwd()
time.Sleep(3 * time.Second)
fileData, fileErr := ioutil.ReadFile(path.Join(wd, "hosts"))
if fileErr != nil {
log.Println(fileErr)
return
}
host := ""
lines := strings.Split(string(fileData), "\n")
for _, line := range lines {
if strings.Index(line, "gateway") > -1 {
host = line[:strings.Index(line, "\t")]
}
}
log.Printf("[up] Sending %s to proxy\n", host)
gatewayURLChan <- host + ":8080"
close(gatewayURLChan)
}()
wg.Wait()
return nil
}
func makeBasicAuthFiles(wd string) error {
pwdFile := wd + "/basic-auth-password"
authPassword, err := password.Generate(63, 10, 0, false, true)
if err != nil {
return err
}
err = makeFile(pwdFile, authPassword)
if err != nil {
return err
}
userFile := wd + "/basic-auth-user"
err = makeFile(userFile, "admin")
if err != nil {
return err
}
return nil
}
func makeFile(filePath, fileContents string) error {
_, err := os.Stat(filePath)
if err == nil {
log.Printf("File exists: %q\n", filePath)
return nil
} else if os.IsNotExist(err) {
log.Printf("Writing to: %q\n", filePath)
return ioutil.WriteFile(filePath, []byte(fileContents), 0644)
} else {
return err
}
}
func makeServiceDefinitions(archSuffix string) []pkg.Service {
wd, _ := os.Getwd()
return []pkg.Service{
pkg.Service{
Name: "basic-auth-plugin",
Image: "docker.io/openfaas/basic-auth-plugin:0.18.10" + archSuffix,
Env: []string{
"port=8080",
"secret_mount_path=" + secretMountDir,
"user_filename=basic-auth-user",
"pass_filename=basic-auth-password",
},
Mounts: []pkg.Mount{
pkg.Mount{
Src: path.Join(path.Join(wd, "secrets"), "basic-auth-password"),
Dest: path.Join(secretMountDir, "basic-auth-password"),
},
pkg.Mount{
Src: path.Join(path.Join(wd, "secrets"), "basic-auth-user"),
Dest: path.Join(secretMountDir, "basic-auth-user"),
},
},
Caps: []string{"CAP_NET_RAW"},
Args: nil,
},
pkg.Service{
Name: "nats",
Env: []string{""},
@ -115,7 +247,7 @@ func makeServiceDefinitions(archSuffix string) []pkg.Service {
pkg.Service{
Name: "gateway",
Env: []string{
"basic_auth=false",
"basic_auth=true",
"functions_provider_url=http://faas-containerd:8081/",
"direct_functions=false",
"read_timeout=60s",
@ -123,10 +255,23 @@ func makeServiceDefinitions(archSuffix string) []pkg.Service {
"upstream_timeout=65s",
"faas_nats_address=nats",
"faas_nats_port=4222",
"auth_proxy_url=http://basic-auth-plugin:8080/validate",
"auth_proxy_pass_body=false",
"secret_mount_path=" + secretMountDir,
"scale_from_zero=true",
},
Image: "docker.io/openfaas/gateway:0.18.8" + archSuffix,
Mounts: []pkg.Mount{},
Caps: []string{"CAP_NET_RAW"},
Image: "docker.io/openfaas/gateway:0.18.8" + archSuffix,
Mounts: []pkg.Mount{
pkg.Mount{
Src: path.Join(path.Join(wd, "secrets"), "basic-auth-password"),
Dest: path.Join(secretMountDir, "basic-auth-password"),
},
pkg.Mount{
Src: path.Join(path.Join(wd, "secrets"), "basic-auth-user"),
Dest: path.Join(secretMountDir, "basic-auth-user"),
},
},
Caps: []string{"CAP_NET_RAW"},
},
pkg.Service{
Name: "queue-worker",
@ -138,10 +283,74 @@ func makeServiceDefinitions(archSuffix string) []pkg.Service {
"ack_wait=5m5s",
"max_inflight=1",
"write_debug=false",
"basic_auth=true",
"secret_mount_path=" + secretMountDir,
},
Image: "docker.io/openfaas/queue-worker:0.9.0",
Mounts: []pkg.Mount{},
Caps: []string{"CAP_NET_RAW"},
Image: "docker.io/openfaas/queue-worker:0.9.0",
Mounts: []pkg.Mount{
pkg.Mount{
Src: path.Join(path.Join(wd, "secrets"), "basic-auth-password"),
Dest: path.Join(secretMountDir, "basic-auth-password"),
},
pkg.Mount{
Src: path.Join(path.Join(wd, "secrets"), "basic-auth-user"),
Dest: path.Join(secretMountDir, "basic-auth-user"),
},
},
Caps: []string{"CAP_NET_RAW"},
},
}
}
func makeNetworkConfig() error {
netConfig := path.Join(pkg.CNIConfDir, pkg.DefaultCNIConfFilename)
log.Printf("Writing network config...\n")
if !dirExists(pkg.CNIConfDir) {
if err := os.MkdirAll(pkg.CNIConfDir, 0755); err != nil {
return fmt.Errorf("cannot create directory: %s", pkg.CNIConfDir)
}
}
if err := ioutil.WriteFile(netConfig, []byte(defaultCNIConf), 644); err != nil {
return fmt.Errorf("cannot write network config: %s", pkg.DefaultCNIConfFilename)
}
return nil
}
func dirEmpty(dirname string) (b bool) {
if !dirExists(dirname) {
return
}
f, err := os.Open(dirname)
if err != nil {
return
}
defer func() { _ = f.Close() }()
// If the first file is EOF, the directory is empty
if _, err = f.Readdir(1); err == io.EOF {
b = true
}
return
}
func dirExists(dirname string) bool {
exists, info := pathExists(dirname)
if !exists {
return false
}
return info.IsDir()
}
func pathExists(path string) (bool, os.FileInfo) {
info, err := os.Stat(path)
if os.IsNotExist(err) {
return false, nil
}
return true, info
}

View File

@ -1,11 +1,12 @@
#!/bin/bash
export ARCH="armv6l"
echo "Downloading Go"
curl -SLsf https://dl.google.com/go/go1.12.14.linux-armv6l.tar.gz > go.tgz
curl -SLsf https://dl.google.com/go/go1.12.14.linux-$ARCH.tar.gz --output /tmp/go.tgz
sudo rm -rf /usr/local/go/
sudo mkdir -p /usr/local/go/
sudo tar -xvf go.tgz -C /usr/local/go/ --strip-components=1
sudo tar -xvf /tmp/go.tgz -C /usr/local/go/ --strip-components=1
export GOPATH=$HOME/go/
export PATH=$PATH:/usr/local/go/bin/

View File

@ -1,11 +1,12 @@
#!/bin/bash
export ARCH="amd64"
echo "Downloading Go"
curl -SLsf https://dl.google.com/go/go1.12.14.linux-amd64.tar.gz > go.tgz
curl -SLsf https://dl.google.com/go/go1.12.14.linux-$ARCH.tar.gz --output /tmp/go.tgz
sudo rm -rf /usr/local/go/
sudo mkdir -p /usr/local/go/
sudo tar -xvf go.tgz -C /usr/local/go/ --strip-components=1
sudo tar -xvf /tmp/go.tgz -C /usr/local/go/ --strip-components=1
export GOPATH=$HOME/go/
export PATH=$PATH:/usr/local/go/bin/

View File

@ -3,10 +3,12 @@ Description=faasd-containerd
[Service]
MemoryLimit=500M
Environment="secret_mount_path={{.SecretMountPath}}"
Environment="basic_auth=true"
ExecStart=/usr/local/bin/faas-containerd
Restart=on-failure
RestartSec=10s
WorkingDirectory=/usr/local/bin/
WorkingDirectory={{.Cwd}}
[Install]
WantedBy=multi-user.target

12
main.go
View File

@ -4,13 +4,19 @@ import (
"os"
"github.com/alexellis/faasd/cmd"
"github.com/alexellis/faasd/pkg"
)
// These values will be injected into these variables at the build time.
var (
// GitCommit Git Commit SHA
GitCommit string
// Version version of the CLI
Version string
)
func main() {
if err := cmd.Execute(pkg.Version, pkg.GitCommit); err != nil {
if err := cmd.Execute(Version, GitCommit); err != nil {
os.Exit(1)
}
return
}

122
pkg/proxy.go Normal file
View File

@ -0,0 +1,122 @@
package pkg
import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"time"
)
func NewProxy(port int, timeout time.Duration) *Proxy {
return &Proxy{
Port: port,
Timeout: timeout,
}
}
type Proxy struct {
Timeout time.Duration
Port int
}
func (p *Proxy) Start(gatewayChan chan string, done chan bool) error {
tcp := p.Port
http.DefaultClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}
ps := proxyState{
Host: "",
}
ps.Host = <-gatewayChan
log.Printf("Starting faasd proxy on %d\n", tcp)
fmt.Printf("Gateway: %s\n", ps.Host)
s := &http.Server{
Addr: fmt.Sprintf(":%d", tcp),
ReadTimeout: p.Timeout,
WriteTimeout: p.Timeout,
MaxHeaderBytes: 1 << 20, // Max header of 1MB
Handler: http.HandlerFunc(makeProxy(&ps)),
}
go func() {
log.Printf("[proxy] Begin listen on %d\n", p.Port)
if err := s.ListenAndServe(); err != http.ErrServerClosed {
log.Printf("Error ListenAndServe: %v", err)
}
}()
log.Println("[proxy] Wait for done")
<-done
log.Println("[proxy] Done received")
if err := s.Shutdown(context.Background()); err != nil {
log.Printf("[proxy] Error in Shutdown: %v", err)
}
return nil
}
// copyHeaders clones the header values from the source into the destination.
func copyHeaders(destination http.Header, source *http.Header) {
for k, v := range *source {
vClone := make([]string, len(v))
copy(vClone, v)
destination[k] = vClone
}
}
type proxyState struct {
Host string
}
func makeProxy(ps *proxyState) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
query := ""
if len(r.URL.RawQuery) > 0 {
query = "?" + r.URL.RawQuery
}
upstream := fmt.Sprintf("http://%s%s%s", ps.Host, r.URL.Path, query)
fmt.Printf("[faasd] proxy: %s\n", upstream)
if r.Body != nil {
defer r.Body.Close()
}
wrapper := ioutil.NopCloser(r.Body)
upReq, upErr := http.NewRequest(r.Method, upstream, wrapper)
copyHeaders(upReq.Header, &r.Header)
if upErr != nil {
log.Println(upErr)
http.Error(w, upErr.Error(), http.StatusInternalServerError)
return
}
upRes, upResErr := http.DefaultClient.Do(upReq)
if upResErr != nil {
log.Println(upResErr)
http.Error(w, upResErr.Error(), http.StatusInternalServerError)
return
}
copyHeaders(w.Header(), &upRes.Header)
w.WriteHeader(upRes.StatusCode)
io.Copy(w, upRes.Body)
}
}

73
pkg/proxy_test.go Normal file
View File

@ -0,0 +1,73 @@
package pkg
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"net/url"
"sync"
"testing"
"time"
)
func Test_Proxy_ToPrivateServer(t *testing.T) {
wantBodyText := "OK"
wantBody := []byte(wantBodyText)
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Body != nil {
defer r.Body.Close()
}
w.WriteHeader(http.StatusOK)
w.Write(wantBody)
}))
defer upstream.Close()
port := 8080
proxy := NewProxy(port, time.Second*1)
gwChan := make(chan string, 1)
doneCh := make(chan bool)
go proxy.Start(gwChan, doneCh)
u, _ := url.Parse(upstream.URL)
log.Println("Host", u.Host)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
gwChan <- u.Host
wg.Done()
}()
wg.Wait()
req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://127.0.0.1:%d", port), nil)
if err != nil {
t.Fatal(err)
}
for i := 1; i < 11; i++ {
res, err := http.DefaultClient.Do(req)
if err != nil {
t.Logf("Try %d, gave error: %s", i, err)
time.Sleep(time.Millisecond * 100)
} else {
resBody, _ := ioutil.ReadAll(res.Body)
if string(resBody) != string(wantBody) {
t.Errorf("want %s, but got %s in body", string(wantBody), string(resBody))
}
break
}
}
go func() {
doneCh <- true
}()
}

View File

@ -5,15 +5,19 @@ import (
"fmt"
"io/ioutil"
"log"
"net"
"os"
"os/exec"
"path"
"path/filepath"
"github.com/alexellis/faasd/pkg/service"
"github.com/alexellis/faasd/pkg/weave"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cio"
"github.com/containerd/containerd/containers"
gocni "github.com/containerd/go-cni"
"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/oci"
@ -22,6 +26,25 @@ import (
const defaultSnapshotter = "overlayfs"
const (
// TODO: CNIBinDir and CNIConfDir should maybe be globally configurable?
// CNIBinDir describes the directory where the CNI binaries are stored
CNIBinDir = "/opt/cni/bin"
// CNIConfDir describes the directory where the CNI plugin's configuration is stored
CNIConfDir = "/etc/cni/net.d"
// netNSPathFmt gives the path to the a process network namespace, given the pid
NetNSPathFmt = "/proc/%d/ns/net"
// defaultCNIConfFilename is the vanity filename of default CNI configuration file
DefaultCNIConfFilename = "10-openfaas.conflist"
// defaultNetworkName names the "docker-bridge"-like CNI plugin-chain installed when no other CNI configuration is present.
// This value appears in iptables comments created by CNI.
DefaultNetworkName = "openfaas-cni-bridge"
// defaultBridgeName is the default bridge device name used in the defaultCNIConf
DefaultBridgeName = "openfaas0"
// defaultSubnet is the default subnet used in the defaultCNIConf -- this value is set to not collide with common container networking subnets:
DefaultSubnet = "10.62.0.0/16"
)
type Supervisor struct {
client *containerd.Client
}
@ -58,9 +81,16 @@ func (s *Supervisor) Start(svcs []Service) error {
wd, _ := os.Getwd()
ip, _, _ := net.ParseCIDR(DefaultSubnet)
ip = ip.To4()
ip[3] = 1
ip.String()
hosts := fmt.Sprintf(`
127.0.0.1 localhost
%s faas-containerd`, ip)
writeHostsErr := ioutil.WriteFile(path.Join(wd, "hosts"),
[]byte(`127.0.0.1 localhost
172.19.0.1 faas-containerd`), 0644)
[]byte(hosts), 0644)
if writeHostsErr != nil {
return fmt.Errorf("cannot write hosts file: %s", writeHostsErr)
@ -118,27 +148,6 @@ func (s *Supervisor) Start(svcs []Service) error {
Options: []string{"rbind", "ro"},
})
hook := func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error {
if s.Hooks == nil {
s.Hooks = &specs.Hooks{}
}
netnsPath, err := exec.LookPath("netns")
if err != nil {
return err
}
s.Hooks.Prestart = []specs.Hook{
{
Path: netnsPath,
Args: []string{
"netns",
},
Env: os.Environ(),
},
}
return nil
}
newContainer, containerCreateErr := s.client.NewContainer(
ctx,
svc.Name,
@ -148,12 +157,11 @@ func (s *Supervisor) Start(svcs []Service) error {
oci.WithCapabilities(svc.Caps),
oci.WithMounts(mounts),
withOCIArgs(svc.Args),
hook,
oci.WithEnv(svc.Env)),
)
if containerCreateErr != nil {
log.Println(containerCreateErr)
log.Printf("Error creating container %s\n", containerCreateErr)
return containerCreateErr
}
@ -161,11 +169,37 @@ func (s *Supervisor) Start(svcs []Service) error {
task, err := newContainer.NewTask(ctx, cio.NewCreator(cio.WithStdio))
if err != nil {
log.Println(err)
log.Printf("Error creating task: %s\n", err)
return err
}
id := uuid.New().String()
netns := fmt.Sprintf(NetNSPathFmt, task.Pid())
cni, err := gocni.New(gocni.WithPluginConfDir(CNIConfDir),
gocni.WithPluginDir([]string{CNIBinDir}))
if err != nil {
return errors.Wrapf(err, "error creating CNI instance")
}
// Load the cni configuration
if err := cni.Load(gocni.WithLoNetwork, gocni.WithConfListFile(filepath.Join(CNIConfDir, DefaultCNIConfFilename))); err != nil {
return errors.Wrapf(err, "failed to load cni configuration: %v", err)
}
labels := map[string]string{}
_, err = cni.Setup(ctx, id, netns, gocni.WithLabels(labels))
if err != nil {
return errors.Wrapf(err, "failed to setup network for namespace %q: %v", id, err)
}
// Get the IP of the default interface.
// defaultInterface := gocni.DefaultPrefix + "0"
// ip := &result.Interfaces[defaultInterface].IPConfigs[0].IP
ip := getIP(newContainer.ID(), task.Pid())
log.Printf("%s has IP: %s\n", newContainer.ID(), ip)
hosts, _ := ioutil.ReadFile("hosts")
@ -175,20 +209,21 @@ func (s *Supervisor) Start(svcs []Service) error {
writeErr := ioutil.WriteFile("hosts", hosts, 0644)
if writeErr != nil {
log.Println("Error writing hosts file")
log.Printf("Error writing file %s %s\n", "hosts", writeErr)
}
// os.Chown("hosts", 101, 101)
exitStatusC, err := task.Wait(ctx)
_, err = task.Wait(ctx)
if err != nil {
log.Println(err)
log.Printf("Wait err: %s\n", err)
return err
}
log.Println("Exited: ", exitStatusC)
// call start on the task to execute the redis server
if err := task.Start(ctx); err != nil {
log.Println("Task err: ", err)
log.Printf("Task: %s\tContainer: %s\n", task.ID(), newContainer.ID())
// log.Println("Exited: ", exitStatusC)
if err = task.Start(ctx); err != nil {
log.Printf("Task err: %s\n", err)
return err
}
}
@ -199,7 +234,7 @@ func (s *Supervisor) Start(svcs []Service) error {
func getIP(containerID string, taskPID uint32) string {
// https://github.com/weaveworks/weave/blob/master/net/netdev.go
peerIDs, err := weave.ConnectedToBridgeVethPeerIds("netns0")
peerIDs, err := weave.ConnectedToBridgeVethPeerIds(DefaultBridgeName)
if err != nil {
log.Fatal(err)
}

View File

@ -64,7 +64,11 @@ func DaemonReload() error {
return nil
}
func InstallUnit(name string) error {
func InstallUnit(name string, tokens map[string]string) error {
if len(tokens["Cwd"]) == 0 {
return fmt.Errorf("key Cwd expected in tokens parameter")
}
tmplName := "./hack/" + name + ".service"
tmpl, err := template.ParseFiles(tmplName)
@ -72,15 +76,9 @@ func InstallUnit(name string) error {
return fmt.Errorf("error loading template %s, error %s", tmplName, err)
}
wd, _ := os.Getwd()
var tpl bytes.Buffer
userData := struct {
Cwd string
}{
Cwd: wd,
}
err = tmpl.Execute(&tpl, userData)
err = tmpl.Execute(&tpl, tokens)
if err != nil {
return err
}

View File

@ -1,23 +1 @@
package pkg
var (
//GitCommit Git Commit SHA
GitCommit string
//Version version of the CLI
Version string
)
//GetVersion get latest version
func GetVersion() string {
if len(Version) == 0 {
return "dev"
}
return Version
}
const Logo = ` __ _
/ _| __ _ __ _ ___ __| |
| |_ / _` + "`" + ` |/ _` + "`" + ` / __|/ _` + "`" + ` |
| _| (_| | (_| \__ \ (_| |
|_| \__,_|\__,_|___/\__,_|
`

23
vendor/github.com/containerd/go-cni/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,23 @@
language: go
go:
- 1.12.x
- tip
go_import_path: github.com/containerd/go-cni
install:
- go get -d
- env GO111MODULE=off go get -u github.com/vbatts/git-validation
- env GO111MODULE=off go get -u github.com/kunalkushwaha/ltag
before_script:
- pushd ..; git clone https://github.com/containerd/project; popd
script:
- DCO_VERBOSITY=-q ../project/script/validate/dco
- ../project/script/validate/fileheader ../project/
- env GO111MODULE=on ../project/script/validate/vendor
- go test -race -coverprofile=coverage.txt -covermode=atomic
after_success:
- bash <(curl -s https://codecov.io/bash)

201
vendor/github.com/containerd/go-cni/LICENSE generated vendored Normal file
View File

@ -0,0 +1,201 @@
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.

60
vendor/github.com/containerd/go-cni/README.md generated vendored Normal file
View File

@ -0,0 +1,60 @@
[![Build Status](https://travis-ci.org/containerd/go-cni.svg?branch=master)](https://travis-ci.org/containerd/go-cni) [![GoDoc](https://godoc.org/github.com/containerd/go-cni?status.svg)](https://godoc.org/github.com/containerd/go-cni)
# go-cni
A generic CNI library to provide APIs for CNI plugin interactions. The library provides APIs to:
- Load CNI network config from different sources
- Setup networks for container namespace
- Remove networks from container namespace
- Query status of CNI network plugin initialization
go-cni aims to support plugins that implement [Container Network Interface](https://github.com/containernetworking/cni)
## Usage
```go
func main() {
id := "123456"
netns := "/proc/9999/ns/net"
defaultIfName := "eth0"
// Initialize library
l = gocni.New(gocni.WithMinNetworkCount(2),
gocni.WithPluginConfDir("/etc/mycni/net.d"),
gocni.WithPluginDir([]string{"/opt/mycni/bin", "/opt/cni/bin"}),
gocni.WithDefaultIfName(defaultIfName))
// Load the cni configuration
err:= l.Load(gocni.WithLoNetwork, gocni.WithDefaultConf)
if err != nil{
log.Errorf("failed to load cni configuration: %v", err)
return
}
// Setup network for namespace.
labels := map[string]string{
"K8S_POD_NAMESPACE": "namespace1",
"K8S_POD_NAME": "pod1",
"K8S_POD_INFRA_CONTAINER_ID": id,
}
result, err := l.Setup(id, netns, gocni.WithLabels(labels))
if err != nil {
log.Errorf("failed to setup network for namespace %q: %v",id, err)
return
}
// Get IP of the default interface
IP := result.Interfaces[defaultIfName].IPConfigs[0].IP.String()
fmt.Printf("IP of the default interface %s:%s", defaultIfName, IP)
}
```
## Project details
The go-cni is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
As a containerd sub-project, you will find the:
* [Project governance](https://github.com/containerd/project/blob/master/GOVERNANCE.md),
* [Maintainers](https://github.com/containerd/project/blob/master/MAINTAINERS),
* and [Contributing guidelines](https://github.com/containerd/project/blob/master/CONTRIBUTING.md)
information in our [`containerd/project`](https://github.com/containerd/project) repository.

220
vendor/github.com/containerd/go-cni/cni.go generated vendored Normal file
View File

@ -0,0 +1,220 @@
/*
Copyright The containerd 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 cni
import (
"context"
"fmt"
"strings"
"sync"
cnilibrary "github.com/containernetworking/cni/libcni"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/pkg/errors"
)
type CNI interface {
// Setup setup the network for the namespace
Setup(ctx context.Context, id string, path string, opts ...NamespaceOpts) (*CNIResult, error)
// Remove tears down the network of the namespace.
Remove(ctx context.Context, id string, path string, opts ...NamespaceOpts) error
// Load loads the cni network config
Load(opts ...CNIOpt) error
// Status checks the status of the cni initialization
Status() error
// GetConfig returns a copy of the CNI plugin configurations as parsed by CNI
GetConfig() *ConfigResult
}
type ConfigResult struct {
PluginDirs []string
PluginConfDir string
PluginMaxConfNum int
Prefix string
Networks []*ConfNetwork
}
type ConfNetwork struct {
Config *NetworkConfList
IFName string
}
// NetworkConfList is a source bytes to string version of cnilibrary.NetworkConfigList
type NetworkConfList struct {
Name string
CNIVersion string
Plugins []*NetworkConf
Source string
}
// NetworkConf is a source bytes to string conversion of cnilibrary.NetworkConfig
type NetworkConf struct {
Network *types.NetConf
Source string
}
type libcni struct {
config
cniConfig cnilibrary.CNI
networkCount int // minimum network plugin configurations needed to initialize cni
networks []*Network
sync.RWMutex
}
func defaultCNIConfig() *libcni {
return &libcni{
config: config{
pluginDirs: []string{DefaultCNIDir},
pluginConfDir: DefaultNetDir,
pluginMaxConfNum: DefaultMaxConfNum,
prefix: DefaultPrefix,
},
cniConfig: &cnilibrary.CNIConfig{
Path: []string{DefaultCNIDir},
},
networkCount: 1,
}
}
// New creates a new libcni instance.
func New(config ...CNIOpt) (CNI, error) {
cni := defaultCNIConfig()
var err error
for _, c := range config {
if err = c(cni); err != nil {
return nil, err
}
}
return cni, nil
}
// Load loads the latest config from cni config files.
func (c *libcni) Load(opts ...CNIOpt) error {
var err error
c.Lock()
defer c.Unlock()
// Reset the networks on a load operation to ensure
// config happens on a clean slate
c.reset()
for _, o := range opts {
if err = o(c); err != nil {
return errors.Wrapf(ErrLoad, fmt.Sprintf("cni config load failed: %v", err))
}
}
return nil
}
// Status returns the status of CNI initialization.
func (c *libcni) Status() error {
c.RLock()
defer c.RUnlock()
if len(c.networks) < c.networkCount {
return ErrCNINotInitialized
}
return nil
}
// Networks returns all the configured networks.
// NOTE: Caller MUST NOT modify anything in the returned array.
func (c *libcni) Networks() []*Network {
c.RLock()
defer c.RUnlock()
return append([]*Network{}, c.networks...)
}
// Setup setups the network in the namespace
func (c *libcni) Setup(ctx context.Context, id string, path string, opts ...NamespaceOpts) (*CNIResult, error) {
if err := c.Status(); err != nil {
return nil, err
}
ns, err := newNamespace(id, path, opts...)
if err != nil {
return nil, err
}
var results []*current.Result
for _, network := range c.Networks() {
r, err := network.Attach(ctx, ns)
if err != nil {
return nil, err
}
results = append(results, r)
}
return c.GetCNIResultFromResults(results)
}
// Remove removes the network config from the namespace
func (c *libcni) Remove(ctx context.Context, id string, path string, opts ...NamespaceOpts) error {
if err := c.Status(); err != nil {
return err
}
ns, err := newNamespace(id, path, opts...)
if err != nil {
return err
}
for _, network := range c.Networks() {
if err := network.Remove(ctx, ns); err != nil {
// Based on CNI spec v0.7.0, empty network namespace is allowed to
// do best effort cleanup. However, it is not handled consistently
// right now:
// https://github.com/containernetworking/plugins/issues/210
// TODO(random-liu): Remove the error handling when the issue is
// fixed and the CNI spec v0.6.0 support is deprecated.
if path == "" && strings.Contains(err.Error(), "no such file or directory") {
continue
}
return err
}
}
return nil
}
// GetConfig returns a copy of the CNI plugin configurations as parsed by CNI
func (c *libcni) GetConfig() *ConfigResult {
c.RLock()
defer c.RUnlock()
r := &ConfigResult{
PluginDirs: c.config.pluginDirs,
PluginConfDir: c.config.pluginConfDir,
PluginMaxConfNum: c.config.pluginMaxConfNum,
Prefix: c.config.prefix,
}
for _, network := range c.networks {
conf := &NetworkConfList{
Name: network.config.Name,
CNIVersion: network.config.CNIVersion,
Source: string(network.config.Bytes),
}
for _, plugin := range network.config.Plugins {
conf.Plugins = append(conf.Plugins, &NetworkConf{
Network: plugin.Network,
Source: string(plugin.Bytes),
})
}
r.Networks = append(r.Networks, &ConfNetwork{
Config: conf,
IFName: network.ifName,
})
}
return r
}
func (c *libcni) reset() {
c.networks = nil
}

55
vendor/github.com/containerd/go-cni/errors.go generated vendored Normal file
View File

@ -0,0 +1,55 @@
/*
Copyright The containerd 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 cni
import (
"github.com/pkg/errors"
)
var (
ErrCNINotInitialized = errors.New("cni plugin not initialized")
ErrInvalidConfig = errors.New("invalid cni config")
ErrNotFound = errors.New("not found")
ErrRead = errors.New("failed to read config file")
ErrInvalidResult = errors.New("invalid result")
ErrLoad = errors.New("failed to load cni config")
)
// IsCNINotInitialized returns true if the error is due to cni config not being initialized
func IsCNINotInitialized(err error) bool {
return errors.Cause(err) == ErrCNINotInitialized
}
// IsInvalidConfig returns true if the error is invalid cni config
func IsInvalidConfig(err error) bool {
return errors.Cause(err) == ErrInvalidConfig
}
// IsNotFound returns true if the error is due to a missing config or result
func IsNotFound(err error) bool {
return errors.Cause(err) == ErrNotFound
}
// IsReadFailure return true if the error is a config read failure
func IsReadFailure(err error) bool {
return errors.Cause(err) == ErrRead
}
// IsInvalidResult return true if the error is due to invalid cni result
func IsInvalidResult(err error) bool {
return errors.Cause(err) == ErrInvalidResult
}

14
vendor/github.com/containerd/go-cni/go.mod generated vendored Normal file
View File

@ -0,0 +1,14 @@
module github.com/containerd/go-cni
require (
github.com/containernetworking/cni v0.7.1
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/onsi/ginkgo v1.10.3 // indirect
github.com/onsi/gomega v1.7.1 // indirect
github.com/pkg/errors v0.8.0
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f // indirect
github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d
)
go 1.12

39
vendor/github.com/containerd/go-cni/go.sum generated vendored Normal file
View File

@ -0,0 +1,39 @@
github.com/containernetworking/cni v0.7.1 h1:fE3r16wpSEyaqY4Z4oFrLMmIGfBYIKpPrHK31EJ9FzE=
github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.3 h1:OoxbjfXVZyod1fmWYhI7SEyaD8B00ynP3T+D5GiyHOY=
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f h1:SrOsK2rwonEK9IsdNEU61zcTdKW68/PuV9wuHHpqngk=
github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d h1:YCdGqZILKLGzbyEYbdau30JBEXbKaKYmkBDU5JUW3D0=
github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

41
vendor/github.com/containerd/go-cni/helper.go generated vendored Normal file
View File

@ -0,0 +1,41 @@
/*
Copyright The containerd 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 cni
import (
"fmt"
"github.com/containernetworking/cni/pkg/types/current"
)
func validateInterfaceConfig(ipConf *current.IPConfig, ifs int) error {
if ipConf == nil {
return fmt.Errorf("invalid IP configuration (nil)")
}
if ipConf.Interface != nil && *ipConf.Interface > ifs {
return fmt.Errorf("invalid IP configuration (interface number %d is > number of interfaces %d)", *ipConf.Interface, ifs)
}
return nil
}
func getIfName(prefix string, i int) string {
return fmt.Sprintf("%s%d", prefix, i)
}
func defaultInterface(prefix string) string {
return getIfName(prefix, 0)
}

77
vendor/github.com/containerd/go-cni/namespace.go generated vendored Normal file
View File

@ -0,0 +1,77 @@
/*
Copyright The containerd 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 cni
import (
"context"
cnilibrary "github.com/containernetworking/cni/libcni"
"github.com/containernetworking/cni/pkg/types/current"
)
type Network struct {
cni cnilibrary.CNI
config *cnilibrary.NetworkConfigList
ifName string
}
func (n *Network) Attach(ctx context.Context, ns *Namespace) (*current.Result, error) {
r, err := n.cni.AddNetworkList(ctx, n.config, ns.config(n.ifName))
if err != nil {
return nil, err
}
return current.NewResultFromResult(r)
}
func (n *Network) Remove(ctx context.Context, ns *Namespace) error {
return n.cni.DelNetworkList(ctx, n.config, ns.config(n.ifName))
}
type Namespace struct {
id string
path string
capabilityArgs map[string]interface{}
args map[string]string
}
func newNamespace(id, path string, opts ...NamespaceOpts) (*Namespace, error) {
ns := &Namespace{
id: id,
path: path,
capabilityArgs: make(map[string]interface{}),
args: make(map[string]string),
}
for _, o := range opts {
if err := o(ns); err != nil {
return nil, err
}
}
return ns, nil
}
func (ns *Namespace) config(ifName string) *cnilibrary.RuntimeConf {
c := &cnilibrary.RuntimeConf{
ContainerID: ns.id,
NetNS: ns.path,
IfName: ifName,
}
for k, v := range ns.args {
c.Args = append(c.Args, [2]string{k, v})
}
c.CapabilityArgs = ns.capabilityArgs
return c
}

75
vendor/github.com/containerd/go-cni/namespace_opts.go generated vendored Normal file
View File

@ -0,0 +1,75 @@
/*
Copyright The containerd 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 cni
type NamespaceOpts func(s *Namespace) error
// Capabilities
func WithCapabilityPortMap(portMapping []PortMapping) NamespaceOpts {
return func(c *Namespace) error {
c.capabilityArgs["portMappings"] = portMapping
return nil
}
}
func WithCapabilityIPRanges(ipRanges []IPRanges) NamespaceOpts {
return func(c *Namespace) error {
c.capabilityArgs["ipRanges"] = ipRanges
return nil
}
}
// WithCapabilityBandWitdh adds support for traffic shaping:
// https://github.com/heptio/cni-plugins/tree/master/plugins/meta/bandwidth
func WithCapabilityBandWidth(bandWidth BandWidth) NamespaceOpts {
return func(c *Namespace) error {
c.capabilityArgs["bandwidth"] = bandWidth
return nil
}
}
// WithCapabilityDNS adds support for dns
func WithCapabilityDNS(dns DNS) NamespaceOpts {
return func(c *Namespace) error {
c.capabilityArgs["dns"] = dns
return nil
}
}
func WithCapability(name string, capability interface{}) NamespaceOpts {
return func(c *Namespace) error {
c.capabilityArgs[name] = capability
return nil
}
}
// Args
func WithLabels(labels map[string]string) NamespaceOpts {
return func(c *Namespace) error {
for k, v := range labels {
c.args[k] = v
}
return nil
}
}
func WithArgs(k, v string) NamespaceOpts {
return func(c *Namespace) error {
c.args[k] = v
return nil
}
}

263
vendor/github.com/containerd/go-cni/opts.go generated vendored Normal file
View File

@ -0,0 +1,263 @@
/*
Copyright The containerd 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 cni
import (
"sort"
"strings"
cnilibrary "github.com/containernetworking/cni/libcni"
"github.com/pkg/errors"
)
type CNIOpt func(c *libcni) error
// WithInterfacePrefix sets the prefix for network interfaces
// e.g. eth or wlan
func WithInterfacePrefix(prefix string) CNIOpt {
return func(c *libcni) error {
c.prefix = prefix
return nil
}
}
// WithPluginDir can be used to set the locations of
// the cni plugin binaries
func WithPluginDir(dirs []string) CNIOpt {
return func(c *libcni) error {
c.pluginDirs = dirs
c.cniConfig = &cnilibrary.CNIConfig{Path: dirs}
return nil
}
}
// WithPluginConfDir can be used to configure the
// cni configuration directory.
func WithPluginConfDir(dir string) CNIOpt {
return func(c *libcni) error {
c.pluginConfDir = dir
return nil
}
}
// WithPluginMaxConfNum can be used to configure the
// max cni plugin config file num.
func WithPluginMaxConfNum(max int) CNIOpt {
return func(c *libcni) error {
c.pluginMaxConfNum = max
return nil
}
}
// WithMinNetworkCount can be used to configure the
// minimum networks to be configured and initialized
// for the status to report success. By default its 1.
func WithMinNetworkCount(count int) CNIOpt {
return func(c *libcni) error {
c.networkCount = count
return nil
}
}
// WithLoNetwork can be used to load the loopback
// network config.
func WithLoNetwork(c *libcni) error {
loConfig, _ := cnilibrary.ConfListFromBytes([]byte(`{
"cniVersion": "0.3.1",
"name": "cni-loopback",
"plugins": [{
"type": "loopback"
}]
}`))
c.networks = append(c.networks, &Network{
cni: c.cniConfig,
config: loConfig,
ifName: "lo",
})
return nil
}
// WithConf can be used to load config directly
// from byte.
func WithConf(bytes []byte) CNIOpt {
return WithConfIndex(bytes, 0)
}
// WithConfIndex can be used to load config directly
// from byte and set the interface name's index.
func WithConfIndex(bytes []byte, index int) CNIOpt {
return func(c *libcni) error {
conf, err := cnilibrary.ConfFromBytes(bytes)
if err != nil {
return err
}
confList, err := cnilibrary.ConfListFromConf(conf)
if err != nil {
return err
}
c.networks = append(c.networks, &Network{
cni: c.cniConfig,
config: confList,
ifName: getIfName(c.prefix, index),
})
return nil
}
}
// WithConfFile can be used to load network config
// from an .conf file. Supported with absolute fileName
// with path only.
func WithConfFile(fileName string) CNIOpt {
return func(c *libcni) error {
conf, err := cnilibrary.ConfFromFile(fileName)
if err != nil {
return err
}
// upconvert to conf list
confList, err := cnilibrary.ConfListFromConf(conf)
if err != nil {
return err
}
c.networks = append(c.networks, &Network{
cni: c.cniConfig,
config: confList,
ifName: getIfName(c.prefix, 0),
})
return nil
}
}
// WithConfListBytes can be used to load network config list directly
// from byte
func WithConfListBytes(bytes []byte) CNIOpt {
return func(c *libcni) error {
confList, err := cnilibrary.ConfListFromBytes(bytes)
if err != nil {
return err
}
i := len(c.networks)
c.networks = append(c.networks, &Network{
cni: c.cniConfig,
config: confList,
ifName: getIfName(c.prefix, i),
})
return nil
}
}
// WithConfListFile can be used to load network config
// from an .conflist file. Supported with absolute fileName
// with path only.
func WithConfListFile(fileName string) CNIOpt {
return func(c *libcni) error {
confList, err := cnilibrary.ConfListFromFile(fileName)
if err != nil {
return err
}
i := len(c.networks)
c.networks = append(c.networks, &Network{
cni: c.cniConfig,
config: confList,
ifName: getIfName(c.prefix, i),
})
return nil
}
}
// WithDefaultConf can be used to detect the default network
// config file from the configured cni config directory and load
// it.
// Since the CNI spec does not specify a way to detect default networks,
// the convention chosen is - the first network configuration in the sorted
// list of network conf files as the default network.
func WithDefaultConf(c *libcni) error {
return loadFromConfDir(c, c.pluginMaxConfNum)
}
// WithAllConf can be used to detect all network config
// files from the configured cni config directory and load
// them.
func WithAllConf(c *libcni) error {
return loadFromConfDir(c, 0)
}
// loadFromConfDir detects network config files from the
// configured cni config directory and load them. max is
// the maximum network config to load (max i<= 0 means no limit).
func loadFromConfDir(c *libcni, max int) error {
files, err := cnilibrary.ConfFiles(c.pluginConfDir, []string{".conf", ".conflist", ".json"})
switch {
case err != nil:
return errors.Wrapf(ErrRead, "failed to read config file: %v", err)
case len(files) == 0:
return errors.Wrapf(ErrCNINotInitialized, "no network config found in %s", c.pluginConfDir)
}
// files contains the network config files associated with cni network.
// Use lexicographical way as a defined order for network config files.
sort.Strings(files)
// Since the CNI spec does not specify a way to detect default networks,
// the convention chosen is - the first network configuration in the sorted
// list of network conf files as the default network and choose the default
// interface provided during init as the network interface for this default
// network. For every other network use a generated interface id.
i := 0
var networks []*Network
for _, confFile := range files {
var confList *cnilibrary.NetworkConfigList
if strings.HasSuffix(confFile, ".conflist") {
confList, err = cnilibrary.ConfListFromFile(confFile)
if err != nil {
return errors.Wrapf(ErrInvalidConfig, "failed to load CNI config list file %s: %v", confFile, err)
}
} else {
conf, err := cnilibrary.ConfFromFile(confFile)
if err != nil {
return errors.Wrapf(ErrInvalidConfig, "failed to load CNI config file %s: %v", confFile, err)
}
// Ensure the config has a "type" so we know what plugin to run.
// Also catches the case where somebody put a conflist into a conf file.
if conf.Network.Type == "" {
return errors.Wrapf(ErrInvalidConfig, "network type not found in %s", confFile)
}
confList, err = cnilibrary.ConfListFromConf(conf)
if err != nil {
return errors.Wrapf(ErrInvalidConfig, "failed to convert CNI config file %s to CNI config list: %v", confFile, err)
}
}
if len(confList.Plugins) == 0 {
return errors.Wrapf(ErrInvalidConfig, "CNI config list in config file %s has no networks, skipping", confFile)
}
networks = append(networks, &Network{
cni: c.cniConfig,
config: confList,
ifName: getIfName(c.prefix, i),
})
i++
if i == max {
break
}
}
if len(networks) == 0 {
return errors.Wrapf(ErrCNINotInitialized, "no valid networks found in %s", c.pluginDirs)
}
c.networks = append(c.networks, networks...)
return nil
}

106
vendor/github.com/containerd/go-cni/result.go generated vendored Normal file
View File

@ -0,0 +1,106 @@
/*
Copyright The containerd 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 cni
import (
"net"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/pkg/errors"
)
type IPConfig struct {
IP net.IP
Gateway net.IP
}
type CNIResult struct {
Interfaces map[string]*Config
DNS []types.DNS
Routes []*types.Route
}
type Config struct {
IPConfigs []*IPConfig
Mac string
Sandbox string
}
// GetCNIResultFromResults returns a structured data containing the
// interface configuration for each of the interfaces created in the namespace.
// Conforms with
// Result:
// a) Interfaces list. Depending on the plugin, this can include the sandbox
// (eg, container or hypervisor) interface name and/or the host interface
// name, the hardware addresses of each interface, and details about the
// sandbox (if any) the interface is in.
// b) IP configuration assigned to each interface. The IPv4 and/or IPv6 addresses,
// gateways, and routes assigned to sandbox and/or host interfaces.
// c) DNS information. Dictionary that includes DNS information for nameservers,
// domain, search domains and options.
func (c *libcni) GetCNIResultFromResults(results []*current.Result) (*CNIResult, error) {
c.RLock()
defer c.RUnlock()
r := &CNIResult{
Interfaces: make(map[string]*Config),
}
// Plugins may not need to return Interfaces in result if
// if there are no multiple interfaces created. In that case
// all configs should be applied against default interface
r.Interfaces[defaultInterface(c.prefix)] = &Config{}
// Walk through all the results
for _, result := range results {
// Walk through all the interface in each result
for _, intf := range result.Interfaces {
r.Interfaces[intf.Name] = &Config{
Mac: intf.Mac,
Sandbox: intf.Sandbox,
}
}
// Walk through all the IPs in the result and attach it to corresponding
// interfaces
for _, ipConf := range result.IPs {
if err := validateInterfaceConfig(ipConf, len(result.Interfaces)); err != nil {
return nil, errors.Wrapf(ErrInvalidResult, "invalid interface config: %v", err)
}
name := c.getInterfaceName(result.Interfaces, ipConf)
r.Interfaces[name].IPConfigs = append(r.Interfaces[name].IPConfigs,
&IPConfig{IP: ipConf.Address.IP, Gateway: ipConf.Gateway})
}
r.DNS = append(r.DNS, result.DNS)
r.Routes = append(r.Routes, result.Routes...)
}
if _, ok := r.Interfaces[defaultInterface(c.prefix)]; !ok {
return nil, errors.Wrapf(ErrNotFound, "default network not found for: %s", defaultInterface(c.prefix))
}
return r, nil
}
// getInterfaceName returns the interface name if the plugins
// return the result with associated interfaces. If interface
// is not present then default interface name is used
func (c *libcni) getInterfaceName(interfaces []*current.Interface,
ipConf *current.IPConfig) string {
if ipConf.Interface != nil {
return interfaces[*ipConf.Interface].Name
}
return defaultInterface(c.prefix)
}

78
vendor/github.com/containerd/go-cni/testutils.go generated vendored Normal file
View File

@ -0,0 +1,78 @@
/*
Copyright The containerd 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 cni
import (
"fmt"
"io/ioutil"
"os"
"path"
"testing"
)
func makeTmpDir(prefix string) (string, error) {
tmpDir, err := ioutil.TempDir(os.TempDir(), prefix)
if err != nil {
return "", err
}
return tmpDir, nil
}
func makeFakeCNIConfig(t *testing.T) (string, string) {
cniDir, err := makeTmpDir("fakecni")
if err != nil {
t.Fatalf("Failed to create plugin config dir: %v", err)
}
cniConfDir := path.Join(cniDir, "net.d")
err = os.MkdirAll(cniConfDir, 0777)
if err != nil {
t.Fatalf("Failed to create network config dir: %v", err)
}
networkConfig1 := path.Join(cniConfDir, "mocknetwork1.conf")
f1, err := os.Create(networkConfig1)
if err != nil {
t.Fatalf("Failed to create network config %v: %v", f1, err)
}
networkConfig2 := path.Join(cniConfDir, "mocknetwork2.conf")
f2, err := os.Create(networkConfig2)
if err != nil {
t.Fatalf("Failed to create network config %v: %v", f2, err)
}
cfg1 := fmt.Sprintf(`{ "name": "%s", "type": "%s", "capabilities": {"portMappings": true} }`, "plugin1", "fakecni")
_, err = f1.WriteString(cfg1)
if err != nil {
t.Fatalf("Failed to write network config file %v: %v", f1, err)
}
f1.Close()
cfg2 := fmt.Sprintf(`{ "name": "%s", "type": "%s", "capabilities": {"portMappings": true} }`, "plugin2", "fakecni")
_, err = f2.WriteString(cfg2)
if err != nil {
t.Fatalf("Failed to write network config file %v: %v", f2, err)
}
f2.Close()
return cniDir, cniConfDir
}
func tearDownCNIConfig(t *testing.T, confDir string) {
err := os.RemoveAll(confDir)
if err != nil {
t.Fatalf("Failed to cleanup CNI configs: %v", err)
}
}

65
vendor/github.com/containerd/go-cni/types.go generated vendored Normal file
View File

@ -0,0 +1,65 @@
/*
Copyright The containerd 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 cni
const (
CNIPluginName = "cni"
DefaultNetDir = "/etc/cni/net.d"
DefaultCNIDir = "/opt/cni/bin"
DefaultMaxConfNum = 1
VendorCNIDirTemplate = "%s/opt/%s/bin"
DefaultPrefix = "eth"
)
type config struct {
pluginDirs []string
pluginConfDir string
pluginMaxConfNum int
prefix string
}
type PortMapping struct {
HostPort int32
ContainerPort int32
Protocol string
HostIP string
}
type IPRanges struct {
Subnet string
RangeStart string
RangeEnd string
Gateway string
}
// BandWidth defines the ingress/egress rate and burst limits
type BandWidth struct {
IngressRate uint64
IngressBurst uint64
EgressRate uint64
EgressBurst uint64
}
// DNS defines the dns config
type DNS struct {
// List of DNS servers of the cluster.
Servers []string
// List of DNS search domains of the cluster.
Searches []string
// List of DNS options.
Options []string
}

202
vendor/github.com/containernetworking/cni/LICENSE generated vendored Normal file
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.

497
vendor/github.com/containernetworking/cni/libcni/api.go generated vendored Normal file
View File

@ -0,0 +1,497 @@
// Copyright 2015 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package libcni
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/containernetworking/cni/pkg/invoke"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/version"
)
var (
CacheDir = "/var/lib/cni"
)
// A RuntimeConf holds the arguments to one invocation of a CNI plugin
// excepting the network configuration, with the nested exception that
// the `runtimeConfig` from the network configuration is included
// here.
type RuntimeConf struct {
ContainerID string
NetNS string
IfName string
Args [][2]string
// A dictionary of capability-specific data passed by the runtime
// to plugins as top-level keys in the 'runtimeConfig' dictionary
// of the plugin's stdin data. libcni will ensure that only keys
// in this map which match the capabilities of the plugin are passed
// to the plugin
CapabilityArgs map[string]interface{}
// A cache directory in which to library data. Defaults to CacheDir
CacheDir string
}
type NetworkConfig struct {
Network *types.NetConf
Bytes []byte
}
type NetworkConfigList struct {
Name string
CNIVersion string
DisableCheck bool
Plugins []*NetworkConfig
Bytes []byte
}
type CNI interface {
AddNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
CheckNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
DelNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
GetNetworkListCachedResult(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
AddNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
CheckNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error
DelNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error
GetNetworkCachedResult(net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
ValidateNetworkList(ctx context.Context, net *NetworkConfigList) ([]string, error)
ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]string, error)
}
type CNIConfig struct {
Path []string
exec invoke.Exec
}
// CNIConfig implements the CNI interface
var _ CNI = &CNIConfig{}
// NewCNIConfig returns a new CNIConfig object that will search for plugins
// in the given paths and use the given exec interface to run those plugins,
// or if the exec interface is not given, will use a default exec handler.
func NewCNIConfig(path []string, exec invoke.Exec) *CNIConfig {
return &CNIConfig{
Path: path,
exec: exec,
}
}
func buildOneConfig(name, cniVersion string, orig *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (*NetworkConfig, error) {
var err error
inject := map[string]interface{}{
"name": name,
"cniVersion": cniVersion,
}
// Add previous plugin result
if prevResult != nil {
inject["prevResult"] = prevResult
}
// Ensure every config uses the same name and version
orig, err = InjectConf(orig, inject)
if err != nil {
return nil, err
}
return injectRuntimeConfig(orig, rt)
}
// This function takes a libcni RuntimeConf structure and injects values into
// a "runtimeConfig" dictionary in the CNI network configuration JSON that
// will be passed to the plugin on stdin.
//
// Only "capabilities arguments" passed by the runtime are currently injected.
// These capabilities arguments are filtered through the plugin's advertised
// capabilities from its config JSON, and any keys in the CapabilityArgs
// matching plugin capabilities are added to the "runtimeConfig" dictionary
// sent to the plugin via JSON on stdin. For example, if the plugin's
// capabilities include "portMappings", and the CapabilityArgs map includes a
// "portMappings" key, that key and its value are added to the "runtimeConfig"
// dictionary to be passed to the plugin's stdin.
func injectRuntimeConfig(orig *NetworkConfig, rt *RuntimeConf) (*NetworkConfig, error) {
var err error
rc := make(map[string]interface{})
for capability, supported := range orig.Network.Capabilities {
if !supported {
continue
}
if data, ok := rt.CapabilityArgs[capability]; ok {
rc[capability] = data
}
}
if len(rc) > 0 {
orig, err = InjectConf(orig, map[string]interface{}{"runtimeConfig": rc})
if err != nil {
return nil, err
}
}
return orig, nil
}
// ensure we have a usable exec if the CNIConfig was not given one
func (c *CNIConfig) ensureExec() invoke.Exec {
if c.exec == nil {
c.exec = &invoke.DefaultExec{
RawExec: &invoke.RawExec{Stderr: os.Stderr},
PluginDecoder: version.PluginDecoder{},
}
}
return c.exec
}
func getResultCacheFilePath(netName string, rt *RuntimeConf) string {
cacheDir := rt.CacheDir
if cacheDir == "" {
cacheDir = CacheDir
}
return filepath.Join(cacheDir, "results", fmt.Sprintf("%s-%s-%s", netName, rt.ContainerID, rt.IfName))
}
func setCachedResult(result types.Result, netName string, rt *RuntimeConf) error {
data, err := json.Marshal(result)
if err != nil {
return err
}
fname := getResultCacheFilePath(netName, rt)
if err := os.MkdirAll(filepath.Dir(fname), 0700); err != nil {
return err
}
return ioutil.WriteFile(fname, data, 0600)
}
func delCachedResult(netName string, rt *RuntimeConf) error {
fname := getResultCacheFilePath(netName, rt)
return os.Remove(fname)
}
func getCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, error) {
fname := getResultCacheFilePath(netName, rt)
data, err := ioutil.ReadFile(fname)
if err != nil {
// Ignore read errors; the cached result may not exist on-disk
return nil, nil
}
// Read the version of the cached result
decoder := version.ConfigDecoder{}
resultCniVersion, err := decoder.Decode(data)
if err != nil {
return nil, err
}
// Ensure we can understand the result
result, err := version.NewResult(resultCniVersion, data)
if err != nil {
return nil, err
}
// Convert to the config version to ensure plugins get prevResult
// in the same version as the config. The cached result version
// should match the config version unless the config was changed
// while the container was running.
result, err = result.GetAsVersion(cniVersion)
if err != nil && resultCniVersion != cniVersion {
return nil, fmt.Errorf("failed to convert cached result version %q to config version %q: %v", resultCniVersion, cniVersion, err)
}
return result, err
}
// GetNetworkListCachedResult returns the cached Result of the previous
// previous AddNetworkList() operation for a network list, or an error.
func (c *CNIConfig) GetNetworkListCachedResult(list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {
return getCachedResult(list.Name, list.CNIVersion, rt)
}
// GetNetworkCachedResult returns the cached Result of the previous
// previous AddNetwork() operation for a network, or an error.
func (c *CNIConfig) GetNetworkCachedResult(net *NetworkConfig, rt *RuntimeConf) (types.Result, error) {
return getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
}
func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (types.Result, error) {
c.ensureExec()
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
if err != nil {
return nil, err
}
newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
if err != nil {
return nil, err
}
return invoke.ExecPluginWithResult(ctx, pluginPath, newConf.Bytes, c.args("ADD", rt), c.exec)
}
// AddNetworkList executes a sequence of plugins with the ADD command
func (c *CNIConfig) AddNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {
var err error
var result types.Result
for _, net := range list.Plugins {
result, err = c.addNetwork(ctx, list.Name, list.CNIVersion, net, result, rt)
if err != nil {
return nil, err
}
}
if err = setCachedResult(result, list.Name, rt); err != nil {
return nil, fmt.Errorf("failed to set network %q cached result: %v", list.Name, err)
}
return result, nil
}
func (c *CNIConfig) checkNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) error {
c.ensureExec()
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
if err != nil {
return err
}
newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
if err != nil {
return err
}
return invoke.ExecPluginWithoutResult(ctx, pluginPath, newConf.Bytes, c.args("CHECK", rt), c.exec)
}
// CheckNetworkList executes a sequence of plugins with the CHECK command
func (c *CNIConfig) CheckNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) error {
// CHECK was added in CNI spec version 0.4.0 and higher
if gtet, err := version.GreaterThanOrEqualTo(list.CNIVersion, "0.4.0"); err != nil {
return err
} else if !gtet {
return fmt.Errorf("configuration version %q does not support the CHECK command", list.CNIVersion)
}
if list.DisableCheck {
return nil
}
cachedResult, err := getCachedResult(list.Name, list.CNIVersion, rt)
if err != nil {
return fmt.Errorf("failed to get network %q cached result: %v", list.Name, err)
}
for _, net := range list.Plugins {
if err := c.checkNetwork(ctx, list.Name, list.CNIVersion, net, cachedResult, rt); err != nil {
return err
}
}
return nil
}
func (c *CNIConfig) delNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) error {
c.ensureExec()
pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)
if err != nil {
return err
}
newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
if err != nil {
return err
}
return invoke.ExecPluginWithoutResult(ctx, pluginPath, newConf.Bytes, c.args("DEL", rt), c.exec)
}
// DelNetworkList executes a sequence of plugins with the DEL command
func (c *CNIConfig) DelNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) error {
var cachedResult types.Result
// Cached result on DEL was added in CNI spec version 0.4.0 and higher
if gtet, err := version.GreaterThanOrEqualTo(list.CNIVersion, "0.4.0"); err != nil {
return err
} else if gtet {
cachedResult, err = getCachedResult(list.Name, list.CNIVersion, rt)
if err != nil {
return fmt.Errorf("failed to get network %q cached result: %v", list.Name, err)
}
}
for i := len(list.Plugins) - 1; i >= 0; i-- {
net := list.Plugins[i]
if err := c.delNetwork(ctx, list.Name, list.CNIVersion, net, cachedResult, rt); err != nil {
return err
}
}
_ = delCachedResult(list.Name, rt)
return nil
}
// AddNetwork executes the plugin with the ADD command
func (c *CNIConfig) AddNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) (types.Result, error) {
result, err := c.addNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, nil, rt)
if err != nil {
return nil, err
}
if err = setCachedResult(result, net.Network.Name, rt); err != nil {
return nil, fmt.Errorf("failed to set network %q cached result: %v", net.Network.Name, err)
}
return result, nil
}
// CheckNetwork executes the plugin with the CHECK command
func (c *CNIConfig) CheckNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error {
// CHECK was added in CNI spec version 0.4.0 and higher
if gtet, err := version.GreaterThanOrEqualTo(net.Network.CNIVersion, "0.4.0"); err != nil {
return err
} else if !gtet {
return fmt.Errorf("configuration version %q does not support the CHECK command", net.Network.CNIVersion)
}
cachedResult, err := getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
if err != nil {
return fmt.Errorf("failed to get network %q cached result: %v", net.Network.Name, err)
}
return c.checkNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt)
}
// DelNetwork executes the plugin with the DEL command
func (c *CNIConfig) DelNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error {
var cachedResult types.Result
// Cached result on DEL was added in CNI spec version 0.4.0 and higher
if gtet, err := version.GreaterThanOrEqualTo(net.Network.CNIVersion, "0.4.0"); err != nil {
return err
} else if gtet {
cachedResult, err = getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
if err != nil {
return fmt.Errorf("failed to get network %q cached result: %v", net.Network.Name, err)
}
}
if err := c.delNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt); err != nil {
return err
}
_ = delCachedResult(net.Network.Name, rt)
return nil
}
// ValidateNetworkList checks that a configuration is reasonably valid.
// - all the specified plugins exist on disk
// - every plugin supports the desired version.
//
// Returns a list of all capabilities supported by the configuration, or error
func (c *CNIConfig) ValidateNetworkList(ctx context.Context, list *NetworkConfigList) ([]string, error) {
version := list.CNIVersion
// holding map for seen caps (in case of duplicates)
caps := map[string]interface{}{}
errs := []error{}
for _, net := range list.Plugins {
if err := c.validatePlugin(ctx, net.Network.Type, version); err != nil {
errs = append(errs, err)
}
for c, enabled := range net.Network.Capabilities {
if !enabled {
continue
}
caps[c] = struct{}{}
}
}
if len(errs) > 0 {
return nil, fmt.Errorf("%v", errs)
}
// make caps list
cc := make([]string, 0, len(caps))
for c := range caps {
cc = append(cc, c)
}
return cc, nil
}
// ValidateNetwork checks that a configuration is reasonably valid.
// It uses the same logic as ValidateNetworkList)
// Returns a list of capabilities
func (c *CNIConfig) ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]string, error) {
caps := []string{}
for c, ok := range net.Network.Capabilities {
if ok {
caps = append(caps, c)
}
}
if err := c.validatePlugin(ctx, net.Network.Type, net.Network.CNIVersion); err != nil {
return nil, err
}
return caps, nil
}
// validatePlugin checks that an individual plugin's configuration is sane
func (c *CNIConfig) validatePlugin(ctx context.Context, pluginName, expectedVersion string) error {
pluginPath, err := invoke.FindInPath(pluginName, c.Path)
if err != nil {
return err
}
vi, err := invoke.GetVersionInfo(ctx, pluginPath, c.exec)
if err != nil {
return err
}
for _, vers := range vi.SupportedVersions() {
if vers == expectedVersion {
return nil
}
}
return fmt.Errorf("plugin %s does not support config version %q", pluginName, expectedVersion)
}
// GetVersionInfo reports which versions of the CNI spec are supported by
// the given plugin.
func (c *CNIConfig) GetVersionInfo(ctx context.Context, pluginType string) (version.PluginInfo, error) {
c.ensureExec()
pluginPath, err := c.exec.FindInPath(pluginType, c.Path)
if err != nil {
return nil, err
}
return invoke.GetVersionInfo(ctx, pluginPath, c.exec)
}
// =====
func (c *CNIConfig) args(action string, rt *RuntimeConf) *invoke.Args {
return &invoke.Args{
Command: action,
ContainerID: rt.ContainerID,
NetNS: rt.NetNS,
PluginArgs: rt.Args,
IfName: rt.IfName,
Path: strings.Join(c.Path, string(os.PathListSeparator)),
}
}

View File

@ -0,0 +1,268 @@
// Copyright 2015 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package libcni
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"sort"
)
type NotFoundError struct {
Dir string
Name string
}
func (e NotFoundError) Error() string {
return fmt.Sprintf(`no net configuration with name "%s" in %s`, e.Name, e.Dir)
}
type NoConfigsFoundError struct {
Dir string
}
func (e NoConfigsFoundError) Error() string {
return fmt.Sprintf(`no net configurations found in %s`, e.Dir)
}
func ConfFromBytes(bytes []byte) (*NetworkConfig, error) {
conf := &NetworkConfig{Bytes: bytes}
if err := json.Unmarshal(bytes, &conf.Network); err != nil {
return nil, fmt.Errorf("error parsing configuration: %s", err)
}
if conf.Network.Type == "" {
return nil, fmt.Errorf("error parsing configuration: missing 'type'")
}
return conf, nil
}
func ConfFromFile(filename string) (*NetworkConfig, error) {
bytes, err := ioutil.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("error reading %s: %s", filename, err)
}
return ConfFromBytes(bytes)
}
func ConfListFromBytes(bytes []byte) (*NetworkConfigList, error) {
rawList := make(map[string]interface{})
if err := json.Unmarshal(bytes, &rawList); err != nil {
return nil, fmt.Errorf("error parsing configuration list: %s", err)
}
rawName, ok := rawList["name"]
if !ok {
return nil, fmt.Errorf("error parsing configuration list: no name")
}
name, ok := rawName.(string)
if !ok {
return nil, fmt.Errorf("error parsing configuration list: invalid name type %T", rawName)
}
var cniVersion string
rawVersion, ok := rawList["cniVersion"]
if ok {
cniVersion, ok = rawVersion.(string)
if !ok {
return nil, fmt.Errorf("error parsing configuration list: invalid cniVersion type %T", rawVersion)
}
}
disableCheck := false
if rawDisableCheck, ok := rawList["disableCheck"]; ok {
disableCheck, ok = rawDisableCheck.(bool)
if !ok {
return nil, fmt.Errorf("error parsing configuration list: invalid disableCheck type %T", rawDisableCheck)
}
}
list := &NetworkConfigList{
Name: name,
DisableCheck: disableCheck,
CNIVersion: cniVersion,
Bytes: bytes,
}
var plugins []interface{}
plug, ok := rawList["plugins"]
if !ok {
return nil, fmt.Errorf("error parsing configuration list: no 'plugins' key")
}
plugins, ok = plug.([]interface{})
if !ok {
return nil, fmt.Errorf("error parsing configuration list: invalid 'plugins' type %T", plug)
}
if len(plugins) == 0 {
return nil, fmt.Errorf("error parsing configuration list: no plugins in list")
}
for i, conf := range plugins {
newBytes, err := json.Marshal(conf)
if err != nil {
return nil, fmt.Errorf("Failed to marshal plugin config %d: %v", i, err)
}
netConf, err := ConfFromBytes(newBytes)
if err != nil {
return nil, fmt.Errorf("Failed to parse plugin config %d: %v", i, err)
}
list.Plugins = append(list.Plugins, netConf)
}
return list, nil
}
func ConfListFromFile(filename string) (*NetworkConfigList, error) {
bytes, err := ioutil.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("error reading %s: %s", filename, err)
}
return ConfListFromBytes(bytes)
}
func ConfFiles(dir string, extensions []string) ([]string, error) {
// In part, adapted from rkt/networking/podenv.go#listFiles
files, err := ioutil.ReadDir(dir)
switch {
case err == nil: // break
case os.IsNotExist(err):
return nil, nil
default:
return nil, err
}
confFiles := []string{}
for _, f := range files {
if f.IsDir() {
continue
}
fileExt := filepath.Ext(f.Name())
for _, ext := range extensions {
if fileExt == ext {
confFiles = append(confFiles, filepath.Join(dir, f.Name()))
}
}
}
return confFiles, nil
}
func LoadConf(dir, name string) (*NetworkConfig, error) {
files, err := ConfFiles(dir, []string{".conf", ".json"})
switch {
case err != nil:
return nil, err
case len(files) == 0:
return nil, NoConfigsFoundError{Dir: dir}
}
sort.Strings(files)
for _, confFile := range files {
conf, err := ConfFromFile(confFile)
if err != nil {
return nil, err
}
if conf.Network.Name == name {
return conf, nil
}
}
return nil, NotFoundError{dir, name}
}
func LoadConfList(dir, name string) (*NetworkConfigList, error) {
files, err := ConfFiles(dir, []string{".conflist"})
if err != nil {
return nil, err
}
sort.Strings(files)
for _, confFile := range files {
conf, err := ConfListFromFile(confFile)
if err != nil {
return nil, err
}
if conf.Name == name {
return conf, nil
}
}
// Try and load a network configuration file (instead of list)
// from the same name, then upconvert.
singleConf, err := LoadConf(dir, name)
if err != nil {
// A little extra logic so the error makes sense
if _, ok := err.(NoConfigsFoundError); len(files) != 0 && ok {
// Config lists found but no config files found
return nil, NotFoundError{dir, name}
}
return nil, err
}
return ConfListFromConf(singleConf)
}
func InjectConf(original *NetworkConfig, newValues map[string]interface{}) (*NetworkConfig, error) {
config := make(map[string]interface{})
err := json.Unmarshal(original.Bytes, &config)
if err != nil {
return nil, fmt.Errorf("unmarshal existing network bytes: %s", err)
}
for key, value := range newValues {
if key == "" {
return nil, fmt.Errorf("keys cannot be empty")
}
if value == nil {
return nil, fmt.Errorf("key '%s' value must not be nil", key)
}
config[key] = value
}
newBytes, err := json.Marshal(config)
if err != nil {
return nil, err
}
return ConfFromBytes(newBytes)
}
// ConfListFromConf "upconverts" a network config in to a NetworkConfigList,
// with the single network as the only entry in the list.
func ConfListFromConf(original *NetworkConfig) (*NetworkConfigList, error) {
// Re-deserialize the config's json, then make a raw map configlist.
// This may seem a bit strange, but it's to make the Bytes fields
// actually make sense. Otherwise, the generated json is littered with
// golang default values.
rawConfig := make(map[string]interface{})
if err := json.Unmarshal(original.Bytes, &rawConfig); err != nil {
return nil, err
}
rawConfigList := map[string]interface{}{
"name": original.Network.Name,
"cniVersion": original.Network.CNIVersion,
"plugins": []interface{}{rawConfig},
}
b, err := json.Marshal(rawConfigList)
if err != nil {
return nil, err
}
return ConfListFromBytes(b)
}

View File

@ -0,0 +1,128 @@
// Copyright 2015 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package invoke
import (
"fmt"
"os"
"strings"
)
type CNIArgs interface {
// For use with os/exec; i.e., return nil to inherit the
// environment from this process
// For use in delegation; inherit the environment from this
// process and allow overrides
AsEnv() []string
}
type inherited struct{}
var inheritArgsFromEnv inherited
func (_ *inherited) AsEnv() []string {
return nil
}
func ArgsFromEnv() CNIArgs {
return &inheritArgsFromEnv
}
type Args struct {
Command string
ContainerID string
NetNS string
PluginArgs [][2]string
PluginArgsStr string
IfName string
Path string
}
// Args implements the CNIArgs interface
var _ CNIArgs = &Args{}
func (args *Args) AsEnv() []string {
env := os.Environ()
pluginArgsStr := args.PluginArgsStr
if pluginArgsStr == "" {
pluginArgsStr = stringify(args.PluginArgs)
}
// Duplicated values which come first will be overrided, so we must put the
// custom values in the end to avoid being overrided by the process environments.
env = append(env,
"CNI_COMMAND="+args.Command,
"CNI_CONTAINERID="+args.ContainerID,
"CNI_NETNS="+args.NetNS,
"CNI_ARGS="+pluginArgsStr,
"CNI_IFNAME="+args.IfName,
"CNI_PATH="+args.Path,
)
return dedupEnv(env)
}
// taken from rkt/networking/net_plugin.go
func stringify(pluginArgs [][2]string) string {
entries := make([]string, len(pluginArgs))
for i, kv := range pluginArgs {
entries[i] = strings.Join(kv[:], "=")
}
return strings.Join(entries, ";")
}
// DelegateArgs implements the CNIArgs interface
// used for delegation to inherit from environments
// and allow some overrides like CNI_COMMAND
var _ CNIArgs = &DelegateArgs{}
type DelegateArgs struct {
Command string
}
func (d *DelegateArgs) AsEnv() []string {
env := os.Environ()
// The custom values should come in the end to override the existing
// process environment of the same key.
env = append(env,
"CNI_COMMAND="+d.Command,
)
return dedupEnv(env)
}
// dedupEnv returns a copy of env with any duplicates removed, in favor of later values.
// Items not of the normal environment "key=value" form are preserved unchanged.
func dedupEnv(env []string) []string {
out := make([]string, 0, len(env))
envMap := map[string]string{}
for _, kv := range env {
// find the first "=" in environment, if not, just keep it
eq := strings.Index(kv, "=")
if eq < 0 {
out = append(out, kv)
continue
}
envMap[kv[:eq]] = kv[eq+1:]
}
for k, v := range envMap {
out = append(out, fmt.Sprintf("%s=%s", k, v))
}
return out
}

View File

@ -0,0 +1,80 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package invoke
import (
"context"
"os"
"path/filepath"
"github.com/containernetworking/cni/pkg/types"
)
func delegateCommon(delegatePlugin string, exec Exec) (string, Exec, error) {
if exec == nil {
exec = defaultExec
}
paths := filepath.SplitList(os.Getenv("CNI_PATH"))
pluginPath, err := exec.FindInPath(delegatePlugin, paths)
if err != nil {
return "", nil, err
}
return pluginPath, exec, nil
}
// DelegateAdd calls the given delegate plugin with the CNI ADD action and
// JSON configuration
func DelegateAdd(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) (types.Result, error) {
pluginPath, realExec, err := delegateCommon(delegatePlugin, exec)
if err != nil {
return nil, err
}
// DelegateAdd will override the original "CNI_COMMAND" env from process with ADD
return ExecPluginWithResult(ctx, pluginPath, netconf, delegateArgs("ADD"), realExec)
}
// DelegateCheck calls the given delegate plugin with the CNI CHECK action and
// JSON configuration
func DelegateCheck(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error {
pluginPath, realExec, err := delegateCommon(delegatePlugin, exec)
if err != nil {
return err
}
// DelegateCheck will override the original CNI_COMMAND env from process with CHECK
return ExecPluginWithoutResult(ctx, pluginPath, netconf, delegateArgs("CHECK"), realExec)
}
// DelegateDel calls the given delegate plugin with the CNI DEL action and
// JSON configuration
func DelegateDel(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error {
pluginPath, realExec, err := delegateCommon(delegatePlugin, exec)
if err != nil {
return err
}
// DelegateDel will override the original CNI_COMMAND env from process with DEL
return ExecPluginWithoutResult(ctx, pluginPath, netconf, delegateArgs("DEL"), realExec)
}
// return CNIArgs used by delegation
func delegateArgs(action string) *DelegateArgs {
return &DelegateArgs{
Command: action,
}
}

View File

@ -0,0 +1,144 @@
// Copyright 2015 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package invoke
import (
"context"
"fmt"
"os"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/version"
)
// Exec is an interface encapsulates all operations that deal with finding
// and executing a CNI plugin. Tests may provide a fake implementation
// to avoid writing fake plugins to temporary directories during the test.
type Exec interface {
ExecPlugin(ctx context.Context, pluginPath string, stdinData []byte, environ []string) ([]byte, error)
FindInPath(plugin string, paths []string) (string, error)
Decode(jsonBytes []byte) (version.PluginInfo, error)
}
// For example, a testcase could pass an instance of the following fakeExec
// object to ExecPluginWithResult() to verify the incoming stdin and environment
// and provide a tailored response:
//
//import (
// "encoding/json"
// "path"
// "strings"
//)
//
//type fakeExec struct {
// version.PluginDecoder
//}
//
//func (f *fakeExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
// net := &types.NetConf{}
// err := json.Unmarshal(stdinData, net)
// if err != nil {
// return nil, fmt.Errorf("failed to unmarshal configuration: %v", err)
// }
// pluginName := path.Base(pluginPath)
// if pluginName != net.Type {
// return nil, fmt.Errorf("plugin name %q did not match config type %q", pluginName, net.Type)
// }
// for _, e := range environ {
// // Check environment for forced failure request
// parts := strings.Split(e, "=")
// if len(parts) > 0 && parts[0] == "FAIL" {
// return nil, fmt.Errorf("failed to execute plugin %s", pluginName)
// }
// }
// return []byte("{\"CNIVersion\":\"0.4.0\"}"), nil
//}
//
//func (f *fakeExec) FindInPath(plugin string, paths []string) (string, error) {
// if len(paths) > 0 {
// return path.Join(paths[0], plugin), nil
// }
// return "", fmt.Errorf("failed to find plugin %s in paths %v", plugin, paths)
//}
func ExecPluginWithResult(ctx context.Context, pluginPath string, netconf []byte, args CNIArgs, exec Exec) (types.Result, error) {
if exec == nil {
exec = defaultExec
}
stdoutBytes, err := exec.ExecPlugin(ctx, pluginPath, netconf, args.AsEnv())
if err != nil {
return nil, err
}
// Plugin must return result in same version as specified in netconf
versionDecoder := &version.ConfigDecoder{}
confVersion, err := versionDecoder.Decode(netconf)
if err != nil {
return nil, err
}
return version.NewResult(confVersion, stdoutBytes)
}
func ExecPluginWithoutResult(ctx context.Context, pluginPath string, netconf []byte, args CNIArgs, exec Exec) error {
if exec == nil {
exec = defaultExec
}
_, err := exec.ExecPlugin(ctx, pluginPath, netconf, args.AsEnv())
return err
}
// GetVersionInfo returns the version information available about the plugin.
// For recent-enough plugins, it uses the information returned by the VERSION
// command. For older plugins which do not recognize that command, it reports
// version 0.1.0
func GetVersionInfo(ctx context.Context, pluginPath string, exec Exec) (version.PluginInfo, error) {
if exec == nil {
exec = defaultExec
}
args := &Args{
Command: "VERSION",
// set fake values required by plugins built against an older version of skel
NetNS: "dummy",
IfName: "dummy",
Path: "dummy",
}
stdin := []byte(fmt.Sprintf(`{"cniVersion":%q}`, version.Current()))
stdoutBytes, err := exec.ExecPlugin(ctx, pluginPath, stdin, args.AsEnv())
if err != nil {
if err.Error() == "unknown CNI_COMMAND: VERSION" {
return version.PluginSupports("0.1.0"), nil
}
return nil, err
}
return exec.Decode(stdoutBytes)
}
// DefaultExec is an object that implements the Exec interface which looks
// for and executes plugins from disk.
type DefaultExec struct {
*RawExec
version.PluginDecoder
}
// DefaultExec implements the Exec interface
var _ Exec = &DefaultExec{}
var defaultExec = &DefaultExec{
RawExec: &RawExec{Stderr: os.Stderr},
}

View File

@ -0,0 +1,43 @@
// Copyright 2015 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package invoke
import (
"fmt"
"os"
"path/filepath"
)
// FindInPath returns the full path of the plugin by searching in the provided path
func FindInPath(plugin string, paths []string) (string, error) {
if plugin == "" {
return "", fmt.Errorf("no plugin name provided")
}
if len(paths) == 0 {
return "", fmt.Errorf("no paths provided")
}
for _, path := range paths {
for _, fe := range ExecutableFileExtensions {
fullpath := filepath.Join(path, plugin) + fe
if fi, err := os.Stat(fullpath); err == nil && fi.Mode().IsRegular() {
return fullpath, nil
}
}
}
return "", fmt.Errorf("failed to find plugin %q in path %s", plugin, paths)
}

View File

@ -0,0 +1,20 @@
// 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.
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
package invoke
// Valid file extensions for plugin executables.
var ExecutableFileExtensions = []string{""}

View File

@ -0,0 +1,18 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package invoke
// Valid file extensions for plugin executables.
var ExecutableFileExtensions = []string{".exe", ""}

View File

@ -0,0 +1,62 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package invoke
import (
"bytes"
"context"
"encoding/json"
"fmt"
"io"
"os/exec"
"github.com/containernetworking/cni/pkg/types"
)
type RawExec struct {
Stderr io.Writer
}
func (e *RawExec) ExecPlugin(ctx context.Context, pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
stdout := &bytes.Buffer{}
c := exec.CommandContext(ctx, pluginPath)
c.Env = environ
c.Stdin = bytes.NewBuffer(stdinData)
c.Stdout = stdout
c.Stderr = e.Stderr
if err := c.Run(); err != nil {
return nil, pluginErr(err, stdout.Bytes())
}
return stdout.Bytes(), nil
}
func pluginErr(err error, output []byte) error {
if _, ok := err.(*exec.ExitError); ok {
emsg := types.Error{}
if len(output) == 0 {
emsg.Msg = "netplugin failed with no error message"
} else if perr := json.Unmarshal(output, &emsg); perr != nil {
emsg.Msg = fmt.Sprintf("netplugin failed but error parsing its diagnostic message %q: %v", string(output), perr)
}
return &emsg
}
return err
}
func (e *RawExec) FindInPath(plugin string, paths []string) (string, error) {
return FindInPath(plugin, paths)
}

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
}

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
}

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
}

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

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
}

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
}

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

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
}

9
vendor/github.com/google/uuid/.travis.yml generated vendored Normal file
View File

@ -0,0 +1,9 @@
language: go
go:
- 1.4.3
- 1.5.3
- tip
script:
- go test -v ./...

10
vendor/github.com/google/uuid/CONTRIBUTING.md generated vendored Normal file
View File

@ -0,0 +1,10 @@
# How to contribute
We definitely welcome patches and contribution to this project!
### Legal requirements
In order to protect both you and ourselves, you will need to sign the
[Contributor License Agreement](https://cla.developers.google.com/clas).
You may have already signed it for other Google projects.

9
vendor/github.com/google/uuid/CONTRIBUTORS generated vendored Normal file
View File

@ -0,0 +1,9 @@
Paul Borman <borman@google.com>
bmatsuo
shawnps
theory
jboverfelt
dsymonds
cd1
wallclockbuilder
dansouza

27
vendor/github.com/google/uuid/LICENSE generated vendored Normal file
View File

@ -0,0 +1,27 @@
Copyright (c) 2009,2014 Google Inc. 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.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
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
OWNER 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.

19
vendor/github.com/google/uuid/README.md generated vendored Normal file
View File

@ -0,0 +1,19 @@
# uuid ![build status](https://travis-ci.org/google/uuid.svg?branch=master)
The uuid package generates and inspects UUIDs based on
[RFC 4122](http://tools.ietf.org/html/rfc4122)
and DCE 1.1: Authentication and Security Services.
This package is based on the github.com/pborman/uuid package (previously named
code.google.com/p/go-uuid). It differs from these earlier packages in that
a UUID is a 16 byte array rather than a byte slice. One loss due to this
change is the ability to represent an invalid UUID (vs a NIL UUID).
###### Install
`go get github.com/google/uuid`
###### Documentation
[![GoDoc](https://godoc.org/github.com/google/uuid?status.svg)](http://godoc.org/github.com/google/uuid)
Full `go doc` style documentation for the package can be viewed online without
installing this package by using the GoDoc site here:
http://godoc.org/github.com/google/uuid

80
vendor/github.com/google/uuid/dce.go generated vendored Normal file
View File

@ -0,0 +1,80 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"encoding/binary"
"fmt"
"os"
)
// A Domain represents a Version 2 domain
type Domain byte
// Domain constants for DCE Security (Version 2) UUIDs.
const (
Person = Domain(0)
Group = Domain(1)
Org = Domain(2)
)
// NewDCESecurity returns a DCE Security (Version 2) UUID.
//
// The domain should be one of Person, Group or Org.
// On a POSIX system the id should be the users UID for the Person
// domain and the users GID for the Group. The meaning of id for
// the domain Org or on non-POSIX systems is site defined.
//
// For a given domain/id pair the same token may be returned for up to
// 7 minutes and 10 seconds.
func NewDCESecurity(domain Domain, id uint32) (UUID, error) {
uuid, err := NewUUID()
if err == nil {
uuid[6] = (uuid[6] & 0x0f) | 0x20 // Version 2
uuid[9] = byte(domain)
binary.BigEndian.PutUint32(uuid[0:], id)
}
return uuid, err
}
// NewDCEPerson returns a DCE Security (Version 2) UUID in the person
// domain with the id returned by os.Getuid.
//
// NewDCESecurity(Person, uint32(os.Getuid()))
func NewDCEPerson() (UUID, error) {
return NewDCESecurity(Person, uint32(os.Getuid()))
}
// NewDCEGroup returns a DCE Security (Version 2) UUID in the group
// domain with the id returned by os.Getgid.
//
// NewDCESecurity(Group, uint32(os.Getgid()))
func NewDCEGroup() (UUID, error) {
return NewDCESecurity(Group, uint32(os.Getgid()))
}
// Domain returns the domain for a Version 2 UUID. Domains are only defined
// for Version 2 UUIDs.
func (uuid UUID) Domain() Domain {
return Domain(uuid[9])
}
// ID returns the id for a Version 2 UUID. IDs are only defined for Version 2
// UUIDs.
func (uuid UUID) ID() uint32 {
return binary.BigEndian.Uint32(uuid[0:4])
}
func (d Domain) String() string {
switch d {
case Person:
return "Person"
case Group:
return "Group"
case Org:
return "Org"
}
return fmt.Sprintf("Domain%d", int(d))
}

12
vendor/github.com/google/uuid/doc.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package uuid generates and inspects UUIDs.
//
// UUIDs are based on RFC 4122 and DCE 1.1: Authentication and Security
// Services.
//
// A UUID is a 16 byte (128 bit) array. UUIDs may be used as keys to
// maps or compared directly.
package uuid

1
vendor/github.com/google/uuid/go.mod generated vendored Normal file
View File

@ -0,0 +1 @@
module github.com/google/uuid

53
vendor/github.com/google/uuid/hash.go generated vendored Normal file
View File

@ -0,0 +1,53 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"crypto/md5"
"crypto/sha1"
"hash"
)
// Well known namespace IDs and UUIDs
var (
NameSpaceDNS = Must(Parse("6ba7b810-9dad-11d1-80b4-00c04fd430c8"))
NameSpaceURL = Must(Parse("6ba7b811-9dad-11d1-80b4-00c04fd430c8"))
NameSpaceOID = Must(Parse("6ba7b812-9dad-11d1-80b4-00c04fd430c8"))
NameSpaceX500 = Must(Parse("6ba7b814-9dad-11d1-80b4-00c04fd430c8"))
Nil UUID // empty UUID, all zeros
)
// NewHash returns a new UUID derived from the hash of space concatenated with
// data generated by h. The hash should be at least 16 byte in length. The
// first 16 bytes of the hash are used to form the UUID. The version of the
// UUID will be the lower 4 bits of version. NewHash is used to implement
// NewMD5 and NewSHA1.
func NewHash(h hash.Hash, space UUID, data []byte, version int) UUID {
h.Reset()
h.Write(space[:])
h.Write(data)
s := h.Sum(nil)
var uuid UUID
copy(uuid[:], s)
uuid[6] = (uuid[6] & 0x0f) | uint8((version&0xf)<<4)
uuid[8] = (uuid[8] & 0x3f) | 0x80 // RFC 4122 variant
return uuid
}
// NewMD5 returns a new MD5 (Version 3) UUID based on the
// supplied name space and data. It is the same as calling:
//
// NewHash(md5.New(), space, data, 3)
func NewMD5(space UUID, data []byte) UUID {
return NewHash(md5.New(), space, data, 3)
}
// NewSHA1 returns a new SHA1 (Version 5) UUID based on the
// supplied name space and data. It is the same as calling:
//
// NewHash(sha1.New(), space, data, 5)
func NewSHA1(space UUID, data []byte) UUID {
return NewHash(sha1.New(), space, data, 5)
}

37
vendor/github.com/google/uuid/marshal.go generated vendored Normal file
View File

@ -0,0 +1,37 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import "fmt"
// MarshalText implements encoding.TextMarshaler.
func (uuid UUID) MarshalText() ([]byte, error) {
var js [36]byte
encodeHex(js[:], uuid)
return js[:], nil
}
// UnmarshalText implements encoding.TextUnmarshaler.
func (uuid *UUID) UnmarshalText(data []byte) error {
id, err := ParseBytes(data)
if err == nil {
*uuid = id
}
return err
}
// MarshalBinary implements encoding.BinaryMarshaler.
func (uuid UUID) MarshalBinary() ([]byte, error) {
return uuid[:], nil
}
// UnmarshalBinary implements encoding.BinaryUnmarshaler.
func (uuid *UUID) UnmarshalBinary(data []byte) error {
if len(data) != 16 {
return fmt.Errorf("invalid UUID (got %d bytes)", len(data))
}
copy(uuid[:], data)
return nil
}

90
vendor/github.com/google/uuid/node.go generated vendored Normal file
View File

@ -0,0 +1,90 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"sync"
)
var (
nodeMu sync.Mutex
ifname string // name of interface being used
nodeID [6]byte // hardware for version 1 UUIDs
zeroID [6]byte // nodeID with only 0's
)
// NodeInterface returns the name of the interface from which the NodeID was
// derived. The interface "user" is returned if the NodeID was set by
// SetNodeID.
func NodeInterface() string {
defer nodeMu.Unlock()
nodeMu.Lock()
return ifname
}
// SetNodeInterface selects the hardware address to be used for Version 1 UUIDs.
// If name is "" then the first usable interface found will be used or a random
// Node ID will be generated. If a named interface cannot be found then false
// is returned.
//
// SetNodeInterface never fails when name is "".
func SetNodeInterface(name string) bool {
defer nodeMu.Unlock()
nodeMu.Lock()
return setNodeInterface(name)
}
func setNodeInterface(name string) bool {
iname, addr := getHardwareInterface(name) // null implementation for js
if iname != "" && addr != nil {
ifname = iname
copy(nodeID[:], addr)
return true
}
// We found no interfaces with a valid hardware address. If name
// does not specify a specific interface generate a random Node ID
// (section 4.1.6)
if name == "" {
ifname = "random"
randomBits(nodeID[:])
return true
}
return false
}
// NodeID returns a slice of a copy of the current Node ID, setting the Node ID
// if not already set.
func NodeID() []byte {
defer nodeMu.Unlock()
nodeMu.Lock()
if nodeID == zeroID {
setNodeInterface("")
}
nid := nodeID
return nid[:]
}
// SetNodeID sets the Node ID to be used for Version 1 UUIDs. The first 6 bytes
// of id are used. If id is less than 6 bytes then false is returned and the
// Node ID is not set.
func SetNodeID(id []byte) bool {
if len(id) < 6 {
return false
}
defer nodeMu.Unlock()
nodeMu.Lock()
copy(nodeID[:], id)
ifname = "user"
return true
}
// NodeID returns the 6 byte node id encoded in uuid. It returns nil if uuid is
// not valid. The NodeID is only well defined for version 1 and 2 UUIDs.
func (uuid UUID) NodeID() []byte {
var node [6]byte
copy(node[:], uuid[10:])
return node[:]
}

12
vendor/github.com/google/uuid/node_js.go generated vendored Normal file
View File

@ -0,0 +1,12 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build js
package uuid
// getHardwareInterface returns nil values for the JS version of the code.
// This remvoves the "net" dependency, because it is not used in the browser.
// Using the "net" library inflates the size of the transpiled JS code by 673k bytes.
func getHardwareInterface(name string) (string, []byte) { return "", nil }

33
vendor/github.com/google/uuid/node_net.go generated vendored Normal file
View File

@ -0,0 +1,33 @@
// Copyright 2017 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !js
package uuid
import "net"
var interfaces []net.Interface // cached list of interfaces
// getHardwareInterface returns the name and hardware address of interface name.
// If name is "" then the name and hardware address of one of the system's
// interfaces is returned. If no interfaces are found (name does not exist or
// there are no interfaces) then "", nil is returned.
//
// Only addresses of at least 6 bytes are returned.
func getHardwareInterface(name string) (string, []byte) {
if interfaces == nil {
var err error
interfaces, err = net.Interfaces()
if err != nil {
return "", nil
}
}
for _, ifs := range interfaces {
if len(ifs.HardwareAddr) >= 6 && (name == "" || name == ifs.Name) {
return ifs.Name, ifs.HardwareAddr
}
}
return "", nil
}

59
vendor/github.com/google/uuid/sql.go generated vendored Normal file
View File

@ -0,0 +1,59 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"database/sql/driver"
"fmt"
)
// Scan implements sql.Scanner so UUIDs can be read from databases transparently
// Currently, database types that map to string and []byte are supported. Please
// consult database-specific driver documentation for matching types.
func (uuid *UUID) Scan(src interface{}) error {
switch src := src.(type) {
case nil:
return nil
case string:
// if an empty UUID comes from a table, we return a null UUID
if src == "" {
return nil
}
// see Parse for required string format
u, err := Parse(src)
if err != nil {
return fmt.Errorf("Scan: %v", err)
}
*uuid = u
case []byte:
// if an empty UUID comes from a table, we return a null UUID
if len(src) == 0 {
return nil
}
// assumes a simple slice of bytes if 16 bytes
// otherwise attempts to parse
if len(src) != 16 {
return uuid.Scan(string(src))
}
copy((*uuid)[:], src)
default:
return fmt.Errorf("Scan: unable to scan type %T into UUID", src)
}
return nil
}
// Value implements sql.Valuer so that UUIDs can be written to databases
// transparently. Currently, UUIDs map to strings. Please consult
// database-specific driver documentation for matching types.
func (uuid UUID) Value() (driver.Value, error) {
return uuid.String(), nil
}

123
vendor/github.com/google/uuid/time.go generated vendored Normal file
View File

@ -0,0 +1,123 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"encoding/binary"
"sync"
"time"
)
// A Time represents a time as the number of 100's of nanoseconds since 15 Oct
// 1582.
type Time int64
const (
lillian = 2299160 // Julian day of 15 Oct 1582
unix = 2440587 // Julian day of 1 Jan 1970
epoch = unix - lillian // Days between epochs
g1582 = epoch * 86400 // seconds between epochs
g1582ns100 = g1582 * 10000000 // 100s of a nanoseconds between epochs
)
var (
timeMu sync.Mutex
lasttime uint64 // last time we returned
clockSeq uint16 // clock sequence for this run
timeNow = time.Now // for testing
)
// UnixTime converts t the number of seconds and nanoseconds using the Unix
// epoch of 1 Jan 1970.
func (t Time) UnixTime() (sec, nsec int64) {
sec = int64(t - g1582ns100)
nsec = (sec % 10000000) * 100
sec /= 10000000
return sec, nsec
}
// GetTime returns the current Time (100s of nanoseconds since 15 Oct 1582) and
// clock sequence as well as adjusting the clock sequence as needed. An error
// is returned if the current time cannot be determined.
func GetTime() (Time, uint16, error) {
defer timeMu.Unlock()
timeMu.Lock()
return getTime()
}
func getTime() (Time, uint16, error) {
t := timeNow()
// If we don't have a clock sequence already, set one.
if clockSeq == 0 {
setClockSequence(-1)
}
now := uint64(t.UnixNano()/100) + g1582ns100
// If time has gone backwards with this clock sequence then we
// increment the clock sequence
if now <= lasttime {
clockSeq = ((clockSeq + 1) & 0x3fff) | 0x8000
}
lasttime = now
return Time(now), clockSeq, nil
}
// ClockSequence returns the current clock sequence, generating one if not
// already set. The clock sequence is only used for Version 1 UUIDs.
//
// The uuid package does not use global static storage for the clock sequence or
// the last time a UUID was generated. Unless SetClockSequence is used, a new
// random clock sequence is generated the first time a clock sequence is
// requested by ClockSequence, GetTime, or NewUUID. (section 4.2.1.1)
func ClockSequence() int {
defer timeMu.Unlock()
timeMu.Lock()
return clockSequence()
}
func clockSequence() int {
if clockSeq == 0 {
setClockSequence(-1)
}
return int(clockSeq & 0x3fff)
}
// SetClockSequence sets the clock sequence to the lower 14 bits of seq. Setting to
// -1 causes a new sequence to be generated.
func SetClockSequence(seq int) {
defer timeMu.Unlock()
timeMu.Lock()
setClockSequence(seq)
}
func setClockSequence(seq int) {
if seq == -1 {
var b [2]byte
randomBits(b[:]) // clock sequence
seq = int(b[0])<<8 | int(b[1])
}
oldSeq := clockSeq
clockSeq = uint16(seq&0x3fff) | 0x8000 // Set our variant
if oldSeq != clockSeq {
lasttime = 0
}
}
// Time returns the time in 100s of nanoseconds since 15 Oct 1582 encoded in
// uuid. The time is only defined for version 1 and 2 UUIDs.
func (uuid UUID) Time() Time {
time := int64(binary.BigEndian.Uint32(uuid[0:4]))
time |= int64(binary.BigEndian.Uint16(uuid[4:6])) << 32
time |= int64(binary.BigEndian.Uint16(uuid[6:8])&0xfff) << 48
return Time(time)
}
// ClockSequence returns the clock sequence encoded in uuid.
// The clock sequence is only well defined for version 1 and 2 UUIDs.
func (uuid UUID) ClockSequence() int {
return int(binary.BigEndian.Uint16(uuid[8:10])) & 0x3fff
}

43
vendor/github.com/google/uuid/util.go generated vendored Normal file
View File

@ -0,0 +1,43 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"io"
)
// randomBits completely fills slice b with random data.
func randomBits(b []byte) {
if _, err := io.ReadFull(rander, b); err != nil {
panic(err.Error()) // rand should never fail
}
}
// xvalues returns the value of a byte as a hexadecimal digit or 255.
var xvalues = [256]byte{
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255, 255, 255, 255, 255, 255,
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 10, 11, 12, 13, 14, 15, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
}
// xtob converts hex characters x1 and x2 into a byte.
func xtob(x1, x2 byte) (byte, bool) {
b1 := xvalues[x1]
b2 := xvalues[x2]
return (b1 << 4) | b2, b1 != 255 && b2 != 255
}

245
vendor/github.com/google/uuid/uuid.go generated vendored Normal file
View File

@ -0,0 +1,245 @@
// Copyright 2018 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"bytes"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"io"
"strings"
)
// A UUID is a 128 bit (16 byte) Universal Unique IDentifier as defined in RFC
// 4122.
type UUID [16]byte
// A Version represents a UUID's version.
type Version byte
// A Variant represents a UUID's variant.
type Variant byte
// Constants returned by Variant.
const (
Invalid = Variant(iota) // Invalid UUID
RFC4122 // The variant specified in RFC4122
Reserved // Reserved, NCS backward compatibility.
Microsoft // Reserved, Microsoft Corporation backward compatibility.
Future // Reserved for future definition.
)
var rander = rand.Reader // random function
// Parse decodes s into a UUID or returns an error. Both the standard UUID
// forms of xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx and
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx are decoded as well as the
// Microsoft encoding {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx} and the raw hex
// encoding: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.
func Parse(s string) (UUID, error) {
var uuid UUID
switch len(s) {
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
case 36:
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
case 36 + 9:
if strings.ToLower(s[:9]) != "urn:uuid:" {
return uuid, fmt.Errorf("invalid urn prefix: %q", s[:9])
}
s = s[9:]
// {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
case 36 + 2:
s = s[1:]
// xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
case 32:
var ok bool
for i := range uuid {
uuid[i], ok = xtob(s[i*2], s[i*2+1])
if !ok {
return uuid, errors.New("invalid UUID format")
}
}
return uuid, nil
default:
return uuid, fmt.Errorf("invalid UUID length: %d", len(s))
}
// s is now at least 36 bytes long
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
return uuid, errors.New("invalid UUID format")
}
for i, x := range [16]int{
0, 2, 4, 6,
9, 11,
14, 16,
19, 21,
24, 26, 28, 30, 32, 34} {
v, ok := xtob(s[x], s[x+1])
if !ok {
return uuid, errors.New("invalid UUID format")
}
uuid[i] = v
}
return uuid, nil
}
// ParseBytes is like Parse, except it parses a byte slice instead of a string.
func ParseBytes(b []byte) (UUID, error) {
var uuid UUID
switch len(b) {
case 36: // xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
case 36 + 9: // urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
if !bytes.Equal(bytes.ToLower(b[:9]), []byte("urn:uuid:")) {
return uuid, fmt.Errorf("invalid urn prefix: %q", b[:9])
}
b = b[9:]
case 36 + 2: // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
b = b[1:]
case 32: // xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
var ok bool
for i := 0; i < 32; i += 2 {
uuid[i/2], ok = xtob(b[i], b[i+1])
if !ok {
return uuid, errors.New("invalid UUID format")
}
}
return uuid, nil
default:
return uuid, fmt.Errorf("invalid UUID length: %d", len(b))
}
// s is now at least 36 bytes long
// it must be of the form xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
if b[8] != '-' || b[13] != '-' || b[18] != '-' || b[23] != '-' {
return uuid, errors.New("invalid UUID format")
}
for i, x := range [16]int{
0, 2, 4, 6,
9, 11,
14, 16,
19, 21,
24, 26, 28, 30, 32, 34} {
v, ok := xtob(b[x], b[x+1])
if !ok {
return uuid, errors.New("invalid UUID format")
}
uuid[i] = v
}
return uuid, nil
}
// MustParse is like Parse but panics if the string cannot be parsed.
// It simplifies safe initialization of global variables holding compiled UUIDs.
func MustParse(s string) UUID {
uuid, err := Parse(s)
if err != nil {
panic(`uuid: Parse(` + s + `): ` + err.Error())
}
return uuid
}
// FromBytes creates a new UUID from a byte slice. Returns an error if the slice
// does not have a length of 16. The bytes are copied from the slice.
func FromBytes(b []byte) (uuid UUID, err error) {
err = uuid.UnmarshalBinary(b)
return uuid, err
}
// Must returns uuid if err is nil and panics otherwise.
func Must(uuid UUID, err error) UUID {
if err != nil {
panic(err)
}
return uuid
}
// String returns the string form of uuid, xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
// , or "" if uuid is invalid.
func (uuid UUID) String() string {
var buf [36]byte
encodeHex(buf[:], uuid)
return string(buf[:])
}
// URN returns the RFC 2141 URN form of uuid,
// urn:uuid:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, or "" if uuid is invalid.
func (uuid UUID) URN() string {
var buf [36 + 9]byte
copy(buf[:], "urn:uuid:")
encodeHex(buf[9:], uuid)
return string(buf[:])
}
func encodeHex(dst []byte, uuid UUID) {
hex.Encode(dst, uuid[:4])
dst[8] = '-'
hex.Encode(dst[9:13], uuid[4:6])
dst[13] = '-'
hex.Encode(dst[14:18], uuid[6:8])
dst[18] = '-'
hex.Encode(dst[19:23], uuid[8:10])
dst[23] = '-'
hex.Encode(dst[24:], uuid[10:])
}
// Variant returns the variant encoded in uuid.
func (uuid UUID) Variant() Variant {
switch {
case (uuid[8] & 0xc0) == 0x80:
return RFC4122
case (uuid[8] & 0xe0) == 0xc0:
return Microsoft
case (uuid[8] & 0xe0) == 0xe0:
return Future
default:
return Reserved
}
}
// Version returns the version of uuid.
func (uuid UUID) Version() Version {
return Version(uuid[6] >> 4)
}
func (v Version) String() string {
if v > 15 {
return fmt.Sprintf("BAD_VERSION_%d", v)
}
return fmt.Sprintf("VERSION_%d", v)
}
func (v Variant) String() string {
switch v {
case RFC4122:
return "RFC4122"
case Reserved:
return "Reserved"
case Microsoft:
return "Microsoft"
case Future:
return "Future"
case Invalid:
return "Invalid"
}
return fmt.Sprintf("BadVariant%d", int(v))
}
// SetRand sets the random number generator to r, which implements io.Reader.
// If r.Read returns an error when the package requests random data then
// a panic will be issued.
//
// Calling SetRand with nil sets the random number generator to the default
// generator.
func SetRand(r io.Reader) {
if r == nil {
rander = rand.Reader
return
}
rander = r
}

44
vendor/github.com/google/uuid/version1.go generated vendored Normal file
View File

@ -0,0 +1,44 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import (
"encoding/binary"
)
// NewUUID returns a Version 1 UUID based on the current NodeID and clock
// sequence, and the current time. If the NodeID has not been set by SetNodeID
// or SetNodeInterface then it will be set automatically. If the NodeID cannot
// be set NewUUID returns nil. If clock sequence has not been set by
// SetClockSequence then it will be set automatically. If GetTime fails to
// return the current NewUUID returns nil and an error.
//
// In most cases, New should be used.
func NewUUID() (UUID, error) {
nodeMu.Lock()
if nodeID == zeroID {
setNodeInterface("")
}
nodeMu.Unlock()
var uuid UUID
now, seq, err := GetTime()
if err != nil {
return uuid, err
}
timeLow := uint32(now & 0xffffffff)
timeMid := uint16((now >> 32) & 0xffff)
timeHi := uint16((now >> 48) & 0x0fff)
timeHi |= 0x1000 // Version 1
binary.BigEndian.PutUint32(uuid[0:], timeLow)
binary.BigEndian.PutUint16(uuid[4:], timeMid)
binary.BigEndian.PutUint16(uuid[6:], timeHi)
binary.BigEndian.PutUint16(uuid[8:], seq)
copy(uuid[10:], nodeID[:])
return uuid, nil
}

38
vendor/github.com/google/uuid/version4.go generated vendored Normal file
View File

@ -0,0 +1,38 @@
// Copyright 2016 Google Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package uuid
import "io"
// New creates a new random UUID or panics. New is equivalent to
// the expression
//
// uuid.Must(uuid.NewRandom())
func New() UUID {
return Must(NewRandom())
}
// NewRandom returns a Random (Version 4) UUID.
//
// The strength of the UUIDs is based on the strength of the crypto/rand
// package.
//
// A note about uniqueness derived from the UUID Wikipedia entry:
//
// Randomly generated UUIDs have 122 random bits. One's annual risk of being
// hit by a meteorite is estimated to be one chance in 17 billion, that
// means the probability is about 0.00000000006 (6 × 1011),
// equivalent to the odds of creating a few tens of trillions of UUIDs in a
// year and having one duplicate.
func NewRandom() (UUID, error) {
var uuid UUID
_, err := io.ReadFull(rander, uuid[:])
if err != nil {
return Nil, err
}
uuid[6] = (uuid[6] & 0x0f) | 0x40 // Version 4
uuid[8] = (uuid[8] & 0x3f) | 0x80 // Variant is 10
return uuid, nil
}

20
vendor/github.com/sethvargo/go-password/LICENSE generated vendored Normal file
View File

@ -0,0 +1,20 @@
Copyright 2017 Seth Vargo <seth@sethvargo.com>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,253 @@
// Package password provides a library for generating high-entropy random
// password strings via the crypto/rand package.
//
// res, err := Generate(64, 10, 10, false, false)
// if err != nil {
// log.Fatal(err)
// }
// log.Printf(res)
//
// Most functions are safe for concurrent use.
package password
import (
"crypto/rand"
"errors"
"math/big"
"strings"
)
// Built-time checks that the generators implement the interface.
var _ PasswordGenerator = (*Generator)(nil)
// PasswordGenerator is an interface that implements the Generate function. This
// is useful for testing where you can pass this interface instead of a real
// password generator to mock responses for predicability.
type PasswordGenerator interface {
Generate(int, int, int, bool, bool) (string, error)
MustGenerate(int, int, int, bool, bool) string
}
const (
// LowerLetters is the list of lowercase letters.
LowerLetters = "abcdefghijklmnopqrstuvwxyz"
// UpperLetters is the list of uppercase letters.
UpperLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
// Digits is the list of permitted digits.
Digits = "0123456789"
// Symbols is the list of symbols.
Symbols = "~!@#$%^&*()_+`-={}|[]\\:\"<>?,./"
)
var (
// ErrExceedsTotalLength is the error returned with the number of digits and
// symbols is greater than the total length.
ErrExceedsTotalLength = errors.New("number of digits and symbols must be less than total length")
// ErrLettersExceedsAvailable is the error returned with the number of letters
// exceeds the number of available letters and repeats are not allowed.
ErrLettersExceedsAvailable = errors.New("number of letters exceeds available letters and repeats are not allowed")
// ErrDigitsExceedsAvailable is the error returned with the number of digits
// exceeds the number of available digits and repeats are not allowed.
ErrDigitsExceedsAvailable = errors.New("number of digits exceeds available digits and repeats are not allowed")
// ErrSymbolsExceedsAvailable is the error returned with the number of symbols
// exceeds the number of available symbols and repeats are not allowed.
ErrSymbolsExceedsAvailable = errors.New("number of symbols exceeds available symbols and repeats are not allowed")
)
// Generator is the stateful generator which can be used to customize the list
// of letters, digits, and/or symbols.
type Generator struct {
lowerLetters string
upperLetters string
digits string
symbols string
}
// GeneratorInput is used as input to the NewGenerator function.
type GeneratorInput struct {
LowerLetters string
UpperLetters string
Digits string
Symbols string
}
// NewGenerator creates a new Generator from the specified configuration. If no
// input is given, all the default values are used. This function is safe for
// concurrent use.
func NewGenerator(i *GeneratorInput) (*Generator, error) {
if i == nil {
i = new(GeneratorInput)
}
g := &Generator{
lowerLetters: i.LowerLetters,
upperLetters: i.UpperLetters,
digits: i.Digits,
symbols: i.Symbols,
}
if g.lowerLetters == "" {
g.lowerLetters = LowerLetters
}
if g.upperLetters == "" {
g.upperLetters = UpperLetters
}
if g.digits == "" {
g.digits = Digits
}
if g.symbols == "" {
g.symbols = Symbols
}
return g, nil
}
// Generate generates a password with the given requirements. length is the
// total number of characters in the password. numDigits is the number of digits
// to include in the result. numSymbols is the number of symbols to include in
// the result. noUpper excludes uppercase letters from the results. allowRepeat
// allows characters to repeat.
//
// The algorithm is fast, but it's not designed to be performant; it favors
// entropy over speed. This function is safe for concurrent use.
func (g *Generator) Generate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) (string, error) {
letters := g.lowerLetters
if !noUpper {
letters += g.upperLetters
}
chars := length - numDigits - numSymbols
if chars < 0 {
return "", ErrExceedsTotalLength
}
if !allowRepeat && chars > len(letters) {
return "", ErrLettersExceedsAvailable
}
if !allowRepeat && numDigits > len(g.digits) {
return "", ErrDigitsExceedsAvailable
}
if !allowRepeat && numSymbols > len(g.symbols) {
return "", ErrSymbolsExceedsAvailable
}
var result string
// Characters
for i := 0; i < chars; i++ {
ch, err := randomElement(letters)
if err != nil {
return "", err
}
if !allowRepeat && strings.Contains(result, ch) {
i--
continue
}
result, err = randomInsert(result, ch)
if err != nil {
return "", err
}
}
// Digits
for i := 0; i < numDigits; i++ {
d, err := randomElement(g.digits)
if err != nil {
return "", err
}
if !allowRepeat && strings.Contains(result, d) {
i--
continue
}
result, err = randomInsert(result, d)
if err != nil {
return "", err
}
}
// Symbols
for i := 0; i < numSymbols; i++ {
sym, err := randomElement(g.symbols)
if err != nil {
return "", err
}
if !allowRepeat && strings.Contains(result, sym) {
i--
continue
}
result, err = randomInsert(result, sym)
if err != nil {
return "", err
}
}
return result, nil
}
// MustGenerate is the same as Generate, but panics on error.
func (g *Generator) MustGenerate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) string {
res, err := g.Generate(length, numDigits, numSymbols, noUpper, allowRepeat)
if err != nil {
panic(err)
}
return res
}
// Generate is the package shortcut for Generator.Generate.
func Generate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) (string, error) {
gen, err := NewGenerator(nil)
if err != nil {
return "", err
}
return gen.Generate(length, numDigits, numSymbols, noUpper, allowRepeat)
}
// MustGenerate is the package shortcut for Generator.MustGenerate.
func MustGenerate(length, numDigits, numSymbols int, noUpper, allowRepeat bool) string {
res, err := Generate(length, numDigits, numSymbols, noUpper, allowRepeat)
if err != nil {
panic(err)
}
return res
}
// randomInsert randomly inserts the given value into the given string.
func randomInsert(s, val string) (string, error) {
if s == "" {
return val, nil
}
n, err := rand.Int(rand.Reader, big.NewInt(int64(len(s)+1)))
if err != nil {
return "", err
}
i := n.Int64()
return s[0:i] + val + s[i:], nil
}
// randomElement extracts a random element from the given string.
func randomElement(s string) (string, error) {
n, err := rand.Int(rand.Reader, big.NewInt(int64(len(s))))
if err != nil {
return "", err
}
return string(s[n.Int64()]), nil
}

View File

@ -0,0 +1,39 @@
package password
// Built-time checks that the generators implement the interface.
var _ PasswordGenerator = (*mockGenerator)(nil)
type mockGenerator struct {
result string
err error
}
// NewMockGenerator creates a new generator that satisfies the PasswordGenerator
// interface. If an error is provided, the error is returned. If a result if
// provided, the result is always returned, regardless of what parameters are
// passed into the Generate or MustGenerate methods.
//
// This function is most useful for tests where you want to have predicable
// results for a transitive resource that depends on go-password.
func NewMockGenerator(result string, err error) *mockGenerator {
return &mockGenerator{
result: result,
err: err,
}
}
// Generate returns the mocked result or error.
func (g *mockGenerator) Generate(int, int, int, bool, bool) (string, error) {
if g.err != nil {
return "", g.err
}
return g.result, nil
}
// MustGenerate returns the mocked result or panics if an error was given.
func (g *mockGenerator) MustGenerate(int, int, int, bool, bool) string {
if g.err != nil {
panic(g.err)
}
return g.result
}