Compare commits
126 Commits
Author | SHA1 | Date | |
---|---|---|---|
9b8de6a613 | |||
dacb671328 | |||
799d3cbf4c | |||
bd589992fb | |||
839d918e35 | |||
c50490eb76 | |||
01a8de9997 | |||
8a88f90f94 | |||
2b6cd5467f | |||
3aadb402e4 | |||
021462563b | |||
d713ec692c | |||
813f541d30 | |||
6eb8e31d21 | |||
051452cdcf | |||
877602d627 | |||
c90b165c6e | |||
28773dc925 | |||
2bd04cb92f | |||
d4775ecff5 | |||
e1f8f9bee5 | |||
68a80bcf9b | |||
1fb9793607 | |||
5cb3a5e897 | |||
b76fdd7c03 | |||
67175607ad | |||
b9560fd5c1 | |||
79192cb1f1 | |||
ad10b6fa91 | |||
219eb9e046 | |||
f7a2fc97e4 | |||
6957f6ca4e | |||
02bfece2e9 | |||
5af9ff493e | |||
44d92c19de | |||
5e0fbd8374 | |||
a78853f29f | |||
2d2583ee33 | |||
f4332fec59 | |||
ed16760739 | |||
30776ff858 | |||
2a48d68937 | |||
117e30ff21 | |||
486ef96e6f | |||
8a0e3fe10e | |||
ca419073e4 | |||
47a9fd80c8 | |||
112288ecb2 | |||
32fc3ee9d3 | |||
c7e2cf7602 | |||
53854dd948 | |||
5c512194eb | |||
a9b4e04bc4 | |||
f5c3d1b1ba | |||
8bf6a7b362 | |||
66e0aaf9c1 | |||
45fd949465 | |||
2ff84a481e | |||
37207f05b4 | |||
832f2699c2 | |||
d1360b82ab | |||
1f33fb729a | |||
754e153b03 | |||
0edf8a4208 | |||
db7e6cfabf | |||
963aaf86e6 | |||
cd9d6b28da | |||
0452c1dd10 | |||
d671d29ad5 | |||
cc6154603e | |||
62b36d2fbc | |||
e5fdd449dd | |||
8db5e4d41b | |||
ec8f6c99d0 | |||
7dea2a4c1b | |||
5a02c5bc61 | |||
bf8f171041 | |||
3603738c6a | |||
d8b1289098 | |||
6551165853 | |||
10a01b09ae | |||
497560f35f | |||
58dd90b996 | |||
d5efdfe1f6 | |||
05f121a406 | |||
825fbd8a95 | |||
1a30688da0 | |||
bee8d6cf30 | |||
a16232968d | |||
1880421389 | |||
a2ed3d9a69 | |||
7bcaae263f | |||
e1f955d9bf | |||
2583a0b4ad | |||
85083ea434 | |||
2290fc8d8a | |||
411d060b81 | |||
5915b49b38 | |||
c25c62742b | |||
b7ffa24326 | |||
15894b36a0 | |||
77b51f0bc9 | |||
bd63528b0b | |||
cf187287af | |||
0dff883769 | |||
d0eeb27494 | |||
e70558cbe1 | |||
0a1421a08c | |||
0f19aa2f8d | |||
e91889678b | |||
8ec6bd6a42 | |||
fc7059c1ae | |||
a96c469e62 | |||
291ab6cc84 | |||
90125f40ba | |||
23d5525ec3 | |||
fd42109a06 | |||
4bb288193c | |||
e8365e126d | |||
7e68430081 | |||
f81a529ebd | |||
630a4d8db6 | |||
3d56f7504d | |||
659a09f34e | |||
b76ace9c64 | |||
0d0dcfc02f |
12
.travis.yml
12
.travis.yml
@ -1,10 +1,10 @@
|
||||
language: go
|
||||
sudo: required
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
|
||||
go:
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
- 1.14.x
|
||||
|
||||
env:
|
||||
global:
|
||||
@ -17,16 +17,17 @@ env:
|
||||
- TARGET=arm64
|
||||
- TARGET=ppc64le
|
||||
- TARGET=s390x
|
||||
- TARGET=mips64le
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
include:
|
||||
- os: windows
|
||||
env: TARGET=amd64
|
||||
go: 1.11.x
|
||||
go: 1.13.x
|
||||
- os: windows
|
||||
env: TARGET=amd64
|
||||
go: 1.12.x
|
||||
go: 1.14.x
|
||||
|
||||
install:
|
||||
- go get github.com/onsi/ginkgo/ginkgo
|
||||
@ -34,6 +35,7 @@ install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
- go get github.com/modocache/gover
|
||||
- go get github.com/mattn/goveralls
|
||||
- go mod vendor
|
||||
|
||||
script:
|
||||
- |
|
||||
|
@ -72,10 +72,11 @@ vagrant ssh
|
||||
# you're now in a shell in a virtual machine
|
||||
sudo su
|
||||
go get github.com/onsi/ginkgo/ginkgo
|
||||
go install github.com/containernetworking/cni/cnitool
|
||||
cd /go/src/github.com/containernetworking/plugins
|
||||
|
||||
# to run the full test suite
|
||||
./test.sh
|
||||
./test_linux.sh
|
||||
|
||||
# to focus on a particular test suite
|
||||
cd plugins/main/loopback
|
||||
|
@ -1,8 +1,10 @@
|
||||
# Owners
|
||||
This is the official list of the CNI network plugins owners:
|
||||
- Bruce Ma <brucema19901024@gmail.com> (@mars1024)
|
||||
- Bryan Boreham <bryan@weave.works> (@bboreham)
|
||||
- Casey Callendrello <casey.callendrello@coreos.com> (@squeed)
|
||||
- Casey Callendrello <cdc@redhat.com> (@squeed)
|
||||
- Dan Williams <dcbw@redhat.com> (@dcbw)
|
||||
- Gabe Rosenhouse <grosenhouse@pivotal.io> (@rosenhouse)
|
||||
- Matt Dupre <matt@tigera.io> (@matthewdupre)
|
||||
- Stefan Junker <stefan.junker@coreos.com> (@steveeJ)
|
||||
- Michael Cambria <mcambria@redhat.com> (@mccv1r0)
|
||||
- Piotr Skarmuk <piotr.skarmuk@gmail.com> (@jellonek)
|
||||
|
@ -32,3 +32,11 @@ Read [CONTRIBUTING](CONTRIBUTING.md) for build and test instructions.
|
||||
|
||||
### Sample
|
||||
The sample plugin provides an example for building your own plugin.
|
||||
|
||||
## Contact
|
||||
|
||||
For any questions about CNI, please reach out via:
|
||||
- Email: [cni-dev](https://groups.google.com/forum/#!forum/cni-dev)
|
||||
- Slack: #cni on the [CNCF slack](https://slack.cncf.io/).
|
||||
|
||||
If you have a _security_ issue to report, please do so privately to the email addresses listed in the [OWNERS](OWNERS.md) file.
|
||||
|
18
Vagrantfile
vendored
18
Vagrantfile
vendored
@ -1,18 +0,0 @@
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
Vagrant.configure(2) do |config|
|
||||
config.vm.box = "bento/ubuntu-16.04"
|
||||
|
||||
config.vm.synced_folder "..", "/go/src/github.com/containernetworking"
|
||||
|
||||
config.vm.provision "shell", inline: <<-SHELL
|
||||
set -e -x -u
|
||||
apt-get update -y || (sleep 40 && apt-get update -y)
|
||||
apt-get install -y git gcc-multilib gcc-mingw-w64
|
||||
wget -qO- https://storage.googleapis.com/golang/go1.12.7.linux-amd64.tar.gz | tar -C /usr/local -xz
|
||||
echo 'export GOPATH=/go' >> /root/.bashrc
|
||||
echo 'export PATH=$PATH:/usr/local/go/bin:$GOPATH/bin' >> /root/.bashrc
|
||||
cd /go/src/github.com/containernetworking/plugins
|
||||
SHELL
|
||||
end
|
13
go.mod
13
go.mod
@ -1,14 +1,14 @@
|
||||
module github.com/containernetworking/plugins
|
||||
|
||||
go 1.12
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/Microsoft/go-winio v0.4.11 // indirect
|
||||
github.com/Microsoft/hcsshim v0.8.6
|
||||
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae
|
||||
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44
|
||||
github.com/containernetworking/cni v0.7.0
|
||||
github.com/coreos/go-iptables v0.4.2
|
||||
github.com/containernetworking/cni v0.8.0
|
||||
github.com/coreos/go-iptables v0.4.5
|
||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7
|
||||
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c
|
||||
github.com/d2g/dhcp4client v1.0.0
|
||||
@ -17,10 +17,6 @@ require (
|
||||
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c
|
||||
github.com/golang/protobuf v1.3.1 // indirect
|
||||
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56
|
||||
github.com/juju/errors v0.0.0-20180806074554-22422dad46e1
|
||||
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 // indirect
|
||||
github.com/juju/testing v0.0.0-20190613124551-e81189438503 // indirect
|
||||
github.com/kr/pretty v0.1.0 // indirect
|
||||
github.com/mattn/go-shellwords v1.0.3
|
||||
github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b
|
||||
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a
|
||||
@ -33,8 +29,5 @@ require (
|
||||
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1 // indirect
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 // indirect
|
||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce // indirect
|
||||
gopkg.in/yaml.v2 v2.2.2 // indirect
|
||||
)
|
||||
|
26
go.sum
26
go.sum
@ -6,10 +6,10 @@ github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae h1:AMzIhMUq
|
||||
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
|
||||
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44 h1:y853v6rXx+zefEcjET3JuKAqvhj+FKflQijjeaSv2iA=
|
||||
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
||||
github.com/containernetworking/cni v0.7.0 h1:1Qy7EwdC08mx5wUB0DpjCuBrk6e/uXg9yI9TvAvgox8=
|
||||
github.com/containernetworking/cni v0.7.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
|
||||
github.com/coreos/go-iptables v0.4.2 h1:KH0EwId05JwWIfb96gWvkiT2cbuOu8ygqUaB+yPAwIg=
|
||||
github.com/coreos/go-iptables v0.4.2/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
|
||||
github.com/containernetworking/cni v0.8.0 h1:BT9lpgGoH4jw3lFC7Odz2prU5ruiYKcgAjMCbgybcKI=
|
||||
github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
|
||||
github.com/coreos/go-iptables v0.4.5 h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38=
|
||||
github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
|
||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM=
|
||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c h1:Xo2rK1pzOm0jO6abTPIQwbAmqBIOj132otexc1mmzFc=
|
||||
@ -28,17 +28,6 @@ github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56 h1:742eGXur0715JMq73aD95/FU0XpVKXqNuTnEfXsLOYQ=
|
||||
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
|
||||
github.com/juju/errors v0.0.0-20180806074554-22422dad46e1 h1:wnhMXidtb70kDZCeLt/EfsVtkXS5c8zLnE9y/6DIRAU=
|
||||
github.com/juju/errors v0.0.0-20180806074554-22422dad46e1/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
|
||||
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8 h1:UUHMLvzt/31azWTN/ifGWef4WUqvXk0iRqdhdy/2uzI=
|
||||
github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
|
||||
github.com/juju/testing v0.0.0-20190613124551-e81189438503 h1:ZUgTbk8oHgP0jpMieifGC9Lv47mHn8Pb3mFX3/Ew4iY=
|
||||
github.com/juju/testing v0.0.0-20190613124551-e81189438503/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mattn/go-shellwords v1.0.3 h1:K/VxK7SZ+cvuPgFSLKi5QPI9Vr/ipOf4C1gN+ntueUk=
|
||||
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||
github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b h1:Ey6yH0acn50T/v6CB75bGP4EMJqnv9WvnjN7oZaj+xE=
|
||||
@ -66,12 +55,5 @@ golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0=
|
||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU=
|
||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
@ -149,12 +149,12 @@ var _ = Describe("Basic PTP using cnitool", func() {
|
||||
})
|
||||
|
||||
Measure("limits traffic only on the restricted bandwith veth device", func(b Benchmarker) {
|
||||
ipRegexp := regexp.MustCompile("10\\.11\\.2\\.\\d{1,3}")
|
||||
ipRegexp := regexp.MustCompile("10\\.1[12]\\.2\\.\\d{1,3}")
|
||||
|
||||
By(fmt.Sprintf("adding %s to %s\n\n", "chained-bridge-bandwidth", contNS1.ShortName()))
|
||||
chainedBridgeBandwidthEnv.runInNS(hostNS, cnitoolBin, "add", "network-chain-test", contNS1.LongName())
|
||||
chainedBridgeIP := ipRegexp.FindString(chainedBridgeBandwidthEnv.runInNS(contNS1, "ip", "addr"))
|
||||
Expect(chainedBridgeIP).To(ContainSubstring("10.11.2."))
|
||||
Expect(chainedBridgeIP).To(ContainSubstring("10.12.2."))
|
||||
|
||||
By(fmt.Sprintf("adding %s to %s\n\n", "basic-bridge", contNS2.ShortName()))
|
||||
basicBridgeEnv.runInNS(hostNS, cnitoolBin, "add", "network-chain-test", contNS2.LongName())
|
||||
|
@ -4,12 +4,12 @@
|
||||
"plugins": [
|
||||
{
|
||||
"type": "bridge",
|
||||
"bridge": "test-bridge-0",
|
||||
"bridge": "test-bridge-1",
|
||||
"isDefaultGateway": true,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.11.2.0/24",
|
||||
"dataDir": "/tmp/foo"
|
||||
"subnet": "10.12.2.0/24",
|
||||
"dataDir": "/tmp/bar"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
37
pkg/errors/errors.go
Normal file
37
pkg/errors/errors.go
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2020 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 errors
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Annotate is used to add extra context to an existing error. The return will be
|
||||
// a new error which carries error message from both context message and existing error.
|
||||
func Annotate(err error, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s: %v", message, err)
|
||||
}
|
||||
|
||||
// Annotatef is used to add extra context with args to an existing error. The return will be
|
||||
// a new error which carries error message from both context message and existing error.
|
||||
func Annotatef(err error, message string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("%s: %v", fmt.Sprintf(message, args...), err)
|
||||
}
|
96
pkg/errors/errors_test.go
Normal file
96
pkg/errors/errors_test.go
Normal file
@ -0,0 +1,96 @@
|
||||
// Copyright 2020 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 errors
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAnnotate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
existingErr error
|
||||
contextMessage string
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
"nil error",
|
||||
nil,
|
||||
"context",
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"normal case",
|
||||
errors.New("existing error"),
|
||||
"context",
|
||||
errors.New("context: existing error"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if !reflect.DeepEqual(Annotatef(test.existingErr, test.contextMessage), test.expectedErr) {
|
||||
t.Errorf("test case %s fails", test.name)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAnnotatef(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
existingErr error
|
||||
contextMessage string
|
||||
contextArgs []interface{}
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
"nil error",
|
||||
nil,
|
||||
"context",
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"normal case",
|
||||
errors.New("existing error"),
|
||||
"context",
|
||||
nil,
|
||||
errors.New("context: existing error"),
|
||||
},
|
||||
{
|
||||
"normal case with args",
|
||||
errors.New("existing error"),
|
||||
"context %s %d",
|
||||
[]interface{}{
|
||||
"arg",
|
||||
100,
|
||||
},
|
||||
errors.New("context arg 100: existing error"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if !reflect.DeepEqual(Annotatef(test.existingErr, test.contextMessage, test.contextArgs...), test.expectedErr) {
|
||||
t.Errorf("test case %s fails", test.name)
|
||||
return
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -21,9 +21,10 @@ import (
|
||||
|
||||
"github.com/Microsoft/hcsshim"
|
||||
"github.com/Microsoft/hcsshim/hcn"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/types/current"
|
||||
"github.com/juju/errors"
|
||||
"github.com/containernetworking/plugins/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -64,14 +65,14 @@ func GenerateHnsEndpoint(epInfo *EndpointInfo, n *NetConf) (*hcsshim.HNSEndpoint
|
||||
// run the IPAM plugin and get back the config to apply
|
||||
hnsEndpoint, err := hcsshim.GetHNSEndpointByName(epInfo.EndpointName)
|
||||
if err != nil && !hcsshim.IsNotExist(err) {
|
||||
return nil, errors.Annotatef(err, "Attempt to get endpoint \"%v\" failed", epInfo.EndpointName)
|
||||
return nil, errors.Annotatef(err, "failed to get endpoint %q", epInfo.EndpointName)
|
||||
}
|
||||
|
||||
if hnsEndpoint != nil {
|
||||
if hnsEndpoint.VirtualNetwork != epInfo.NetworkId {
|
||||
_, err = hnsEndpoint.Delete()
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "Failed to delete endpoint %v", epInfo.EndpointName)
|
||||
return nil, errors.Annotatef(err, "failed to delete endpoint %s", epInfo.EndpointName)
|
||||
}
|
||||
hnsEndpoint = nil
|
||||
}
|
||||
@ -98,7 +99,7 @@ func GenerateHcnEndpoint(epInfo *EndpointInfo, n *NetConf) (*hcn.HostComputeEndp
|
||||
// run the IPAM plugin and get back the config to apply
|
||||
hcnEndpoint, err := hcn.GetEndpointByName(epInfo.EndpointName)
|
||||
if err != nil && !hcn.IsNotFoundError(err) {
|
||||
return nil, errors.Annotatef(err, "Attempt to get endpoint \"%v\" failed", epInfo.EndpointName)
|
||||
return nil, errors.Annotatef(err, "failed to get endpoint %q", epInfo.EndpointName)
|
||||
}
|
||||
|
||||
if hcnEndpoint != nil {
|
||||
@ -108,12 +109,10 @@ func GenerateHcnEndpoint(epInfo *EndpointInfo, n *NetConf) (*hcn.HostComputeEndp
|
||||
if !strings.EqualFold(hcnEndpoint.HostComputeNetwork, epInfo.NetworkId) {
|
||||
err = hcnEndpoint.Delete()
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "Failed to delete endpoint %v", epInfo.EndpointName)
|
||||
hcnEndpoint = nil
|
||||
|
||||
return nil, errors.Annotatef(err, "failed to delete endpoint %s", epInfo.EndpointName)
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("Endpoint \"%v\" already exits", epInfo.EndpointName)
|
||||
return nil, fmt.Errorf("endpoint %q already exits", epInfo.EndpointName)
|
||||
}
|
||||
}
|
||||
|
||||
@ -270,7 +269,7 @@ func AddHcnEndpoint(epName string, expectedNetworkId string, namespace string,
|
||||
if err != nil {
|
||||
return nil, errors.Annotatef(err, "failed to Remove Endpoint after AddNamespaceEndpoint failure")
|
||||
}
|
||||
return nil, errors.Annotatef(err, "Failed to Add endpoint to namespace")
|
||||
return nil, errors.Annotate(err, "failed to Add endpoint to namespace")
|
||||
}
|
||||
return hcnEndpoint, nil
|
||||
|
||||
|
@ -20,10 +20,11 @@ import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"strings"
|
||||
|
||||
"github.com/Microsoft/hcsshim/hcn"
|
||||
"github.com/buger/jsonparser"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// NetConf is the CNI spec
|
||||
@ -46,8 +47,16 @@ type RuntimeDNS struct {
|
||||
Search []string `json:"searches,omitempty"`
|
||||
}
|
||||
|
||||
type PortMapEntry struct {
|
||||
HostPort int `json:"hostPort"`
|
||||
ContainerPort int `json:"containerPort"`
|
||||
Protocol string `json:"protocol"`
|
||||
HostIP string `json:"hostIP,omitempty"`
|
||||
}
|
||||
|
||||
type RuntimeConfig struct {
|
||||
DNS RuntimeDNS `json:"dns"`
|
||||
DNS RuntimeDNS `json:"dns"`
|
||||
PortMaps []PortMapEntry `json:"portMappings,omitempty"`
|
||||
}
|
||||
|
||||
type policy struct {
|
||||
@ -207,3 +216,21 @@ func (n *NetConf) ApplyDefaultPAPolicy(paAddress string) {
|
||||
Value: []byte(`{"Type": "PA", "PA": "` + paAddress + `"}`),
|
||||
})
|
||||
}
|
||||
|
||||
// ApplyPortMappingPolicy is used to configure HostPort<>ContainerPort mapping in HNS
|
||||
func (n *NetConf) ApplyPortMappingPolicy(portMappings []PortMapEntry) {
|
||||
if portMappings == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if n.Policies == nil {
|
||||
n.Policies = make([]policy, 0)
|
||||
}
|
||||
|
||||
for _, portMapping := range portMappings {
|
||||
n.Policies = append(n.Policies, policy{
|
||||
Name: "EndpointPolicy",
|
||||
Value: []byte(fmt.Sprintf(`{"Type": "NAT", "InternalPort": %d, "ExternalPort": %d, "Protocol": "%s"}`, portMapping.ContainerPort, portMapping.HostPort, portMapping.Protocol)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -128,6 +128,53 @@ var _ = Describe("HNS NetConf", func() {
|
||||
})
|
||||
})
|
||||
|
||||
Describe("ApplyPortMappingPolicy", func() {
|
||||
Context("when portMappings not activated", func() {
|
||||
It("does nothing", func() {
|
||||
n := NetConf{}
|
||||
n.ApplyPortMappingPolicy(nil)
|
||||
Expect(n.Policies).Should(BeNil())
|
||||
|
||||
n.ApplyPortMappingPolicy([]PortMapEntry{})
|
||||
Expect(n.Policies).Should(HaveLen(0))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when portMappings is activated", func() {
|
||||
It("creates NAT policies", func() {
|
||||
n := NetConf{}
|
||||
n.ApplyPortMappingPolicy([]PortMapEntry{
|
||||
{
|
||||
ContainerPort: 80,
|
||||
HostPort: 8080,
|
||||
Protocol: "TCP",
|
||||
HostIP: "ignored",
|
||||
},
|
||||
})
|
||||
|
||||
Expect(n.Policies).Should(HaveLen(1))
|
||||
|
||||
policy := n.Policies[0]
|
||||
Expect(policy.Name).Should(Equal("EndpointPolicy"))
|
||||
|
||||
value := make(map[string]interface{})
|
||||
json.Unmarshal(policy.Value, &value)
|
||||
|
||||
Expect(value).Should(HaveKey("Type"))
|
||||
Expect(value["Type"]).Should(Equal("NAT"))
|
||||
|
||||
Expect(value).Should(HaveKey("InternalPort"))
|
||||
Expect(value["InternalPort"]).Should(Equal(float64(80)))
|
||||
|
||||
Expect(value).Should(HaveKey("ExternalPort"))
|
||||
Expect(value["ExternalPort"]).Should(Equal(float64(8080)))
|
||||
|
||||
Expect(value).Should(HaveKey("Protocol"))
|
||||
Expect(value["Protocol"]).Should(Equal("TCP"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("MarshalPolicies", func() {
|
||||
Context("when not set by user", func() {
|
||||
It("sets it by adding a policy", func() {
|
||||
|
@ -21,10 +21,12 @@ import (
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
"github.com/containernetworking/plugins/pkg/utils/hwaddr"
|
||||
"github.com/safchain/ethtool"
|
||||
"github.com/vishvananda/netlink"
|
||||
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
"github.com/containernetworking/plugins/pkg/utils/hwaddr"
|
||||
"github.com/containernetworking/plugins/pkg/utils/sysctl"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -158,6 +160,9 @@ func SetupVethWithName(contVethName, hostVethName string, mtu int, hostNS ns.Net
|
||||
if err = netlink.LinkSetUp(hostVeth); err != nil {
|
||||
return fmt.Errorf("failed to set %q up: %v", hostVethName, err)
|
||||
}
|
||||
|
||||
// we want to own the routes for this interface
|
||||
_, _ = sysctl.Sysctl(fmt.Sprintf("net/ipv6/conf/%s/accept_ra", hostVethName), "0")
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
@ -178,7 +183,7 @@ func SetupVeth(contVethName string, mtu int, hostNS ns.NetNS) (net.Interface, ne
|
||||
func DelLinkByName(ifName string) error {
|
||||
iface, err := netlink.LinkByName(ifName)
|
||||
if err != nil {
|
||||
if err.Error() == "Link not found" {
|
||||
if _, ok := err.(netlink.LinkNotFoundError); ok {
|
||||
return ErrLinkNotFound
|
||||
}
|
||||
return fmt.Errorf("failed to lookup %q: %v", ifName, err)
|
||||
@ -195,7 +200,7 @@ func DelLinkByName(ifName string) error {
|
||||
func DelLinkByNameAddr(ifName string) ([]*net.IPNet, error) {
|
||||
iface, err := netlink.LinkByName(ifName)
|
||||
if err != nil {
|
||||
if err != nil && err.Error() == "Link not found" {
|
||||
if _, ok := err.(netlink.LinkNotFoundError); ok {
|
||||
return nil, ErrLinkNotFound
|
||||
}
|
||||
return nil, fmt.Errorf("failed to lookup %q: %v", ifName, err)
|
||||
|
@ -26,6 +26,11 @@ import (
|
||||
|
||||
// Returns an object representing the current OS thread's network namespace
|
||||
func GetCurrentNS() (NetNS, error) {
|
||||
// Lock the thread in case other goroutine executes in it and changes its
|
||||
// network namespace after getCurrentThreadNetNSPath(), otherwise it might
|
||||
// return an unexpected network namespace.
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
return GetNS(getCurrentThreadNetNSPath())
|
||||
}
|
||||
|
||||
@ -178,7 +183,16 @@ func (ns *netNS) Do(toRun func(NetNS) error) error {
|
||||
if err = ns.Set(); err != nil {
|
||||
return fmt.Errorf("error switching to ns %v: %v", ns.file.Name(), err)
|
||||
}
|
||||
defer threadNS.Set() // switch back
|
||||
defer func() {
|
||||
err := threadNS.Set() // switch back
|
||||
if err == nil {
|
||||
// Unlock the current thread only when we successfully switched back
|
||||
// to the original namespace; otherwise leave the thread locked which
|
||||
// will force the runtime to scrap the current thread, that is maybe
|
||||
// not as optimal but at least always safe to do.
|
||||
runtime.UnlockOSThread()
|
||||
}
|
||||
}()
|
||||
|
||||
return toRun(hostNS)
|
||||
}
|
||||
@ -193,6 +207,10 @@ func (ns *netNS) Do(toRun func(NetNS) error) error {
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
|
||||
// Start the callback in a new green thread so that if we later fail
|
||||
// to switch the namespace back to the original one, we can safely
|
||||
// leave the thread locked to die without a risk of the current thread
|
||||
// left lingering with incorrect namespace.
|
||||
var innerError error
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
"github.com/containernetworking/plugins/pkg/testutils"
|
||||
@ -118,6 +119,33 @@ var _ = Describe("Linux namespace operations", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
Context("when called concurrently", func() {
|
||||
It("provides the original namespace as the argument to the callback", func() {
|
||||
concurrency := 200
|
||||
origNS, err := ns.GetCurrentNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
origNSInode, err := getInodeNS(origNS)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(concurrency)
|
||||
for i := 0; i < concurrency; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
targetNetNS.Do(func(hostNS ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
hostNSInode, err := getInodeNS(hostNS)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(hostNSInode).To(Equal(origNSInode))
|
||||
return nil
|
||||
})
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the callback returns an error", func() {
|
||||
It("restores the calling thread to the original namespace before returning", func() {
|
||||
err := originalNetNS.Do(func(ns.NetNS) error {
|
||||
|
60
pkg/testutils/dns.go
Normal file
60
pkg/testutils/dns.go
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright 2019 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 testutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
)
|
||||
|
||||
// TmpResolvConf will create a temporary file and write the provided DNS settings to
|
||||
// it in the resolv.conf format. It returns the path of the created temporary file or
|
||||
// an error if any occurs while creating/writing the file. It is the caller's
|
||||
// responsibility to remove the file.
|
||||
func TmpResolvConf(dnsConf types.DNS) (string, error) {
|
||||
f, err := ioutil.TempFile("", "cni_test_resolv.conf")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get temp file for CNI test resolv.conf: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
path := f.Name()
|
||||
defer func() {
|
||||
if err != nil {
|
||||
os.RemoveAll(path)
|
||||
}
|
||||
}()
|
||||
|
||||
// see "man 5 resolv.conf" for the format of resolv.conf
|
||||
var resolvConfLines []string
|
||||
for _, nameserver := range dnsConf.Nameservers {
|
||||
resolvConfLines = append(resolvConfLines, fmt.Sprintf("nameserver %s", nameserver))
|
||||
}
|
||||
resolvConfLines = append(resolvConfLines, fmt.Sprintf("domain %s", dnsConf.Domain))
|
||||
resolvConfLines = append(resolvConfLines, fmt.Sprintf("search %s", strings.Join(dnsConf.Search, " ")))
|
||||
resolvConfLines = append(resolvConfLines, fmt.Sprintf("options %s", strings.Join(dnsConf.Options, " ")))
|
||||
|
||||
resolvConf := strings.Join(resolvConfLines, "\n")
|
||||
_, err = f.Write([]byte(resolvConf))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to write temp resolv.conf for CNI test: %v", err)
|
||||
}
|
||||
|
||||
return path, err
|
||||
}
|
@ -22,17 +22,36 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const nsRunDir = "/var/run/netns"
|
||||
func getNsRunDir() string {
|
||||
xdgRuntimeDir := os.Getenv("XDG_RUNTIME_DIR")
|
||||
|
||||
/// If XDG_RUNTIME_DIR is set, check if the current user owns /var/run. If
|
||||
// the owner is different, we are most likely running in a user namespace.
|
||||
// In that case use $XDG_RUNTIME_DIR/netns as runtime dir.
|
||||
if xdgRuntimeDir != "" {
|
||||
if s, err := os.Stat("/var/run"); err == nil {
|
||||
st, ok := s.Sys().(*syscall.Stat_t)
|
||||
if ok && int(st.Uid) != os.Geteuid() {
|
||||
return path.Join(xdgRuntimeDir, "netns")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "/var/run/netns"
|
||||
}
|
||||
|
||||
// Creates a new persistent (bind-mounted) network namespace and returns an object
|
||||
// representing that namespace, without switching to it.
|
||||
func NewNS() (ns.NetNS, error) {
|
||||
|
||||
nsRunDir := getNsRunDir()
|
||||
|
||||
b := make([]byte, 16)
|
||||
_, err := rand.Reader.Read(b)
|
||||
if err != nil {
|
||||
@ -135,7 +154,7 @@ func NewNS() (ns.NetNS, error) {
|
||||
func UnmountNS(ns ns.NetNS) error {
|
||||
nsPath := ns.Path()
|
||||
// Only unmount if it's been bind-mounted (don't touch namespaces in /proc...)
|
||||
if strings.HasPrefix(nsPath, nsRunDir) {
|
||||
if strings.HasPrefix(nsPath, getNsRunDir()) {
|
||||
if err := unix.Unmount(nsPath, 0); err != nil {
|
||||
return fmt.Errorf("failed to unmount NS: at %s: %v", nsPath, err)
|
||||
}
|
||||
|
121
pkg/utils/iptables.go
Normal file
121
pkg/utils/iptables.go
Normal file
@ -0,0 +1,121 @@
|
||||
// Copyright 2017 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 utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
)
|
||||
|
||||
const statusChainExists = 1
|
||||
|
||||
// EnsureChain idempotently creates the iptables chain. It does not
|
||||
// return an error if the chain already exists.
|
||||
func EnsureChain(ipt *iptables.IPTables, table, chain string) error {
|
||||
if ipt == nil {
|
||||
return errors.New("failed to ensure iptable chain: IPTables was nil")
|
||||
}
|
||||
exists, err := ChainExists(ipt, table, chain)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list iptables chains: %v", err)
|
||||
}
|
||||
if !exists {
|
||||
err = ipt.NewChain(table, chain)
|
||||
if err != nil {
|
||||
eerr, eok := err.(*iptables.Error)
|
||||
if eok && eerr.ExitStatus() != statusChainExists {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ChainExists checks whether an iptables chain exists.
|
||||
func ChainExists(ipt *iptables.IPTables, table, chain string) (bool, error) {
|
||||
if ipt == nil {
|
||||
return false, errors.New("failed to check iptable chain: IPTables was nil")
|
||||
}
|
||||
chains, err := ipt.ListChains(table)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, ch := range chains {
|
||||
if ch == chain {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// DeleteRule idempotently delete the iptables rule in the specified table/chain.
|
||||
// It does not return an error if the referring chain doesn't exist
|
||||
func DeleteRule(ipt *iptables.IPTables, table, chain string, rulespec ...string) error {
|
||||
if ipt == nil {
|
||||
return errors.New("failed to ensure iptable chain: IPTables was nil")
|
||||
}
|
||||
if err := ipt.Delete(table, chain, rulespec...); err != nil {
|
||||
eerr, eok := err.(*iptables.Error)
|
||||
switch {
|
||||
case eok && eerr.IsNotExist():
|
||||
// swallow here, the chain was already deleted
|
||||
return nil
|
||||
case eok && eerr.ExitStatus() == 2:
|
||||
// swallow here, invalid command line parameter because the referring rule is missing
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("Failed to delete referring rule %s %s: %v", table, chain, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteChain idempotently deletes the specified table/chain.
|
||||
// It does not return an errors if the chain does not exist
|
||||
func DeleteChain(ipt *iptables.IPTables, table, chain string) error {
|
||||
if ipt == nil {
|
||||
return errors.New("failed to ensure iptable chain: IPTables was nil")
|
||||
}
|
||||
|
||||
err := ipt.DeleteChain(table, chain)
|
||||
eerr, eok := err.(*iptables.Error)
|
||||
switch {
|
||||
case eok && eerr.IsNotExist():
|
||||
// swallow here, the chain was already deleted
|
||||
return nil
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// ClearChain idempotently clear the iptables rules in the specified table/chain.
|
||||
// If the chain does not exist, a new one will be created
|
||||
func ClearChain(ipt *iptables.IPTables, table, chain string) error {
|
||||
if ipt == nil {
|
||||
return errors.New("failed to ensure iptable chain: IPTables was nil")
|
||||
}
|
||||
err := ipt.ClearChain(table, chain)
|
||||
eerr, eok := err.(*iptables.Error)
|
||||
switch {
|
||||
case eok && eerr.IsNotExist():
|
||||
// swallow here, the chain was already deleted
|
||||
return EnsureChain(ipt, table, chain)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
97
pkg/utils/iptables_test.go
Normal file
97
pkg/utils/iptables_test.go
Normal file
@ -0,0 +1,97 @@
|
||||
// Copyright 2017-2018 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 utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"runtime"
|
||||
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
"github.com/containernetworking/plugins/pkg/testutils"
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
const TABLE = "filter" // We'll monkey around here
|
||||
|
||||
var _ = Describe("chain tests", func() {
|
||||
var testChain string
|
||||
var ipt *iptables.IPTables
|
||||
var cleanup func()
|
||||
|
||||
BeforeEach(func() {
|
||||
|
||||
// Save a reference to the original namespace,
|
||||
// Add a new NS
|
||||
currNs, err := ns.GetCurrentNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
testNs, err := testutils.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
testChain = fmt.Sprintf("cni-test-%d", rand.Intn(10000000))
|
||||
|
||||
ipt, err = iptables.NewWithProtocol(iptables.ProtocolIPv4)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
runtime.LockOSThread()
|
||||
err = testNs.Set()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
cleanup = func() {
|
||||
if ipt == nil {
|
||||
return
|
||||
}
|
||||
ipt.ClearChain(TABLE, testChain)
|
||||
ipt.DeleteChain(TABLE, testChain)
|
||||
currNs.Set()
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
cleanup()
|
||||
})
|
||||
|
||||
Describe("EnsureChain", func() {
|
||||
It("creates chains idempotently", func() {
|
||||
err := EnsureChain(ipt, TABLE, testChain)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Create it again!
|
||||
err = EnsureChain(ipt, TABLE, testChain)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("DeleteChain", func() {
|
||||
It("delete chains idempotently", func() {
|
||||
// Create chain
|
||||
err := EnsureChain(ipt, TABLE, testChain)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Delete chain
|
||||
err = DeleteChain(ipt, TABLE, testChain)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Delete it again!
|
||||
err = DeleteChain(ipt, TABLE, testChain)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
})
|
@ -35,7 +35,7 @@ func Sysctl(name string, params ...string) (string, error) {
|
||||
}
|
||||
|
||||
func getSysctl(name string) (string, error) {
|
||||
fullName := filepath.Join("/proc/sys", strings.Replace(name, ".", "/", -1))
|
||||
fullName := filepath.Join("/proc/sys", toNormalName(name))
|
||||
fullName = filepath.Clean(fullName)
|
||||
data, err := ioutil.ReadFile(fullName)
|
||||
if err != nil {
|
||||
@ -46,7 +46,7 @@ func getSysctl(name string) (string, error) {
|
||||
}
|
||||
|
||||
func setSysctl(name, value string) (string, error) {
|
||||
fullName := filepath.Join("/proc/sys", strings.Replace(name, ".", "/", -1))
|
||||
fullName := filepath.Join("/proc/sys", toNormalName(name))
|
||||
fullName = filepath.Clean(fullName)
|
||||
if err := ioutil.WriteFile(fullName, []byte(value), 0644); err != nil {
|
||||
return "", err
|
||||
@ -54,3 +54,27 @@ func setSysctl(name, value string) (string, error) {
|
||||
|
||||
return getSysctl(name)
|
||||
}
|
||||
|
||||
// Normalize names by using slash as separator
|
||||
// Sysctl names can use dots or slashes as separator:
|
||||
// - if dots are used, dots and slashes are interchanged.
|
||||
// - if slashes are used, slashes and dots are left intact.
|
||||
// Separator in use is determined by first occurrence.
|
||||
func toNormalName(name string) string {
|
||||
interchange := false
|
||||
for _, c := range name {
|
||||
if c == '.' {
|
||||
interchange = true
|
||||
break
|
||||
}
|
||||
if c == '/' {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if interchange {
|
||||
r := strings.NewReplacer(".", "/", "/", ".")
|
||||
return r.Replace(name)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
114
pkg/utils/sysctl/sysctl_linux_test.go
Normal file
114
pkg/utils/sysctl/sysctl_linux_test.go
Normal file
@ -0,0 +1,114 @@
|
||||
// Copyright 2017-2020 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 sysctl_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
"github.com/containernetworking/plugins/pkg/testutils"
|
||||
"github.com/containernetworking/plugins/pkg/utils/sysctl"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
const (
|
||||
sysctlDotKeyTemplate = "net.ipv4.conf.%s.proxy_arp"
|
||||
sysctlSlashKeyTemplate = "net/ipv4/conf/%s/proxy_arp"
|
||||
)
|
||||
|
||||
var _ = Describe("Sysctl tests", func() {
|
||||
var testIfaceName string
|
||||
var cleanup func()
|
||||
|
||||
BeforeEach(func() {
|
||||
|
||||
// Save a reference to the original namespace,
|
||||
// Add a new NS
|
||||
currNs, err := ns.GetCurrentNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
testNs, err := testutils.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
testIfaceName = fmt.Sprintf("cnitest.%d", rand.Intn(100000))
|
||||
testIface := &netlink.Dummy{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
Name: testIfaceName,
|
||||
Namespace: netlink.NsFd(int(testNs.Fd())),
|
||||
},
|
||||
}
|
||||
|
||||
err = netlink.LinkAdd(testIface)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
runtime.LockOSThread()
|
||||
err = testNs.Set()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
cleanup = func() {
|
||||
netlink.LinkDel(testIface)
|
||||
currNs.Set()
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
cleanup()
|
||||
})
|
||||
|
||||
Describe("Sysctl", func() {
|
||||
It("reads keys with dot separators", func() {
|
||||
sysctlIfaceName := strings.Replace(testIfaceName, ".", "/", -1)
|
||||
sysctlKey := fmt.Sprintf(sysctlDotKeyTemplate, sysctlIfaceName)
|
||||
|
||||
_, err := sysctl.Sysctl(sysctlKey)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Sysctl", func() {
|
||||
It("reads keys with slash separators", func() {
|
||||
sysctlKey := fmt.Sprintf(sysctlSlashKeyTemplate, testIfaceName)
|
||||
|
||||
_, err := sysctl.Sysctl(sysctlKey)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Sysctl", func() {
|
||||
It("writes keys with dot separators", func() {
|
||||
sysctlIfaceName := strings.Replace(testIfaceName, ".", "/", -1)
|
||||
sysctlKey := fmt.Sprintf(sysctlDotKeyTemplate, sysctlIfaceName)
|
||||
|
||||
_, err := sysctl.Sysctl(sysctlKey, "1")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Sysctl", func() {
|
||||
It("writes keys with slash separators", func() {
|
||||
sysctlKey := fmt.Sprintf(sysctlSlashKeyTemplate, testIfaceName)
|
||||
|
||||
_, err := sysctl.Sysctl(sysctlKey, "1")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
})
|
27
pkg/utils/sysctl/sysctl_suite_test.go
Normal file
27
pkg/utils/sysctl/sysctl_suite_test.go
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2017-2020 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 sysctl_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestSysctl(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Sysctl Suite")
|
||||
}
|
@ -34,7 +34,6 @@ import (
|
||||
)
|
||||
|
||||
const listenFdsStart = 3
|
||||
const resendCount = 3
|
||||
|
||||
var errNoMoreTries = errors.New("no more tries")
|
||||
|
||||
|
@ -34,7 +34,7 @@ import (
|
||||
// RFC 2131 suggests using exponential backoff, starting with 4sec
|
||||
// and randomized to +/- 1sec
|
||||
const resendDelay0 = 4 * time.Second
|
||||
const resendDelayMax = 32 * time.Second
|
||||
const resendDelayMax = 62 * time.Second
|
||||
|
||||
const (
|
||||
leaseStateBound = iota
|
||||
@ -335,8 +335,9 @@ func jitter(span time.Duration) time.Duration {
|
||||
|
||||
func backoffRetry(f func() (*dhcp4.Packet, error)) (*dhcp4.Packet, error) {
|
||||
var baseDelay time.Duration = resendDelay0
|
||||
var sleepTime time.Duration
|
||||
|
||||
for i := 0; i < resendCount; i++ {
|
||||
for {
|
||||
pkt, err := f()
|
||||
if err == nil {
|
||||
return pkt, nil
|
||||
@ -344,10 +345,16 @@ func backoffRetry(f func() (*dhcp4.Packet, error)) (*dhcp4.Packet, error) {
|
||||
|
||||
log.Print(err)
|
||||
|
||||
time.Sleep(baseDelay + jitter(time.Second))
|
||||
sleepTime = baseDelay + jitter(time.Second)
|
||||
|
||||
log.Printf("retrying in %f seconds", sleepTime.Seconds())
|
||||
|
||||
time.Sleep(sleepTime)
|
||||
|
||||
if baseDelay < resendDelayMax {
|
||||
baseDelay *= 2
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,12 +65,11 @@ options four
|
||||
|
||||
func parse(contents string) (*types.DNS, error) {
|
||||
f, err := ioutil.TempFile("", "host_local_resolv")
|
||||
defer f.Close()
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
if _, err := f.WriteString(contents); err != nil {
|
||||
return nil, err
|
||||
|
@ -60,3 +60,9 @@ The plugin also support following [capability argument](https://github.com/conta
|
||||
The following [args conventions](https://github.com/containernetworking/cni/blob/master/CONVENTIONS.md#args-in-network-config) are supported:
|
||||
|
||||
* `ips` (array of strings): A list of custom IPs to attempt to allocate, with prefix (e.g. '10.10.0.1/24')
|
||||
|
||||
Notice: If some of above are used at same time, only one will work according to the priorities below
|
||||
|
||||
1. [capability argument](https://github.com/containernetworking/cni/blob/master/CONVENTIONS.md)
|
||||
1. [args conventions](https://github.com/containernetworking/cni/blob/master/CONVENTIONS.md#args-in-network-config)
|
||||
1. [CNI_ARGS](https://github.com/containernetworking/cni/blob/master/SPEC.md#parameters)
|
||||
|
@ -145,11 +145,44 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
if len(n.RuntimeConfig.IPs) != 0 {
|
||||
// args IP overwrites IP, so clear IPAM Config
|
||||
n.IPAM.Addresses = make([]Address, 0, len(n.RuntimeConfig.IPs))
|
||||
for _, addr := range n.RuntimeConfig.IPs {
|
||||
n.IPAM.Addresses = append(n.IPAM.Addresses, Address{AddressStr: addr})
|
||||
// load IP from CNI_ARGS
|
||||
if envArgs != "" {
|
||||
e := IPAMEnvArgs{}
|
||||
err := types.LoadArgs(envArgs, &e)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
if e.IP != "" {
|
||||
for _, item := range strings.Split(string(e.IP), ",") {
|
||||
ipstr := strings.TrimSpace(item)
|
||||
|
||||
ip, subnet, err := net.ParseCIDR(ipstr)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("invalid CIDR %s: %s", ipstr, err)
|
||||
}
|
||||
|
||||
addr := Address{
|
||||
Address: net.IPNet{IP: ip, Mask: subnet.Mask},
|
||||
AddressStr: ipstr,
|
||||
}
|
||||
n.IPAM.Addresses = append(n.IPAM.Addresses, addr)
|
||||
}
|
||||
}
|
||||
|
||||
if e.GATEWAY != "" {
|
||||
for _, item := range strings.Split(string(e.GATEWAY), ",") {
|
||||
gwip := net.ParseIP(strings.TrimSpace(item))
|
||||
if gwip == nil {
|
||||
return nil, "", fmt.Errorf("invalid gateway address: %s", item)
|
||||
}
|
||||
|
||||
for i := range n.IPAM.Addresses {
|
||||
if n.IPAM.Addresses[i].Address.Contains(gwip) {
|
||||
n.IPAM.Addresses[i].Gateway = gwip
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,6 +195,15 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// import address from runtimeConfig
|
||||
if len(n.RuntimeConfig.IPs) != 0 {
|
||||
// runtimeConfig IP overwrites IP, so clear IPAM Config
|
||||
n.IPAM.Addresses = make([]Address, 0, len(n.RuntimeConfig.IPs))
|
||||
for _, addr := range n.RuntimeConfig.IPs {
|
||||
n.IPAM.Addresses = append(n.IPAM.Addresses, Address{AddressStr: addr})
|
||||
}
|
||||
}
|
||||
|
||||
if n.IPAM == nil {
|
||||
return nil, "", fmt.Errorf("IPAM config missing 'ipam' key")
|
||||
}
|
||||
@ -191,50 +233,6 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
if envArgs != "" {
|
||||
e := IPAMEnvArgs{}
|
||||
err := types.LoadArgs(envArgs, &e)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
if e.IP != "" {
|
||||
for _, item := range strings.Split(string(e.IP), ",") {
|
||||
ipstr := strings.TrimSpace(item)
|
||||
|
||||
ip, subnet, err := net.ParseCIDR(ipstr)
|
||||
if err != nil {
|
||||
return nil, "", fmt.Errorf("invalid CIDR %s: %s", ipstr, err)
|
||||
}
|
||||
|
||||
addr := Address{Address: net.IPNet{IP: ip, Mask: subnet.Mask}}
|
||||
if addr.Address.IP.To4() != nil {
|
||||
addr.Version = "4"
|
||||
numV4++
|
||||
} else {
|
||||
addr.Version = "6"
|
||||
numV6++
|
||||
}
|
||||
n.IPAM.Addresses = append(n.IPAM.Addresses, addr)
|
||||
}
|
||||
}
|
||||
|
||||
if e.GATEWAY != "" {
|
||||
for _, item := range strings.Split(string(e.GATEWAY), ",") {
|
||||
gwip := net.ParseIP(strings.TrimSpace(item))
|
||||
if gwip == nil {
|
||||
return nil, "", fmt.Errorf("invalid gateway address: %s", item)
|
||||
}
|
||||
|
||||
for i := range n.IPAM.Addresses {
|
||||
if n.IPAM.Addresses[i].Address.Contains(gwip) {
|
||||
n.IPAM.Addresses[i].Gateway = gwip
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// CNI spec 0.2.0 and below supported only one v4 and v6 address
|
||||
if numV4 > 1 || numV6 > 1 {
|
||||
for _, v := range types020.SupportedVersions {
|
||||
|
@ -404,6 +404,82 @@ var _ = Describe("static Operations", func() {
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("allocates and releases multiple addresses with ADD/DEL, from RuntimeConfig/ARGS/CNI_ARGS", func() {
|
||||
const ifname string = "eth0"
|
||||
const nspath string = "/some/where"
|
||||
|
||||
conf := `{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "mynet",
|
||||
"type": "ipvlan",
|
||||
"master": "foo0",
|
||||
"capabilities": {"ips": true},
|
||||
"ipam": {
|
||||
"type": "static",
|
||||
"routes": [
|
||||
{ "dst": "0.0.0.0/0", "gw": "10.10.0.254" },
|
||||
{ "dst": "3ffe:ffff:0:01ff::1/64",
|
||||
"gw": "3ffe:ffff:0::1" } ],
|
||||
"dns": {
|
||||
"nameservers" : ["8.8.8.8"],
|
||||
"domain": "example.com",
|
||||
"search": [ "example.com" ]
|
||||
}
|
||||
},
|
||||
"RuntimeConfig": {
|
||||
"ips" : ["10.10.0.1/24", "3ffe:ffff:0:01ff::1/64"]
|
||||
},
|
||||
"args": {
|
||||
"cni": {
|
||||
"ips" : ["10.10.0.2/24", "3ffe:ffff:0:01ff::2/64"]
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy",
|
||||
Netns: nspath,
|
||||
IfName: ifname,
|
||||
StdinData: []byte(conf),
|
||||
Args: "IP=10.10.0.3/24,11.11.0.3/24;GATEWAY=10.10.0.254",
|
||||
}
|
||||
|
||||
// Allocate the IP
|
||||
r, raw, err := testutils.CmdAddWithArgs(args, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0))
|
||||
|
||||
result, err := current.GetResult(r)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// only addresses in runtimeConfig configured because of its priorities
|
||||
Expect(*result.IPs[0]).To(Equal(
|
||||
current.IPConfig{
|
||||
Version: "4",
|
||||
Address: mustCIDR("10.10.0.1/24"),
|
||||
}))
|
||||
Expect(*result.IPs[1]).To(Equal(
|
||||
current.IPConfig{
|
||||
Version: "6",
|
||||
Address: mustCIDR("3ffe:ffff:0:01ff::1/64"),
|
||||
},
|
||||
))
|
||||
Expect(len(result.IPs)).To(Equal(2))
|
||||
Expect(result.Routes).To(Equal([]*types.Route{
|
||||
{Dst: mustCIDR("0.0.0.0/0"), GW: net.ParseIP("10.10.0.254")},
|
||||
{Dst: mustCIDR("3ffe:ffff:0:01ff::1/64"), GW: net.ParseIP("3ffe:ffff:0::1")},
|
||||
}))
|
||||
|
||||
// Release the IP
|
||||
err = testutils.CmdDelWithArgs(args, func() error {
|
||||
return cmdDel(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
func mustCIDR(s string) net.IPNet {
|
||||
|
@ -14,17 +14,18 @@ If the bridge is missing, the plugin will create one on first use and, if gatewa
|
||||
## Example configuration
|
||||
```
|
||||
{
|
||||
"name": "mynet",
|
||||
"type": "bridge",
|
||||
"bridge": "mynet0",
|
||||
"isDefaultGateway": true,
|
||||
"forceAddress": false,
|
||||
"ipMasq": true,
|
||||
"hairpinMode": true,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.10.0.0/16"
|
||||
}
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "mynet",
|
||||
"type": "bridge",
|
||||
"bridge": "mynet0",
|
||||
"isDefaultGateway": true,
|
||||
"forceAddress": false,
|
||||
"ipMasq": true,
|
||||
"hairpinMode": true,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.10.0.0/16"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
@ -32,10 +33,10 @@ If the bridge is missing, the plugin will create one on first use and, if gatewa
|
||||
```
|
||||
{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "mynet",
|
||||
"type": "bridge",
|
||||
"bridge": "mynet0",
|
||||
"ipam": {}
|
||||
"name": "mynet",
|
||||
"type": "bridge",
|
||||
"bridge": "mynet0",
|
||||
"ipam": {}
|
||||
}
|
||||
```
|
||||
|
||||
@ -56,4 +57,4 @@ If the bridge is missing, the plugin will create one on first use and, if gatewa
|
||||
|
||||
*Note:* The VLAN parameter configures the VLAN tag on the host end of the veth and also enables the vlan_filtering feature on the bridge interface.
|
||||
|
||||
*Note:* To configure uplink for L2 network you need to allow the vlan on the uplink interface by using the following command ``` bridge vlan add vid VLAN_ID dev DEV```.
|
||||
*Note:* To configure uplink for L2 network you need to allow the vlan on the uplink interface by using the following command ``` bridge vlan add vid VLAN_ID dev DEV```.
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"net"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/j-keck/arping"
|
||||
"github.com/vishvananda/netlink"
|
||||
@ -35,6 +36,7 @@ import (
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
"github.com/containernetworking/plugins/pkg/utils"
|
||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
||||
"github.com/containernetworking/plugins/pkg/utils/sysctl"
|
||||
)
|
||||
|
||||
// For testcases to force an error after IPAM has been performed
|
||||
@ -75,6 +77,9 @@ func loadNetConf(bytes []byte) (*NetConf, string, error) {
|
||||
if err := json.Unmarshal(bytes, n); err != nil {
|
||||
return nil, "", fmt.Errorf("failed to load netconf: %v", err)
|
||||
}
|
||||
if n.Vlan < 0 || n.Vlan > 4094 {
|
||||
return nil, "", fmt.Errorf("invalid VLAN ID %d (must be between 0 and 4094)", n.Vlan)
|
||||
}
|
||||
return n, n.CNIVersion, nil
|
||||
}
|
||||
|
||||
@ -221,7 +226,9 @@ func ensureBridge(brName string, mtu int, promiscMode, vlanFiltering bool) (*net
|
||||
// default packet limit
|
||||
TxQLen: -1,
|
||||
},
|
||||
VlanFiltering: &vlanFiltering,
|
||||
}
|
||||
if vlanFiltering {
|
||||
br.VlanFiltering = &vlanFiltering
|
||||
}
|
||||
|
||||
err := netlink.LinkAdd(br)
|
||||
@ -242,6 +249,9 @@ func ensureBridge(brName string, mtu int, promiscMode, vlanFiltering bool) (*net
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// we want to own the routes for this interface
|
||||
_, _ = sysctl.Sysctl(fmt.Sprintf("net/ipv6/conf/%s/accept_ra", brName), "0")
|
||||
|
||||
if err := netlink.LinkSetUp(br); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -439,11 +449,6 @@ func cmdAdd(args *skel.CmdArgs) error {
|
||||
|
||||
// Configure the container hardware address and IP address(es)
|
||||
if err := netns.Do(func(_ ns.NetNS) error {
|
||||
contVeth, err := net.InterfaceByName(args.IfName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Disable IPv6 DAD just in case hairpin mode is enabled on the
|
||||
// bridge. Hairpin mode causes echos of neighbor solicitation
|
||||
// packets, which causes DAD failures.
|
||||
@ -460,8 +465,36 @@ func cmdAdd(args *skel.CmdArgs) error {
|
||||
if err := ipam.ConfigureIface(args.IfName, result); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// check bridge port state
|
||||
retries := []int{0, 50, 500, 1000, 1000}
|
||||
for idx, sleep := range retries {
|
||||
time.Sleep(time.Duration(sleep) * time.Millisecond)
|
||||
|
||||
hostVeth, err := netlink.LinkByName(hostInterface.Name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hostVeth.Attrs().OperState == netlink.OperUp {
|
||||
break
|
||||
}
|
||||
|
||||
if idx == len(retries)-1 {
|
||||
return fmt.Errorf("bridge port in error state: %s", hostVeth.Attrs().OperState)
|
||||
}
|
||||
}
|
||||
|
||||
// Send a gratuitous arp
|
||||
if err := netns.Do(func(_ ns.NetNS) error {
|
||||
contVeth, err := net.InterfaceByName(args.IfName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Send a gratuitous arp
|
||||
for _, ipc := range result.IPs {
|
||||
if ipc.Version == "4" {
|
||||
_ = arping.GratuitousArpOverIface(ipc.Address.IP, *contVeth)
|
||||
|
@ -27,7 +27,7 @@ import (
|
||||
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/types/020"
|
||||
types020 "github.com/containernetworking/cni/pkg/types/020"
|
||||
"github.com/containernetworking/cni/pkg/types/current"
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
"github.com/containernetworking/plugins/pkg/testutils"
|
||||
@ -1125,6 +1125,7 @@ var _ = Describe("bridge Operations", func() {
|
||||
AfterEach(func() {
|
||||
Expect(os.RemoveAll(dataDir)).To(Succeed())
|
||||
Expect(originalNS.Close()).To(Succeed())
|
||||
Expect(testutils.UnmountNS(originalNS)).To(Succeed())
|
||||
})
|
||||
|
||||
It("creates a bridge", func() {
|
||||
@ -1644,4 +1645,48 @@ var _ = Describe("bridge Operations", func() {
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("check vlan id when loading net conf", func() {
|
||||
tests := []struct {
|
||||
tc testCase
|
||||
err error
|
||||
}{
|
||||
{
|
||||
tc: testCase{
|
||||
cniVersion: "0.4.0",
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
tc: testCase{
|
||||
cniVersion: "0.4.0",
|
||||
vlan: 0,
|
||||
},
|
||||
err: nil,
|
||||
},
|
||||
{
|
||||
tc: testCase{
|
||||
cniVersion: "0.4.0",
|
||||
vlan: -100,
|
||||
},
|
||||
err: fmt.Errorf("invalid VLAN ID -100 (must be between 0 and 4094)"),
|
||||
},
|
||||
{
|
||||
tc: testCase{
|
||||
cniVersion: "0.4.0",
|
||||
vlan: 5000,
|
||||
},
|
||||
err: fmt.Errorf("invalid VLAN ID 5000 (must be between 0 and 4094)"),
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
_, _, err := loadNetConf([]byte(test.tc.netConfJSON("")))
|
||||
if test.err == nil {
|
||||
Expect(err).To(BeNil())
|
||||
} else {
|
||||
Expect(err).To(Equal(test.err))
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -17,6 +17,9 @@ The device can be specified with any one of four properties:
|
||||
|
||||
For this plugin, `CNI_IFNAME` will be ignored. Upon DEL, the device will be moved back.
|
||||
|
||||
The plugin also supports the following [capability argument](https://github.com/containernetworking/cni/blob/master/CONVENTIONS.md):
|
||||
* `deviceID`: A PCI address of the network device, e.g `0000:00:1f.6`
|
||||
|
||||
## Example configuration
|
||||
|
||||
A sample configuration with `device` property looks like:
|
||||
@ -38,3 +41,26 @@ A sample configuration with `pciBusID` property looks like:
|
||||
"pciBusID": "0000:3d:00.1"
|
||||
}
|
||||
```
|
||||
|
||||
A sample configuration utilizing `deviceID` runtime configuration looks like:
|
||||
|
||||
1. From operator perspective:
|
||||
```json
|
||||
{
|
||||
"cniVersion": "0.3.1",
|
||||
"type": "host-device",
|
||||
"capabilities": {
|
||||
"deviceID": true
|
||||
}
|
||||
}
|
||||
```
|
||||
2. From plugin perspective:
|
||||
```json
|
||||
{
|
||||
"cniVersion": "0.3.1",
|
||||
"type": "host-device",
|
||||
"runtimeConfig": {
|
||||
"deviceID": "0000:3d:00.1"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
@ -46,10 +46,13 @@ const (
|
||||
//NetConf for host-device config, look the README to learn how to use those parameters
|
||||
type NetConf struct {
|
||||
types.NetConf
|
||||
Device string `json:"device"` // Device-Name, something like eth0 or can0 etc.
|
||||
HWAddr string `json:"hwaddr"` // MAC Address of target network interface
|
||||
KernelPath string `json:"kernelpath"` // Kernelpath of the device
|
||||
PCIAddr string `json:"pciBusID"` // PCI Address of target network device
|
||||
Device string `json:"device"` // Device-Name, something like eth0 or can0 etc.
|
||||
HWAddr string `json:"hwaddr"` // MAC Address of target network interface
|
||||
KernelPath string `json:"kernelpath"` // Kernelpath of the device
|
||||
PCIAddr string `json:"pciBusID"` // PCI Address of target network device
|
||||
RuntimeConfig struct {
|
||||
DeviceID string `json:"deviceID,omitempty"`
|
||||
} `json:"runtimeConfig,omitempty"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
@ -64,9 +67,16 @@ func loadConf(bytes []byte) (*NetConf, error) {
|
||||
if err := json.Unmarshal(bytes, n); err != nil {
|
||||
return nil, fmt.Errorf("failed to load netconf: %v", err)
|
||||
}
|
||||
|
||||
if n.RuntimeConfig.DeviceID != "" {
|
||||
// Override PCI device with the standardized DeviceID provided in Runtime Config.
|
||||
n.PCIAddr = n.RuntimeConfig.DeviceID
|
||||
}
|
||||
|
||||
if n.Device == "" && n.HWAddr == "" && n.KernelPath == "" && n.PCIAddr == "" {
|
||||
return nil, fmt.Errorf(`specify either "device", "hwaddr", "kernelpath" or "pciBusID"`)
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
@ -183,6 +193,10 @@ func moveLinkIn(hostDev netlink.Link, containerNs ns.NetNS, ifName string) (netl
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find %q: %v", hostDev.Attrs().Name, err)
|
||||
}
|
||||
// Devices can be renamed only when down
|
||||
if err = netlink.LinkSetDown(contDev); err != nil {
|
||||
return fmt.Errorf("failed to set %q down: %v", hostDev.Attrs().Name, err)
|
||||
}
|
||||
// Save host device name into the container device's alias property
|
||||
if err := netlink.LinkSetAlias(contDev, hostDev.Attrs().Name); err != nil {
|
||||
return fmt.Errorf("failed to set alias to %q: %v", hostDev.Attrs().Name, err)
|
||||
@ -296,7 +310,12 @@ func getLink(devname, hwaddr, kernelpath, pciaddr string) (netlink.Link, error)
|
||||
} else if len(pciaddr) > 0 {
|
||||
netDir := filepath.Join(sysBusPCI, pciaddr, "net")
|
||||
if _, err := os.Lstat(netDir); err != nil {
|
||||
return nil, fmt.Errorf("no net directory under pci device %s: %q", pciaddr, err)
|
||||
virtioNetDir := filepath.Join(sysBusPCI, pciaddr, "virtio*", "net")
|
||||
matches, err := filepath.Glob(virtioNetDir)
|
||||
if matches == nil || err != nil {
|
||||
return nil, fmt.Errorf("no net directory under pci device %s", pciaddr)
|
||||
}
|
||||
netDir = matches[0]
|
||||
}
|
||||
fInfo, err := ioutil.ReadDir(netDir)
|
||||
if err != nil {
|
||||
|
@ -233,7 +233,8 @@ var _ = Describe("base functionality", func() {
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
originalNS.Close()
|
||||
Expect(originalNS.Close()).To(Succeed())
|
||||
Expect(testutils.UnmountNS(originalNS)).To(Succeed())
|
||||
})
|
||||
|
||||
It("Works with a valid config without IPAM", func() {
|
||||
|
@ -297,6 +297,7 @@ var _ = Describe("ipvlan Operations", func() {
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(originalNS.Close()).To(Succeed())
|
||||
Expect(testutils.UnmountNS(originalNS)).To(Succeed())
|
||||
})
|
||||
|
||||
It("creates an ipvlan link in a non-default namespace", func() {
|
||||
|
@ -15,9 +15,15 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/types/current"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
|
||||
@ -25,9 +31,34 @@ import (
|
||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
||||
)
|
||||
|
||||
func parseNetConf(bytes []byte) (*types.NetConf, error) {
|
||||
conf := &types.NetConf{}
|
||||
if err := json.Unmarshal(bytes, conf); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse network config: %v", err)
|
||||
}
|
||||
|
||||
if conf.RawPrevResult != nil {
|
||||
if err := version.ParsePrevResult(conf); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse prevResult: %v", err)
|
||||
}
|
||||
if _, err := current.NewResultFromResult(conf.PrevResult); err != nil {
|
||||
return nil, fmt.Errorf("failed to convert result to current version: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
func cmdAdd(args *skel.CmdArgs) error {
|
||||
conf, err := parseNetConf(args.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var v4Addr, v6Addr *net.IPNet
|
||||
|
||||
args.IfName = "lo" // ignore config, this only works for loopback
|
||||
err := ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
|
||||
err = ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
|
||||
link, err := netlink.LinkByName(args.IfName)
|
||||
if err != nil {
|
||||
return err // not tested
|
||||
@ -38,14 +69,69 @@ func cmdAdd(args *skel.CmdArgs) error {
|
||||
return err // not tested
|
||||
}
|
||||
|
||||
v4Addrs, err := netlink.AddrList(link, netlink.FAMILY_V4)
|
||||
if err != nil {
|
||||
return err // not tested
|
||||
}
|
||||
if len(v4Addrs) != 0 {
|
||||
v4Addr = v4Addrs[0].IPNet
|
||||
// sanity check that this is a loopback address
|
||||
for _, addr := range v4Addrs {
|
||||
if !addr.IP.IsLoopback() {
|
||||
return fmt.Errorf("loopback interface found with non-loopback address %q", addr.IP)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
v6Addrs, err := netlink.AddrList(link, netlink.FAMILY_V6)
|
||||
if err != nil {
|
||||
return err // not tested
|
||||
}
|
||||
if len(v6Addrs) != 0 {
|
||||
v6Addr = v6Addrs[0].IPNet
|
||||
// sanity check that this is a loopback address
|
||||
for _, addr := range v6Addrs {
|
||||
if !addr.IP.IsLoopback() {
|
||||
return fmt.Errorf("loopback interface found with non-loopback address %q", addr.IP)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err // not tested
|
||||
}
|
||||
|
||||
result := current.Result{}
|
||||
return result.Print()
|
||||
var result types.Result
|
||||
if conf.PrevResult != nil {
|
||||
// If loopback has previous result which passes from previous CNI plugin,
|
||||
// loopback should pass it transparently
|
||||
result = conf.PrevResult
|
||||
} else {
|
||||
loopbackInterface := ¤t.Interface{Name: args.IfName, Mac: "00:00:00:00:00:00", Sandbox: args.Netns}
|
||||
r := ¤t.Result{CNIVersion: conf.CNIVersion, Interfaces: []*current.Interface{loopbackInterface}}
|
||||
|
||||
if v4Addr != nil {
|
||||
r.IPs = append(r.IPs, ¤t.IPConfig{
|
||||
Version: "4",
|
||||
Interface: current.Int(0),
|
||||
Address: *v4Addr,
|
||||
})
|
||||
}
|
||||
|
||||
if v6Addr != nil {
|
||||
r.IPs = append(r.IPs, ¤t.IPConfig{
|
||||
Version: "6",
|
||||
Interface: current.Int(0),
|
||||
Address: *v6Addr,
|
||||
})
|
||||
}
|
||||
|
||||
result = r
|
||||
}
|
||||
|
||||
return types.PrintResult(result, conf.CNIVersion)
|
||||
}
|
||||
|
||||
func cmdDel(args *skel.CmdArgs) error {
|
||||
@ -78,6 +164,18 @@ func main() {
|
||||
}
|
||||
|
||||
func cmdCheck(args *skel.CmdArgs) error {
|
||||
// TODO: implement
|
||||
return nil
|
||||
args.IfName = "lo" // ignore config, this only works for loopback
|
||||
|
||||
return ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
|
||||
link, err := netlink.LinkByName(args.IfName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if link.Attrs().Flags&net.FlagUp != net.FlagUp {
|
||||
return errors.New("loopback interface is down")
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
@ -45,21 +45,20 @@ var _ = Describe("Loopback", func() {
|
||||
environ = []string{
|
||||
fmt.Sprintf("CNI_CONTAINERID=%s", "dummy"),
|
||||
fmt.Sprintf("CNI_NETNS=%s", networkNS.Path()),
|
||||
fmt.Sprintf("CNI_IFNAME=%s", "this is ignored"),
|
||||
fmt.Sprintf("CNI_IFNAME=%s", "lo"),
|
||||
fmt.Sprintf("CNI_ARGS=%s", "none"),
|
||||
fmt.Sprintf("CNI_PATH=%s", "/some/test/path"),
|
||||
}
|
||||
command.Stdin = strings.NewReader(`{ "cniVersion": "0.1.0" }`)
|
||||
command.Stdin = strings.NewReader(`{ "name": "loopback-test", "cniVersion": "0.1.0" }`)
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(networkNS.Close()).To(Succeed())
|
||||
Expect(testutils.UnmountNS(networkNS)).To(Succeed())
|
||||
})
|
||||
|
||||
Context("when given a network namespace", func() {
|
||||
It("sets the lo device to UP", func() {
|
||||
|
||||
Skip("TODO: add network name")
|
||||
command.Env = append(environ, fmt.Sprintf("CNI_COMMAND=%s", "ADD"))
|
||||
|
||||
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
|
||||
@ -80,8 +79,6 @@ var _ = Describe("Loopback", func() {
|
||||
})
|
||||
|
||||
It("sets the lo device to DOWN", func() {
|
||||
|
||||
Skip("TODO: add network name")
|
||||
command.Env = append(environ, fmt.Sprintf("CNI_COMMAND=%s", "DEL"))
|
||||
|
||||
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
|
||||
|
@ -23,12 +23,12 @@ Since each macvlan interface has its own MAC address, it makes it easy to use wi
|
||||
|
||||
* `name` (string, required): the name of the network
|
||||
* `type` (string, required): "macvlan"
|
||||
* `master` (string, optional): name of the host interface to enslave. Defaults to default route interace.
|
||||
* `master` (string, optional): name of the host interface to enslave. Defaults to default route interface.
|
||||
* `mode` (string, optional): one of "bridge", "private", "vepa", "passthru". Defaults to "bridge".
|
||||
* `mtu` (integer, optional): explicitly set MTU to the specified value. Defaults to the value chosen by the kernel.
|
||||
* `mtu` (integer, optional): explicitly set MTU to the specified value. Defaults to the value chosen by the kernel. The value must be \[0, master's MTU\].
|
||||
* `ipam` (dictionary, required): IPAM configuration to be used for this network. For interface only without ip address, create empty dictionary.
|
||||
|
||||
## Notes
|
||||
|
||||
* If are testing on a laptop, please remember that most wireless cards do not support being enslaved by macvlan.
|
||||
* If you are testing on a laptop, please remember that most wireless cards do not support being enslaved by macvlan.
|
||||
* A single master interface can not be enslaved by both `macvlan` and `ipvlan`.
|
||||
|
@ -45,6 +45,17 @@ type NetConf struct {
|
||||
Master string `json:"master"`
|
||||
Mode string `json:"mode"`
|
||||
MTU int `json:"mtu"`
|
||||
Mac string `json:"mac,omitempty"`
|
||||
|
||||
RuntimeConfig struct {
|
||||
Mac string `json:"mac,omitempty"`
|
||||
} `json:"runtimeConfig,omitempty"`
|
||||
}
|
||||
|
||||
// MacEnvArgs represents CNI_ARG
|
||||
type MacEnvArgs struct {
|
||||
types.CommonArgs
|
||||
MAC types.UnmarshallableString `json:"mac,omitempty"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
@ -73,7 +84,7 @@ func getDefaultRouteInterfaceName() (string, error) {
|
||||
return "", fmt.Errorf("no default route interface found")
|
||||
}
|
||||
|
||||
func loadConf(bytes []byte) (*NetConf, string, error) {
|
||||
func loadConf(bytes []byte, envArgs string) (*NetConf, string, error) {
|
||||
n := &NetConf{}
|
||||
if err := json.Unmarshal(bytes, n); err != nil {
|
||||
return nil, "", fmt.Errorf("failed to load netconf: %v", err)
|
||||
@ -85,9 +96,43 @@ func loadConf(bytes []byte) (*NetConf, string, error) {
|
||||
}
|
||||
n.Master = defaultRouteInterface
|
||||
}
|
||||
|
||||
// check existing and MTU of master interface
|
||||
masterMTU, err := getMTUByName(n.Master)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if n.MTU < 0 || n.MTU > masterMTU {
|
||||
return nil, "", fmt.Errorf("invalid MTU %d, must be [0, master MTU(%d)]", n.MTU, masterMTU)
|
||||
}
|
||||
|
||||
if envArgs != "" {
|
||||
e := MacEnvArgs{}
|
||||
err := types.LoadArgs(envArgs, &e)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
if e.MAC != "" {
|
||||
n.Mac = string(e.MAC)
|
||||
}
|
||||
}
|
||||
|
||||
if n.RuntimeConfig.Mac != "" {
|
||||
n.Mac = n.RuntimeConfig.Mac
|
||||
}
|
||||
|
||||
return n, n.CNIVersion, nil
|
||||
}
|
||||
|
||||
func getMTUByName(ifName string) (int, error) {
|
||||
link, err := netlink.LinkByName(ifName)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return link.Attrs().MTU, nil
|
||||
}
|
||||
|
||||
func modeFromString(s string) (netlink.MacvlanMode, error) {
|
||||
switch s {
|
||||
case "", "bridge":
|
||||
@ -138,14 +183,24 @@ func createMacvlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Inter
|
||||
return nil, err
|
||||
}
|
||||
|
||||
linkAttrs := netlink.LinkAttrs{
|
||||
MTU: conf.MTU,
|
||||
Name: tmpName,
|
||||
ParentIndex: m.Attrs().Index,
|
||||
Namespace: netlink.NsFd(int(netns.Fd())),
|
||||
}
|
||||
|
||||
if conf.Mac != "" {
|
||||
addr, err := net.ParseMAC(conf.Mac)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid args %v for MAC addr: %v", conf.Mac, err)
|
||||
}
|
||||
linkAttrs.HardwareAddr = addr
|
||||
}
|
||||
|
||||
mv := &netlink.Macvlan{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
MTU: conf.MTU,
|
||||
Name: tmpName,
|
||||
ParentIndex: m.Attrs().Index,
|
||||
Namespace: netlink.NsFd(int(netns.Fd())),
|
||||
},
|
||||
Mode: mode,
|
||||
LinkAttrs: linkAttrs,
|
||||
Mode: mode,
|
||||
}
|
||||
|
||||
if err := netlink.LinkAdd(mv); err != nil {
|
||||
@ -186,7 +241,7 @@ func createMacvlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Inter
|
||||
}
|
||||
|
||||
func cmdAdd(args *skel.CmdArgs) error {
|
||||
n, cniVersion, err := loadConf(args.StdinData)
|
||||
n, cniVersion, err := loadConf(args.StdinData, args.Args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -293,7 +348,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
||||
}
|
||||
|
||||
func cmdDel(args *skel.CmdArgs) error {
|
||||
n, _, err := loadConf(args.StdinData)
|
||||
n, _, err := loadConf(args.StdinData, args.Args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -331,7 +386,7 @@ func main() {
|
||||
|
||||
func cmdCheck(args *skel.CmdArgs) error {
|
||||
|
||||
n, _, err := loadConf(args.StdinData)
|
||||
n, _, err := loadConf(args.StdinData, args.Args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -123,6 +123,7 @@ var _ = Describe("macvlan Operations", func() {
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(originalNS.Close()).To(Succeed())
|
||||
Expect(testutils.UnmountNS(originalNS)).To(Succeed())
|
||||
})
|
||||
|
||||
It("creates an macvlan link in a non-default namespace", func() {
|
||||
@ -593,4 +594,176 @@ var _ = Describe("macvlan Operations", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("configures and deconfigures l2 macvlan link with mac address (from CNI_ARGS) with CNI v4 ADD/DEL", func() {
|
||||
const IFNAME = "macvl0"
|
||||
|
||||
conf := fmt.Sprintf(`{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "mynet",
|
||||
"type": "macvlan",
|
||||
"master": "%s",
|
||||
"ipam": {}
|
||||
}`, MASTER_NAME)
|
||||
|
||||
targetNs, err := testutils.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer targetNs.Close()
|
||||
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy",
|
||||
Netns: targetNs.Path(),
|
||||
IfName: IFNAME,
|
||||
StdinData: []byte(conf),
|
||||
Args: "IgnoreUnknown=true;MAC=c2:11:22:33:44:55",
|
||||
}
|
||||
|
||||
var result *current.Result
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
r, _, err := testutils.CmdAddWithArgs(args, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
result, err = current.GetResult(r)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(len(result.Interfaces)).To(Equal(1))
|
||||
Expect(result.Interfaces[0].Name).To(Equal(IFNAME))
|
||||
Expect(len(result.IPs)).To(Equal(0))
|
||||
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Make sure macvlan link exists in the target namespace
|
||||
err = targetNs.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
link, err := netlink.LinkByName(IFNAME)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(link.Attrs().Name).To(Equal(IFNAME))
|
||||
|
||||
hwaddr, err := net.ParseMAC("c2:11:22:33:44:55")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr))
|
||||
|
||||
addrs, err := netlink.AddrList(link, syscall.AF_INET)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(len(addrs)).To(Equal(0))
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
err := testutils.CmdDelWithArgs(args, func() error {
|
||||
return cmdDel(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Make sure macvlan link has been deleted
|
||||
err = targetNs.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
link, err := netlink.LinkByName(IFNAME)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(link).To(BeNil())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("configures and deconfigures l2 macvlan link with mac address (from RuntimeConfig) with ADD/DEL", func() {
|
||||
const IFNAME = "macvl0"
|
||||
|
||||
conf := fmt.Sprintf(`{
|
||||
"cniVersion": "0.3.1",
|
||||
"capabilities": {"mac": true},
|
||||
"RuntimeConfig": {
|
||||
"mac": "c2:11:22:33:44:55"
|
||||
},
|
||||
"name": "mynet",
|
||||
"type": "macvlan",
|
||||
"master": "%s",
|
||||
"ipam": {}
|
||||
}`, MASTER_NAME)
|
||||
|
||||
targetNs, err := testutils.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer targetNs.Close()
|
||||
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy",
|
||||
Netns: targetNs.Path(),
|
||||
IfName: IFNAME,
|
||||
StdinData: []byte(conf),
|
||||
}
|
||||
|
||||
var result *current.Result
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
r, _, err := testutils.CmdAddWithArgs(args, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
result, err = current.GetResult(r)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(len(result.Interfaces)).To(Equal(1))
|
||||
Expect(result.Interfaces[0].Name).To(Equal(IFNAME))
|
||||
Expect(len(result.IPs)).To(Equal(0))
|
||||
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Make sure macvlan link exists in the target namespace
|
||||
err = targetNs.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
link, err := netlink.LinkByName(IFNAME)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(link.Attrs().Name).To(Equal(IFNAME))
|
||||
|
||||
hwaddr, err := net.ParseMAC("c2:11:22:33:44:55")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr))
|
||||
|
||||
addrs, err := netlink.AddrList(link, syscall.AF_INET)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(len(addrs)).To(Equal(0))
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
err := testutils.CmdDelWithArgs(args, func() error {
|
||||
return cmdDel(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Make sure macvlan link has been deleted
|
||||
err = targetNs.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
link, err := netlink.LinkByName(IFNAME)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(link).To(BeNil())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
@ -228,7 +228,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
||||
}
|
||||
defer netns.Close()
|
||||
|
||||
hostInterface, containerInterface, err := setupContainerVeth(netns, args.IfName, conf.MTU, result)
|
||||
hostInterface, _, err := setupContainerVeth(netns, args.IfName, conf.MTU, result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -247,12 +247,23 @@ func cmdAdd(args *skel.CmdArgs) error {
|
||||
}
|
||||
}
|
||||
|
||||
result.DNS = conf.DNS
|
||||
result.Interfaces = []*current.Interface{hostInterface, containerInterface}
|
||||
// Only override the DNS settings in the previous result if any DNS fields
|
||||
// were provided to the ptp plugin. This allows, for example, IPAM plugins
|
||||
// to specify the DNS settings instead of the ptp plugin.
|
||||
if dnsConfSet(conf.DNS) {
|
||||
result.DNS = conf.DNS
|
||||
}
|
||||
|
||||
return types.PrintResult(result, conf.CNIVersion)
|
||||
}
|
||||
|
||||
func dnsConfSet(dnsConf types.DNS) bool {
|
||||
return dnsConf.Nameservers != nil ||
|
||||
dnsConf.Search != nil ||
|
||||
dnsConf.Options != nil ||
|
||||
dnsConf.Domain != ""
|
||||
}
|
||||
|
||||
func cmdDel(args *skel.CmdArgs) error {
|
||||
conf := NetConf{}
|
||||
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
|
||||
|
@ -17,6 +17,7 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
@ -98,9 +99,10 @@ var _ = Describe("ptp Operations", func() {
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(originalNS.Close()).To(Succeed())
|
||||
Expect(testutils.UnmountNS(originalNS)).To(Succeed())
|
||||
})
|
||||
|
||||
doTest := func(conf string, numIPs int) {
|
||||
doTest := func(conf string, numIPs int, expectedDNSConf types.DNS) {
|
||||
const IFNAME = "ptp0"
|
||||
|
||||
targetNs, err := testutils.NewNS()
|
||||
@ -175,6 +177,9 @@ var _ = Describe("ptp Operations", func() {
|
||||
Expect(res.Interfaces[1].Mac).To(Equal(wantMac))
|
||||
Expect(res.Interfaces[1].Sandbox).To(Equal(targetNs.Path()))
|
||||
|
||||
// make sure DNS is correct
|
||||
Expect(res.DNS).To(Equal(expectedDNSConf))
|
||||
|
||||
// Call the plugins with the DEL command, deleting the veth endpoints
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
@ -327,7 +332,16 @@ var _ = Describe("ptp Operations", func() {
|
||||
}
|
||||
|
||||
It("configures and deconfigures a ptp link with ADD/DEL", func() {
|
||||
conf := `{
|
||||
dnsConf := types.DNS{
|
||||
Nameservers: []string{"10.1.2.123"},
|
||||
Domain: "some.domain.test",
|
||||
Search: []string{"search.test"},
|
||||
Options: []string{"option1:foo"},
|
||||
}
|
||||
dnsConfBytes, err := json.Marshal(dnsConf)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
conf := fmt.Sprintf(`{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "mynet",
|
||||
"type": "ptp",
|
||||
@ -336,10 +350,11 @@ var _ = Describe("ptp Operations", func() {
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.1.2.0/24"
|
||||
}
|
||||
}`
|
||||
},
|
||||
"dns": %s
|
||||
}`, string(dnsConfBytes))
|
||||
|
||||
doTest(conf, 1)
|
||||
doTest(conf, 1, dnsConf)
|
||||
})
|
||||
|
||||
It("configures and deconfigures a dual-stack ptp link with ADD/DEL", func() {
|
||||
@ -358,7 +373,112 @@ var _ = Describe("ptp Operations", func() {
|
||||
}
|
||||
}`
|
||||
|
||||
doTest(conf, 2)
|
||||
doTest(conf, 2, types.DNS{})
|
||||
})
|
||||
|
||||
It("does not override IPAM DNS settings if no DNS settings provided", func() {
|
||||
ipamDNSConf := types.DNS{
|
||||
Nameservers: []string{"10.1.2.123"},
|
||||
Domain: "some.domain.test",
|
||||
Search: []string{"search.test"},
|
||||
Options: []string{"option1:foo"},
|
||||
}
|
||||
resolvConfPath, err := testutils.TmpResolvConf(ipamDNSConf)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer os.RemoveAll(resolvConfPath)
|
||||
|
||||
conf := fmt.Sprintf(`{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "mynet",
|
||||
"type": "ptp",
|
||||
"ipMasq": true,
|
||||
"mtu": 5000,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.1.2.0/24",
|
||||
"resolvConf": "%s"
|
||||
}
|
||||
}`, resolvConfPath)
|
||||
|
||||
doTest(conf, 1, ipamDNSConf)
|
||||
})
|
||||
|
||||
It("overrides IPAM DNS settings if any DNS settings provided", func() {
|
||||
ipamDNSConf := types.DNS{
|
||||
Nameservers: []string{"10.1.2.123"},
|
||||
Domain: "some.domain.test",
|
||||
Search: []string{"search.test"},
|
||||
Options: []string{"option1:foo"},
|
||||
}
|
||||
resolvConfPath, err := testutils.TmpResolvConf(ipamDNSConf)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer os.RemoveAll(resolvConfPath)
|
||||
|
||||
for _, ptpDNSConf := range []types.DNS{
|
||||
{
|
||||
Nameservers: []string{"10.1.2.234"},
|
||||
},
|
||||
{
|
||||
Domain: "someother.domain.test",
|
||||
},
|
||||
{
|
||||
Search: []string{"search.elsewhere.test"},
|
||||
},
|
||||
{
|
||||
Options: []string{"option2:bar"},
|
||||
},
|
||||
} {
|
||||
dnsConfBytes, err := json.Marshal(ptpDNSConf)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
conf := fmt.Sprintf(`{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "mynet",
|
||||
"type": "ptp",
|
||||
"ipMasq": true,
|
||||
"mtu": 5000,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.1.2.0/24",
|
||||
"resolvConf": "%s"
|
||||
},
|
||||
"dns": %s
|
||||
}`, resolvConfPath, string(dnsConfBytes))
|
||||
|
||||
doTest(conf, 1, ptpDNSConf)
|
||||
}
|
||||
})
|
||||
|
||||
It("overrides IPAM DNS settings if any empty list DNS settings provided", func() {
|
||||
ipamDNSConf := types.DNS{
|
||||
Nameservers: []string{"10.1.2.123"},
|
||||
Domain: "some.domain.test",
|
||||
Search: []string{"search.test"},
|
||||
Options: []string{"option1:foo"},
|
||||
}
|
||||
resolvConfPath, err := testutils.TmpResolvConf(ipamDNSConf)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer os.RemoveAll(resolvConfPath)
|
||||
|
||||
conf := fmt.Sprintf(`{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "mynet",
|
||||
"type": "ptp",
|
||||
"ipMasq": true,
|
||||
"mtu": 5000,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.1.2.0/24",
|
||||
"resolvConf": "%s"
|
||||
},
|
||||
"dns": {
|
||||
"nameservers": [],
|
||||
"search": [],
|
||||
"options": []
|
||||
}
|
||||
}`, resolvConfPath)
|
||||
|
||||
doTest(conf, 1, types.DNS{})
|
||||
})
|
||||
|
||||
It("deconfigures an unconfigured ptp link with DEL", func() {
|
||||
|
@ -53,14 +53,32 @@ func loadConf(bytes []byte) (*NetConf, string, error) {
|
||||
return nil, "", fmt.Errorf("failed to load netconf: %v", err)
|
||||
}
|
||||
if n.Master == "" {
|
||||
return nil, "", fmt.Errorf(`"master" field is required. It specifies the host interface name to create the VLAN for.`)
|
||||
return nil, "", fmt.Errorf("\"master\" field is required. It specifies the host interface name to create the VLAN for.")
|
||||
}
|
||||
if n.VlanId < 0 || n.VlanId > 4094 {
|
||||
return nil, "", fmt.Errorf(`invalid VLAN ID %d (must be between 0 and 4095 inclusive)`, n.VlanId)
|
||||
return nil, "", fmt.Errorf("invalid VLAN ID %d (must be between 0 and 4095 inclusive)", n.VlanId)
|
||||
}
|
||||
|
||||
// check existing and MTU of master interface
|
||||
masterMTU, err := getMTUByName(n.Master)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if n.MTU < 0 || n.MTU > masterMTU {
|
||||
return nil, "", fmt.Errorf("invalid MTU %d, must be [0, master MTU(%d)]", n.MTU, masterMTU)
|
||||
}
|
||||
|
||||
return n, n.CNIVersion, nil
|
||||
}
|
||||
|
||||
func getMTUByName(ifName string) (int, error) {
|
||||
link, err := netlink.LinkByName(ifName)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return link.Attrs().MTU, nil
|
||||
}
|
||||
|
||||
func createVlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Interface, error) {
|
||||
vlan := ¤t.Interface{}
|
||||
|
||||
@ -76,10 +94,6 @@ func createVlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Interfac
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if conf.MTU <= 0 {
|
||||
conf.MTU = m.Attrs().MTU
|
||||
}
|
||||
|
||||
v := &netlink.Vlan{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
MTU: conf.MTU,
|
||||
@ -193,7 +207,7 @@ func cmdDel(args *skel.CmdArgs) error {
|
||||
|
||||
err = ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
|
||||
err = ip.DelLinkByName(args.IfName)
|
||||
if err != nil && err != ip.ErrLinkNotFound {
|
||||
if err != nil && err == ip.ErrLinkNotFound {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
|
@ -121,6 +121,7 @@ var _ = Describe("vlan Operations", func() {
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(originalNS.Close()).To(Succeed())
|
||||
Expect(testutils.UnmountNS(originalNS)).To(Succeed())
|
||||
})
|
||||
|
||||
It("creates an vlan link in a non-default namespace with given MTU", func() {
|
||||
@ -291,6 +292,19 @@ var _ = Describe("vlan Operations", func() {
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// DEL can be called multiple times, make sure no error is returned
|
||||
// if the device is already removed.
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
err = testutils.CmdDelWithArgs(args, func() error {
|
||||
return cmdDel(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("configures and deconfigures an CNI V4 vlan link with ADD/CHECK/DEL", func() {
|
||||
@ -407,4 +421,75 @@ var _ = Describe("vlan Operations", func() {
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
Describe("fails to create vlan link with invalid MTU", func() {
|
||||
conf := `{
|
||||
"cniVersion": "0.3.1",
|
||||
"name": "mynet",
|
||||
"type": "vlan",
|
||||
"master": "%s",
|
||||
"mtu": %d,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.1.2.0/24"
|
||||
}
|
||||
}`
|
||||
BeforeEach(func() {
|
||||
var err error
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
// set master link's MTU to 1500
|
||||
link, err := netlink.LinkByName(MASTER_NAME)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = netlink.LinkSetMTU(link, 1500)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("fails to create vlan link with greater MTU than master interface", func() {
|
||||
var err error
|
||||
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy",
|
||||
Netns: "/var/run/netns/test",
|
||||
IfName: "eth0",
|
||||
StdinData: []byte(fmt.Sprintf(conf, MASTER_NAME, 1600)),
|
||||
}
|
||||
|
||||
_ = originalNS.Do(func(netNS ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
_, _, err = testutils.CmdAddWithArgs(args, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).To(Equal(fmt.Errorf("invalid MTU 1600, must be [0, master MTU(1500)]")))
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
It("fails to create vlan link with negative MTU", func() {
|
||||
var err error
|
||||
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy",
|
||||
Netns: "/var/run/netns/test",
|
||||
IfName: "eth0",
|
||||
StdinData: []byte(fmt.Sprintf(conf, MASTER_NAME, -100)),
|
||||
}
|
||||
|
||||
_ = originalNS.Do(func(netNS ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
_, _, err = testutils.CmdAddWithArgs(args, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).To(Equal(fmt.Errorf("invalid MTU -100, must be [0, master MTU(1500)]")))
|
||||
return nil
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -35,7 +35,8 @@ With win-bridge plugin, all containers (on the same host) are plugged into an L2
|
||||
],
|
||||
"loopbackDSR": true,
|
||||
"capabilities": {
|
||||
"dns": true
|
||||
"dns": true,
|
||||
"portMappings": true
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -54,4 +55,5 @@ With win-bridge plugin, all containers (on the same host) are plugged into an L2
|
||||
* `HcnPolicyArgs` (list, optional): List of hcn policies to be used (only used when ApiVersion is 2).
|
||||
* `loopbackDSR` (bool, optional): If true, will add a policy to allow the interface to support loopback direct server return.
|
||||
* `capabilities` (dictionary, optional): Runtime capabilities to enable.
|
||||
* `dns` (boolean, optional): If true, will take the dns config supplied by the runtime and override other settings.
|
||||
* `dns` (boolean, optional): If true, will take the dns config supplied by the runtime and override other settings.
|
||||
* `portMappings` (boolean, optional): If true, will handle HostPort<>ContainerPort mapping using NAT HNS Policies
|
@ -22,13 +22,13 @@ import (
|
||||
|
||||
"github.com/Microsoft/hcsshim"
|
||||
"github.com/Microsoft/hcsshim/hcn"
|
||||
"github.com/juju/errors"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/types/current"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
|
||||
"github.com/containernetworking/plugins/pkg/errors"
|
||||
"github.com/containernetworking/plugins/pkg/hns"
|
||||
"github.com/containernetworking/plugins/pkg/ipam"
|
||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
||||
@ -72,7 +72,7 @@ func ProcessEndpointArgs(args *skel.CmdArgs, n *NetConf) (*hns.EndpointInfo, err
|
||||
return nil, errors.Annotatef(err, "error while NewResultFromResult")
|
||||
} else {
|
||||
if len(result.IPs) == 0 {
|
||||
return nil, errors.New("IPAM plugin return is missing IP config")
|
||||
return nil, fmt.Errorf("IPAM plugin return is missing IP config")
|
||||
}
|
||||
epInfo.IpAddress = result.IPs[0].Address.IP
|
||||
epInfo.Gateway = result.IPs[0].Address.IP.Mask(result.IPs[0].Address.Mask)
|
||||
@ -86,6 +86,9 @@ func ProcessEndpointArgs(args *skel.CmdArgs, n *NetConf) (*hns.EndpointInfo, err
|
||||
n.ApplyOutboundNatPolicy(n.IPMasqNetwork)
|
||||
}
|
||||
|
||||
// Add HostPort mapping if any present
|
||||
n.ApplyPortMappingPolicy(n.RuntimeConfig.PortMaps)
|
||||
|
||||
epInfo.DNS = n.GetDNS()
|
||||
|
||||
return epInfo, nil
|
||||
@ -107,7 +110,6 @@ func cmdHnsAdd(args *skel.CmdArgs, n *NetConf) (*current.Result, error) {
|
||||
}
|
||||
|
||||
epName := hns.ConstructEndpointName(args.ContainerID, args.Netns, n.Name)
|
||||
|
||||
hnsEndpoint, err := hns.ProvisionEndpoint(epName, hnsNetwork.Id, args.ContainerID, args.Netns, func() (*hcsshim.HNSEndpoint, error) {
|
||||
epInfo, err := ProcessEndpointArgs(args, n)
|
||||
epInfo.NetworkId = hnsNetwork.Id
|
||||
@ -130,7 +132,6 @@ func cmdHnsAdd(args *skel.CmdArgs, n *NetConf) (*current.Result, error) {
|
||||
}
|
||||
|
||||
return result, nil
|
||||
|
||||
}
|
||||
|
||||
func cmdHcnAdd(args *skel.CmdArgs, n *NetConf) (*current.Result, error) {
|
||||
@ -194,7 +195,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
||||
}
|
||||
|
||||
if result == nil {
|
||||
return errors.New("result for ADD not populated correctly")
|
||||
return fmt.Errorf("result for ADD not populated correctly")
|
||||
}
|
||||
return types.PrintResult(result, cniVersion)
|
||||
}
|
||||
|
@ -21,13 +21,13 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/Microsoft/hcsshim"
|
||||
"github.com/juju/errors"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/types/current"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
|
||||
"github.com/containernetworking/plugins/pkg/errors"
|
||||
"github.com/containernetworking/plugins/pkg/hns"
|
||||
"github.com/containernetworking/plugins/pkg/ipam"
|
||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
||||
@ -100,12 +100,12 @@ func cmdAdd(args *skel.CmdArgs) error {
|
||||
}
|
||||
|
||||
if len(result.IPs) == 0 {
|
||||
return nil, errors.New("IPAM plugin return is missing IP config")
|
||||
return nil, fmt.Errorf("IPAM plugin return is missing IP config")
|
||||
}
|
||||
|
||||
ipAddr := result.IPs[0].Address.IP.To4()
|
||||
if ipAddr == nil {
|
||||
return nil, errors.New("win-overlay doesn't support IPv6 now")
|
||||
return nil, fmt.Errorf("win-overlay doesn't support IPv6 now")
|
||||
}
|
||||
|
||||
// conjure a MAC based on the IP for Overlay
|
||||
|
@ -621,6 +621,21 @@ var _ = Describe("bandwidth test", func() {
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Validating input", func() {
|
||||
It("Should allow only 4GB burst rate", func() {
|
||||
err := validateRateAndBurst(5000, 4*1024*1024*1024*8-16) // 2 bytes less than the max should pass
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
err = validateRateAndBurst(5000, 4*1024*1024*1024*8) // we're 1 bit above MaxUint32
|
||||
Expect(err).To(HaveOccurred())
|
||||
err = validateRateAndBurst(0, 1)
|
||||
Expect(err).To(HaveOccurred())
|
||||
err = validateRateAndBurst(1, 0)
|
||||
Expect(err).To(HaveOccurred())
|
||||
err = validateRateAndBurst(0, 0)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Describe("Getting the host interface which plugin should work on from veth peer of container interface", func() {
|
||||
It("Should work with multiple host veth interfaces", func() {
|
||||
conf := `{
|
||||
@ -874,8 +889,8 @@ var _ = Describe("bandwidth test", func() {
|
||||
|
||||
Context("when chaining bandwidth plugin with PTP using 0.3.0 config", func() {
|
||||
var ptpConf string
|
||||
var rateInBits int
|
||||
var burstInBits int
|
||||
var rateInBits uint64
|
||||
var burstInBits uint64
|
||||
var packetInBytes int
|
||||
var containerWithoutTbfNS ns.NetNS
|
||||
var containerWithTbfNS ns.NetNS
|
||||
@ -889,7 +904,7 @@ var _ = Describe("bandwidth test", func() {
|
||||
|
||||
BeforeEach(func() {
|
||||
rateInBytes := 1000
|
||||
rateInBits = rateInBytes * 8
|
||||
rateInBits = uint64(rateInBytes * 8)
|
||||
burstInBits = rateInBits * 2
|
||||
packetInBytes = rateInBytes * 25
|
||||
|
||||
@ -1019,8 +1034,8 @@ var _ = Describe("bandwidth test", func() {
|
||||
|
||||
Context("when chaining bandwidth plugin with PTP using 0.4.0 config", func() {
|
||||
var ptpConf string
|
||||
var rateInBits int
|
||||
var burstInBits int
|
||||
var rateInBits uint64
|
||||
var burstInBits uint64
|
||||
var packetInBytes int
|
||||
var containerWithoutTbfNS ns.NetNS
|
||||
var containerWithTbfNS ns.NetNS
|
||||
@ -1034,7 +1049,7 @@ var _ = Describe("bandwidth test", func() {
|
||||
|
||||
BeforeEach(func() {
|
||||
rateInBytes := 1000
|
||||
rateInBits = rateInBytes * 8
|
||||
rateInBits = uint64(rateInBytes * 8)
|
||||
burstInBits = rateInBits * 2
|
||||
packetInBytes = rateInBytes * 25
|
||||
|
||||
|
@ -50,7 +50,7 @@ func TeardownIfb(deviceName string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func CreateIngressQdisc(rateInBits, burstInBits int, hostDeviceName string) error {
|
||||
func CreateIngressQdisc(rateInBits, burstInBits uint64, hostDeviceName string) error {
|
||||
hostDevice, err := netlink.LinkByName(hostDeviceName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get host device: %s", err)
|
||||
@ -58,7 +58,7 @@ func CreateIngressQdisc(rateInBits, burstInBits int, hostDeviceName string) erro
|
||||
return createTBF(rateInBits, burstInBits, hostDevice.Attrs().Index)
|
||||
}
|
||||
|
||||
func CreateEgressQdisc(rateInBits, burstInBits int, hostDeviceName string, ifbDeviceName string) error {
|
||||
func CreateEgressQdisc(rateInBits, burstInBits uint64, hostDeviceName string, ifbDeviceName string) error {
|
||||
ifbDevice, err := netlink.LinkByName(ifbDeviceName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("get ifb device: %s", err)
|
||||
@ -113,7 +113,7 @@ func CreateEgressQdisc(rateInBits, burstInBits int, hostDeviceName string, ifbDe
|
||||
return nil
|
||||
}
|
||||
|
||||
func createTBF(rateInBits, burstInBits, linkIndex int) error {
|
||||
func createTBF(rateInBits, burstInBits uint64, linkIndex int) error {
|
||||
// Equivalent to
|
||||
// tc qdisc add dev link root tbf
|
||||
// rate netConf.BandwidthLimits.Rate
|
||||
|
@ -17,6 +17,7 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
|
||||
@ -37,11 +38,11 @@ const ifbDevicePrefix = "bwp"
|
||||
// BandwidthEntry corresponds to a single entry in the bandwidth argument,
|
||||
// see CONVENTIONS.md
|
||||
type BandwidthEntry struct {
|
||||
IngressRate int `json:"ingressRate"` //Bandwidth rate in bps for traffic through container. 0 for no limit. If ingressRate is set, ingressBurst must also be set
|
||||
IngressBurst int `json:"ingressBurst"` //Bandwidth burst in bits for traffic through container. 0 for no limit. If ingressBurst is set, ingressRate must also be set
|
||||
IngressRate uint64 `json:"ingressRate"` //Bandwidth rate in bps for traffic through container. 0 for no limit. If ingressRate is set, ingressBurst must also be set
|
||||
IngressBurst uint64 `json:"ingressBurst"` //Bandwidth burst in bits for traffic through container. 0 for no limit. If ingressBurst is set, ingressRate must also be set
|
||||
|
||||
EgressRate int `json:"egressRate"` //Bandwidth rate in bps for traffic through container. 0 for no limit. If egressRate is set, egressBurst must also be set
|
||||
EgressBurst int `json:"egressBurst"` //Bandwidth burst in bits for traffic through container. 0 for no limit. If egressBurst is set, egressRate must also be set
|
||||
EgressRate uint64 `json:"egressRate"` //Bandwidth rate in bps for traffic through container. 0 for no limit. If egressRate is set, egressBurst must also be set
|
||||
EgressBurst uint64 `json:"egressBurst"` //Bandwidth burst in bits for traffic through container. 0 for no limit. If egressBurst is set, egressRate must also be set
|
||||
}
|
||||
|
||||
func (bw *BandwidthEntry) isZero() bool {
|
||||
@ -101,7 +102,7 @@ func getBandwidth(conf *PluginConf) *BandwidthEntry {
|
||||
return conf.BandwidthEntry
|
||||
}
|
||||
|
||||
func validateRateAndBurst(rate int, burst int) error {
|
||||
func validateRateAndBurst(rate, burst uint64) error {
|
||||
switch {
|
||||
case burst < 0 || rate < 0:
|
||||
return fmt.Errorf("rate and burst must be a positive integer")
|
||||
@ -109,6 +110,8 @@ func validateRateAndBurst(rate int, burst int) error {
|
||||
return fmt.Errorf("if rate is set, burst must also be set")
|
||||
case rate == 0 && burst != 0:
|
||||
return fmt.Errorf("if burst is set, rate must also be set")
|
||||
case burst/8 >= math.MaxUint32:
|
||||
return fmt.Errorf("burst cannot be more than 4GB")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -28,7 +28,7 @@ The following network configuration file
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "firewall",
|
||||
"type": "firewall"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -72,7 +72,7 @@ look like:
|
||||
},
|
||||
{
|
||||
"type": "firewall",
|
||||
"backend": "firewalld"
|
||||
"backend": "firewalld"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -111,7 +111,7 @@ look like:
|
||||
},
|
||||
{
|
||||
"type": "firewall",
|
||||
"backend": "iptables"
|
||||
"backend": "iptables"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -129,7 +129,21 @@ when containers are created and from where rules will be removed when containers
|
||||
CNI-FORWARD will have a pair of rules added, one for each direction, using the IPAM assigned IP address
|
||||
of the container as shown:
|
||||
|
||||
`CNI_FORWARD` chain:
|
||||
- `-s 10.88.0.2 -m conntrack --ctstate RELATED,ESTABLISHED -j CNI-FORWARD`
|
||||
- `-d 10.88.0.2 -j CNI-FORWARD`
|
||||
`CNI-FORWARD` chain:
|
||||
- `-s 10.88.0.2 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT`
|
||||
- `-d 10.88.0.2 -j ACCEPT`
|
||||
|
||||
The `CNI-FORWARD` chain first sends all traffic to `CNI-ADMIN` chain, which is intended as an user-controlled chain for custom rules that run prior to rules managed by the `firewall` plugin. The `firewall` plugin does not add, delete or modify rules in the `CNI-ADMIN` chain.
|
||||
|
||||
`CNI-FORWARD` chain:
|
||||
- `-j CNI-ADMIN`
|
||||
|
||||
The chain name `CNI-ADMIN` can be overridden by specifying `iptablesAdminChainName` in the plugin configuration
|
||||
|
||||
```
|
||||
{
|
||||
"type": "firewall",
|
||||
"backend": "iptables",
|
||||
"iptablesAdminChainName": "SOME-OTHER-CHAIN-NAME",
|
||||
}
|
||||
```
|
||||
|
@ -27,7 +27,6 @@ import (
|
||||
"github.com/containernetworking/cni/pkg/types/current"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
||||
)
|
||||
|
||||
@ -68,9 +67,15 @@ func parseConf(data []byte) (*FirewallNetConf, *current.Result, error) {
|
||||
return nil, nil, fmt.Errorf("failed to load netconf: %v", err)
|
||||
}
|
||||
|
||||
// Default the firewalld zone to trusted
|
||||
if conf.FirewalldZone == "" {
|
||||
conf.FirewalldZone = "trusted"
|
||||
}
|
||||
|
||||
// Parse previous result.
|
||||
if conf.RawPrevResult == nil {
|
||||
return nil, nil, fmt.Errorf("missing prevResult from earlier plugin")
|
||||
// return early if there was no previous result, which is allowed for DEL calls
|
||||
return &conf, ¤t.Result{}, nil
|
||||
}
|
||||
|
||||
// Parse previous result.
|
||||
@ -85,11 +90,6 @@ func parseConf(data []byte) (*FirewallNetConf, *current.Result, error) {
|
||||
return nil, nil, fmt.Errorf("could not convert result to current version: %v", err)
|
||||
}
|
||||
|
||||
// Default the firewalld zone to trusted
|
||||
if conf.FirewalldZone == "" {
|
||||
conf.FirewalldZone = "trusted"
|
||||
}
|
||||
|
||||
return &conf, result, nil
|
||||
}
|
||||
|
||||
@ -116,6 +116,10 @@ func cmdAdd(args *skel.CmdArgs) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if conf.PrevResult == nil {
|
||||
return fmt.Errorf("missing prevResult from earlier plugin")
|
||||
}
|
||||
|
||||
backend, err := getBackend(conf)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -142,12 +146,6 @@ func cmdDel(args *skel.CmdArgs) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// Tolerate errors if the container namespace has been torn down already
|
||||
containerNS, err := ns.GetNS(args.Netns)
|
||||
if err == nil {
|
||||
defer containerNS.Close()
|
||||
}
|
||||
|
||||
// Runtime errors are ignored
|
||||
if err := backend.Del(conf, result); err != nil {
|
||||
return err
|
||||
@ -167,8 +165,8 @@ func cmdCheck(args *skel.CmdArgs) error {
|
||||
}
|
||||
|
||||
// Ensure we have previous result.
|
||||
if result == nil {
|
||||
return fmt.Errorf("Required prevResult missing")
|
||||
if conf.PrevResult == nil {
|
||||
return fmt.Errorf("missing prevResult from earlier plugin")
|
||||
}
|
||||
|
||||
backend, err := getBackend(conf)
|
||||
|
@ -270,6 +270,13 @@ var _ = Describe("firewall plugin iptables backend", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
validateFullRuleset(fullConf)
|
||||
|
||||
// ensure creation is idempotent
|
||||
_, _, err = testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
@ -22,6 +22,7 @@ import (
|
||||
"net"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types/current"
|
||||
"github.com/containernetworking/plugins/pkg/utils"
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
)
|
||||
|
||||
@ -32,20 +33,6 @@ func getPrivChainRules(ip string) [][]string {
|
||||
return rules
|
||||
}
|
||||
|
||||
func ensureChain(ipt *iptables.IPTables, table, chain string) error {
|
||||
chains, err := ipt.ListChains(table)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list iptables chains: %v", err)
|
||||
}
|
||||
for _, ch := range chains {
|
||||
if ch == chain {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return ipt.NewChain(table, chain)
|
||||
}
|
||||
|
||||
func generateFilterRule(privChainName string) []string {
|
||||
return []string{"-m", "comment", "--comment", "CNI firewall plugin rules", "-j", privChainName}
|
||||
}
|
||||
@ -70,13 +57,13 @@ func ensureFirstChainRule(ipt *iptables.IPTables, chain string, rule []string) e
|
||||
|
||||
func (ib *iptablesBackend) setupChains(ipt *iptables.IPTables) error {
|
||||
privRule := generateFilterRule(ib.privChainName)
|
||||
adminRule := generateFilterRule(ib.adminChainName)
|
||||
adminRule := generateAdminRule(ib.adminChainName)
|
||||
|
||||
// Ensure our private chains exist
|
||||
if err := ensureChain(ipt, "filter", ib.privChainName); err != nil {
|
||||
if err := utils.EnsureChain(ipt, "filter", ib.privChainName); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ensureChain(ipt, "filter", ib.adminChainName); err != nil {
|
||||
if err := utils.EnsureChain(ipt, "filter", ib.adminChainName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -160,10 +147,10 @@ func (ib *iptablesBackend) checkRules(conf *FirewallNetConf, result *current.Res
|
||||
}
|
||||
|
||||
// Ensure our private chains exist
|
||||
if err := ensureChain(ipt, "filter", ib.privChainName); err != nil {
|
||||
if err := utils.EnsureChain(ipt, "filter", ib.privChainName); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ensureChain(ipt, "filter", ib.adminChainName); err != nil {
|
||||
if err := utils.EnsureChain(ipt, "filter", ib.adminChainName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -178,7 +165,7 @@ func (ib *iptablesBackend) checkRules(conf *FirewallNetConf, result *current.Res
|
||||
}
|
||||
|
||||
// Ensure our admin override chain rule exists in our private chain
|
||||
adminRule := generateFilterRule(ib.adminChainName)
|
||||
adminRule := generateAdminRule(ib.adminChainName)
|
||||
adminExists, err := ipt.Exists("filter", ib.privChainName, adminRule...)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -89,7 +89,7 @@ Additionally, for the bridge plugin, `isGateway` will be set to `true`, if not p
|
||||
|
||||
## Windows Support (Experimental)
|
||||
This plugin supports delegating to the windows CNI plugins (overlay.exe, l2bridge.exe) to work in conjunction with [Flannel on Windows](https://github.com/coreos/flannel/issues/833).
|
||||
Flannel sets up an [HNS Network](https://docs.microsoft.com/en-us/virtualization/windowscontainers/manage-containers/container-networking) in L2Bridge mode for host-gw and in Overlay mode for vxlan.
|
||||
Flannel sets up an [HNS Network](https://docs.microsoft.com/en-us/virtualization/windowscontainers/container-networking/architecture) in L2Bridge mode for host-gw and in Overlay mode for vxlan.
|
||||
|
||||
The following fields must be set in the delegated plugin configuration:
|
||||
* `name` (string, required): the name of the network (must match the name in Flannel config / name of the HNS network)
|
||||
@ -132,4 +132,4 @@ For this example, Flannel CNI would generate the following config to delegate to
|
||||
"type": "host-local"
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
|
@ -146,12 +146,19 @@ func saveScratchNetConf(containerID, dataDir string, netconf []byte) error {
|
||||
return ioutil.WriteFile(path, netconf, 0600)
|
||||
}
|
||||
|
||||
func consumeScratchNetConf(containerID, dataDir string) ([]byte, error) {
|
||||
func consumeScratchNetConf(containerID, dataDir string) (func(error), []byte, error) {
|
||||
path := filepath.Join(dataDir, containerID)
|
||||
// Ignore errors when removing - Per spec safe to continue during DEL
|
||||
defer os.Remove(path)
|
||||
|
||||
return ioutil.ReadFile(path)
|
||||
// cleanup will do clean job when no error happens in consuming/using process
|
||||
cleanup := func(err error) {
|
||||
if err == nil {
|
||||
// Ignore errors when removing - Per spec safe to continue during DEL
|
||||
_ = os.Remove(path)
|
||||
}
|
||||
}
|
||||
netConfBytes, err := ioutil.ReadFile(path)
|
||||
|
||||
return cleanup, netConfBytes, err
|
||||
}
|
||||
|
||||
func delegateAdd(cid, dataDir string, netconf map[string]interface{}) error {
|
||||
|
@ -68,8 +68,8 @@ func doCmdAdd(args *skel.CmdArgs, n *NetConf, fenv *subnetEnv) error {
|
||||
return delegateAdd(args.ContainerID, n.DataDir, n.Delegate)
|
||||
}
|
||||
|
||||
func doCmdDel(args *skel.CmdArgs, n *NetConf) error {
|
||||
netconfBytes, err := consumeScratchNetConf(args.ContainerID, n.DataDir)
|
||||
func doCmdDel(args *skel.CmdArgs, n *NetConf) (err error) {
|
||||
cleanup, netConfBytes, err := consumeScratchNetConf(args.ContainerID, n.DataDir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// Per spec should ignore error if resources are missing / already removed
|
||||
@ -78,10 +78,15 @@ func doCmdDel(args *skel.CmdArgs, n *NetConf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// cleanup will work when no error happens
|
||||
defer func() {
|
||||
cleanup(err)
|
||||
}()
|
||||
|
||||
nc := &types.NetConf{}
|
||||
if err = json.Unmarshal(netconfBytes, nc); err != nil {
|
||||
if err = json.Unmarshal(netConfBytes, nc); err != nil {
|
||||
return fmt.Errorf("failed to parse netconf: %v", err)
|
||||
}
|
||||
|
||||
return invoke.DelegateDel(context.TODO(), nc.Type, netconfBytes, nil)
|
||||
return invoke.DelegateDel(context.TODO(), nc.Type, netConfBytes, nil)
|
||||
}
|
||||
|
@ -55,8 +55,8 @@ func doCmdAdd(args *skel.CmdArgs, n *NetConf, fenv *subnetEnv) error {
|
||||
return delegateAdd(hns.GetSandboxContainerID(args.ContainerID, args.Netns), n.DataDir, n.Delegate)
|
||||
}
|
||||
|
||||
func doCmdDel(args *skel.CmdArgs, n *NetConf) error {
|
||||
netconfBytes, err := consumeScratchNetConf(hns.GetSandboxContainerID(args.ContainerID, args.Netns), n.DataDir)
|
||||
func doCmdDel(args *skel.CmdArgs, n *NetConf) (err error) {
|
||||
cleanup, netConfBytes, err := consumeScratchNetConf(hns.GetSandboxContainerID(args.ContainerID, args.Netns), n.DataDir)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
// Per spec should ignore error if resources are missing / already removed
|
||||
@ -65,10 +65,15 @@ func doCmdDel(args *skel.CmdArgs, n *NetConf) error {
|
||||
return err
|
||||
}
|
||||
|
||||
// cleanup will work when no error happens
|
||||
defer func() {
|
||||
cleanup(err)
|
||||
}()
|
||||
|
||||
nc := &types.NetConf{}
|
||||
if err = json.Unmarshal(netconfBytes, nc); err != nil {
|
||||
if err = json.Unmarshal(netConfBytes, nc); err != nil {
|
||||
return fmt.Errorf("failed to parse netconf: %v", err)
|
||||
}
|
||||
|
||||
return invoke.DelegateDel(context.TODO(), nc.Type, netconfBytes, nil)
|
||||
return invoke.DelegateDel(context.TODO(), nc.Type, netConfBytes, nil)
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ will masquerade traffic as needed.
|
||||
The DNAT rule rewrites the destination port and address of new connections.
|
||||
There is a top-level chain, `CNI-HOSTPORT-DNAT` which is always created and
|
||||
never deleted. Each plugin execution creates an additional chain for ease
|
||||
of cleanup. So, if a single container exists on IP 172.16.30.2 with ports
|
||||
of cleanup. So, if a single container exists on IP 172.16.30.2/24 with ports
|
||||
8080 and 8043 on the host forwarded to ports 80 and 443 in the container, the
|
||||
rules look like this:
|
||||
|
||||
@ -86,10 +86,10 @@ rules look like this:
|
||||
- `-j MARK --set-xmark 0x2000/0x2000`
|
||||
|
||||
`CNI-DN-xxxxxx` chain:
|
||||
- `-p tcp -s 172.16.30.2 --dport 8080 -j CNI-HOSTPORT-SETMARK` (masquerade hairpin traffic)
|
||||
- `-p tcp -s 172.16.30.0/24 --dport 8080 -j CNI-HOSTPORT-SETMARK` (masquerade hairpin traffic)
|
||||
- `-p tcp -s 127.0.0.1 --dport 8080 -j CNI-HOSTPORT-SETMARK` (masquerade localhost traffic)
|
||||
- `-p tcp --dport 8080 -j DNAT --to-destination 172.16.30.2:80` (rewrite destination)
|
||||
- `-p tcp -s 172.16.30.2 --dport 8043 -j CNI-HOSTPORT-SETMARK`
|
||||
- `-p tcp -s 172.16.30.0/24 --dport 8043 -j CNI-HOSTPORT-SETMARK`
|
||||
- `-p tcp -s 127.0.0.1 --dport 8043 -j CNI-HOSTPORT-SETMARK`
|
||||
- `-p tcp --dport 8043 -j DNAT --to-destination 172.16.30.2:443`
|
||||
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/containernetworking/plugins/pkg/utils"
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
"github.com/mattn/go-shellwords"
|
||||
)
|
||||
@ -35,16 +36,11 @@ type chain struct {
|
||||
|
||||
// setup idempotently creates the chain. It will not error if the chain exists.
|
||||
func (c *chain) setup(ipt *iptables.IPTables) error {
|
||||
// create the chain
|
||||
exists, err := chainExists(ipt, c.table, c.name)
|
||||
|
||||
err := utils.EnsureChain(ipt, c.table, c.name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !exists {
|
||||
if err := ipt.NewChain(c.table, c.name); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Add the rules to the chain
|
||||
for _, rule := range c.rules {
|
||||
@ -74,7 +70,7 @@ func (c *chain) teardown(ipt *iptables.IPTables) error {
|
||||
// flush the chain
|
||||
// This will succeed *and create the chain* if it does not exist.
|
||||
// If the chain doesn't exist, the next checks will fail.
|
||||
if err := ipt.ClearChain(c.table, c.name); err != nil {
|
||||
if err := utils.ClearChain(ipt, c.table, c.name); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -94,17 +90,15 @@ func (c *chain) teardown(ipt *iptables.IPTables) error {
|
||||
}
|
||||
chainParts = chainParts[2:] // List results always include an -A CHAINNAME
|
||||
|
||||
if err := ipt.Delete(c.table, entryChain, chainParts...); err != nil {
|
||||
return fmt.Errorf("Failed to delete referring rule %s %s: %v", c.table, entryChainRule, err)
|
||||
if err := utils.DeleteRule(ipt, c.table, entryChain, chainParts...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := ipt.DeleteChain(c.table, c.name); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
return utils.DeleteChain(ipt, c.table, c.name)
|
||||
}
|
||||
|
||||
// insertUnique will add a rule to a chain if it does not already exist.
|
||||
@ -125,24 +119,10 @@ func insertUnique(ipt *iptables.IPTables, table, chain string, prepend bool, rul
|
||||
}
|
||||
}
|
||||
|
||||
func chainExists(ipt *iptables.IPTables, tableName, chainName string) (bool, error) {
|
||||
chains, err := ipt.ListChains(tableName)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, ch := range chains {
|
||||
if ch == chainName {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// check the chain.
|
||||
func (c *chain) check(ipt *iptables.IPTables) error {
|
||||
|
||||
exists, err := chainExists(ipt, c.table, c.name)
|
||||
exists, err := utils.ChainExists(ipt, c.table, c.name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/containernetworking/plugins/pkg/ns"
|
||||
"github.com/containernetworking/plugins/pkg/testutils"
|
||||
@ -32,6 +33,7 @@ const TABLE = "filter" // We'll monkey around here
|
||||
var _ = Describe("chain tests", func() {
|
||||
var testChain chain
|
||||
var ipt *iptables.IPTables
|
||||
var testNs ns.NetNS
|
||||
var cleanup func()
|
||||
|
||||
BeforeEach(func() {
|
||||
@ -41,7 +43,7 @@ var _ = Describe("chain tests", func() {
|
||||
currNs, err := ns.GetCurrentNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
testNs, err := testutils.NewNS()
|
||||
testNs, err = testutils.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
tlChainName := fmt.Sprintf("cni-test-%d", rand.Intn(10000000))
|
||||
@ -195,4 +197,38 @@ var _ = Describe("chain tests", func() {
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
It("deletes chains idempotently in parallel", func() {
|
||||
defer cleanup()
|
||||
// number of parallel executions
|
||||
N := 10
|
||||
var wg sync.WaitGroup
|
||||
err := testChain.setup(ipt)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
errCh := make(chan error, N)
|
||||
for i := 0; i < N; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// teardown chain
|
||||
errCh <- testNs.Do(func(ns.NetNS) error {
|
||||
return testChain.teardown(ipt)
|
||||
})
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
close(errCh)
|
||||
for err := range errCh {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
|
||||
chains, err := ipt.ListChains(TABLE)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
for _, chain := range chains {
|
||||
if chain == testChain.name {
|
||||
Fail("Chain was not deleted")
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
})
|
||||
|
@ -60,9 +60,9 @@ type PortMapConf struct {
|
||||
|
||||
// These are fields parsed out of the config or the environment;
|
||||
// included here for convenience
|
||||
ContainerID string `json:"-"`
|
||||
ContIPv4 net.IP `json:"-"`
|
||||
ContIPv6 net.IP `json:"-"`
|
||||
ContainerID string `json:"-"`
|
||||
ContIPv4 net.IPNet `json:"-"`
|
||||
ContIPv6 net.IPNet `json:"-"`
|
||||
}
|
||||
|
||||
// The default mark bit to signal that masquerading is required
|
||||
@ -85,13 +85,13 @@ func cmdAdd(args *skel.CmdArgs) error {
|
||||
|
||||
netConf.ContainerID = args.ContainerID
|
||||
|
||||
if netConf.ContIPv4 != nil {
|
||||
if netConf.ContIPv4.IP != nil {
|
||||
if err := forwardPorts(netConf, netConf.ContIPv4); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if netConf.ContIPv6 != nil {
|
||||
if netConf.ContIPv6.IP != nil {
|
||||
if err := forwardPorts(netConf, netConf.ContIPv6); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -107,6 +107,10 @@ func cmdDel(args *skel.CmdArgs) error {
|
||||
return fmt.Errorf("failed to parse config: %v", err)
|
||||
}
|
||||
|
||||
if len(netConf.RuntimeConfig.PortMaps) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
netConf.ContainerID = args.ContainerID
|
||||
|
||||
// We don't need to parse out whether or not we're using v6 or snat,
|
||||
@ -138,13 +142,13 @@ func cmdCheck(args *skel.CmdArgs) error {
|
||||
|
||||
conf.ContainerID = args.ContainerID
|
||||
|
||||
if conf.ContIPv4 != nil {
|
||||
if conf.ContIPv4.IP != nil {
|
||||
if err := checkPorts(conf, conf.ContIPv4); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if conf.ContIPv6 != nil {
|
||||
if conf.ContIPv6.IP != nil {
|
||||
if err := checkPorts(conf, conf.ContIPv6); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -205,9 +209,9 @@ func parseConfig(stdin []byte, ifName string) (*PortMapConf, *current.Result, er
|
||||
|
||||
if conf.PrevResult != nil {
|
||||
for _, ip := range result.IPs {
|
||||
if ip.Version == "6" && conf.ContIPv6 != nil {
|
||||
if ip.Version == "6" && conf.ContIPv6.IP != nil {
|
||||
continue
|
||||
} else if ip.Version == "4" && conf.ContIPv4 != nil {
|
||||
} else if ip.Version == "4" && conf.ContIPv4.IP != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -223,9 +227,9 @@ func parseConfig(stdin []byte, ifName string) (*PortMapConf, *current.Result, er
|
||||
}
|
||||
switch ip.Version {
|
||||
case "6":
|
||||
conf.ContIPv6 = ip.Address.IP
|
||||
conf.ContIPv6 = ip.Address
|
||||
case "4":
|
||||
conf.ContIPv4 = ip.Address.IP
|
||||
conf.ContIPv4 = ip.Address
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -48,9 +48,9 @@ const MarkMasqChainName = "CNI-HOSTPORT-MASQ"
|
||||
const OldTopLevelSNATChainName = "CNI-HOSTPORT-SNAT"
|
||||
|
||||
// forwardPorts establishes port forwarding to a given container IP.
|
||||
// containerIP can be either v4 or v6.
|
||||
func forwardPorts(config *PortMapConf, containerIP net.IP) error {
|
||||
isV6 := (containerIP.To4() == nil)
|
||||
// containerNet.IP can be either v4 or v6.
|
||||
func forwardPorts(config *PortMapConf, containerNet net.IPNet) error {
|
||||
isV6 := (containerNet.IP.To4() == nil)
|
||||
|
||||
var ipt *iptables.IPTables
|
||||
var err error
|
||||
@ -86,7 +86,7 @@ func forwardPorts(config *PortMapConf, containerIP net.IP) error {
|
||||
if !isV6 {
|
||||
// Set the route_localnet bit on the host interface, so that
|
||||
// 127/8 can cross a routing boundary.
|
||||
hostIfName := getRoutableHostIF(containerIP)
|
||||
hostIfName := getRoutableHostIF(containerNet.IP)
|
||||
if hostIfName != "" {
|
||||
if err := enableLocalnetRouting(hostIfName); err != nil {
|
||||
return fmt.Errorf("unable to enable route_localnet: %v", err)
|
||||
@ -104,7 +104,7 @@ func forwardPorts(config *PortMapConf, containerIP net.IP) error {
|
||||
dnatChain := genDnatChain(config.Name, config.ContainerID)
|
||||
// First, idempotently tear down this chain in case there was some
|
||||
// sort of collision or bad state.
|
||||
fillDnatRules(&dnatChain, config, containerIP)
|
||||
fillDnatRules(&dnatChain, config, containerNet)
|
||||
if err := dnatChain.setup(ipt); err != nil {
|
||||
return fmt.Errorf("unable to setup DNAT: %v", err)
|
||||
}
|
||||
@ -112,10 +112,10 @@ func forwardPorts(config *PortMapConf, containerIP net.IP) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkPorts(config *PortMapConf, containerIP net.IP) error {
|
||||
func checkPorts(config *PortMapConf, containerNet net.IPNet) error {
|
||||
|
||||
dnatChain := genDnatChain(config.Name, config.ContainerID)
|
||||
fillDnatRules(&dnatChain, config, containerIP)
|
||||
fillDnatRules(&dnatChain, config, containerNet)
|
||||
|
||||
ip4t := maybeGetIptables(false)
|
||||
ip6t := maybeGetIptables(true)
|
||||
@ -124,7 +124,7 @@ func checkPorts(config *PortMapConf, containerIP net.IP) error {
|
||||
}
|
||||
|
||||
if ip4t != nil {
|
||||
exists, err := chainExists(ip4t, dnatChain.table, dnatChain.name)
|
||||
exists, err := utils.ChainExists(ip4t, dnatChain.table, dnatChain.name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -137,7 +137,7 @@ func checkPorts(config *PortMapConf, containerIP net.IP) error {
|
||||
}
|
||||
|
||||
if ip6t != nil {
|
||||
exists, err := chainExists(ip6t, dnatChain.table, dnatChain.name)
|
||||
exists, err := utils.ChainExists(ip6t, dnatChain.table, dnatChain.name)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -180,8 +180,8 @@ func genDnatChain(netName, containerID string) chain {
|
||||
|
||||
// dnatRules generates the destination NAT rules, one per port, to direct
|
||||
// traffic from hostip:hostport to podip:podport
|
||||
func fillDnatRules(c *chain, config *PortMapConf, containerIP net.IP) {
|
||||
isV6 := (containerIP.To4() == nil)
|
||||
func fillDnatRules(c *chain, config *PortMapConf, containerNet net.IPNet) {
|
||||
isV6 := (containerNet.IP.To4() == nil)
|
||||
comment := trimComment(fmt.Sprintf(`dnat name: "%s" id: "%s"`, config.Name, config.ContainerID))
|
||||
entries := config.RuntimeConfig.PortMaps
|
||||
setMarkChainName := SetMarkChainName
|
||||
@ -224,10 +224,27 @@ func fillDnatRules(c *chain, config *PortMapConf, containerIP net.IP) {
|
||||
// the ordering is important here; the mark rules must be first.
|
||||
c.rules = make([][]string, 0, 3*len(entries))
|
||||
for _, entry := range entries {
|
||||
// If a HostIP is given, only process the entry if host and container address families match
|
||||
// and append it to the iptables rules
|
||||
addRuleBaseDst := false
|
||||
if entry.HostIP != "" {
|
||||
hostIP := net.ParseIP(entry.HostIP)
|
||||
isHostV6 := (hostIP.To4() == nil)
|
||||
|
||||
if isV6 != isHostV6 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Unspecified addresses can not be used as destination
|
||||
if !hostIP.IsUnspecified() {
|
||||
addRuleBaseDst = true
|
||||
}
|
||||
}
|
||||
|
||||
ruleBase := []string{
|
||||
"-p", entry.Protocol,
|
||||
"--dport", strconv.Itoa(entry.HostPort)}
|
||||
if entry.HostIP != "" {
|
||||
if addRuleBaseDst {
|
||||
ruleBase = append(ruleBase,
|
||||
"-d", entry.HostIP)
|
||||
}
|
||||
@ -239,7 +256,7 @@ func fillDnatRules(c *chain, config *PortMapConf, containerIP net.IP) {
|
||||
copy(hpRule, ruleBase)
|
||||
|
||||
hpRule = append(hpRule,
|
||||
"-s", containerIP.String(),
|
||||
"-s", containerNet.String(),
|
||||
"-j", setMarkChainName,
|
||||
)
|
||||
c.rules = append(c.rules, hpRule)
|
||||
@ -262,7 +279,7 @@ func fillDnatRules(c *chain, config *PortMapConf, containerIP net.IP) {
|
||||
copy(dnatRule, ruleBase)
|
||||
dnatRule = append(dnatRule,
|
||||
"-j", "DNAT",
|
||||
"--to-destination", fmtIpPort(containerIP, entry.ContainerPort),
|
||||
"--to-destination", fmtIpPort(containerNet.IP, entry.ContainerPort),
|
||||
)
|
||||
c.rules = append(c.rules, dnatRule)
|
||||
}
|
||||
|
@ -96,119 +96,133 @@ var _ = Describe("portmap integration tests", func() {
|
||||
}
|
||||
})
|
||||
|
||||
// This needs to be done using Ginkgo's asynchronous testing mode.
|
||||
It("forwards a TCP port on ipv4", func(done Done) {
|
||||
var err error
|
||||
hostPort := rand.Intn(10000) + 1025
|
||||
runtimeConfig := libcni.RuntimeConf{
|
||||
ContainerID: fmt.Sprintf("unit-test-%d", hostPort),
|
||||
NetNS: targetNS.Path(),
|
||||
IfName: "eth0",
|
||||
CapabilityArgs: map[string]interface{}{
|
||||
"portMappings": []map[string]interface{}{
|
||||
{
|
||||
"hostPort": hostPort,
|
||||
"containerPort": containerPort,
|
||||
"protocol": "tcp",
|
||||
Describe("Creating an interface in a namespace with the ptp plugin", func() {
|
||||
// This needs to be done using Ginkgo's asynchronous testing mode.
|
||||
It("forwards a TCP port on ipv4", func(done Done) {
|
||||
var err error
|
||||
hostPort := rand.Intn(10000) + 1025
|
||||
runtimeConfig := libcni.RuntimeConf{
|
||||
ContainerID: fmt.Sprintf("unit-test-%d", hostPort),
|
||||
NetNS: targetNS.Path(),
|
||||
IfName: "eth0",
|
||||
CapabilityArgs: map[string]interface{}{
|
||||
"portMappings": []map[string]interface{}{
|
||||
{
|
||||
"hostPort": hostPort,
|
||||
"containerPort": containerPort,
|
||||
"protocol": "tcp",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Make delete idempotent, so we can clean up on failure
|
||||
netDeleted := false
|
||||
deleteNetwork := func() error {
|
||||
if netDeleted {
|
||||
return nil
|
||||
}
|
||||
netDeleted = true
|
||||
return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig)
|
||||
}
|
||||
|
||||
// we'll also manually check the iptables chains
|
||||
ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
dnatChainName := genDnatChain("cni-portmap-unit-test", runtimeConfig.ContainerID).name
|
||||
|
||||
// Create the network
|
||||
resI, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer deleteNetwork()
|
||||
|
||||
// Undo Docker's forwarding policy
|
||||
cmd := exec.Command("iptables", "-t", "filter",
|
||||
"-P", "FORWARD", "ACCEPT")
|
||||
cmd.Stderr = GinkgoWriter
|
||||
err = cmd.Run()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Check the chain exists
|
||||
_, err = ipt.List("nat", dnatChainName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
result, err := current.GetResult(resI)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
var contIP net.IP
|
||||
|
||||
for _, ip := range result.IPs {
|
||||
intfIndex := *ip.Interface
|
||||
if result.Interfaces[intfIndex].Sandbox == "" {
|
||||
continue
|
||||
// Make delete idempotent, so we can clean up on failure
|
||||
netDeleted := false
|
||||
deleteNetwork := func() error {
|
||||
if netDeleted {
|
||||
return nil
|
||||
}
|
||||
netDeleted = true
|
||||
return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig)
|
||||
}
|
||||
contIP = ip.Address.IP
|
||||
}
|
||||
if contIP == nil {
|
||||
Fail("could not determine container IP")
|
||||
}
|
||||
|
||||
hostIP := getLocalIP()
|
||||
fmt.Fprintf(GinkgoWriter, "hostIP: %s:%d, contIP: %s:%d\n",
|
||||
hostIP, hostPort, contIP, containerPort)
|
||||
// we'll also manually check the iptables chains
|
||||
ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
dnatChainName := genDnatChain("cni-portmap-unit-test", runtimeConfig.ContainerID).name
|
||||
|
||||
// dump iptables-save output for debugging
|
||||
cmd = exec.Command("iptables-save")
|
||||
cmd.Stderr = GinkgoWriter
|
||||
cmd.Stdout = GinkgoWriter
|
||||
Expect(cmd.Run()).To(Succeed())
|
||||
// Create the network
|
||||
resI, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer deleteNetwork()
|
||||
|
||||
// Sanity check: verify that the container is reachable directly
|
||||
contOK := testEchoServer(contIP.String(), containerPort, "")
|
||||
// Undo Docker's forwarding policy
|
||||
cmd := exec.Command("iptables", "-t", "filter",
|
||||
"-P", "FORWARD", "ACCEPT")
|
||||
cmd.Stderr = GinkgoWriter
|
||||
err = cmd.Run()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Verify that a connection to the forwarded port works
|
||||
dnatOK := testEchoServer(hostIP, hostPort, "")
|
||||
// Check the chain exists
|
||||
_, err = ipt.List("nat", dnatChainName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Verify that a connection to localhost works
|
||||
snatOK := testEchoServer("127.0.0.1", hostPort, "")
|
||||
result, err := current.GetResult(resI)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
var contIP net.IP
|
||||
|
||||
// verify that hairpin works
|
||||
hairpinOK := testEchoServer(hostIP, hostPort, targetNS.Path())
|
||||
for _, ip := range result.IPs {
|
||||
intfIndex := *ip.Interface
|
||||
if result.Interfaces[intfIndex].Sandbox == "" {
|
||||
continue
|
||||
}
|
||||
contIP = ip.Address.IP
|
||||
}
|
||||
if contIP == nil {
|
||||
Fail("could not determine container IP")
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
session.Terminate()
|
||||
err = deleteNetwork()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
hostIP := getLocalIP()
|
||||
fmt.Fprintf(GinkgoWriter, "hostIP: %s:%d, contIP: %s:%d\n",
|
||||
hostIP, hostPort, contIP, containerPort)
|
||||
|
||||
// Verify iptables rules are gone
|
||||
_, err = ipt.List("nat", dnatChainName)
|
||||
Expect(err).To(MatchError(ContainSubstring("iptables: No chain/target/match by that name.")))
|
||||
// dump iptables-save output for debugging
|
||||
cmd = exec.Command("iptables-save")
|
||||
cmd.Stderr = GinkgoWriter
|
||||
cmd.Stdout = GinkgoWriter
|
||||
Expect(cmd.Run()).To(Succeed())
|
||||
|
||||
// Check that everything succeeded *after* we clean up the network
|
||||
if !contOK {
|
||||
Fail("connection direct to " + contIP.String() + " failed")
|
||||
}
|
||||
if !dnatOK {
|
||||
Fail("Connection to " + hostIP + " was not forwarded")
|
||||
}
|
||||
if !snatOK {
|
||||
Fail("connection to 127.0.0.1 was not forwarded")
|
||||
}
|
||||
if !hairpinOK {
|
||||
Fail("Hairpin connection failed")
|
||||
}
|
||||
// dump ip routes output for debugging
|
||||
cmd = exec.Command("ip", "route")
|
||||
cmd.Stderr = GinkgoWriter
|
||||
cmd.Stdout = GinkgoWriter
|
||||
Expect(cmd.Run()).To(Succeed())
|
||||
|
||||
close(done)
|
||||
// dump ip addresses output for debugging
|
||||
cmd = exec.Command("ip", "addr")
|
||||
cmd.Stderr = GinkgoWriter
|
||||
cmd.Stdout = GinkgoWriter
|
||||
Expect(cmd.Run()).To(Succeed())
|
||||
|
||||
}, TIMEOUT*9)
|
||||
// Sanity check: verify that the container is reachable directly
|
||||
contOK := testEchoServer(contIP.String(), containerPort, "")
|
||||
|
||||
// Verify that a connection to the forwarded port works
|
||||
dnatOK := testEchoServer(hostIP, hostPort, "")
|
||||
|
||||
// Verify that a connection to localhost works
|
||||
snatOK := testEchoServer("127.0.0.1", hostPort, "")
|
||||
|
||||
// verify that hairpin works
|
||||
hairpinOK := testEchoServer(hostIP, hostPort, targetNS.Path())
|
||||
|
||||
// Cleanup
|
||||
session.Terminate()
|
||||
err = deleteNetwork()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Verify iptables rules are gone
|
||||
_, err = ipt.List("nat", dnatChainName)
|
||||
Expect(err).To(MatchError(ContainSubstring("iptables: No chain/target/match by that name.")))
|
||||
|
||||
// Check that everything succeeded *after* we clean up the network
|
||||
if !contOK {
|
||||
Fail("connection direct to " + contIP.String() + " failed")
|
||||
}
|
||||
if !dnatOK {
|
||||
Fail("Connection to " + hostIP + " was not forwarded")
|
||||
}
|
||||
if !snatOK {
|
||||
Fail("connection to 127.0.0.1 was not forwarded")
|
||||
}
|
||||
if !hairpinOK {
|
||||
Fail("Hairpin connection failed")
|
||||
}
|
||||
|
||||
close(done)
|
||||
|
||||
}, TIMEOUT*9)
|
||||
})
|
||||
})
|
||||
|
||||
// testEchoServer returns true if we found an echo server on the port
|
||||
|
@ -16,7 +16,8 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
@ -77,8 +78,10 @@ var _ = Describe("portmapping configuration", func() {
|
||||
Expect(c.SNAT).To(Equal(&fvar))
|
||||
Expect(c.Name).To(Equal("test"))
|
||||
|
||||
Expect(c.ContIPv4).To(Equal(net.ParseIP("10.0.0.2")))
|
||||
Expect(c.ContIPv6).To(Equal(net.ParseIP("2001:db8:1::2")))
|
||||
n, err := types.ParseCIDR("10.0.0.2/24")
|
||||
Expect(c.ContIPv4).To(Equal(*n))
|
||||
n, err = types.ParseCIDR("2001:db8:1::2/64")
|
||||
Expect(c.ContIPv6).To(Equal(*n))
|
||||
})
|
||||
|
||||
It("Correctly parses a DEL config", func() {
|
||||
@ -167,7 +170,11 @@ var _ = Describe("portmapping configuration", func() {
|
||||
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"},
|
||||
{ "hostPort": 8081, "containerPort": 80, "protocol": "tcp"},
|
||||
{ "hostPort": 8080, "containerPort": 81, "protocol": "udp"},
|
||||
{ "hostPort": 8082, "containerPort": 82, "protocol": "udp"}
|
||||
{ "hostPort": 8082, "containerPort": 82, "protocol": "udp"},
|
||||
{ "hostPort": 8083, "containerPort": 83, "protocol": "tcp", "hostIP": "192.168.0.2"},
|
||||
{ "hostPort": 8084, "containerPort": 84, "protocol": "tcp", "hostIP": "0.0.0.0"},
|
||||
{ "hostPort": 8085, "containerPort": 85, "protocol": "tcp", "hostIP": "2001:db8:a::1"},
|
||||
{ "hostPort": 8086, "containerPort": 86, "protocol": "tcp", "hostIP": "::"}
|
||||
]
|
||||
},
|
||||
"snat": true,
|
||||
@ -186,14 +193,15 @@ var _ = Describe("portmapping configuration", func() {
|
||||
entryChains: []string{"CNI-HOSTPORT-DNAT"},
|
||||
}))
|
||||
|
||||
fillDnatRules(&ch, conf, net.ParseIP("10.0.0.2"))
|
||||
n, err := types.ParseCIDR("10.0.0.2/24")
|
||||
fillDnatRules(&ch, conf, *n)
|
||||
|
||||
Expect(ch.entryRules).To(Equal([][]string{
|
||||
{"-m", "comment", "--comment",
|
||||
fmt.Sprintf("dnat name: \"test\" id: \"%s\"", containerID),
|
||||
"-m", "multiport",
|
||||
"-p", "tcp",
|
||||
"--destination-ports", "8080,8081",
|
||||
"--destination-ports", "8080,8081,8083,8084,8085,8086",
|
||||
"a", "b"},
|
||||
{"-m", "comment", "--comment",
|
||||
fmt.Sprintf("dnat name: \"test\" id: \"%s\"", containerID),
|
||||
@ -204,34 +212,53 @@ var _ = Describe("portmapping configuration", func() {
|
||||
}))
|
||||
|
||||
Expect(ch.rules).To(Equal([][]string{
|
||||
{"-p", "tcp", "--dport", "8080", "-s", "10.0.0.2", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
// tcp rules and not hostIP
|
||||
{"-p", "tcp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "tcp", "--dport", "8080", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
|
||||
{"-p", "tcp", "--dport", "8081", "-s", "10.0.0.2", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "tcp", "--dport", "8081", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "tcp", "--dport", "8081", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
|
||||
{"-p", "udp", "--dport", "8080", "-s", "10.0.0.2", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
// udp rules and not hostIP
|
||||
{"-p", "udp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "udp", "--dport", "8080", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:81"},
|
||||
{"-p", "udp", "--dport", "8082", "-s", "10.0.0.2", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "udp", "--dport", "8082", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "udp", "--dport", "8082", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "10.0.0.2:82"},
|
||||
// tcp rules and hostIP
|
||||
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-j", "DNAT", "--to-destination", "10.0.0.2:83"},
|
||||
// tcp rules and hostIP = "0.0.0.0"
|
||||
{"-p", "tcp", "--dport", "8084", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "tcp", "--dport", "8084", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "tcp", "--dport", "8084", "-j", "DNAT", "--to-destination", "10.0.0.2:84"},
|
||||
}))
|
||||
|
||||
ch.rules = nil
|
||||
ch.entryRules = nil
|
||||
|
||||
fillDnatRules(&ch, conf, net.ParseIP("2001:db8::2"))
|
||||
n, err = types.ParseCIDR("2001:db8::2/64")
|
||||
fillDnatRules(&ch, conf, *n)
|
||||
|
||||
Expect(ch.rules).To(Equal([][]string{
|
||||
{"-p", "tcp", "--dport", "8080", "-s", "2001:db8::2", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
// tcp rules and not hostIP
|
||||
{"-p", "tcp", "--dport", "8080", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "[2001:db8::2]:80"},
|
||||
{"-p", "tcp", "--dport", "8081", "-s", "2001:db8::2", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "tcp", "--dport", "8081", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "[2001:db8::2]:80"},
|
||||
{"-p", "udp", "--dport", "8080", "-s", "2001:db8::2", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
// udp rules and not hostIP
|
||||
{"-p", "udp", "--dport", "8080", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "[2001:db8::2]:81"},
|
||||
{"-p", "udp", "--dport", "8082", "-s", "2001:db8::2", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "udp", "--dport", "8082", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "[2001:db8::2]:82"},
|
||||
// tcp rules and hostIP
|
||||
{"-p", "tcp", "--dport", "8085", "-d", "2001:db8:a::1", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "tcp", "--dport", "8085", "-d", "2001:db8:a::1", "-j", "DNAT", "--to-destination", "[2001:db8::2]:85"},
|
||||
// tcp rules and hostIP = "::"
|
||||
{"-p", "tcp", "--dport", "8086", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||
{"-p", "tcp", "--dport", "8086", "-j", "DNAT", "--to-destination", "[2001:db8::2]:86"},
|
||||
}))
|
||||
|
||||
// Disable snat, generate rules
|
||||
@ -240,12 +267,15 @@ var _ = Describe("portmapping configuration", func() {
|
||||
fvar := false
|
||||
conf.SNAT = &fvar
|
||||
|
||||
fillDnatRules(&ch, conf, net.ParseIP("10.0.0.2"))
|
||||
n, err = types.ParseCIDR("10.0.0.2/24")
|
||||
fillDnatRules(&ch, conf, *n)
|
||||
Expect(ch.rules).To(Equal([][]string{
|
||||
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
|
||||
{"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
|
||||
{"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:81"},
|
||||
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "10.0.0.2:82"},
|
||||
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-j", "DNAT", "--to-destination", "10.0.0.2:83"},
|
||||
{"-p", "tcp", "--dport", "8084", "-j", "DNAT", "--to-destination", "10.0.0.2:84"},
|
||||
}))
|
||||
})
|
||||
|
||||
@ -276,9 +306,10 @@ var _ = Describe("portmapping configuration", func() {
|
||||
conf.ContainerID = containerID
|
||||
|
||||
ch = genDnatChain(conf.Name, containerID)
|
||||
fillDnatRules(&ch, conf, net.ParseIP("10.0.0.2"))
|
||||
n, err := types.ParseCIDR("10.0.0.2/24")
|
||||
fillDnatRules(&ch, conf, *n)
|
||||
Expect(ch.rules).To(Equal([][]string{
|
||||
{"-p", "tcp", "--dport", "8080", "-s", "10.0.0.2", "-j", "PLZ-SET-MARK"},
|
||||
{"-p", "tcp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "PLZ-SET-MARK"},
|
||||
{"-p", "tcp", "--dport", "8080", "-s", "127.0.0.1", "-j", "PLZ-SET-MARK"},
|
||||
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
|
||||
}))
|
||||
|
@ -237,7 +237,7 @@ func doRoutes(ipCfgs []*current.IPConfig, origRoutes []*types.Route, iface strin
|
||||
if ipCfg.Version == "4" {
|
||||
src.Mask = net.CIDRMask(32, 32)
|
||||
} else {
|
||||
src.Mask = net.CIDRMask(64, 64)
|
||||
src.Mask = net.CIDRMask(128, 128)
|
||||
}
|
||||
|
||||
log.Printf("Source to use %s", src.String())
|
||||
@ -258,7 +258,7 @@ func doRoutes(ipCfgs []*current.IPConfig, origRoutes []*types.Route, iface strin
|
||||
dest.Mask = net.CIDRMask(0, 32)
|
||||
} else {
|
||||
dest.IP = net.IPv6zero
|
||||
dest.Mask = net.CIDRMask(0, 64)
|
||||
dest.Mask = net.CIDRMask(0, 128)
|
||||
}
|
||||
|
||||
route := netlink.Route{
|
||||
@ -296,6 +296,10 @@ func doRoutes(ipCfgs []*current.IPConfig, origRoutes []*types.Route, iface strin
|
||||
|
||||
route.Table = table
|
||||
|
||||
// Reset the route flags since if it is dynamically created,
|
||||
// adding it to the new table will fail with "invalid argument"
|
||||
route.Flags = 0
|
||||
|
||||
// We use route replace in case the route already exists, which
|
||||
// is possible for the default gateway we added above.
|
||||
err = netlink.RouteReplace(&route)
|
||||
|
@ -25,6 +25,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/j-keck/arping"
|
||||
"github.com/vishvananda/netlink"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
@ -179,7 +180,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = current.NewResultFromResult(tuningConf.PrevResult)
|
||||
result, err := current.NewResultFromResult(tuningConf.PrevResult)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -208,6 +209,13 @@ func cmdAdd(args *skel.CmdArgs) error {
|
||||
if err = changeMacAddr(args.IfName, tuningConf.Mac); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, ipc := range result.IPs {
|
||||
if ipc.Version == "4" {
|
||||
_ = arping.GratuitousArpOverIfaceByName(ipc.Address.IP, args.IfName)
|
||||
}
|
||||
}
|
||||
|
||||
updateResultsMacAddr(*tuningConf, args.IfName, tuningConf.Mac)
|
||||
}
|
||||
|
||||
|
@ -16,12 +16,12 @@ rm -Rf ${SRC_DIR}/${RELEASE_DIR}
|
||||
mkdir -p ${SRC_DIR}/${RELEASE_DIR}
|
||||
mkdir -p ${OUTPUT_DIR}
|
||||
|
||||
$DOCKER run -ti -v ${SRC_DIR}:/go/src/github.com/containernetworking/plugins --rm golang:1.12-alpine \
|
||||
$DOCKER run -ti -v ${SRC_DIR}:/go/src/github.com/containernetworking/plugins --rm golang:1.14-alpine \
|
||||
/bin/sh -xe -c "\
|
||||
apk --no-cache add bash tar;
|
||||
cd /go/src/github.com/containernetworking/plugins; umask 0022;
|
||||
|
||||
for arch in amd64 arm arm64 ppc64le s390x; do \
|
||||
for arch in amd64 arm arm64 ppc64le s390x mips64le; do \
|
||||
rm -f ${OUTPUT_DIR}/*; \
|
||||
CGO_ENABLED=0 GOARCH=\$arch ./build_linux.sh ${BUILDFLAGS}; \
|
||||
for format in tgz; do \
|
||||
|
@ -57,3 +57,7 @@ if [ -n "${vetRes}" ]; then
|
||||
echo -e "govet checking failed:\n${vetRes}"
|
||||
exit 255
|
||||
fi
|
||||
|
||||
# Run the pkg/ns tests as non root user
|
||||
mkdir /tmp/cni-rootless
|
||||
(export XDG_RUNTIME_DIR=/tmp/cni-rootless; cd pkg/ns/; unshare -rmn go test)
|
||||
|
943
vendor/github.com/Microsoft/hcsshim/mksyscall_windows.go
generated
vendored
943
vendor/github.com/Microsoft/hcsshim/mksyscall_windows.go
generated
vendored
@ -1,943 +0,0 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
/*
|
||||
mksyscall_windows generates windows system call bodies
|
||||
|
||||
It parses all files specified on command line containing function
|
||||
prototypes (like syscall_windows.go) and prints system call bodies
|
||||
to standard output.
|
||||
|
||||
The prototypes are marked by lines beginning with "//sys" and read
|
||||
like func declarations if //sys is replaced by func, but:
|
||||
|
||||
* The parameter lists must give a name for each argument. This
|
||||
includes return parameters.
|
||||
|
||||
* The parameter lists must give a type for each argument:
|
||||
the (x, y, z int) shorthand is not allowed.
|
||||
|
||||
* If the return parameter is an error number, it must be named err.
|
||||
|
||||
* If go func name needs to be different from it's winapi dll name,
|
||||
the winapi name could be specified at the end, after "=" sign, like
|
||||
//sys LoadLibrary(libname string) (handle uint32, err error) = LoadLibraryA
|
||||
|
||||
* Each function that returns err needs to supply a condition, that
|
||||
return value of winapi will be tested against to detect failure.
|
||||
This would set err to windows "last-error", otherwise it will be nil.
|
||||
The value can be provided at end of //sys declaration, like
|
||||
//sys LoadLibrary(libname string) (handle uint32, err error) [failretval==-1] = LoadLibraryA
|
||||
and is [failretval==0] by default.
|
||||
|
||||
Usage:
|
||||
mksyscall_windows [flags] [path ...]
|
||||
|
||||
The flags are:
|
||||
-output
|
||||
Specify output file name (outputs to console if blank).
|
||||
-trace
|
||||
Generate print statement after every syscall.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var (
|
||||
filename = flag.String("output", "", "output file name (standard output if omitted)")
|
||||
printTraceFlag = flag.Bool("trace", false, "generate print statement after every syscall")
|
||||
systemDLL = flag.Bool("systemdll", true, "whether all DLLs should be loaded from the Windows system directory")
|
||||
winio = flag.Bool("winio", false, "import go-winio")
|
||||
)
|
||||
|
||||
func trim(s string) string {
|
||||
return strings.Trim(s, " \t")
|
||||
}
|
||||
|
||||
var packageName string
|
||||
|
||||
func packagename() string {
|
||||
return packageName
|
||||
}
|
||||
|
||||
func syscalldot() string {
|
||||
if packageName == "syscall" {
|
||||
return ""
|
||||
}
|
||||
return "syscall."
|
||||
}
|
||||
|
||||
// Param is function parameter
|
||||
type Param struct {
|
||||
Name string
|
||||
Type string
|
||||
fn *Fn
|
||||
tmpVarIdx int
|
||||
}
|
||||
|
||||
// tmpVar returns temp variable name that will be used to represent p during syscall.
|
||||
func (p *Param) tmpVar() string {
|
||||
if p.tmpVarIdx < 0 {
|
||||
p.tmpVarIdx = p.fn.curTmpVarIdx
|
||||
p.fn.curTmpVarIdx++
|
||||
}
|
||||
return fmt.Sprintf("_p%d", p.tmpVarIdx)
|
||||
}
|
||||
|
||||
// BoolTmpVarCode returns source code for bool temp variable.
|
||||
func (p *Param) BoolTmpVarCode() string {
|
||||
const code = `var %s uint32
|
||||
if %s {
|
||||
%s = 1
|
||||
} else {
|
||||
%s = 0
|
||||
}`
|
||||
tmp := p.tmpVar()
|
||||
return fmt.Sprintf(code, tmp, p.Name, tmp, tmp)
|
||||
}
|
||||
|
||||
// SliceTmpVarCode returns source code for slice temp variable.
|
||||
func (p *Param) SliceTmpVarCode() string {
|
||||
const code = `var %s *%s
|
||||
if len(%s) > 0 {
|
||||
%s = &%s[0]
|
||||
}`
|
||||
tmp := p.tmpVar()
|
||||
return fmt.Sprintf(code, tmp, p.Type[2:], p.Name, tmp, p.Name)
|
||||
}
|
||||
|
||||
// StringTmpVarCode returns source code for string temp variable.
|
||||
func (p *Param) StringTmpVarCode() string {
|
||||
errvar := p.fn.Rets.ErrorVarName()
|
||||
if errvar == "" {
|
||||
errvar = "_"
|
||||
}
|
||||
tmp := p.tmpVar()
|
||||
const code = `var %s %s
|
||||
%s, %s = %s(%s)`
|
||||
s := fmt.Sprintf(code, tmp, p.fn.StrconvType(), tmp, errvar, p.fn.StrconvFunc(), p.Name)
|
||||
if errvar == "-" {
|
||||
return s
|
||||
}
|
||||
const morecode = `
|
||||
if %s != nil {
|
||||
return
|
||||
}`
|
||||
return s + fmt.Sprintf(morecode, errvar)
|
||||
}
|
||||
|
||||
// TmpVarCode returns source code for temp variable.
|
||||
func (p *Param) TmpVarCode() string {
|
||||
switch {
|
||||
case p.Type == "bool":
|
||||
return p.BoolTmpVarCode()
|
||||
case strings.HasPrefix(p.Type, "[]"):
|
||||
return p.SliceTmpVarCode()
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// TmpVarHelperCode returns source code for helper's temp variable.
|
||||
func (p *Param) TmpVarHelperCode() string {
|
||||
if p.Type != "string" {
|
||||
return ""
|
||||
}
|
||||
return p.StringTmpVarCode()
|
||||
}
|
||||
|
||||
// SyscallArgList returns source code fragments representing p parameter
|
||||
// in syscall. Slices are translated into 2 syscall parameters: pointer to
|
||||
// the first element and length.
|
||||
func (p *Param) SyscallArgList() []string {
|
||||
t := p.HelperType()
|
||||
var s string
|
||||
switch {
|
||||
case t[0] == '*':
|
||||
s = fmt.Sprintf("unsafe.Pointer(%s)", p.Name)
|
||||
case t == "bool":
|
||||
s = p.tmpVar()
|
||||
case strings.HasPrefix(t, "[]"):
|
||||
return []string{
|
||||
fmt.Sprintf("uintptr(unsafe.Pointer(%s))", p.tmpVar()),
|
||||
fmt.Sprintf("uintptr(len(%s))", p.Name),
|
||||
}
|
||||
default:
|
||||
s = p.Name
|
||||
}
|
||||
return []string{fmt.Sprintf("uintptr(%s)", s)}
|
||||
}
|
||||
|
||||
// IsError determines if p parameter is used to return error.
|
||||
func (p *Param) IsError() bool {
|
||||
return p.Name == "err" && p.Type == "error"
|
||||
}
|
||||
|
||||
// HelperType returns type of parameter p used in helper function.
|
||||
func (p *Param) HelperType() string {
|
||||
if p.Type == "string" {
|
||||
return p.fn.StrconvType()
|
||||
}
|
||||
return p.Type
|
||||
}
|
||||
|
||||
// join concatenates parameters ps into a string with sep separator.
|
||||
// Each parameter is converted into string by applying fn to it
|
||||
// before conversion.
|
||||
func join(ps []*Param, fn func(*Param) string, sep string) string {
|
||||
if len(ps) == 0 {
|
||||
return ""
|
||||
}
|
||||
a := make([]string, 0)
|
||||
for _, p := range ps {
|
||||
a = append(a, fn(p))
|
||||
}
|
||||
return strings.Join(a, sep)
|
||||
}
|
||||
|
||||
// Rets describes function return parameters.
|
||||
type Rets struct {
|
||||
Name string
|
||||
Type string
|
||||
ReturnsError bool
|
||||
FailCond string
|
||||
}
|
||||
|
||||
// ErrorVarName returns error variable name for r.
|
||||
func (r *Rets) ErrorVarName() string {
|
||||
if r.ReturnsError {
|
||||
return "err"
|
||||
}
|
||||
if r.Type == "error" {
|
||||
return r.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// ToParams converts r into slice of *Param.
|
||||
func (r *Rets) ToParams() []*Param {
|
||||
ps := make([]*Param, 0)
|
||||
if len(r.Name) > 0 {
|
||||
ps = append(ps, &Param{Name: r.Name, Type: r.Type})
|
||||
}
|
||||
if r.ReturnsError {
|
||||
ps = append(ps, &Param{Name: "err", Type: "error"})
|
||||
}
|
||||
return ps
|
||||
}
|
||||
|
||||
// List returns source code of syscall return parameters.
|
||||
func (r *Rets) List() string {
|
||||
s := join(r.ToParams(), func(p *Param) string { return p.Name + " " + p.Type }, ", ")
|
||||
if len(s) > 0 {
|
||||
s = "(" + s + ")"
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// PrintList returns source code of trace printing part correspondent
|
||||
// to syscall return values.
|
||||
func (r *Rets) PrintList() string {
|
||||
return join(r.ToParams(), func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `)
|
||||
}
|
||||
|
||||
// SetReturnValuesCode returns source code that accepts syscall return values.
|
||||
func (r *Rets) SetReturnValuesCode() string {
|
||||
if r.Name == "" && !r.ReturnsError {
|
||||
return ""
|
||||
}
|
||||
retvar := "r0"
|
||||
if r.Name == "" {
|
||||
retvar = "r1"
|
||||
}
|
||||
errvar := "_"
|
||||
if r.ReturnsError {
|
||||
errvar = "e1"
|
||||
}
|
||||
return fmt.Sprintf("%s, _, %s := ", retvar, errvar)
|
||||
}
|
||||
|
||||
func (r *Rets) useLongHandleErrorCode(retvar string) string {
|
||||
const code = `if %s {
|
||||
if e1 != 0 {
|
||||
err = errnoErr(e1)
|
||||
} else {
|
||||
err = %sEINVAL
|
||||
}
|
||||
}`
|
||||
cond := retvar + " == 0"
|
||||
if r.FailCond != "" {
|
||||
cond = strings.Replace(r.FailCond, "failretval", retvar, 1)
|
||||
}
|
||||
return fmt.Sprintf(code, cond, syscalldot())
|
||||
}
|
||||
|
||||
// SetErrorCode returns source code that sets return parameters.
|
||||
func (r *Rets) SetErrorCode() string {
|
||||
const code = `if r0 != 0 {
|
||||
%s = %sErrno(r0)
|
||||
}`
|
||||
const hrCode = `if int32(r0) < 0 {
|
||||
if r0&0x1fff0000 == 0x00070000 {
|
||||
r0 &= 0xffff
|
||||
}
|
||||
%s = %sErrno(r0)
|
||||
}`
|
||||
if r.Name == "" && !r.ReturnsError {
|
||||
return ""
|
||||
}
|
||||
if r.Name == "" {
|
||||
return r.useLongHandleErrorCode("r1")
|
||||
}
|
||||
if r.Type == "error" {
|
||||
if r.Name == "hr" {
|
||||
return fmt.Sprintf(hrCode, r.Name, syscalldot())
|
||||
} else {
|
||||
return fmt.Sprintf(code, r.Name, syscalldot())
|
||||
}
|
||||
}
|
||||
s := ""
|
||||
switch {
|
||||
case r.Type[0] == '*':
|
||||
s = fmt.Sprintf("%s = (%s)(unsafe.Pointer(r0))", r.Name, r.Type)
|
||||
case r.Type == "bool":
|
||||
s = fmt.Sprintf("%s = r0 != 0", r.Name)
|
||||
default:
|
||||
s = fmt.Sprintf("%s = %s(r0)", r.Name, r.Type)
|
||||
}
|
||||
if !r.ReturnsError {
|
||||
return s
|
||||
}
|
||||
return s + "\n\t" + r.useLongHandleErrorCode(r.Name)
|
||||
}
|
||||
|
||||
// Fn describes syscall function.
|
||||
type Fn struct {
|
||||
Name string
|
||||
Params []*Param
|
||||
Rets *Rets
|
||||
PrintTrace bool
|
||||
confirmproc bool
|
||||
dllname string
|
||||
dllfuncname string
|
||||
src string
|
||||
// TODO: get rid of this field and just use parameter index instead
|
||||
curTmpVarIdx int // insure tmp variables have uniq names
|
||||
}
|
||||
|
||||
// extractParams parses s to extract function parameters.
|
||||
func extractParams(s string, f *Fn) ([]*Param, error) {
|
||||
s = trim(s)
|
||||
if s == "" {
|
||||
return nil, nil
|
||||
}
|
||||
a := strings.Split(s, ",")
|
||||
ps := make([]*Param, len(a))
|
||||
for i := range ps {
|
||||
s2 := trim(a[i])
|
||||
b := strings.Split(s2, " ")
|
||||
if len(b) != 2 {
|
||||
b = strings.Split(s2, "\t")
|
||||
if len(b) != 2 {
|
||||
return nil, errors.New("Could not extract function parameter from \"" + s2 + "\"")
|
||||
}
|
||||
}
|
||||
ps[i] = &Param{
|
||||
Name: trim(b[0]),
|
||||
Type: trim(b[1]),
|
||||
fn: f,
|
||||
tmpVarIdx: -1,
|
||||
}
|
||||
}
|
||||
return ps, nil
|
||||
}
|
||||
|
||||
// extractSection extracts text out of string s starting after start
|
||||
// and ending just before end. found return value will indicate success,
|
||||
// and prefix, body and suffix will contain correspondent parts of string s.
|
||||
func extractSection(s string, start, end rune) (prefix, body, suffix string, found bool) {
|
||||
s = trim(s)
|
||||
if strings.HasPrefix(s, string(start)) {
|
||||
// no prefix
|
||||
body = s[1:]
|
||||
} else {
|
||||
a := strings.SplitN(s, string(start), 2)
|
||||
if len(a) != 2 {
|
||||
return "", "", s, false
|
||||
}
|
||||
prefix = a[0]
|
||||
body = a[1]
|
||||
}
|
||||
a := strings.SplitN(body, string(end), 2)
|
||||
if len(a) != 2 {
|
||||
return "", "", "", false
|
||||
}
|
||||
return prefix, a[0], a[1], true
|
||||
}
|
||||
|
||||
// newFn parses string s and return created function Fn.
|
||||
func newFn(s string) (*Fn, error) {
|
||||
s = trim(s)
|
||||
f := &Fn{
|
||||
Rets: &Rets{},
|
||||
src: s,
|
||||
PrintTrace: *printTraceFlag,
|
||||
}
|
||||
// function name and args
|
||||
prefix, body, s, found := extractSection(s, '(', ')')
|
||||
if !found || prefix == "" {
|
||||
return nil, errors.New("Could not extract function name and parameters from \"" + f.src + "\"")
|
||||
}
|
||||
f.Name = prefix
|
||||
var err error
|
||||
f.Params, err = extractParams(body, f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// return values
|
||||
_, body, s, found = extractSection(s, '(', ')')
|
||||
if found {
|
||||
r, err := extractParams(body, f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch len(r) {
|
||||
case 0:
|
||||
case 1:
|
||||
if r[0].IsError() {
|
||||
f.Rets.ReturnsError = true
|
||||
} else {
|
||||
f.Rets.Name = r[0].Name
|
||||
f.Rets.Type = r[0].Type
|
||||
}
|
||||
case 2:
|
||||
if !r[1].IsError() {
|
||||
return nil, errors.New("Only last windows error is allowed as second return value in \"" + f.src + "\"")
|
||||
}
|
||||
f.Rets.ReturnsError = true
|
||||
f.Rets.Name = r[0].Name
|
||||
f.Rets.Type = r[0].Type
|
||||
default:
|
||||
return nil, errors.New("Too many return values in \"" + f.src + "\"")
|
||||
}
|
||||
}
|
||||
// fail condition
|
||||
_, body, s, found = extractSection(s, '[', ']')
|
||||
if found {
|
||||
f.Rets.FailCond = body
|
||||
}
|
||||
// dll and dll function names
|
||||
s = trim(s)
|
||||
if s == "" {
|
||||
return f, nil
|
||||
}
|
||||
if !strings.HasPrefix(s, "=") {
|
||||
return nil, errors.New("Could not extract dll name from \"" + f.src + "\"")
|
||||
}
|
||||
s = trim(s[1:])
|
||||
a := strings.Split(s, ".")
|
||||
switch len(a) {
|
||||
case 1:
|
||||
f.dllfuncname = a[0]
|
||||
case 2:
|
||||
f.dllname = a[0]
|
||||
f.dllfuncname = a[1]
|
||||
default:
|
||||
return nil, errors.New("Could not extract dll name from \"" + f.src + "\"")
|
||||
}
|
||||
if f.dllfuncname[len(f.dllfuncname)-1] == '?' {
|
||||
f.confirmproc = true
|
||||
f.dllfuncname = f.dllfuncname[0 : len(f.dllfuncname)-1]
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// DLLName returns DLL name for function f.
|
||||
func (f *Fn) DLLName() string {
|
||||
if f.dllname == "" {
|
||||
return "kernel32"
|
||||
}
|
||||
return f.dllname
|
||||
}
|
||||
|
||||
// DLLName returns DLL function name for function f.
|
||||
func (f *Fn) DLLFuncName() string {
|
||||
if f.dllfuncname == "" {
|
||||
return f.Name
|
||||
}
|
||||
return f.dllfuncname
|
||||
}
|
||||
|
||||
func (f *Fn) ConfirmProc() bool {
|
||||
return f.confirmproc
|
||||
}
|
||||
|
||||
// ParamList returns source code for function f parameters.
|
||||
func (f *Fn) ParamList() string {
|
||||
return join(f.Params, func(p *Param) string { return p.Name + " " + p.Type }, ", ")
|
||||
}
|
||||
|
||||
// HelperParamList returns source code for helper function f parameters.
|
||||
func (f *Fn) HelperParamList() string {
|
||||
return join(f.Params, func(p *Param) string { return p.Name + " " + p.HelperType() }, ", ")
|
||||
}
|
||||
|
||||
// ParamPrintList returns source code of trace printing part correspondent
|
||||
// to syscall input parameters.
|
||||
func (f *Fn) ParamPrintList() string {
|
||||
return join(f.Params, func(p *Param) string { return fmt.Sprintf(`"%s=", %s, `, p.Name, p.Name) }, `", ", `)
|
||||
}
|
||||
|
||||
// ParamCount return number of syscall parameters for function f.
|
||||
func (f *Fn) ParamCount() int {
|
||||
n := 0
|
||||
for _, p := range f.Params {
|
||||
n += len(p.SyscallArgList())
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// SyscallParamCount determines which version of Syscall/Syscall6/Syscall9/...
|
||||
// to use. It returns parameter count for correspondent SyscallX function.
|
||||
func (f *Fn) SyscallParamCount() int {
|
||||
n := f.ParamCount()
|
||||
switch {
|
||||
case n <= 3:
|
||||
return 3
|
||||
case n <= 6:
|
||||
return 6
|
||||
case n <= 9:
|
||||
return 9
|
||||
case n <= 12:
|
||||
return 12
|
||||
case n <= 15:
|
||||
return 15
|
||||
default:
|
||||
panic("too many arguments to system call")
|
||||
}
|
||||
}
|
||||
|
||||
// Syscall determines which SyscallX function to use for function f.
|
||||
func (f *Fn) Syscall() string {
|
||||
c := f.SyscallParamCount()
|
||||
if c == 3 {
|
||||
return syscalldot() + "Syscall"
|
||||
}
|
||||
return syscalldot() + "Syscall" + strconv.Itoa(c)
|
||||
}
|
||||
|
||||
// SyscallParamList returns source code for SyscallX parameters for function f.
|
||||
func (f *Fn) SyscallParamList() string {
|
||||
a := make([]string, 0)
|
||||
for _, p := range f.Params {
|
||||
a = append(a, p.SyscallArgList()...)
|
||||
}
|
||||
for len(a) < f.SyscallParamCount() {
|
||||
a = append(a, "0")
|
||||
}
|
||||
return strings.Join(a, ", ")
|
||||
}
|
||||
|
||||
// HelperCallParamList returns source code of call into function f helper.
|
||||
func (f *Fn) HelperCallParamList() string {
|
||||
a := make([]string, 0, len(f.Params))
|
||||
for _, p := range f.Params {
|
||||
s := p.Name
|
||||
if p.Type == "string" {
|
||||
s = p.tmpVar()
|
||||
}
|
||||
a = append(a, s)
|
||||
}
|
||||
return strings.Join(a, ", ")
|
||||
}
|
||||
|
||||
// IsUTF16 is true, if f is W (utf16) function. It is false
|
||||
// for all A (ascii) functions.
|
||||
func (_ *Fn) IsUTF16() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// StrconvFunc returns name of Go string to OS string function for f.
|
||||
func (f *Fn) StrconvFunc() string {
|
||||
if f.IsUTF16() {
|
||||
return syscalldot() + "UTF16PtrFromString"
|
||||
}
|
||||
return syscalldot() + "BytePtrFromString"
|
||||
}
|
||||
|
||||
// StrconvType returns Go type name used for OS string for f.
|
||||
func (f *Fn) StrconvType() string {
|
||||
if f.IsUTF16() {
|
||||
return "*uint16"
|
||||
}
|
||||
return "*byte"
|
||||
}
|
||||
|
||||
// HasStringParam is true, if f has at least one string parameter.
|
||||
// Otherwise it is false.
|
||||
func (f *Fn) HasStringParam() bool {
|
||||
for _, p := range f.Params {
|
||||
if p.Type == "string" {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var uniqDllFuncName = make(map[string]bool)
|
||||
|
||||
// IsNotDuplicate is true if f is not a duplicated function
|
||||
func (f *Fn) IsNotDuplicate() bool {
|
||||
funcName := f.DLLFuncName()
|
||||
if uniqDllFuncName[funcName] == false {
|
||||
uniqDllFuncName[funcName] = true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// HelperName returns name of function f helper.
|
||||
func (f *Fn) HelperName() string {
|
||||
if !f.HasStringParam() {
|
||||
return f.Name
|
||||
}
|
||||
return "_" + f.Name
|
||||
}
|
||||
|
||||
// Source files and functions.
|
||||
type Source struct {
|
||||
Funcs []*Fn
|
||||
Files []string
|
||||
StdLibImports []string
|
||||
ExternalImports []string
|
||||
}
|
||||
|
||||
func (src *Source) Import(pkg string) {
|
||||
src.StdLibImports = append(src.StdLibImports, pkg)
|
||||
sort.Strings(src.StdLibImports)
|
||||
}
|
||||
|
||||
func (src *Source) ExternalImport(pkg string) {
|
||||
src.ExternalImports = append(src.ExternalImports, pkg)
|
||||
sort.Strings(src.ExternalImports)
|
||||
}
|
||||
|
||||
// ParseFiles parses files listed in fs and extracts all syscall
|
||||
// functions listed in sys comments. It returns source files
|
||||
// and functions collection *Source if successful.
|
||||
func ParseFiles(fs []string) (*Source, error) {
|
||||
src := &Source{
|
||||
Funcs: make([]*Fn, 0),
|
||||
Files: make([]string, 0),
|
||||
StdLibImports: []string{
|
||||
"unsafe",
|
||||
},
|
||||
ExternalImports: make([]string, 0),
|
||||
}
|
||||
for _, file := range fs {
|
||||
if err := src.ParseFile(file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return src, nil
|
||||
}
|
||||
|
||||
// DLLs return dll names for a source set src.
|
||||
func (src *Source) DLLs() []string {
|
||||
uniq := make(map[string]bool)
|
||||
r := make([]string, 0)
|
||||
for _, f := range src.Funcs {
|
||||
name := f.DLLName()
|
||||
if _, found := uniq[name]; !found {
|
||||
uniq[name] = true
|
||||
r = append(r, name)
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// ParseFile adds additional file path to a source set src.
|
||||
func (src *Source) ParseFile(path string) error {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
s := bufio.NewScanner(file)
|
||||
for s.Scan() {
|
||||
t := trim(s.Text())
|
||||
if len(t) < 7 {
|
||||
continue
|
||||
}
|
||||
if !strings.HasPrefix(t, "//sys") {
|
||||
continue
|
||||
}
|
||||
t = t[5:]
|
||||
if !(t[0] == ' ' || t[0] == '\t') {
|
||||
continue
|
||||
}
|
||||
f, err := newFn(t[1:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
src.Funcs = append(src.Funcs, f)
|
||||
}
|
||||
if err := s.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
src.Files = append(src.Files, path)
|
||||
|
||||
// get package name
|
||||
fset := token.NewFileSet()
|
||||
_, err = file.Seek(0, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pkg, err := parser.ParseFile(fset, "", file, parser.PackageClauseOnly)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
packageName = pkg.Name.Name
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsStdRepo returns true if src is part of standard library.
|
||||
func (src *Source) IsStdRepo() (bool, error) {
|
||||
if len(src.Files) == 0 {
|
||||
return false, errors.New("no input files provided")
|
||||
}
|
||||
abspath, err := filepath.Abs(src.Files[0])
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
goroot := runtime.GOROOT()
|
||||
if runtime.GOOS == "windows" {
|
||||
abspath = strings.ToLower(abspath)
|
||||
goroot = strings.ToLower(goroot)
|
||||
}
|
||||
sep := string(os.PathSeparator)
|
||||
if !strings.HasSuffix(goroot, sep) {
|
||||
goroot += sep
|
||||
}
|
||||
return strings.HasPrefix(abspath, goroot), nil
|
||||
}
|
||||
|
||||
// Generate output source file from a source set src.
|
||||
func (src *Source) Generate(w io.Writer) error {
|
||||
const (
|
||||
pkgStd = iota // any package in std library
|
||||
pkgXSysWindows // x/sys/windows package
|
||||
pkgOther
|
||||
)
|
||||
isStdRepo, err := src.IsStdRepo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var pkgtype int
|
||||
switch {
|
||||
case isStdRepo:
|
||||
pkgtype = pkgStd
|
||||
case packageName == "windows":
|
||||
// TODO: this needs better logic than just using package name
|
||||
pkgtype = pkgXSysWindows
|
||||
default:
|
||||
pkgtype = pkgOther
|
||||
}
|
||||
if *systemDLL {
|
||||
switch pkgtype {
|
||||
case pkgStd:
|
||||
src.Import("internal/syscall/windows/sysdll")
|
||||
case pkgXSysWindows:
|
||||
default:
|
||||
src.ExternalImport("golang.org/x/sys/windows")
|
||||
}
|
||||
}
|
||||
if *winio {
|
||||
src.ExternalImport("github.com/Microsoft/go-winio")
|
||||
}
|
||||
if packageName != "syscall" {
|
||||
src.Import("syscall")
|
||||
}
|
||||
funcMap := template.FuncMap{
|
||||
"packagename": packagename,
|
||||
"syscalldot": syscalldot,
|
||||
"newlazydll": func(dll string) string {
|
||||
arg := "\"" + dll + ".dll\""
|
||||
if !*systemDLL {
|
||||
return syscalldot() + "NewLazyDLL(" + arg + ")"
|
||||
}
|
||||
if strings.HasPrefix(dll, "api_") || strings.HasPrefix(dll, "ext_") {
|
||||
arg = strings.Replace(arg, "_", "-", -1)
|
||||
}
|
||||
switch pkgtype {
|
||||
case pkgStd:
|
||||
return syscalldot() + "NewLazyDLL(sysdll.Add(" + arg + "))"
|
||||
case pkgXSysWindows:
|
||||
return "NewLazySystemDLL(" + arg + ")"
|
||||
default:
|
||||
return "windows.NewLazySystemDLL(" + arg + ")"
|
||||
}
|
||||
},
|
||||
}
|
||||
t := template.Must(template.New("main").Funcs(funcMap).Parse(srcTemplate))
|
||||
err = t.Execute(w, src)
|
||||
if err != nil {
|
||||
return errors.New("Failed to execute template: " + err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func usage() {
|
||||
fmt.Fprintf(os.Stderr, "usage: mksyscall_windows [flags] [path ...]\n")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func main() {
|
||||
flag.Usage = usage
|
||||
flag.Parse()
|
||||
if len(flag.Args()) <= 0 {
|
||||
fmt.Fprintf(os.Stderr, "no files to parse provided\n")
|
||||
usage()
|
||||
}
|
||||
|
||||
src, err := ParseFiles(flag.Args())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := src.Generate(&buf); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
data, err := format.Source(buf.Bytes())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if *filename == "" {
|
||||
_, err = os.Stdout.Write(data)
|
||||
} else {
|
||||
err = ioutil.WriteFile(*filename, data, 0644)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: use println instead to print in the following template
|
||||
const srcTemplate = `
|
||||
|
||||
{{define "main"}}// Code generated mksyscall_windows.exe DO NOT EDIT
|
||||
|
||||
package {{packagename}}
|
||||
|
||||
import (
|
||||
{{range .StdLibImports}}"{{.}}"
|
||||
{{end}}
|
||||
|
||||
{{range .ExternalImports}}"{{.}}"
|
||||
{{end}}
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
// Do the interface allocations only once for common
|
||||
// Errno values.
|
||||
const (
|
||||
errnoERROR_IO_PENDING = 997
|
||||
)
|
||||
|
||||
var (
|
||||
errERROR_IO_PENDING error = {{syscalldot}}Errno(errnoERROR_IO_PENDING)
|
||||
)
|
||||
|
||||
// errnoErr returns common boxed Errno values, to prevent
|
||||
// allocations at runtime.
|
||||
func errnoErr(e {{syscalldot}}Errno) error {
|
||||
switch e {
|
||||
case 0:
|
||||
return nil
|
||||
case errnoERROR_IO_PENDING:
|
||||
return errERROR_IO_PENDING
|
||||
}
|
||||
// TODO: add more here, after collecting data on the common
|
||||
// error values see on Windows. (perhaps when running
|
||||
// all.bat?)
|
||||
return e
|
||||
}
|
||||
|
||||
var (
|
||||
{{template "dlls" .}}
|
||||
{{template "funcnames" .}})
|
||||
{{range .Funcs}}{{if .HasStringParam}}{{template "helperbody" .}}{{end}}{{template "funcbody" .}}{{end}}
|
||||
{{end}}
|
||||
|
||||
{{/* help functions */}}
|
||||
|
||||
{{define "dlls"}}{{range .DLLs}} mod{{.}} = {{newlazydll .}}
|
||||
{{end}}{{end}}
|
||||
|
||||
{{define "funcnames"}}{{range .Funcs}}{{if .IsNotDuplicate}} proc{{.DLLFuncName}} = mod{{.DLLName}}.NewProc("{{.DLLFuncName}}"){{end}}
|
||||
{{end}}{{end}}
|
||||
|
||||
{{define "helperbody"}}
|
||||
func {{.Name}}({{.ParamList}}) {{template "results" .}}{
|
||||
{{template "helpertmpvars" .}} return {{.HelperName}}({{.HelperCallParamList}})
|
||||
}
|
||||
{{end}}
|
||||
|
||||
{{define "funcbody"}}
|
||||
func {{.HelperName}}({{.HelperParamList}}) {{template "results" .}}{
|
||||
{{template "tmpvars" .}} {{template "syscallcheck" .}}{{template "syscall" .}}
|
||||
{{template "seterror" .}}{{template "printtrace" .}} return
|
||||
}
|
||||
{{end}}
|
||||
|
||||
{{define "helpertmpvars"}}{{range .Params}}{{if .TmpVarHelperCode}} {{.TmpVarHelperCode}}
|
||||
{{end}}{{end}}{{end}}
|
||||
|
||||
{{define "tmpvars"}}{{range .Params}}{{if .TmpVarCode}} {{.TmpVarCode}}
|
||||
{{end}}{{end}}{{end}}
|
||||
|
||||
{{define "results"}}{{if .Rets.List}}{{.Rets.List}} {{end}}{{end}}
|
||||
|
||||
{{define "syscall"}}{{.Rets.SetReturnValuesCode}}{{.Syscall}}(proc{{.DLLFuncName}}.Addr(), {{.ParamCount}}, {{.SyscallParamList}}){{end}}
|
||||
|
||||
{{define "syscallcheck"}}{{if .ConfirmProc}}if {{.Rets.ErrorVarName}} = proc{{.DLLFuncName}}.Find(); {{.Rets.ErrorVarName}} != nil {
|
||||
return
|
||||
}
|
||||
{{end}}{{end}}
|
||||
|
||||
|
||||
{{define "seterror"}}{{if .Rets.SetErrorCode}} {{.Rets.SetErrorCode}}
|
||||
{{end}}{{end}}
|
||||
|
||||
{{define "printtrace"}}{{if .PrintTrace}} print("SYSCALL: {{.Name}}(", {{.ParamPrintList}}") (", {{.Rets.PrintList}}")\n")
|
||||
{{end}}{{end}}
|
||||
|
||||
`
|
239
vendor/github.com/containernetworking/cni/libcni/api.go
generated
vendored
239
vendor/github.com/containernetworking/cni/libcni/api.go
generated
vendored
@ -25,6 +25,7 @@ import (
|
||||
|
||||
"github.com/containernetworking/cni/pkg/invoke"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/utils"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
)
|
||||
|
||||
@ -32,6 +33,10 @@ var (
|
||||
CacheDir = "/var/lib/cni"
|
||||
)
|
||||
|
||||
const (
|
||||
CNICacheV1 = "cniCacheV1"
|
||||
)
|
||||
|
||||
// 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
|
||||
@ -48,7 +53,7 @@ type RuntimeConf struct {
|
||||
// to the plugin
|
||||
CapabilityArgs map[string]interface{}
|
||||
|
||||
// A cache directory in which to library data. Defaults to CacheDir
|
||||
// DEPRECATED. Will be removed in a future release.
|
||||
CacheDir string
|
||||
}
|
||||
|
||||
@ -69,19 +74,23 @@ 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)
|
||||
GetNetworkListCachedConfig(net *NetworkConfigList, rt *RuntimeConf) ([]byte, *RuntimeConf, 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)
|
||||
GetNetworkCachedConfig(net *NetworkConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, 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
|
||||
Path []string
|
||||
exec invoke.Exec
|
||||
cacheDir string
|
||||
}
|
||||
|
||||
// CNIConfig implements the CNI interface
|
||||
@ -91,9 +100,18 @@ var _ CNI = &CNIConfig{}
|
||||
// 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 NewCNIConfigWithCacheDir(path, "", exec)
|
||||
}
|
||||
|
||||
// NewCNIConfigWithCacheDir returns a new CNIConfig object that will search for plugins
|
||||
// in the given paths use the given exec interface to run those plugins,
|
||||
// or if the exec interface is not given, will use a default exec handler.
|
||||
// The given cache directory will be used for temporary data storage when needed.
|
||||
func NewCNIConfigWithCacheDir(path []string, cacheDir string, exec invoke.Exec) *CNIConfig {
|
||||
return &CNIConfig{
|
||||
Path: path,
|
||||
exec: exec,
|
||||
Path: path,
|
||||
cacheDir: cacheDir,
|
||||
exec: exec,
|
||||
}
|
||||
}
|
||||
|
||||
@ -164,33 +182,122 @@ func (c *CNIConfig) ensureExec() invoke.Exec {
|
||||
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))
|
||||
type cachedInfo struct {
|
||||
Kind string `json:"kind"`
|
||||
ContainerID string `json:"containerId"`
|
||||
Config []byte `json:"config"`
|
||||
IfName string `json:"ifName"`
|
||||
NetworkName string `json:"networkName"`
|
||||
CniArgs [][2]string `json:"cniArgs,omitempty"`
|
||||
CapabilityArgs map[string]interface{} `json:"capabilityArgs,omitempty"`
|
||||
RawResult map[string]interface{} `json:"result,omitempty"`
|
||||
Result types.Result `json:"-"`
|
||||
}
|
||||
|
||||
func setCachedResult(result types.Result, netName string, rt *RuntimeConf) error {
|
||||
// getCacheDir returns the cache directory in this order:
|
||||
// 1) global cacheDir from CNIConfig object
|
||||
// 2) deprecated cacheDir from RuntimeConf object
|
||||
// 3) fall back to default cache directory
|
||||
func (c *CNIConfig) getCacheDir(rt *RuntimeConf) string {
|
||||
if c.cacheDir != "" {
|
||||
return c.cacheDir
|
||||
}
|
||||
if rt.CacheDir != "" {
|
||||
return rt.CacheDir
|
||||
}
|
||||
return CacheDir
|
||||
}
|
||||
|
||||
func (c *CNIConfig) getCacheFilePath(netName string, rt *RuntimeConf) (string, error) {
|
||||
if netName == "" || rt.ContainerID == "" || rt.IfName == "" {
|
||||
return "", fmt.Errorf("cache file path requires network name (%q), container ID (%q), and interface name (%q)", netName, rt.ContainerID, rt.IfName)
|
||||
}
|
||||
return filepath.Join(c.getCacheDir(rt), "results", fmt.Sprintf("%s-%s-%s", netName, rt.ContainerID, rt.IfName)), nil
|
||||
}
|
||||
|
||||
func (c *CNIConfig) cacheAdd(result types.Result, config []byte, netName string, rt *RuntimeConf) error {
|
||||
cached := cachedInfo{
|
||||
Kind: CNICacheV1,
|
||||
ContainerID: rt.ContainerID,
|
||||
Config: config,
|
||||
IfName: rt.IfName,
|
||||
NetworkName: netName,
|
||||
CniArgs: rt.Args,
|
||||
CapabilityArgs: rt.CapabilityArgs,
|
||||
}
|
||||
|
||||
// We need to get type.Result into cachedInfo as JSON map
|
||||
// Marshal to []byte, then Unmarshal into cached.RawResult
|
||||
data, err := json.Marshal(result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fname := getResultCacheFilePath(netName, rt)
|
||||
|
||||
err = json.Unmarshal(data, &cached.RawResult)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newBytes, err := json.Marshal(&cached)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fname, err := c.getCacheFilePath(netName, rt)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(fname), 0700); err != nil {
|
||||
return err
|
||||
}
|
||||
return ioutil.WriteFile(fname, data, 0600)
|
||||
|
||||
return ioutil.WriteFile(fname, newBytes, 0600)
|
||||
}
|
||||
|
||||
func delCachedResult(netName string, rt *RuntimeConf) error {
|
||||
fname := getResultCacheFilePath(netName, rt)
|
||||
func (c *CNIConfig) cacheDel(netName string, rt *RuntimeConf) error {
|
||||
fname, err := c.getCacheFilePath(netName, rt)
|
||||
if err != nil {
|
||||
// Ignore error
|
||||
return nil
|
||||
}
|
||||
return os.Remove(fname)
|
||||
}
|
||||
|
||||
func getCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, error) {
|
||||
fname := getResultCacheFilePath(netName, rt)
|
||||
func (c *CNIConfig) getCachedConfig(netName string, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
|
||||
var bytes []byte
|
||||
|
||||
fname, err := c.getCacheFilePath(netName, rt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
bytes, err = ioutil.ReadFile(fname)
|
||||
if err != nil {
|
||||
// Ignore read errors; the cached result may not exist on-disk
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
unmarshaled := cachedInfo{}
|
||||
if err := json.Unmarshal(bytes, &unmarshaled); err != nil {
|
||||
return nil, nil, fmt.Errorf("failed to unmarshal cached network %q config: %v", netName, err)
|
||||
}
|
||||
if unmarshaled.Kind != CNICacheV1 {
|
||||
return nil, nil, fmt.Errorf("read cached network %q config has wrong kind: %v", netName, unmarshaled.Kind)
|
||||
}
|
||||
|
||||
newRt := *rt
|
||||
if unmarshaled.CniArgs != nil {
|
||||
newRt.Args = unmarshaled.CniArgs
|
||||
}
|
||||
newRt.CapabilityArgs = unmarshaled.CapabilityArgs
|
||||
|
||||
return unmarshaled.Config, &newRt, nil
|
||||
}
|
||||
|
||||
func (c *CNIConfig) getLegacyCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, error) {
|
||||
fname, err := c.getCacheFilePath(netName, rt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data, err := ioutil.ReadFile(fname)
|
||||
if err != nil {
|
||||
// Ignore read errors; the cached result may not exist on-disk
|
||||
@ -221,16 +328,73 @@ func getCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result,
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (c *CNIConfig) getCachedResult(netName, cniVersion string, rt *RuntimeConf) (types.Result, error) {
|
||||
fname, err := c.getCacheFilePath(netName, rt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fdata, err := ioutil.ReadFile(fname)
|
||||
if err != nil {
|
||||
// Ignore read errors; the cached result may not exist on-disk
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
cachedInfo := cachedInfo{}
|
||||
if err := json.Unmarshal(fdata, &cachedInfo); err != nil || cachedInfo.Kind != CNICacheV1 {
|
||||
return c.getLegacyCachedResult(netName, cniVersion, rt)
|
||||
}
|
||||
|
||||
newBytes, err := json.Marshal(&cachedInfo.RawResult)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to marshal cached network %q config: %v", netName, err)
|
||||
}
|
||||
|
||||
// Read the version of the cached result
|
||||
decoder := version.ConfigDecoder{}
|
||||
resultCniVersion, err := decoder.Decode(newBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ensure we can understand the result
|
||||
result, err := version.NewResult(resultCniVersion, newBytes)
|
||||
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.
|
||||
// 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)
|
||||
return c.getCachedResult(list.Name, list.CNIVersion, rt)
|
||||
}
|
||||
|
||||
// GetNetworkCachedResult returns the cached Result of the previous
|
||||
// previous AddNetwork() operation for a network, or an error.
|
||||
// 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)
|
||||
return c.getCachedResult(net.Network.Name, net.Network.CNIVersion, rt)
|
||||
}
|
||||
|
||||
// GetNetworkListCachedConfig copies the input RuntimeConf to output
|
||||
// RuntimeConf with fields updated with info from the cached Config.
|
||||
func (c *CNIConfig) GetNetworkListCachedConfig(list *NetworkConfigList, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
|
||||
return c.getCachedConfig(list.Name, rt)
|
||||
}
|
||||
|
||||
// GetNetworkCachedConfig copies the input RuntimeConf to output
|
||||
// RuntimeConf with fields updated with info from the cached Config.
|
||||
func (c *CNIConfig) GetNetworkCachedConfig(net *NetworkConfig, rt *RuntimeConf) ([]byte, *RuntimeConf, error) {
|
||||
return c.getCachedConfig(net.Network.Name, rt)
|
||||
}
|
||||
|
||||
func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (types.Result, error) {
|
||||
@ -239,6 +403,15 @@ func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := utils.ValidateContainerID(rt.ContainerID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := utils.ValidateNetworkName(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := utils.ValidateInterfaceName(rt.IfName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)
|
||||
if err != nil {
|
||||
@ -259,7 +432,7 @@ func (c *CNIConfig) AddNetworkList(ctx context.Context, list *NetworkConfigList,
|
||||
}
|
||||
}
|
||||
|
||||
if err = setCachedResult(result, list.Name, rt); err != nil {
|
||||
if err = c.cacheAdd(result, list.Bytes, list.Name, rt); err != nil {
|
||||
return nil, fmt.Errorf("failed to set network %q cached result: %v", list.Name, err)
|
||||
}
|
||||
|
||||
@ -294,7 +467,7 @@ func (c *CNIConfig) CheckNetworkList(ctx context.Context, list *NetworkConfigLis
|
||||
return nil
|
||||
}
|
||||
|
||||
cachedResult, err := getCachedResult(list.Name, list.CNIVersion, rt)
|
||||
cachedResult, err := c.getCachedResult(list.Name, list.CNIVersion, rt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get network %q cached result: %v", list.Name, err)
|
||||
}
|
||||
@ -331,7 +504,7 @@ func (c *CNIConfig) DelNetworkList(ctx context.Context, list *NetworkConfigList,
|
||||
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)
|
||||
cachedResult, err = c.getCachedResult(list.Name, list.CNIVersion, rt)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get network %q cached result: %v", list.Name, err)
|
||||
}
|
||||
@ -343,7 +516,7 @@ func (c *CNIConfig) DelNetworkList(ctx context.Context, list *NetworkConfigList,
|
||||
return err
|
||||
}
|
||||
}
|
||||
_ = delCachedResult(list.Name, rt)
|
||||
_ = c.cacheDel(list.Name, rt)
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -355,7 +528,7 @@ func (c *CNIConfig) AddNetwork(ctx context.Context, net *NetworkConfig, rt *Runt
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = setCachedResult(result, net.Network.Name, rt); err != nil {
|
||||
if err = c.cacheAdd(result, net.Bytes, net.Network.Name, rt); err != nil {
|
||||
return nil, fmt.Errorf("failed to set network %q cached result: %v", net.Network.Name, err)
|
||||
}
|
||||
|
||||
@ -371,7 +544,7 @@ func (c *CNIConfig) CheckNetwork(ctx context.Context, net *NetworkConfig, rt *Ru
|
||||
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)
|
||||
cachedResult, err := c.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)
|
||||
}
|
||||
@ -386,7 +559,7 @@ func (c *CNIConfig) DelNetwork(ctx context.Context, net *NetworkConfig, rt *Runt
|
||||
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)
|
||||
cachedResult, err = c.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)
|
||||
}
|
||||
@ -395,7 +568,7 @@ func (c *CNIConfig) DelNetwork(ctx context.Context, net *NetworkConfig, rt *Runt
|
||||
if err := c.delNetwork(ctx, net.Network.Name, net.Network.CNIVersion, net, cachedResult, rt); err != nil {
|
||||
return err
|
||||
}
|
||||
_ = delCachedResult(net.Network.Name, rt)
|
||||
_ = c.cacheDel(net.Network.Name, rt)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -454,10 +627,14 @@ func (c *CNIConfig) ValidateNetwork(ctx context.Context, net *NetworkConfig) ([]
|
||||
|
||||
// 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)
|
||||
c.ensureExec()
|
||||
pluginPath, err := c.exec.FindInPath(pluginName, c.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if expectedVersion == "" {
|
||||
expectedVersion = "0.1.0"
|
||||
}
|
||||
|
||||
vi, err := invoke.GetVersionInfo(ctx, pluginPath, c.exec)
|
||||
if err != nil {
|
||||
|
4
vendor/github.com/containernetworking/cni/libcni/conf.go
generated
vendored
4
vendor/github.com/containernetworking/cni/libcni/conf.go
generated
vendored
@ -114,11 +114,11 @@ func ConfListFromBytes(bytes []byte) (*NetworkConfigList, error) {
|
||||
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)
|
||||
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)
|
||||
return nil, fmt.Errorf("failed to parse plugin config %d: %v", i, err)
|
||||
}
|
||||
list.Plugins = append(list.Plugins, netConf)
|
||||
}
|
||||
|
70
vendor/github.com/containernetworking/cni/pkg/invoke/args.go
generated
vendored
70
vendor/github.com/containernetworking/cni/pkg/invoke/args.go
generated
vendored
@ -15,6 +15,7 @@
|
||||
package invoke
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
@ -22,6 +23,8 @@ import (
|
||||
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
|
||||
}
|
||||
|
||||
@ -29,7 +32,7 @@ type inherited struct{}
|
||||
|
||||
var inheritArgsFromEnv inherited
|
||||
|
||||
func (_ *inherited) AsEnv() []string {
|
||||
func (*inherited) AsEnv() []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -57,17 +60,17 @@ func (args *Args) AsEnv() []string {
|
||||
pluginArgsStr = stringify(args.PluginArgs)
|
||||
}
|
||||
|
||||
// Ensure that the custom values are first, so any value present in
|
||||
// the process environment won't override them.
|
||||
env = append([]string{
|
||||
"CNI_COMMAND=" + args.Command,
|
||||
"CNI_CONTAINERID=" + args.ContainerID,
|
||||
"CNI_NETNS=" + args.NetNS,
|
||||
"CNI_ARGS=" + pluginArgsStr,
|
||||
"CNI_IFNAME=" + args.IfName,
|
||||
"CNI_PATH=" + args.Path,
|
||||
}, env...)
|
||||
return env
|
||||
// Duplicated values which come first will be overridden, so we must put the
|
||||
// custom values in the end to avoid being overridden 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
|
||||
@ -80,3 +83,46 @@ func stringify(pluginArgs [][2]string) string {
|
||||
|
||||
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
|
||||
}
|
||||
|
29
vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go
generated
vendored
29
vendor/github.com/containernetworking/cni/pkg/invoke/delegate.go
generated
vendored
@ -16,22 +16,17 @@ package invoke
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
)
|
||||
|
||||
func delegateCommon(expectedCommand, delegatePlugin string, exec Exec) (string, Exec, error) {
|
||||
func delegateCommon(delegatePlugin string, exec Exec) (string, Exec, error) {
|
||||
if exec == nil {
|
||||
exec = defaultExec
|
||||
}
|
||||
|
||||
if os.Getenv("CNI_COMMAND") != expectedCommand {
|
||||
return "", nil, fmt.Errorf("CNI_COMMAND is not " + expectedCommand)
|
||||
}
|
||||
|
||||
paths := filepath.SplitList(os.Getenv("CNI_PATH"))
|
||||
pluginPath, err := exec.FindInPath(delegatePlugin, paths)
|
||||
if err != nil {
|
||||
@ -44,32 +39,42 @@ func delegateCommon(expectedCommand, delegatePlugin string, exec Exec) (string,
|
||||
// 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("ADD", delegatePlugin, exec)
|
||||
pluginPath, realExec, err := delegateCommon(delegatePlugin, exec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ExecPluginWithResult(ctx, pluginPath, netconf, ArgsFromEnv(), realExec)
|
||||
// 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("CHECK", delegatePlugin, exec)
|
||||
pluginPath, realExec, err := delegateCommon(delegatePlugin, exec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ExecPluginWithoutResult(ctx, pluginPath, netconf, ArgsFromEnv(), realExec)
|
||||
// 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("DEL", delegatePlugin, exec)
|
||||
pluginPath, realExec, err := delegateCommon(delegatePlugin, exec)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ExecPluginWithoutResult(ctx, pluginPath, netconf, ArgsFromEnv(), realExec)
|
||||
// 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,
|
||||
}
|
||||
}
|
||||
|
52
vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go
generated
vendored
52
vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec.go
generated
vendored
@ -21,6 +21,8 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
)
|
||||
@ -31,30 +33,54 @@ type RawExec struct {
|
||||
|
||||
func (e *RawExec) ExecPlugin(ctx context.Context, pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
|
||||
stdout := &bytes.Buffer{}
|
||||
stderr := &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())
|
||||
c.Stderr = stderr
|
||||
|
||||
// Retry the command on "text file busy" errors
|
||||
for i := 0; i <= 5; i++ {
|
||||
err := c.Run()
|
||||
|
||||
// Command succeeded
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
|
||||
// If the plugin is currently about to be written, then we wait a
|
||||
// second and try it again
|
||||
if strings.Contains(err.Error(), "text file busy") {
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
// All other errors except than the busy text file
|
||||
return nil, e.pluginErr(err, stdout.Bytes(), stderr.Bytes())
|
||||
}
|
||||
|
||||
// Copy stderr to caller's buffer in case plugin printed to both
|
||||
// stdout and stderr for some reason. Ignore failures as stderr is
|
||||
// only informational.
|
||||
if e.Stderr != nil && stderr.Len() > 0 {
|
||||
_, _ = stderr.WriteTo(e.Stderr)
|
||||
}
|
||||
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)
|
||||
func (e *RawExec) pluginErr(err error, stdout, stderr []byte) error {
|
||||
emsg := types.Error{}
|
||||
if len(stdout) == 0 {
|
||||
if len(stderr) == 0 {
|
||||
emsg.Msg = fmt.Sprintf("netplugin failed with no error message: %v", err)
|
||||
} else {
|
||||
emsg.Msg = fmt.Sprintf("netplugin failed: %q", string(stderr))
|
||||
}
|
||||
return &emsg
|
||||
} else if perr := json.Unmarshal(stdout, &emsg); perr != nil {
|
||||
emsg.Msg = fmt.Sprintf("netplugin failed but error parsing its diagnostic message %q: %v", string(stdout), perr)
|
||||
}
|
||||
|
||||
return err
|
||||
return &emsg
|
||||
}
|
||||
|
||||
func (e *RawExec) FindInPath(plugin string, paths []string) (string, error) {
|
||||
|
99
vendor/github.com/containernetworking/cni/pkg/skel/skel.go
generated
vendored
99
vendor/github.com/containernetworking/cni/pkg/skel/skel.go
generated
vendored
@ -27,6 +27,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/utils"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
)
|
||||
|
||||
@ -53,16 +54,7 @@ type dispatcher struct {
|
||||
|
||||
type reqForCmdEntry map[string]bool
|
||||
|
||||
// internal only error to indicate lack of required environment variables
|
||||
type missingEnvError struct {
|
||||
msg string
|
||||
}
|
||||
|
||||
func (e missingEnvError) Error() string {
|
||||
return e.msg
|
||||
}
|
||||
|
||||
func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, error) {
|
||||
func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, *types.Error) {
|
||||
var cmd, contID, netns, ifName, args, path string
|
||||
|
||||
vars := []struct {
|
||||
@ -138,7 +130,7 @@ func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, error) {
|
||||
|
||||
if len(argsMissing) > 0 {
|
||||
joined := strings.Join(argsMissing, ",")
|
||||
return "", nil, missingEnvError{fmt.Sprintf("required env variables [%s] missing", joined)}
|
||||
return "", nil, types.NewError(types.ErrInvalidEnvironmentVariables, fmt.Sprintf("required env variables [%s] missing", joined), "")
|
||||
}
|
||||
|
||||
if cmd == "VERSION" {
|
||||
@ -147,7 +139,7 @@ func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, error) {
|
||||
|
||||
stdinData, err := ioutil.ReadAll(t.Stdin)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("error reading from stdin: %v", err)
|
||||
return "", nil, types.NewError(types.ErrIOFailure, fmt.Sprintf("error reading from stdin: %v", err), "")
|
||||
}
|
||||
|
||||
cmdArgs := &CmdArgs{
|
||||
@ -161,39 +153,39 @@ func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, error) {
|
||||
return cmd, cmdArgs, nil
|
||||
}
|
||||
|
||||
func createTypedError(f string, args ...interface{}) *types.Error {
|
||||
return &types.Error{
|
||||
Code: 100,
|
||||
Msg: fmt.Sprintf(f, args...),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *dispatcher) checkVersionAndCall(cmdArgs *CmdArgs, pluginVersionInfo version.PluginInfo, toCall func(*CmdArgs) error) error {
|
||||
func (t *dispatcher) checkVersionAndCall(cmdArgs *CmdArgs, pluginVersionInfo version.PluginInfo, toCall func(*CmdArgs) error) *types.Error {
|
||||
configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
return types.NewError(types.ErrDecodingFailure, err.Error(), "")
|
||||
}
|
||||
verErr := t.VersionReconciler.Check(configVersion, pluginVersionInfo)
|
||||
if verErr != nil {
|
||||
return &types.Error{
|
||||
Code: types.ErrIncompatibleCNIVersion,
|
||||
Msg: "incompatible CNI versions",
|
||||
Details: verErr.Details(),
|
||||
}
|
||||
return types.NewError(types.ErrIncompatibleCNIVersion, "incompatible CNI versions", verErr.Details())
|
||||
}
|
||||
|
||||
return toCall(cmdArgs)
|
||||
if err = toCall(cmdArgs); err != nil {
|
||||
if e, ok := err.(*types.Error); ok {
|
||||
// don't wrap Error in Error
|
||||
return e
|
||||
}
|
||||
return types.NewError(types.ErrInternal, err.Error(), "")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateConfig(jsonBytes []byte) error {
|
||||
func validateConfig(jsonBytes []byte) *types.Error {
|
||||
var conf struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
if err := json.Unmarshal(jsonBytes, &conf); err != nil {
|
||||
return fmt.Errorf("error reading network config: %s", err)
|
||||
return types.NewError(types.ErrDecodingFailure, fmt.Sprintf("error unmarshall network config: %v", err), "")
|
||||
}
|
||||
if conf.Name == "" {
|
||||
return fmt.Errorf("missing network name")
|
||||
return types.NewError(types.ErrInvalidNetworkConfig, "missing network name", "")
|
||||
}
|
||||
if err := utils.ValidateNetworkName(conf.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -202,17 +194,22 @@ func (t *dispatcher) pluginMain(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error,
|
||||
cmd, cmdArgs, err := t.getCmdArgsFromEnv()
|
||||
if err != nil {
|
||||
// Print the about string to stderr when no command is set
|
||||
if _, ok := err.(missingEnvError); ok && t.Getenv("CNI_COMMAND") == "" && about != "" {
|
||||
fmt.Fprintln(t.Stderr, about)
|
||||
if err.Code == types.ErrInvalidEnvironmentVariables && t.Getenv("CNI_COMMAND") == "" && about != "" {
|
||||
_, _ = fmt.Fprintln(t.Stderr, about)
|
||||
return nil
|
||||
}
|
||||
return createTypedError(err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
if cmd != "VERSION" {
|
||||
err = validateConfig(cmdArgs.StdinData)
|
||||
if err != nil {
|
||||
return createTypedError(err.Error())
|
||||
if err = validateConfig(cmdArgs.StdinData); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = utils.ValidateContainerID(cmdArgs.ContainerID); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = utils.ValidateInterfaceName(cmdArgs.IfName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@ -222,45 +219,37 @@ func (t *dispatcher) pluginMain(cmdAdd, cmdCheck, cmdDel func(_ *CmdArgs) error,
|
||||
case "CHECK":
|
||||
configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData)
|
||||
if err != nil {
|
||||
return createTypedError(err.Error())
|
||||
return types.NewError(types.ErrDecodingFailure, err.Error(), "")
|
||||
}
|
||||
if gtet, err := version.GreaterThanOrEqualTo(configVersion, "0.4.0"); err != nil {
|
||||
return createTypedError(err.Error())
|
||||
return types.NewError(types.ErrDecodingFailure, err.Error(), "")
|
||||
} else if !gtet {
|
||||
return &types.Error{
|
||||
Code: types.ErrIncompatibleCNIVersion,
|
||||
Msg: "config version does not allow CHECK",
|
||||
}
|
||||
return types.NewError(types.ErrIncompatibleCNIVersion, "config version does not allow CHECK", "")
|
||||
}
|
||||
for _, pluginVersion := range versionInfo.SupportedVersions() {
|
||||
gtet, err := version.GreaterThanOrEqualTo(pluginVersion, configVersion)
|
||||
if err != nil {
|
||||
return createTypedError(err.Error())
|
||||
return types.NewError(types.ErrDecodingFailure, err.Error(), "")
|
||||
} else if gtet {
|
||||
if err := t.checkVersionAndCall(cmdArgs, versionInfo, cmdCheck); err != nil {
|
||||
return createTypedError(err.Error())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return &types.Error{
|
||||
Code: types.ErrIncompatibleCNIVersion,
|
||||
Msg: "plugin version does not allow CHECK",
|
||||
}
|
||||
return types.NewError(types.ErrIncompatibleCNIVersion, "plugin version does not allow CHECK", "")
|
||||
case "DEL":
|
||||
err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdDel)
|
||||
case "VERSION":
|
||||
err = versionInfo.Encode(t.Stdout)
|
||||
if err := versionInfo.Encode(t.Stdout); err != nil {
|
||||
return types.NewError(types.ErrIOFailure, err.Error(), "")
|
||||
}
|
||||
default:
|
||||
return createTypedError("unknown CNI_COMMAND: %v", cmd)
|
||||
return types.NewError(types.ErrInvalidEnvironmentVariables, fmt.Sprintf("unknown CNI_COMMAND: %v", cmd), "")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if e, ok := err.(*types.Error); ok {
|
||||
// don't wrap Error in Error
|
||||
return e
|
||||
}
|
||||
return createTypedError(err.Error())
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
14
vendor/github.com/containernetworking/cni/pkg/types/020/types.go
generated
vendored
14
vendor/github.com/containernetworking/cni/pkg/types/020/types.go
generated
vendored
@ -86,20 +86,6 @@ func (r *Result) PrintTo(writer io.Writer) error {
|
||||
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
|
||||
|
2
vendor/github.com/containernetworking/cni/pkg/types/args.go
generated
vendored
2
vendor/github.com/containernetworking/cni/pkg/types/args.go
generated
vendored
@ -36,7 +36,7 @@ func (b *UnmarshallableBool) UnmarshalText(data []byte) error {
|
||||
case "0", "false":
|
||||
*b = false
|
||||
default:
|
||||
return fmt.Errorf("Boolean unmarshal error: invalid input %s", s)
|
||||
return fmt.Errorf("boolean unmarshal error: invalid input %s", s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
17
vendor/github.com/containernetworking/cni/pkg/types/current/types.go
generated
vendored
17
vendor/github.com/containernetworking/cni/pkg/types/current/types.go
generated
vendored
@ -207,23 +207,6 @@ func (r *Result) PrintTo(writer io.Writer) error {
|
||||
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
|
||||
|
28
vendor/github.com/containernetworking/cni/pkg/types/types.go
generated
vendored
28
vendor/github.com/containernetworking/cni/pkg/types/types.go
generated
vendored
@ -16,7 +16,6 @@ package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
@ -101,9 +100,6 @@ type Result interface {
|
||||
|
||||
// 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 {
|
||||
@ -134,9 +130,16 @@ func (r *Route) String() string {
|
||||
// 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
|
||||
ErrUnknown uint = iota // 0
|
||||
ErrIncompatibleCNIVersion // 1
|
||||
ErrUnsupportedField // 2
|
||||
ErrUnknownContainer // 3
|
||||
ErrInvalidEnvironmentVariables // 4
|
||||
ErrIOFailure // 5
|
||||
ErrDecodingFailure // 6
|
||||
ErrInvalidNetworkConfig // 7
|
||||
ErrTryAgainLater uint = 11
|
||||
ErrInternal uint = 999
|
||||
)
|
||||
|
||||
type Error struct {
|
||||
@ -145,6 +148,14 @@ type Error struct {
|
||||
Details string `json:"details,omitempty"`
|
||||
}
|
||||
|
||||
func NewError(code uint, msg, details string) *Error {
|
||||
return &Error{
|
||||
Code: code,
|
||||
Msg: msg,
|
||||
Details: details,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Error) Error() string {
|
||||
details := ""
|
||||
if e.Details != "" {
|
||||
@ -194,6 +205,3 @@ func prettyPrint(obj interface{}) error {
|
||||
_, 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")
|
||||
|
84
vendor/github.com/containernetworking/cni/pkg/utils/utils.go
generated
vendored
Normal file
84
vendor/github.com/containernetworking/cni/pkg/utils/utils.go
generated
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
// Copyright 2019 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 utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"unicode"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
)
|
||||
|
||||
const (
|
||||
// cniValidNameChars is the regexp used to validate valid characters in
|
||||
// containerID and networkName
|
||||
cniValidNameChars = `[a-zA-Z0-9][a-zA-Z0-9_.\-]`
|
||||
|
||||
// maxInterfaceNameLength is the length max of a valid interface name
|
||||
maxInterfaceNameLength = 15
|
||||
)
|
||||
|
||||
var cniReg = regexp.MustCompile(`^` + cniValidNameChars + `*$`)
|
||||
|
||||
// ValidateContainerID will validate that the supplied containerID is not empty does not contain invalid characters
|
||||
func ValidateContainerID(containerID string) *types.Error {
|
||||
|
||||
if containerID == "" {
|
||||
return types.NewError(types.ErrUnknownContainer, "missing containerID", "")
|
||||
}
|
||||
if !cniReg.MatchString(containerID) {
|
||||
return types.NewError(types.ErrInvalidEnvironmentVariables, "invalid characters in containerID", containerID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateNetworkName will validate that the supplied networkName does not contain invalid characters
|
||||
func ValidateNetworkName(networkName string) *types.Error {
|
||||
|
||||
if networkName == "" {
|
||||
return types.NewError(types.ErrInvalidNetworkConfig, "missing network name:", "")
|
||||
}
|
||||
if !cniReg.MatchString(networkName) {
|
||||
return types.NewError(types.ErrInvalidNetworkConfig, "invalid characters found in network name", networkName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateInterfaceName will validate the interface name based on the three rules below
|
||||
// 1. The name must not be empty
|
||||
// 2. The name must be less than 16 characters
|
||||
// 3. The name must not be "." or ".."
|
||||
// 3. The name must not contain / or : or any whitespace characters
|
||||
// ref to https://github.com/torvalds/linux/blob/master/net/core/dev.c#L1024
|
||||
func ValidateInterfaceName(ifName string) *types.Error {
|
||||
if len(ifName) == 0 {
|
||||
return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is empty", "")
|
||||
}
|
||||
if len(ifName) > maxInterfaceNameLength {
|
||||
return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is too long", fmt.Sprintf("interface name should be less than %d characters", maxInterfaceNameLength+1))
|
||||
}
|
||||
if ifName == "." || ifName == ".." {
|
||||
return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name is . or ..", "")
|
||||
}
|
||||
for _, r := range bytes.Runes([]byte(ifName)) {
|
||||
if r == '/' || r == ':' || unicode.IsSpace(r) {
|
||||
return types.NewError(types.ErrInvalidEnvironmentVariables, "interface name contains / or : or whitespace characters", "")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
16
vendor/github.com/coreos/go-iptables/iptables/iptables.go
generated
vendored
16
vendor/github.com/coreos/go-iptables/iptables/iptables.go
generated
vendored
@ -48,9 +48,13 @@ func (e *Error) Error() string {
|
||||
|
||||
// IsNotExist returns true if the error is due to the chain or rule not existing
|
||||
func (e *Error) IsNotExist() bool {
|
||||
return e.ExitStatus() == 1 &&
|
||||
(e.msg == fmt.Sprintf("%s: Bad rule (does a matching rule exist in that chain?).\n", getIptablesCommand(e.proto)) ||
|
||||
e.msg == fmt.Sprintf("%s: No chain/target/match by that name.\n", getIptablesCommand(e.proto)))
|
||||
if e.ExitStatus() != 1 {
|
||||
return false
|
||||
}
|
||||
cmdIptables := getIptablesCommand(e.proto)
|
||||
msgNoRuleExist := fmt.Sprintf("%s: Bad rule (does a matching rule exist in that chain?).\n", cmdIptables)
|
||||
msgNoChainExist := fmt.Sprintf("%s: No chain/target/match by that name.\n", cmdIptables)
|
||||
return strings.Contains(e.msg, msgNoRuleExist) || strings.Contains(e.msg, msgNoChainExist)
|
||||
}
|
||||
|
||||
// Protocol to differentiate between IPv4 and IPv6
|
||||
@ -101,7 +105,13 @@ func NewWithProtocol(proto Protocol) (*IPTables, error) {
|
||||
return nil, err
|
||||
}
|
||||
vstring, err := getIptablesVersionString(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get iptables version: %v", err)
|
||||
}
|
||||
v1, v2, v3, mode, err := extractIptablesVersion(vstring)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to extract iptables version from [%s]: %v", vstring, err)
|
||||
}
|
||||
|
||||
checkPresent, waitPresent, randomFullyPresent := getIptablesCommandSupport(v1, v2, v3)
|
||||
|
||||
|
23
vendor/github.com/juju/errors/.gitignore
generated
vendored
23
vendor/github.com/juju/errors/.gitignore
generated
vendored
@ -1,23 +0,0 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
191
vendor/github.com/juju/errors/LICENSE
generated
vendored
191
vendor/github.com/juju/errors/LICENSE
generated
vendored
@ -1,191 +0,0 @@
|
||||
All files in this repository are licensed as follows. If you contribute
|
||||
to this repository, it is assumed that you license your contribution
|
||||
under the same license unless you state otherwise.
|
||||
|
||||
All files Copyright (C) 2015 Canonical Ltd. unless otherwise specified in the file.
|
||||
|
||||
This software is licensed under the LGPLv3, included below.
|
||||
|
||||
As a special exception to the GNU Lesser General Public License version 3
|
||||
("LGPL3"), the copyright holders of this Library give you permission to
|
||||
convey to a third party a Combined Work that links statically or dynamically
|
||||
to this Library without providing any Minimal Corresponding Source or
|
||||
Minimal Application Code as set out in 4d or providing the installation
|
||||
information set out in section 4e, provided that you comply with the other
|
||||
provisions of LGPL3 and provided that you meet, for the Application the
|
||||
terms and conditions of the license(s) which apply to the Application.
|
||||
|
||||
Except as stated in this special exception, the provisions of LGPL3 will
|
||||
continue to comply in full to this Library. If you modify this Library, you
|
||||
may apply this exception to your version of this Library, but you are not
|
||||
obliged to do so. If you do not wish to do so, delete this exception
|
||||
statement from your version. This exception does not (and cannot) modify any
|
||||
license terms which apply to the Application, with which you must still
|
||||
comply.
|
||||
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
24
vendor/github.com/juju/errors/Makefile
generated
vendored
24
vendor/github.com/juju/errors/Makefile
generated
vendored
@ -1,24 +0,0 @@
|
||||
PROJECT := github.com/juju/errors
|
||||
|
||||
.PHONY: check-licence check-go check docs
|
||||
|
||||
check: check-licence check-go
|
||||
go test $(PROJECT)/...
|
||||
|
||||
check-licence:
|
||||
@(fgrep -rl "Licensed under the LGPLv3" --exclude *.s .;\
|
||||
fgrep -rl "MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT" --exclude *.s .;\
|
||||
find . -name "*.go") | sed -e 's,\./,,' | sort | uniq -u | \
|
||||
xargs -I {} echo FAIL: licence missed: {}
|
||||
|
||||
check-go:
|
||||
$(eval GOFMT := $(strip $(shell gofmt -l .| sed -e "s/^/ /g")))
|
||||
@(if [ x$(GOFMT) != x"" ]; then \
|
||||
echo go fmt is sad: $(GOFMT); \
|
||||
exit 1; \
|
||||
fi )
|
||||
@(go tool vet -all -composites=false -copylocks=false .)
|
||||
|
||||
docs:
|
||||
godoc2md github.com/juju/errors > README.md
|
||||
sed -i 's|\[godoc-link-here\]|[](https://godoc.org/github.com/juju/errors)|' README.md
|
707
vendor/github.com/juju/errors/README.md
generated
vendored
707
vendor/github.com/juju/errors/README.md
generated
vendored
@ -1,707 +0,0 @@
|
||||
|
||||
# errors
|
||||
import "github.com/juju/errors"
|
||||
|
||||
[](https://godoc.org/github.com/juju/errors)
|
||||
|
||||
The juju/errors provides an easy way to annotate errors without losing the
|
||||
orginal error context.
|
||||
|
||||
The exported `New` and `Errorf` functions are designed to replace the
|
||||
`errors.New` and `fmt.Errorf` functions respectively. The same underlying
|
||||
error is there, but the package also records the location at which the error
|
||||
was created.
|
||||
|
||||
A primary use case for this library is to add extra context any time an
|
||||
error is returned from a function.
|
||||
|
||||
|
||||
if err := SomeFunc(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
This instead becomes:
|
||||
|
||||
|
||||
if err := SomeFunc(); err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
which just records the file and line number of the Trace call, or
|
||||
|
||||
|
||||
if err := SomeFunc(); err != nil {
|
||||
return errors.Annotate(err, "more context")
|
||||
}
|
||||
|
||||
which also adds an annotation to the error.
|
||||
|
||||
When you want to check to see if an error is of a particular type, a helper
|
||||
function is normally exported by the package that returned the error, like the
|
||||
`os` package does. The underlying cause of the error is available using the
|
||||
`Cause` function.
|
||||
|
||||
|
||||
os.IsNotExist(errors.Cause(err))
|
||||
|
||||
The result of the `Error()` call on an annotated error is the annotations joined
|
||||
with colons, then the result of the `Error()` method for the underlying error
|
||||
that was the cause.
|
||||
|
||||
|
||||
err := errors.Errorf("original")
|
||||
err = errors.Annotatef(err, "context")
|
||||
err = errors.Annotatef(err, "more context")
|
||||
err.Error() -> "more context: context: original"
|
||||
|
||||
Obviously recording the file, line and functions is not very useful if you
|
||||
cannot get them back out again.
|
||||
|
||||
|
||||
errors.ErrorStack(err)
|
||||
|
||||
will return something like:
|
||||
|
||||
|
||||
first error
|
||||
github.com/juju/errors/annotation_test.go:193:
|
||||
github.com/juju/errors/annotation_test.go:194: annotation
|
||||
github.com/juju/errors/annotation_test.go:195:
|
||||
github.com/juju/errors/annotation_test.go:196: more context
|
||||
github.com/juju/errors/annotation_test.go:197:
|
||||
|
||||
The first error was generated by an external system, so there was no location
|
||||
associated. The second, fourth, and last lines were generated with Trace calls,
|
||||
and the other two through Annotate.
|
||||
|
||||
Sometimes when responding to an error you want to return a more specific error
|
||||
for the situation.
|
||||
|
||||
|
||||
if err := FindField(field); err != nil {
|
||||
return errors.Wrap(err, errors.NotFoundf(field))
|
||||
}
|
||||
|
||||
This returns an error where the complete error stack is still available, and
|
||||
`errors.Cause()` will return the `NotFound` error.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## func AlreadyExistsf
|
||||
``` go
|
||||
func AlreadyExistsf(format string, args ...interface{}) error
|
||||
```
|
||||
AlreadyExistsf returns an error which satisfies IsAlreadyExists().
|
||||
|
||||
|
||||
## func Annotate
|
||||
``` go
|
||||
func Annotate(other error, message string) error
|
||||
```
|
||||
Annotate is used to add extra context to an existing error. The location of
|
||||
the Annotate call is recorded with the annotations. The file, line and
|
||||
function are also recorded.
|
||||
|
||||
For example:
|
||||
|
||||
|
||||
if err := SomeFunc(); err != nil {
|
||||
return errors.Annotate(err, "failed to frombulate")
|
||||
}
|
||||
|
||||
|
||||
## func Annotatef
|
||||
``` go
|
||||
func Annotatef(other error, format string, args ...interface{}) error
|
||||
```
|
||||
Annotatef is used to add extra context to an existing error. The location of
|
||||
the Annotate call is recorded with the annotations. The file, line and
|
||||
function are also recorded.
|
||||
|
||||
For example:
|
||||
|
||||
|
||||
if err := SomeFunc(); err != nil {
|
||||
return errors.Annotatef(err, "failed to frombulate the %s", arg)
|
||||
}
|
||||
|
||||
|
||||
## func BadRequestf
|
||||
``` go
|
||||
func BadRequestf(format string, args ...interface{}) error
|
||||
```
|
||||
BadRequestf returns an error which satisfies IsBadRequest().
|
||||
|
||||
|
||||
## func Cause
|
||||
``` go
|
||||
func Cause(err error) error
|
||||
```
|
||||
Cause returns the cause of the given error. This will be either the
|
||||
original error, or the result of a Wrap or Mask call.
|
||||
|
||||
Cause is the usual way to diagnose errors that may have been wrapped by
|
||||
the other errors functions.
|
||||
|
||||
|
||||
## func DeferredAnnotatef
|
||||
``` go
|
||||
func DeferredAnnotatef(err *error, format string, args ...interface{})
|
||||
```
|
||||
DeferredAnnotatef annotates the given error (when it is not nil) with the given
|
||||
format string and arguments (like fmt.Sprintf). If *err is nil, DeferredAnnotatef
|
||||
does nothing. This method is used in a defer statement in order to annotate any
|
||||
resulting error with the same message.
|
||||
|
||||
For example:
|
||||
|
||||
|
||||
defer DeferredAnnotatef(&err, "failed to frombulate the %s", arg)
|
||||
|
||||
|
||||
## func Details
|
||||
``` go
|
||||
func Details(err error) string
|
||||
```
|
||||
Details returns information about the stack of errors wrapped by err, in
|
||||
the format:
|
||||
|
||||
|
||||
[{filename:99: error one} {otherfile:55: cause of error one}]
|
||||
|
||||
This is a terse alternative to ErrorStack as it returns a single line.
|
||||
|
||||
|
||||
## func ErrorStack
|
||||
``` go
|
||||
func ErrorStack(err error) string
|
||||
```
|
||||
ErrorStack returns a string representation of the annotated error. If the
|
||||
error passed as the parameter is not an annotated error, the result is
|
||||
simply the result of the Error() method on that error.
|
||||
|
||||
If the error is an annotated error, a multi-line string is returned where
|
||||
each line represents one entry in the annotation stack. The full filename
|
||||
from the call stack is used in the output.
|
||||
|
||||
|
||||
first error
|
||||
github.com/juju/errors/annotation_test.go:193:
|
||||
github.com/juju/errors/annotation_test.go:194: annotation
|
||||
github.com/juju/errors/annotation_test.go:195:
|
||||
github.com/juju/errors/annotation_test.go:196: more context
|
||||
github.com/juju/errors/annotation_test.go:197:
|
||||
|
||||
|
||||
## func Errorf
|
||||
``` go
|
||||
func Errorf(format string, args ...interface{}) error
|
||||
```
|
||||
Errorf creates a new annotated error and records the location that the
|
||||
error is created. This should be a drop in replacement for fmt.Errorf.
|
||||
|
||||
For example:
|
||||
|
||||
|
||||
return errors.Errorf("validation failed: %s", message)
|
||||
|
||||
|
||||
## func Forbiddenf
|
||||
``` go
|
||||
func Forbiddenf(format string, args ...interface{}) error
|
||||
```
|
||||
Forbiddenf returns an error which satistifes IsForbidden()
|
||||
|
||||
|
||||
## func IsAlreadyExists
|
||||
``` go
|
||||
func IsAlreadyExists(err error) bool
|
||||
```
|
||||
IsAlreadyExists reports whether the error was created with
|
||||
AlreadyExistsf() or NewAlreadyExists().
|
||||
|
||||
|
||||
## func IsBadRequest
|
||||
``` go
|
||||
func IsBadRequest(err error) bool
|
||||
```
|
||||
IsBadRequest reports whether err was created with BadRequestf() or
|
||||
NewBadRequest().
|
||||
|
||||
|
||||
## func IsForbidden
|
||||
``` go
|
||||
func IsForbidden(err error) bool
|
||||
```
|
||||
IsForbidden reports whether err was created with Forbiddenf() or
|
||||
NewForbidden().
|
||||
|
||||
|
||||
## func IsMethodNotAllowed
|
||||
``` go
|
||||
func IsMethodNotAllowed(err error) bool
|
||||
```
|
||||
IsMethodNotAllowed reports whether err was created with MethodNotAllowedf() or
|
||||
NewMethodNotAllowed().
|
||||
|
||||
|
||||
## func IsNotAssigned
|
||||
``` go
|
||||
func IsNotAssigned(err error) bool
|
||||
```
|
||||
IsNotAssigned reports whether err was created with NotAssignedf() or
|
||||
NewNotAssigned().
|
||||
|
||||
|
||||
## func IsNotFound
|
||||
``` go
|
||||
func IsNotFound(err error) bool
|
||||
```
|
||||
IsNotFound reports whether err was created with NotFoundf() or
|
||||
NewNotFound().
|
||||
|
||||
|
||||
## func IsNotImplemented
|
||||
``` go
|
||||
func IsNotImplemented(err error) bool
|
||||
```
|
||||
IsNotImplemented reports whether err was created with
|
||||
NotImplementedf() or NewNotImplemented().
|
||||
|
||||
|
||||
## func IsNotProvisioned
|
||||
``` go
|
||||
func IsNotProvisioned(err error) bool
|
||||
```
|
||||
IsNotProvisioned reports whether err was created with NotProvisionedf() or
|
||||
NewNotProvisioned().
|
||||
|
||||
|
||||
## func IsNotSupported
|
||||
``` go
|
||||
func IsNotSupported(err error) bool
|
||||
```
|
||||
IsNotSupported reports whether the error was created with
|
||||
NotSupportedf() or NewNotSupported().
|
||||
|
||||
|
||||
## func IsNotValid
|
||||
``` go
|
||||
func IsNotValid(err error) bool
|
||||
```
|
||||
IsNotValid reports whether the error was created with NotValidf() or
|
||||
NewNotValid().
|
||||
|
||||
|
||||
## func IsUnauthorized
|
||||
``` go
|
||||
func IsUnauthorized(err error) bool
|
||||
```
|
||||
IsUnauthorized reports whether err was created with Unauthorizedf() or
|
||||
NewUnauthorized().
|
||||
|
||||
|
||||
## func IsUserNotFound
|
||||
``` go
|
||||
func IsUserNotFound(err error) bool
|
||||
```
|
||||
IsUserNotFound reports whether err was created with UserNotFoundf() or
|
||||
NewUserNotFound().
|
||||
|
||||
|
||||
## func Mask
|
||||
``` go
|
||||
func Mask(other error) error
|
||||
```
|
||||
Mask hides the underlying error type, and records the location of the masking.
|
||||
|
||||
|
||||
## func Maskf
|
||||
``` go
|
||||
func Maskf(other error, format string, args ...interface{}) error
|
||||
```
|
||||
Mask masks the given error with the given format string and arguments (like
|
||||
fmt.Sprintf), returning a new error that maintains the error stack, but
|
||||
hides the underlying error type. The error string still contains the full
|
||||
annotations. If you want to hide the annotations, call Wrap.
|
||||
|
||||
|
||||
## func MethodNotAllowedf
|
||||
``` go
|
||||
func MethodNotAllowedf(format string, args ...interface{}) error
|
||||
```
|
||||
MethodNotAllowedf returns an error which satisfies IsMethodNotAllowed().
|
||||
|
||||
|
||||
## func New
|
||||
``` go
|
||||
func New(message string) error
|
||||
```
|
||||
New is a drop in replacement for the standard library errors module that records
|
||||
the location that the error is created.
|
||||
|
||||
For example:
|
||||
|
||||
|
||||
return errors.New("validation failed")
|
||||
|
||||
|
||||
## func NewAlreadyExists
|
||||
``` go
|
||||
func NewAlreadyExists(err error, msg string) error
|
||||
```
|
||||
NewAlreadyExists returns an error which wraps err and satisfies
|
||||
IsAlreadyExists().
|
||||
|
||||
|
||||
## func NewBadRequest
|
||||
``` go
|
||||
func NewBadRequest(err error, msg string) error
|
||||
```
|
||||
NewBadRequest returns an error which wraps err that satisfies
|
||||
IsBadRequest().
|
||||
|
||||
|
||||
## func NewForbidden
|
||||
``` go
|
||||
func NewForbidden(err error, msg string) error
|
||||
```
|
||||
NewForbidden returns an error which wraps err that satisfies
|
||||
IsForbidden().
|
||||
|
||||
|
||||
## func NewMethodNotAllowed
|
||||
``` go
|
||||
func NewMethodNotAllowed(err error, msg string) error
|
||||
```
|
||||
NewMethodNotAllowed returns an error which wraps err that satisfies
|
||||
IsMethodNotAllowed().
|
||||
|
||||
|
||||
## func NewNotAssigned
|
||||
``` go
|
||||
func NewNotAssigned(err error, msg string) error
|
||||
```
|
||||
NewNotAssigned returns an error which wraps err that satisfies
|
||||
IsNotAssigned().
|
||||
|
||||
|
||||
## func NewNotFound
|
||||
``` go
|
||||
func NewNotFound(err error, msg string) error
|
||||
```
|
||||
NewNotFound returns an error which wraps err that satisfies
|
||||
IsNotFound().
|
||||
|
||||
|
||||
## func NewNotImplemented
|
||||
``` go
|
||||
func NewNotImplemented(err error, msg string) error
|
||||
```
|
||||
NewNotImplemented returns an error which wraps err and satisfies
|
||||
IsNotImplemented().
|
||||
|
||||
|
||||
## func NewNotProvisioned
|
||||
``` go
|
||||
func NewNotProvisioned(err error, msg string) error
|
||||
```
|
||||
NewNotProvisioned returns an error which wraps err that satisfies
|
||||
IsNotProvisioned().
|
||||
|
||||
|
||||
## func NewNotSupported
|
||||
``` go
|
||||
func NewNotSupported(err error, msg string) error
|
||||
```
|
||||
NewNotSupported returns an error which wraps err and satisfies
|
||||
IsNotSupported().
|
||||
|
||||
|
||||
## func NewNotValid
|
||||
``` go
|
||||
func NewNotValid(err error, msg string) error
|
||||
```
|
||||
NewNotValid returns an error which wraps err and satisfies IsNotValid().
|
||||
|
||||
|
||||
## func NewUnauthorized
|
||||
``` go
|
||||
func NewUnauthorized(err error, msg string) error
|
||||
```
|
||||
NewUnauthorized returns an error which wraps err and satisfies
|
||||
IsUnauthorized().
|
||||
|
||||
|
||||
## func NewUserNotFound
|
||||
``` go
|
||||
func NewUserNotFound(err error, msg string) error
|
||||
```
|
||||
NewUserNotFound returns an error which wraps err and satisfies
|
||||
IsUserNotFound().
|
||||
|
||||
|
||||
## func NotAssignedf
|
||||
``` go
|
||||
func NotAssignedf(format string, args ...interface{}) error
|
||||
```
|
||||
NotAssignedf returns an error which satisfies IsNotAssigned().
|
||||
|
||||
|
||||
## func NotFoundf
|
||||
``` go
|
||||
func NotFoundf(format string, args ...interface{}) error
|
||||
```
|
||||
NotFoundf returns an error which satisfies IsNotFound().
|
||||
|
||||
|
||||
## func NotImplementedf
|
||||
``` go
|
||||
func NotImplementedf(format string, args ...interface{}) error
|
||||
```
|
||||
NotImplementedf returns an error which satisfies IsNotImplemented().
|
||||
|
||||
|
||||
## func NotProvisionedf
|
||||
``` go
|
||||
func NotProvisionedf(format string, args ...interface{}) error
|
||||
```
|
||||
NotProvisionedf returns an error which satisfies IsNotProvisioned().
|
||||
|
||||
|
||||
## func NotSupportedf
|
||||
``` go
|
||||
func NotSupportedf(format string, args ...interface{}) error
|
||||
```
|
||||
NotSupportedf returns an error which satisfies IsNotSupported().
|
||||
|
||||
|
||||
## func NotValidf
|
||||
``` go
|
||||
func NotValidf(format string, args ...interface{}) error
|
||||
```
|
||||
NotValidf returns an error which satisfies IsNotValid().
|
||||
|
||||
|
||||
## func Trace
|
||||
``` go
|
||||
func Trace(other error) error
|
||||
```
|
||||
Trace adds the location of the Trace call to the stack. The Cause of the
|
||||
resulting error is the same as the error parameter. If the other error is
|
||||
nil, the result will be nil.
|
||||
|
||||
For example:
|
||||
|
||||
|
||||
if err := SomeFunc(); err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
|
||||
## func Unauthorizedf
|
||||
``` go
|
||||
func Unauthorizedf(format string, args ...interface{}) error
|
||||
```
|
||||
Unauthorizedf returns an error which satisfies IsUnauthorized().
|
||||
|
||||
|
||||
## func UserNotFoundf
|
||||
``` go
|
||||
func UserNotFoundf(format string, args ...interface{}) error
|
||||
```
|
||||
UserNotFoundf returns an error which satisfies IsUserNotFound().
|
||||
|
||||
|
||||
## func Wrap
|
||||
``` go
|
||||
func Wrap(other, newDescriptive error) error
|
||||
```
|
||||
Wrap changes the Cause of the error. The location of the Wrap call is also
|
||||
stored in the error stack.
|
||||
|
||||
For example:
|
||||
|
||||
|
||||
if err := SomeFunc(); err != nil {
|
||||
newErr := &packageError{"more context", private_value}
|
||||
return errors.Wrap(err, newErr)
|
||||
}
|
||||
|
||||
|
||||
## func Wrapf
|
||||
``` go
|
||||
func Wrapf(other, newDescriptive error, format string, args ...interface{}) error
|
||||
```
|
||||
Wrapf changes the Cause of the error, and adds an annotation. The location
|
||||
of the Wrap call is also stored in the error stack.
|
||||
|
||||
For example:
|
||||
|
||||
|
||||
if err := SomeFunc(); err != nil {
|
||||
return errors.Wrapf(err, simpleErrorType, "invalid value %q", value)
|
||||
}
|
||||
|
||||
|
||||
|
||||
## type Err
|
||||
``` go
|
||||
type Err struct {
|
||||
// contains filtered or unexported fields
|
||||
}
|
||||
```
|
||||
Err holds a description of an error along with information about
|
||||
where the error was created.
|
||||
|
||||
It may be embedded in custom error types to add extra information that
|
||||
this errors package can understand.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
### func NewErr
|
||||
``` go
|
||||
func NewErr(format string, args ...interface{}) Err
|
||||
```
|
||||
NewErr is used to return an Err for the purpose of embedding in other
|
||||
structures. The location is not specified, and needs to be set with a call
|
||||
to SetLocation.
|
||||
|
||||
For example:
|
||||
|
||||
|
||||
type FooError struct {
|
||||
errors.Err
|
||||
code int
|
||||
}
|
||||
|
||||
func NewFooError(code int) error {
|
||||
err := &FooError{errors.NewErr("foo"), code}
|
||||
err.SetLocation(1)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
### func NewErrWithCause
|
||||
``` go
|
||||
func NewErrWithCause(other error, format string, args ...interface{}) Err
|
||||
```
|
||||
NewErrWithCause is used to return an Err with case by other error for the purpose of embedding in other
|
||||
structures. The location is not specified, and needs to be set with a call
|
||||
to SetLocation.
|
||||
|
||||
For example:
|
||||
|
||||
|
||||
type FooError struct {
|
||||
errors.Err
|
||||
code int
|
||||
}
|
||||
|
||||
func (e *FooError) Annotate(format string, args ...interface{}) error {
|
||||
err := &FooError{errors.NewErrWithCause(e.Err, format, args...), e.code}
|
||||
err.SetLocation(1)
|
||||
return err
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
### func (\*Err) Cause
|
||||
``` go
|
||||
func (e *Err) Cause() error
|
||||
```
|
||||
The Cause of an error is the most recent error in the error stack that
|
||||
meets one of these criteria: the original error that was raised; the new
|
||||
error that was passed into the Wrap function; the most recently masked
|
||||
error; or nil if the error itself is considered the Cause. Normally this
|
||||
method is not invoked directly, but instead through the Cause stand alone
|
||||
function.
|
||||
|
||||
|
||||
|
||||
### func (\*Err) Error
|
||||
``` go
|
||||
func (e *Err) Error() string
|
||||
```
|
||||
Error implements error.Error.
|
||||
|
||||
|
||||
|
||||
### func (\*Err) Format
|
||||
``` go
|
||||
func (e *Err) Format(s fmt.State, verb rune)
|
||||
```
|
||||
Format implements fmt.Formatter
|
||||
When printing errors with %+v it also prints the stack trace.
|
||||
%#v unsurprisingly will print the real underlying type.
|
||||
|
||||
|
||||
|
||||
### func (\*Err) Location
|
||||
``` go
|
||||
func (e *Err) Location() (filename string, line int)
|
||||
```
|
||||
Location is the file and line of where the error was most recently
|
||||
created or annotated.
|
||||
|
||||
|
||||
|
||||
### func (\*Err) Message
|
||||
``` go
|
||||
func (e *Err) Message() string
|
||||
```
|
||||
Message returns the message stored with the most recent location. This is
|
||||
the empty string if the most recent call was Trace, or the message stored
|
||||
with Annotate or Mask.
|
||||
|
||||
|
||||
|
||||
### func (\*Err) SetLocation
|
||||
``` go
|
||||
func (e *Err) SetLocation(callDepth int)
|
||||
```
|
||||
SetLocation records the source location of the error at callDepth stack
|
||||
frames above the call.
|
||||
|
||||
|
||||
|
||||
### func (\*Err) StackTrace
|
||||
``` go
|
||||
func (e *Err) StackTrace() []string
|
||||
```
|
||||
StackTrace returns one string for each location recorded in the stack of
|
||||
errors. The first value is the originating error, with a line for each
|
||||
other annotation or tracing of the error.
|
||||
|
||||
|
||||
|
||||
### func (\*Err) Underlying
|
||||
``` go
|
||||
func (e *Err) Underlying() error
|
||||
```
|
||||
Underlying returns the previous error in the error stack, if any. A client
|
||||
should not ever really call this method. It is used to build the error
|
||||
stack and should not be introspected by client calls. Or more
|
||||
specifically, clients should not depend on anything but the `Cause` of an
|
||||
error.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- - -
|
||||
Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md)
|
5
vendor/github.com/juju/errors/dependencies.tsv
generated
vendored
5
vendor/github.com/juju/errors/dependencies.tsv
generated
vendored
@ -1,5 +0,0 @@
|
||||
github.com/juju/loggo git 8232ab8918d91c72af1a9fb94d3edbe31d88b790 2017-06-05T01:46:07Z
|
||||
github.com/juju/testing git 72703b1e95eb8ce4737fd8a3d8496c6b0be280a6 2018-05-17T13:41:05Z
|
||||
gopkg.in/check.v1 git 4f90aeace3a26ad7021961c297b22c42160c7b25 2016-01-05T16:49:36Z
|
||||
gopkg.in/mgo.v2 git f2b6f6c918c452ad107eec89615f074e3bd80e33 2016-08-18T01:52:18Z
|
||||
gopkg.in/yaml.v2 git 1be3d31502d6eabc0dd7ce5b0daab022e14a5538 2017-07-12T05:45:46Z
|
|
81
vendor/github.com/juju/errors/doc.go
generated
vendored
81
vendor/github.com/juju/errors/doc.go
generated
vendored
@ -1,81 +0,0 @@
|
||||
// Copyright 2013, 2014 Canonical Ltd.
|
||||
// Licensed under the LGPLv3, see LICENCE file for details.
|
||||
|
||||
/*
|
||||
[godoc-link-here]
|
||||
|
||||
The juju/errors provides an easy way to annotate errors without losing the
|
||||
orginal error context.
|
||||
|
||||
The exported `New` and `Errorf` functions are designed to replace the
|
||||
`errors.New` and `fmt.Errorf` functions respectively. The same underlying
|
||||
error is there, but the package also records the location at which the error
|
||||
was created.
|
||||
|
||||
A primary use case for this library is to add extra context any time an
|
||||
error is returned from a function.
|
||||
|
||||
if err := SomeFunc(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
This instead becomes:
|
||||
|
||||
if err := SomeFunc(); err != nil {
|
||||
return errors.Trace(err)
|
||||
}
|
||||
|
||||
which just records the file and line number of the Trace call, or
|
||||
|
||||
if err := SomeFunc(); err != nil {
|
||||
return errors.Annotate(err, "more context")
|
||||
}
|
||||
|
||||
which also adds an annotation to the error.
|
||||
|
||||
When you want to check to see if an error is of a particular type, a helper
|
||||
function is normally exported by the package that returned the error, like the
|
||||
`os` package does. The underlying cause of the error is available using the
|
||||
`Cause` function.
|
||||
|
||||
os.IsNotExist(errors.Cause(err))
|
||||
|
||||
The result of the `Error()` call on an annotated error is the annotations joined
|
||||
with colons, then the result of the `Error()` method for the underlying error
|
||||
that was the cause.
|
||||
|
||||
err := errors.Errorf("original")
|
||||
err = errors.Annotatef(err, "context")
|
||||
err = errors.Annotatef(err, "more context")
|
||||
err.Error() -> "more context: context: original"
|
||||
|
||||
Obviously recording the file, line and functions is not very useful if you
|
||||
cannot get them back out again.
|
||||
|
||||
errors.ErrorStack(err)
|
||||
|
||||
will return something like:
|
||||
|
||||
first error
|
||||
github.com/juju/errors/annotation_test.go:193:
|
||||
github.com/juju/errors/annotation_test.go:194: annotation
|
||||
github.com/juju/errors/annotation_test.go:195:
|
||||
github.com/juju/errors/annotation_test.go:196: more context
|
||||
github.com/juju/errors/annotation_test.go:197:
|
||||
|
||||
The first error was generated by an external system, so there was no location
|
||||
associated. The second, fourth, and last lines were generated with Trace calls,
|
||||
and the other two through Annotate.
|
||||
|
||||
Sometimes when responding to an error you want to return a more specific error
|
||||
for the situation.
|
||||
|
||||
if err := FindField(field); err != nil {
|
||||
return errors.Wrap(err, errors.NotFoundf(field))
|
||||
}
|
||||
|
||||
This returns an error where the complete error stack is still available, and
|
||||
`errors.Cause()` will return the `NotFound` error.
|
||||
|
||||
*/
|
||||
package errors
|
172
vendor/github.com/juju/errors/error.go
generated
vendored
172
vendor/github.com/juju/errors/error.go
generated
vendored
@ -1,172 +0,0 @@
|
||||
// Copyright 2014 Canonical Ltd.
|
||||
// Licensed under the LGPLv3, see LICENCE file for details.
|
||||
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// Err holds a description of an error along with information about
|
||||
// where the error was created.
|
||||
//
|
||||
// It may be embedded in custom error types to add extra information that
|
||||
// this errors package can understand.
|
||||
type Err struct {
|
||||
// message holds an annotation of the error.
|
||||
message string
|
||||
|
||||
// cause holds the cause of the error as returned
|
||||
// by the Cause method.
|
||||
cause error
|
||||
|
||||
// previous holds the previous error in the error stack, if any.
|
||||
previous error
|
||||
|
||||
// file and line hold the source code location where the error was
|
||||
// created.
|
||||
file string
|
||||
line int
|
||||
}
|
||||
|
||||
// NewErr is used to return an Err for the purpose of embedding in other
|
||||
// structures. The location is not specified, and needs to be set with a call
|
||||
// to SetLocation.
|
||||
//
|
||||
// For example:
|
||||
// type FooError struct {
|
||||
// errors.Err
|
||||
// code int
|
||||
// }
|
||||
//
|
||||
// func NewFooError(code int) error {
|
||||
// err := &FooError{errors.NewErr("foo"), code}
|
||||
// err.SetLocation(1)
|
||||
// return err
|
||||
// }
|
||||
func NewErr(format string, args ...interface{}) Err {
|
||||
return Err{
|
||||
message: fmt.Sprintf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
// NewErrWithCause is used to return an Err with case by other error for the purpose of embedding in other
|
||||
// structures. The location is not specified, and needs to be set with a call
|
||||
// to SetLocation.
|
||||
//
|
||||
// For example:
|
||||
// type FooError struct {
|
||||
// errors.Err
|
||||
// code int
|
||||
// }
|
||||
//
|
||||
// func (e *FooError) Annotate(format string, args ...interface{}) error {
|
||||
// err := &FooError{errors.NewErrWithCause(e.Err, format, args...), e.code}
|
||||
// err.SetLocation(1)
|
||||
// return err
|
||||
// })
|
||||
func NewErrWithCause(other error, format string, args ...interface{}) Err {
|
||||
return Err{
|
||||
message: fmt.Sprintf(format, args...),
|
||||
cause: Cause(other),
|
||||
previous: other,
|
||||
}
|
||||
}
|
||||
|
||||
// Location is the file and line of where the error was most recently
|
||||
// created or annotated.
|
||||
func (e *Err) Location() (filename string, line int) {
|
||||
return e.file, e.line
|
||||
}
|
||||
|
||||
// Underlying returns the previous error in the error stack, if any. A client
|
||||
// should not ever really call this method. It is used to build the error
|
||||
// stack and should not be introspected by client calls. Or more
|
||||
// specifically, clients should not depend on anything but the `Cause` of an
|
||||
// error.
|
||||
func (e *Err) Underlying() error {
|
||||
return e.previous
|
||||
}
|
||||
|
||||
// The Cause of an error is the most recent error in the error stack that
|
||||
// meets one of these criteria: the original error that was raised; the new
|
||||
// error that was passed into the Wrap function; the most recently masked
|
||||
// error; or nil if the error itself is considered the Cause. Normally this
|
||||
// method is not invoked directly, but instead through the Cause stand alone
|
||||
// function.
|
||||
func (e *Err) Cause() error {
|
||||
return e.cause
|
||||
}
|
||||
|
||||
// Message returns the message stored with the most recent location. This is
|
||||
// the empty string if the most recent call was Trace, or the message stored
|
||||
// with Annotate or Mask.
|
||||
func (e *Err) Message() string {
|
||||
return e.message
|
||||
}
|
||||
|
||||
// Error implements error.Error.
|
||||
func (e *Err) Error() string {
|
||||
// We want to walk up the stack of errors showing the annotations
|
||||
// as long as the cause is the same.
|
||||
err := e.previous
|
||||
if !sameError(Cause(err), e.cause) && e.cause != nil {
|
||||
err = e.cause
|
||||
}
|
||||
switch {
|
||||
case err == nil:
|
||||
return e.message
|
||||
case e.message == "":
|
||||
return err.Error()
|
||||
}
|
||||
return fmt.Sprintf("%s: %v", e.message, err)
|
||||
}
|
||||
|
||||
// Format implements fmt.Formatter
|
||||
// When printing errors with %+v it also prints the stack trace.
|
||||
// %#v unsurprisingly will print the real underlying type.
|
||||
func (e *Err) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
fmt.Fprintf(s, "%s", ErrorStack(e))
|
||||
return
|
||||
case s.Flag('#'):
|
||||
// avoid infinite recursion by wrapping e into a type
|
||||
// that doesn't implement Formatter.
|
||||
fmt.Fprintf(s, "%#v", (*unformatter)(e))
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
fmt.Fprintf(s, "%s", e.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// helper for Format
|
||||
type unformatter Err
|
||||
|
||||
func (unformatter) Format() { /* break the fmt.Formatter interface */ }
|
||||
|
||||
// SetLocation records the source location of the error at callDepth stack
|
||||
// frames above the call.
|
||||
func (e *Err) SetLocation(callDepth int) {
|
||||
_, file, line, _ := runtime.Caller(callDepth + 1)
|
||||
e.file = trimGoPath(file)
|
||||
e.line = line
|
||||
}
|
||||
|
||||
// StackTrace returns one string for each location recorded in the stack of
|
||||
// errors. The first value is the originating error, with a line for each
|
||||
// other annotation or tracing of the error.
|
||||
func (e *Err) StackTrace() []string {
|
||||
return errorStack(e)
|
||||
}
|
||||
|
||||
// Ideally we'd have a way to check identity, but deep equals will do.
|
||||
func sameError(e1, e2 error) bool {
|
||||
return reflect.DeepEqual(e1, e2)
|
||||
}
|
333
vendor/github.com/juju/errors/errortypes.go
generated
vendored
333
vendor/github.com/juju/errors/errortypes.go
generated
vendored
@ -1,333 +0,0 @@
|
||||
// Copyright 2014 Canonical Ltd.
|
||||
// Licensed under the LGPLv3, see LICENCE file for details.
|
||||
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// wrap is a helper to construct an *wrapper.
|
||||
func wrap(err error, format, suffix string, args ...interface{}) Err {
|
||||
newErr := Err{
|
||||
message: fmt.Sprintf(format+suffix, args...),
|
||||
previous: err,
|
||||
}
|
||||
newErr.SetLocation(2)
|
||||
return newErr
|
||||
}
|
||||
|
||||
// timeout represents an error on timeout.
|
||||
type timeout struct {
|
||||
Err
|
||||
}
|
||||
|
||||
// Timeoutf returns an error which satisfies IsTimeout().
|
||||
func Timeoutf(format string, args ...interface{}) error {
|
||||
return &timeout{wrap(nil, format, " timeout", args...)}
|
||||
}
|
||||
|
||||
// NewTimeout returns an error which wraps err that satisfies
|
||||
// IsTimeout().
|
||||
func NewTimeout(err error, msg string) error {
|
||||
return &timeout{wrap(err, msg, "")}
|
||||
}
|
||||
|
||||
// IsTimeout reports whether err was created with Timeoutf() or
|
||||
// NewTimeout().
|
||||
func IsTimeout(err error) bool {
|
||||
err = Cause(err)
|
||||
_, ok := err.(*timeout)
|
||||
return ok
|
||||
}
|
||||
|
||||
// notFound represents an error when something has not been found.
|
||||
type notFound struct {
|
||||
Err
|
||||
}
|
||||
|
||||
// NotFoundf returns an error which satisfies IsNotFound().
|
||||
func NotFoundf(format string, args ...interface{}) error {
|
||||
return ¬Found{wrap(nil, format, " not found", args...)}
|
||||
}
|
||||
|
||||
// NewNotFound returns an error which wraps err that satisfies
|
||||
// IsNotFound().
|
||||
func NewNotFound(err error, msg string) error {
|
||||
return ¬Found{wrap(err, msg, "")}
|
||||
}
|
||||
|
||||
// IsNotFound reports whether err was created with NotFoundf() or
|
||||
// NewNotFound().
|
||||
func IsNotFound(err error) bool {
|
||||
err = Cause(err)
|
||||
_, ok := err.(*notFound)
|
||||
return ok
|
||||
}
|
||||
|
||||
// userNotFound represents an error when an inexistent user is looked up.
|
||||
type userNotFound struct {
|
||||
Err
|
||||
}
|
||||
|
||||
// UserNotFoundf returns an error which satisfies IsUserNotFound().
|
||||
func UserNotFoundf(format string, args ...interface{}) error {
|
||||
return &userNotFound{wrap(nil, format, " user not found", args...)}
|
||||
}
|
||||
|
||||
// NewUserNotFound returns an error which wraps err and satisfies
|
||||
// IsUserNotFound().
|
||||
func NewUserNotFound(err error, msg string) error {
|
||||
return &userNotFound{wrap(err, msg, "")}
|
||||
}
|
||||
|
||||
// IsUserNotFound reports whether err was created with UserNotFoundf() or
|
||||
// NewUserNotFound().
|
||||
func IsUserNotFound(err error) bool {
|
||||
err = Cause(err)
|
||||
_, ok := err.(*userNotFound)
|
||||
return ok
|
||||
}
|
||||
|
||||
// unauthorized represents an error when an operation is unauthorized.
|
||||
type unauthorized struct {
|
||||
Err
|
||||
}
|
||||
|
||||
// Unauthorizedf returns an error which satisfies IsUnauthorized().
|
||||
func Unauthorizedf(format string, args ...interface{}) error {
|
||||
return &unauthorized{wrap(nil, format, "", args...)}
|
||||
}
|
||||
|
||||
// NewUnauthorized returns an error which wraps err and satisfies
|
||||
// IsUnauthorized().
|
||||
func NewUnauthorized(err error, msg string) error {
|
||||
return &unauthorized{wrap(err, msg, "")}
|
||||
}
|
||||
|
||||
// IsUnauthorized reports whether err was created with Unauthorizedf() or
|
||||
// NewUnauthorized().
|
||||
func IsUnauthorized(err error) bool {
|
||||
err = Cause(err)
|
||||
_, ok := err.(*unauthorized)
|
||||
return ok
|
||||
}
|
||||
|
||||
// notImplemented represents an error when something is not
|
||||
// implemented.
|
||||
type notImplemented struct {
|
||||
Err
|
||||
}
|
||||
|
||||
// NotImplementedf returns an error which satisfies IsNotImplemented().
|
||||
func NotImplementedf(format string, args ...interface{}) error {
|
||||
return ¬Implemented{wrap(nil, format, " not implemented", args...)}
|
||||
}
|
||||
|
||||
// NewNotImplemented returns an error which wraps err and satisfies
|
||||
// IsNotImplemented().
|
||||
func NewNotImplemented(err error, msg string) error {
|
||||
return ¬Implemented{wrap(err, msg, "")}
|
||||
}
|
||||
|
||||
// IsNotImplemented reports whether err was created with
|
||||
// NotImplementedf() or NewNotImplemented().
|
||||
func IsNotImplemented(err error) bool {
|
||||
err = Cause(err)
|
||||
_, ok := err.(*notImplemented)
|
||||
return ok
|
||||
}
|
||||
|
||||
// alreadyExists represents and error when something already exists.
|
||||
type alreadyExists struct {
|
||||
Err
|
||||
}
|
||||
|
||||
// AlreadyExistsf returns an error which satisfies IsAlreadyExists().
|
||||
func AlreadyExistsf(format string, args ...interface{}) error {
|
||||
return &alreadyExists{wrap(nil, format, " already exists", args...)}
|
||||
}
|
||||
|
||||
// NewAlreadyExists returns an error which wraps err and satisfies
|
||||
// IsAlreadyExists().
|
||||
func NewAlreadyExists(err error, msg string) error {
|
||||
return &alreadyExists{wrap(err, msg, "")}
|
||||
}
|
||||
|
||||
// IsAlreadyExists reports whether the error was created with
|
||||
// AlreadyExistsf() or NewAlreadyExists().
|
||||
func IsAlreadyExists(err error) bool {
|
||||
err = Cause(err)
|
||||
_, ok := err.(*alreadyExists)
|
||||
return ok
|
||||
}
|
||||
|
||||
// notSupported represents an error when something is not supported.
|
||||
type notSupported struct {
|
||||
Err
|
||||
}
|
||||
|
||||
// NotSupportedf returns an error which satisfies IsNotSupported().
|
||||
func NotSupportedf(format string, args ...interface{}) error {
|
||||
return ¬Supported{wrap(nil, format, " not supported", args...)}
|
||||
}
|
||||
|
||||
// NewNotSupported returns an error which wraps err and satisfies
|
||||
// IsNotSupported().
|
||||
func NewNotSupported(err error, msg string) error {
|
||||
return ¬Supported{wrap(err, msg, "")}
|
||||
}
|
||||
|
||||
// IsNotSupported reports whether the error was created with
|
||||
// NotSupportedf() or NewNotSupported().
|
||||
func IsNotSupported(err error) bool {
|
||||
err = Cause(err)
|
||||
_, ok := err.(*notSupported)
|
||||
return ok
|
||||
}
|
||||
|
||||
// notValid represents an error when something is not valid.
|
||||
type notValid struct {
|
||||
Err
|
||||
}
|
||||
|
||||
// NotValidf returns an error which satisfies IsNotValid().
|
||||
func NotValidf(format string, args ...interface{}) error {
|
||||
return ¬Valid{wrap(nil, format, " not valid", args...)}
|
||||
}
|
||||
|
||||
// NewNotValid returns an error which wraps err and satisfies IsNotValid().
|
||||
func NewNotValid(err error, msg string) error {
|
||||
return ¬Valid{wrap(err, msg, "")}
|
||||
}
|
||||
|
||||
// IsNotValid reports whether the error was created with NotValidf() or
|
||||
// NewNotValid().
|
||||
func IsNotValid(err error) bool {
|
||||
err = Cause(err)
|
||||
_, ok := err.(*notValid)
|
||||
return ok
|
||||
}
|
||||
|
||||
// notProvisioned represents an error when something is not yet provisioned.
|
||||
type notProvisioned struct {
|
||||
Err
|
||||
}
|
||||
|
||||
// NotProvisionedf returns an error which satisfies IsNotProvisioned().
|
||||
func NotProvisionedf(format string, args ...interface{}) error {
|
||||
return ¬Provisioned{wrap(nil, format, " not provisioned", args...)}
|
||||
}
|
||||
|
||||
// NewNotProvisioned returns an error which wraps err that satisfies
|
||||
// IsNotProvisioned().
|
||||
func NewNotProvisioned(err error, msg string) error {
|
||||
return ¬Provisioned{wrap(err, msg, "")}
|
||||
}
|
||||
|
||||
// IsNotProvisioned reports whether err was created with NotProvisionedf() or
|
||||
// NewNotProvisioned().
|
||||
func IsNotProvisioned(err error) bool {
|
||||
err = Cause(err)
|
||||
_, ok := err.(*notProvisioned)
|
||||
return ok
|
||||
}
|
||||
|
||||
// notAssigned represents an error when something is not yet assigned to
|
||||
// something else.
|
||||
type notAssigned struct {
|
||||
Err
|
||||
}
|
||||
|
||||
// NotAssignedf returns an error which satisfies IsNotAssigned().
|
||||
func NotAssignedf(format string, args ...interface{}) error {
|
||||
return ¬Assigned{wrap(nil, format, " not assigned", args...)}
|
||||
}
|
||||
|
||||
// NewNotAssigned returns an error which wraps err that satisfies
|
||||
// IsNotAssigned().
|
||||
func NewNotAssigned(err error, msg string) error {
|
||||
return ¬Assigned{wrap(err, msg, "")}
|
||||
}
|
||||
|
||||
// IsNotAssigned reports whether err was created with NotAssignedf() or
|
||||
// NewNotAssigned().
|
||||
func IsNotAssigned(err error) bool {
|
||||
err = Cause(err)
|
||||
_, ok := err.(*notAssigned)
|
||||
return ok
|
||||
}
|
||||
|
||||
// badRequest represents an error when a request has bad parameters.
|
||||
type badRequest struct {
|
||||
Err
|
||||
}
|
||||
|
||||
// BadRequestf returns an error which satisfies IsBadRequest().
|
||||
func BadRequestf(format string, args ...interface{}) error {
|
||||
return &badRequest{wrap(nil, format, "", args...)}
|
||||
}
|
||||
|
||||
// NewBadRequest returns an error which wraps err that satisfies
|
||||
// IsBadRequest().
|
||||
func NewBadRequest(err error, msg string) error {
|
||||
return &badRequest{wrap(err, msg, "")}
|
||||
}
|
||||
|
||||
// IsBadRequest reports whether err was created with BadRequestf() or
|
||||
// NewBadRequest().
|
||||
func IsBadRequest(err error) bool {
|
||||
err = Cause(err)
|
||||
_, ok := err.(*badRequest)
|
||||
return ok
|
||||
}
|
||||
|
||||
// methodNotAllowed represents an error when an HTTP request
|
||||
// is made with an inappropriate method.
|
||||
type methodNotAllowed struct {
|
||||
Err
|
||||
}
|
||||
|
||||
// MethodNotAllowedf returns an error which satisfies IsMethodNotAllowed().
|
||||
func MethodNotAllowedf(format string, args ...interface{}) error {
|
||||
return &methodNotAllowed{wrap(nil, format, "", args...)}
|
||||
}
|
||||
|
||||
// NewMethodNotAllowed returns an error which wraps err that satisfies
|
||||
// IsMethodNotAllowed().
|
||||
func NewMethodNotAllowed(err error, msg string) error {
|
||||
return &methodNotAllowed{wrap(err, msg, "")}
|
||||
}
|
||||
|
||||
// IsMethodNotAllowed reports whether err was created with MethodNotAllowedf() or
|
||||
// NewMethodNotAllowed().
|
||||
func IsMethodNotAllowed(err error) bool {
|
||||
err = Cause(err)
|
||||
_, ok := err.(*methodNotAllowed)
|
||||
return ok
|
||||
}
|
||||
|
||||
// forbidden represents an error when a request cannot be completed because of
|
||||
// missing privileges
|
||||
type forbidden struct {
|
||||
Err
|
||||
}
|
||||
|
||||
// Forbiddenf returns an error which satistifes IsForbidden()
|
||||
func Forbiddenf(format string, args ...interface{}) error {
|
||||
return &forbidden{wrap(nil, format, "", args...)}
|
||||
}
|
||||
|
||||
// NewForbidden returns an error which wraps err that satisfies
|
||||
// IsForbidden().
|
||||
func NewForbidden(err error, msg string) error {
|
||||
return &forbidden{wrap(err, msg, "")}
|
||||
}
|
||||
|
||||
// IsForbidden reports whether err was created with Forbiddenf() or
|
||||
// NewForbidden().
|
||||
func IsForbidden(err error) bool {
|
||||
err = Cause(err)
|
||||
_, ok := err.(*forbidden)
|
||||
return ok
|
||||
}
|
330
vendor/github.com/juju/errors/functions.go
generated
vendored
330
vendor/github.com/juju/errors/functions.go
generated
vendored
@ -1,330 +0,0 @@
|
||||
// Copyright 2014 Canonical Ltd.
|
||||
// Licensed under the LGPLv3, see LICENCE file for details.
|
||||
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// New is a drop in replacement for the standard library errors module that records
|
||||
// the location that the error is created.
|
||||
//
|
||||
// For example:
|
||||
// return errors.New("validation failed")
|
||||
//
|
||||
func New(message string) error {
|
||||
err := &Err{message: message}
|
||||
err.SetLocation(1)
|
||||
return err
|
||||
}
|
||||
|
||||
// Errorf creates a new annotated error and records the location that the
|
||||
// error is created. This should be a drop in replacement for fmt.Errorf.
|
||||
//
|
||||
// For example:
|
||||
// return errors.Errorf("validation failed: %s", message)
|
||||
//
|
||||
func Errorf(format string, args ...interface{}) error {
|
||||
err := &Err{message: fmt.Sprintf(format, args...)}
|
||||
err.SetLocation(1)
|
||||
return err
|
||||
}
|
||||
|
||||
// Trace adds the location of the Trace call to the stack. The Cause of the
|
||||
// resulting error is the same as the error parameter. If the other error is
|
||||
// nil, the result will be nil.
|
||||
//
|
||||
// For example:
|
||||
// if err := SomeFunc(); err != nil {
|
||||
// return errors.Trace(err)
|
||||
// }
|
||||
//
|
||||
func Trace(other error) error {
|
||||
if other == nil {
|
||||
return nil
|
||||
}
|
||||
err := &Err{previous: other, cause: Cause(other)}
|
||||
err.SetLocation(1)
|
||||
return err
|
||||
}
|
||||
|
||||
// Annotate is used to add extra context to an existing error. The location of
|
||||
// the Annotate call is recorded with the annotations. The file, line and
|
||||
// function are also recorded.
|
||||
//
|
||||
// For example:
|
||||
// if err := SomeFunc(); err != nil {
|
||||
// return errors.Annotate(err, "failed to frombulate")
|
||||
// }
|
||||
//
|
||||
func Annotate(other error, message string) error {
|
||||
if other == nil {
|
||||
return nil
|
||||
}
|
||||
err := &Err{
|
||||
previous: other,
|
||||
cause: Cause(other),
|
||||
message: message,
|
||||
}
|
||||
err.SetLocation(1)
|
||||
return err
|
||||
}
|
||||
|
||||
// Annotatef is used to add extra context to an existing error. The location of
|
||||
// the Annotate call is recorded with the annotations. The file, line and
|
||||
// function are also recorded.
|
||||
//
|
||||
// For example:
|
||||
// if err := SomeFunc(); err != nil {
|
||||
// return errors.Annotatef(err, "failed to frombulate the %s", arg)
|
||||
// }
|
||||
//
|
||||
func Annotatef(other error, format string, args ...interface{}) error {
|
||||
if other == nil {
|
||||
return nil
|
||||
}
|
||||
err := &Err{
|
||||
previous: other,
|
||||
cause: Cause(other),
|
||||
message: fmt.Sprintf(format, args...),
|
||||
}
|
||||
err.SetLocation(1)
|
||||
return err
|
||||
}
|
||||
|
||||
// DeferredAnnotatef annotates the given error (when it is not nil) with the given
|
||||
// format string and arguments (like fmt.Sprintf). If *err is nil, DeferredAnnotatef
|
||||
// does nothing. This method is used in a defer statement in order to annotate any
|
||||
// resulting error with the same message.
|
||||
//
|
||||
// For example:
|
||||
//
|
||||
// defer DeferredAnnotatef(&err, "failed to frombulate the %s", arg)
|
||||
//
|
||||
func DeferredAnnotatef(err *error, format string, args ...interface{}) {
|
||||
if *err == nil {
|
||||
return
|
||||
}
|
||||
newErr := &Err{
|
||||
message: fmt.Sprintf(format, args...),
|
||||
cause: Cause(*err),
|
||||
previous: *err,
|
||||
}
|
||||
newErr.SetLocation(1)
|
||||
*err = newErr
|
||||
}
|
||||
|
||||
// Wrap changes the Cause of the error. The location of the Wrap call is also
|
||||
// stored in the error stack.
|
||||
//
|
||||
// For example:
|
||||
// if err := SomeFunc(); err != nil {
|
||||
// newErr := &packageError{"more context", private_value}
|
||||
// return errors.Wrap(err, newErr)
|
||||
// }
|
||||
//
|
||||
func Wrap(other, newDescriptive error) error {
|
||||
err := &Err{
|
||||
previous: other,
|
||||
cause: newDescriptive,
|
||||
}
|
||||
err.SetLocation(1)
|
||||
return err
|
||||
}
|
||||
|
||||
// Wrapf changes the Cause of the error, and adds an annotation. The location
|
||||
// of the Wrap call is also stored in the error stack.
|
||||
//
|
||||
// For example:
|
||||
// if err := SomeFunc(); err != nil {
|
||||
// return errors.Wrapf(err, simpleErrorType, "invalid value %q", value)
|
||||
// }
|
||||
//
|
||||
func Wrapf(other, newDescriptive error, format string, args ...interface{}) error {
|
||||
err := &Err{
|
||||
message: fmt.Sprintf(format, args...),
|
||||
previous: other,
|
||||
cause: newDescriptive,
|
||||
}
|
||||
err.SetLocation(1)
|
||||
return err
|
||||
}
|
||||
|
||||
// Mask masks the given error with the given format string and arguments (like
|
||||
// fmt.Sprintf), returning a new error that maintains the error stack, but
|
||||
// hides the underlying error type. The error string still contains the full
|
||||
// annotations. If you want to hide the annotations, call Wrap.
|
||||
func Maskf(other error, format string, args ...interface{}) error {
|
||||
if other == nil {
|
||||
return nil
|
||||
}
|
||||
err := &Err{
|
||||
message: fmt.Sprintf(format, args...),
|
||||
previous: other,
|
||||
}
|
||||
err.SetLocation(1)
|
||||
return err
|
||||
}
|
||||
|
||||
// Mask hides the underlying error type, and records the location of the masking.
|
||||
func Mask(other error) error {
|
||||
if other == nil {
|
||||
return nil
|
||||
}
|
||||
err := &Err{
|
||||
previous: other,
|
||||
}
|
||||
err.SetLocation(1)
|
||||
return err
|
||||
}
|
||||
|
||||
// Cause returns the cause of the given error. This will be either the
|
||||
// original error, or the result of a Wrap or Mask call.
|
||||
//
|
||||
// Cause is the usual way to diagnose errors that may have been wrapped by
|
||||
// the other errors functions.
|
||||
func Cause(err error) error {
|
||||
var diag error
|
||||
if err, ok := err.(causer); ok {
|
||||
diag = err.Cause()
|
||||
}
|
||||
if diag != nil {
|
||||
return diag
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type causer interface {
|
||||
Cause() error
|
||||
}
|
||||
|
||||
type wrapper interface {
|
||||
// Message returns the top level error message,
|
||||
// not including the message from the Previous
|
||||
// error.
|
||||
Message() string
|
||||
|
||||
// Underlying returns the Previous error, or nil
|
||||
// if there is none.
|
||||
Underlying() error
|
||||
}
|
||||
|
||||
type locationer interface {
|
||||
Location() (string, int)
|
||||
}
|
||||
|
||||
var (
|
||||
_ wrapper = (*Err)(nil)
|
||||
_ locationer = (*Err)(nil)
|
||||
_ causer = (*Err)(nil)
|
||||
)
|
||||
|
||||
// Details returns information about the stack of errors wrapped by err, in
|
||||
// the format:
|
||||
//
|
||||
// [{filename:99: error one} {otherfile:55: cause of error one}]
|
||||
//
|
||||
// This is a terse alternative to ErrorStack as it returns a single line.
|
||||
func Details(err error) string {
|
||||
if err == nil {
|
||||
return "[]"
|
||||
}
|
||||
var s []byte
|
||||
s = append(s, '[')
|
||||
for {
|
||||
s = append(s, '{')
|
||||
if err, ok := err.(locationer); ok {
|
||||
file, line := err.Location()
|
||||
if file != "" {
|
||||
s = append(s, fmt.Sprintf("%s:%d", file, line)...)
|
||||
s = append(s, ": "...)
|
||||
}
|
||||
}
|
||||
if cerr, ok := err.(wrapper); ok {
|
||||
s = append(s, cerr.Message()...)
|
||||
err = cerr.Underlying()
|
||||
} else {
|
||||
s = append(s, err.Error()...)
|
||||
err = nil
|
||||
}
|
||||
s = append(s, '}')
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
s = append(s, ' ')
|
||||
}
|
||||
s = append(s, ']')
|
||||
return string(s)
|
||||
}
|
||||
|
||||
// ErrorStack returns a string representation of the annotated error. If the
|
||||
// error passed as the parameter is not an annotated error, the result is
|
||||
// simply the result of the Error() method on that error.
|
||||
//
|
||||
// If the error is an annotated error, a multi-line string is returned where
|
||||
// each line represents one entry in the annotation stack. The full filename
|
||||
// from the call stack is used in the output.
|
||||
//
|
||||
// first error
|
||||
// github.com/juju/errors/annotation_test.go:193:
|
||||
// github.com/juju/errors/annotation_test.go:194: annotation
|
||||
// github.com/juju/errors/annotation_test.go:195:
|
||||
// github.com/juju/errors/annotation_test.go:196: more context
|
||||
// github.com/juju/errors/annotation_test.go:197:
|
||||
func ErrorStack(err error) string {
|
||||
return strings.Join(errorStack(err), "\n")
|
||||
}
|
||||
|
||||
func errorStack(err error) []string {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// We want the first error first
|
||||
var lines []string
|
||||
for {
|
||||
var buff []byte
|
||||
if err, ok := err.(locationer); ok {
|
||||
file, line := err.Location()
|
||||
// Strip off the leading GOPATH/src path elements.
|
||||
file = trimGoPath(file)
|
||||
if file != "" {
|
||||
buff = append(buff, fmt.Sprintf("%s:%d", file, line)...)
|
||||
buff = append(buff, ": "...)
|
||||
}
|
||||
}
|
||||
if cerr, ok := err.(wrapper); ok {
|
||||
message := cerr.Message()
|
||||
buff = append(buff, message...)
|
||||
// If there is a cause for this error, and it is different to the cause
|
||||
// of the underlying error, then output the error string in the stack trace.
|
||||
var cause error
|
||||
if err1, ok := err.(causer); ok {
|
||||
cause = err1.Cause()
|
||||
}
|
||||
err = cerr.Underlying()
|
||||
if cause != nil && !sameError(Cause(err), cause) {
|
||||
if message != "" {
|
||||
buff = append(buff, ": "...)
|
||||
}
|
||||
buff = append(buff, cause.Error()...)
|
||||
}
|
||||
} else {
|
||||
buff = append(buff, err.Error()...)
|
||||
err = nil
|
||||
}
|
||||
lines = append(lines, string(buff))
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
// reverse the lines to get the original error, which was at the end of
|
||||
// the list, back to the start.
|
||||
var result []string
|
||||
for i := len(lines); i > 0; i-- {
|
||||
result = append(result, lines[i-1])
|
||||
}
|
||||
return result
|
||||
}
|
19
vendor/github.com/juju/errors/path.go
generated
vendored
19
vendor/github.com/juju/errors/path.go
generated
vendored
@ -1,19 +0,0 @@
|
||||
// Copyright 2013, 2014 Canonical Ltd.
|
||||
// Licensed under the LGPLv3, see LICENCE file for details.
|
||||
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/build"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var goPath = build.Default.GOPATH
|
||||
var srcDir = filepath.Join(goPath, "src")
|
||||
|
||||
func trimGoPath(filename string) string {
|
||||
return strings.TrimPrefix(filename, fmt.Sprintf("%s%s", srcDir, string(os.PathSeparator)))
|
||||
}
|
383
vendor/golang.org/x/net/internal/iana/gen.go
generated
vendored
383
vendor/golang.org/x/net/internal/iana/gen.go
generated
vendored
@ -1,383 +0,0 @@
|
||||
// Copyright 2013 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
//go:generate go run gen.go
|
||||
|
||||
// This program generates internet protocol constants and tables by
|
||||
// reading IANA protocol registries.
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var registries = []struct {
|
||||
url string
|
||||
parse func(io.Writer, io.Reader) error
|
||||
}{
|
||||
{
|
||||
"https://www.iana.org/assignments/dscp-registry/dscp-registry.xml",
|
||||
parseDSCPRegistry,
|
||||
},
|
||||
{
|
||||
"https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xml",
|
||||
parseProtocolNumbers,
|
||||
},
|
||||
{
|
||||
"https://www.iana.org/assignments/address-family-numbers/address-family-numbers.xml",
|
||||
parseAddrFamilyNumbers,
|
||||
},
|
||||
}
|
||||
|
||||
func main() {
|
||||
var bb bytes.Buffer
|
||||
fmt.Fprintf(&bb, "// go generate gen.go\n")
|
||||
fmt.Fprintf(&bb, "// Code generated by the command above; DO NOT EDIT.\n\n")
|
||||
fmt.Fprintf(&bb, "// Package iana provides protocol number resources managed by the Internet Assigned Numbers Authority (IANA).\n")
|
||||
fmt.Fprintf(&bb, `package iana // import "golang.org/x/net/internal/iana"`+"\n\n")
|
||||
for _, r := range registries {
|
||||
resp, err := http.Get(r.url)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
fmt.Fprintf(os.Stderr, "got HTTP status code %v for %v\n", resp.StatusCode, r.url)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := r.parse(&bb, resp.Body); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Fprintf(&bb, "\n")
|
||||
}
|
||||
b, err := format.Source(bb.Bytes())
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := ioutil.WriteFile("const.go", b, 0644); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func parseDSCPRegistry(w io.Writer, r io.Reader) error {
|
||||
dec := xml.NewDecoder(r)
|
||||
var dr dscpRegistry
|
||||
if err := dec.Decode(&dr); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Fprintf(w, "// %s, Updated: %s\n", dr.Title, dr.Updated)
|
||||
fmt.Fprintf(w, "const (\n")
|
||||
for _, dr := range dr.escapeDSCP() {
|
||||
fmt.Fprintf(w, "DiffServ%s = %#02x", dr.Name, dr.Value)
|
||||
fmt.Fprintf(w, "// %s\n", dr.OrigName)
|
||||
}
|
||||
for _, er := range dr.escapeECN() {
|
||||
fmt.Fprintf(w, "%s = %#02x", er.Descr, er.Value)
|
||||
fmt.Fprintf(w, "// %s\n", er.OrigDescr)
|
||||
}
|
||||
fmt.Fprintf(w, ")\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
type dscpRegistry struct {
|
||||
XMLName xml.Name `xml:"registry"`
|
||||
Title string `xml:"title"`
|
||||
Updated string `xml:"updated"`
|
||||
Note string `xml:"note"`
|
||||
Registries []struct {
|
||||
Title string `xml:"title"`
|
||||
Registries []struct {
|
||||
Title string `xml:"title"`
|
||||
Records []struct {
|
||||
Name string `xml:"name"`
|
||||
Space string `xml:"space"`
|
||||
} `xml:"record"`
|
||||
} `xml:"registry"`
|
||||
Records []struct {
|
||||
Value string `xml:"value"`
|
||||
Descr string `xml:"description"`
|
||||
} `xml:"record"`
|
||||
} `xml:"registry"`
|
||||
}
|
||||
|
||||
type canonDSCPRecord struct {
|
||||
OrigName string
|
||||
Name string
|
||||
Value int
|
||||
}
|
||||
|
||||
func (drr *dscpRegistry) escapeDSCP() []canonDSCPRecord {
|
||||
var drs []canonDSCPRecord
|
||||
for _, preg := range drr.Registries {
|
||||
if !strings.Contains(preg.Title, "Differentiated Services Field Codepoints") {
|
||||
continue
|
||||
}
|
||||
for _, reg := range preg.Registries {
|
||||
if !strings.Contains(reg.Title, "Pool 1 Codepoints") {
|
||||
continue
|
||||
}
|
||||
drs = make([]canonDSCPRecord, len(reg.Records))
|
||||
sr := strings.NewReplacer(
|
||||
"+", "",
|
||||
"-", "",
|
||||
"/", "",
|
||||
".", "",
|
||||
" ", "",
|
||||
)
|
||||
for i, dr := range reg.Records {
|
||||
s := strings.TrimSpace(dr.Name)
|
||||
drs[i].OrigName = s
|
||||
drs[i].Name = sr.Replace(s)
|
||||
n, err := strconv.ParseUint(dr.Space, 2, 8)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
drs[i].Value = int(n) << 2
|
||||
}
|
||||
}
|
||||
}
|
||||
return drs
|
||||
}
|
||||
|
||||
type canonECNRecord struct {
|
||||
OrigDescr string
|
||||
Descr string
|
||||
Value int
|
||||
}
|
||||
|
||||
func (drr *dscpRegistry) escapeECN() []canonECNRecord {
|
||||
var ers []canonECNRecord
|
||||
for _, reg := range drr.Registries {
|
||||
if !strings.Contains(reg.Title, "ECN Field") {
|
||||
continue
|
||||
}
|
||||
ers = make([]canonECNRecord, len(reg.Records))
|
||||
sr := strings.NewReplacer(
|
||||
"Capable", "",
|
||||
"Not-ECT", "",
|
||||
"ECT(1)", "",
|
||||
"ECT(0)", "",
|
||||
"CE", "",
|
||||
"(", "",
|
||||
")", "",
|
||||
"+", "",
|
||||
"-", "",
|
||||
"/", "",
|
||||
".", "",
|
||||
" ", "",
|
||||
)
|
||||
for i, er := range reg.Records {
|
||||
s := strings.TrimSpace(er.Descr)
|
||||
ers[i].OrigDescr = s
|
||||
ss := strings.Split(s, " ")
|
||||
if len(ss) > 1 {
|
||||
ers[i].Descr = strings.Join(ss[1:], " ")
|
||||
} else {
|
||||
ers[i].Descr = ss[0]
|
||||
}
|
||||
ers[i].Descr = sr.Replace(er.Descr)
|
||||
n, err := strconv.ParseUint(er.Value, 2, 8)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ers[i].Value = int(n)
|
||||
}
|
||||
}
|
||||
return ers
|
||||
}
|
||||
|
||||
func parseProtocolNumbers(w io.Writer, r io.Reader) error {
|
||||
dec := xml.NewDecoder(r)
|
||||
var pn protocolNumbers
|
||||
if err := dec.Decode(&pn); err != nil {
|
||||
return err
|
||||
}
|
||||
prs := pn.escape()
|
||||
prs = append([]canonProtocolRecord{{
|
||||
Name: "IP",
|
||||
Descr: "IPv4 encapsulation, pseudo protocol number",
|
||||
Value: 0,
|
||||
}}, prs...)
|
||||
fmt.Fprintf(w, "// %s, Updated: %s\n", pn.Title, pn.Updated)
|
||||
fmt.Fprintf(w, "const (\n")
|
||||
for _, pr := range prs {
|
||||
if pr.Name == "" {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(w, "Protocol%s = %d", pr.Name, pr.Value)
|
||||
s := pr.Descr
|
||||
if s == "" {
|
||||
s = pr.OrigName
|
||||
}
|
||||
fmt.Fprintf(w, "// %s\n", s)
|
||||
}
|
||||
fmt.Fprintf(w, ")\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
type protocolNumbers struct {
|
||||
XMLName xml.Name `xml:"registry"`
|
||||
Title string `xml:"title"`
|
||||
Updated string `xml:"updated"`
|
||||
RegTitle string `xml:"registry>title"`
|
||||
Note string `xml:"registry>note"`
|
||||
Records []struct {
|
||||
Value string `xml:"value"`
|
||||
Name string `xml:"name"`
|
||||
Descr string `xml:"description"`
|
||||
} `xml:"registry>record"`
|
||||
}
|
||||
|
||||
type canonProtocolRecord struct {
|
||||
OrigName string
|
||||
Name string
|
||||
Descr string
|
||||
Value int
|
||||
}
|
||||
|
||||
func (pn *protocolNumbers) escape() []canonProtocolRecord {
|
||||
prs := make([]canonProtocolRecord, len(pn.Records))
|
||||
sr := strings.NewReplacer(
|
||||
"-in-", "in",
|
||||
"-within-", "within",
|
||||
"-over-", "over",
|
||||
"+", "P",
|
||||
"-", "",
|
||||
"/", "",
|
||||
".", "",
|
||||
" ", "",
|
||||
)
|
||||
for i, pr := range pn.Records {
|
||||
if strings.Contains(pr.Name, "Deprecated") ||
|
||||
strings.Contains(pr.Name, "deprecated") {
|
||||
continue
|
||||
}
|
||||
prs[i].OrigName = pr.Name
|
||||
s := strings.TrimSpace(pr.Name)
|
||||
switch pr.Name {
|
||||
case "ISIS over IPv4":
|
||||
prs[i].Name = "ISIS"
|
||||
case "manet":
|
||||
prs[i].Name = "MANET"
|
||||
default:
|
||||
prs[i].Name = sr.Replace(s)
|
||||
}
|
||||
ss := strings.Split(pr.Descr, "\n")
|
||||
for i := range ss {
|
||||
ss[i] = strings.TrimSpace(ss[i])
|
||||
}
|
||||
if len(ss) > 1 {
|
||||
prs[i].Descr = strings.Join(ss, " ")
|
||||
} else {
|
||||
prs[i].Descr = ss[0]
|
||||
}
|
||||
prs[i].Value, _ = strconv.Atoi(pr.Value)
|
||||
}
|
||||
return prs
|
||||
}
|
||||
|
||||
func parseAddrFamilyNumbers(w io.Writer, r io.Reader) error {
|
||||
dec := xml.NewDecoder(r)
|
||||
var afn addrFamilylNumbers
|
||||
if err := dec.Decode(&afn); err != nil {
|
||||
return err
|
||||
}
|
||||
afrs := afn.escape()
|
||||
fmt.Fprintf(w, "// %s, Updated: %s\n", afn.Title, afn.Updated)
|
||||
fmt.Fprintf(w, "const (\n")
|
||||
for _, afr := range afrs {
|
||||
if afr.Name == "" {
|
||||
continue
|
||||
}
|
||||
fmt.Fprintf(w, "AddrFamily%s = %d", afr.Name, afr.Value)
|
||||
fmt.Fprintf(w, "// %s\n", afr.Descr)
|
||||
}
|
||||
fmt.Fprintf(w, ")\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
type addrFamilylNumbers struct {
|
||||
XMLName xml.Name `xml:"registry"`
|
||||
Title string `xml:"title"`
|
||||
Updated string `xml:"updated"`
|
||||
RegTitle string `xml:"registry>title"`
|
||||
Note string `xml:"registry>note"`
|
||||
Records []struct {
|
||||
Value string `xml:"value"`
|
||||
Descr string `xml:"description"`
|
||||
} `xml:"registry>record"`
|
||||
}
|
||||
|
||||
type canonAddrFamilyRecord struct {
|
||||
Name string
|
||||
Descr string
|
||||
Value int
|
||||
}
|
||||
|
||||
func (afn *addrFamilylNumbers) escape() []canonAddrFamilyRecord {
|
||||
afrs := make([]canonAddrFamilyRecord, len(afn.Records))
|
||||
sr := strings.NewReplacer(
|
||||
"IP version 4", "IPv4",
|
||||
"IP version 6", "IPv6",
|
||||
"Identifier", "ID",
|
||||
"-", "",
|
||||
"-", "",
|
||||
"/", "",
|
||||
".", "",
|
||||
" ", "",
|
||||
)
|
||||
for i, afr := range afn.Records {
|
||||
if strings.Contains(afr.Descr, "Unassigned") ||
|
||||
strings.Contains(afr.Descr, "Reserved") {
|
||||
continue
|
||||
}
|
||||
afrs[i].Descr = afr.Descr
|
||||
s := strings.TrimSpace(afr.Descr)
|
||||
switch s {
|
||||
case "IP (IP version 4)":
|
||||
afrs[i].Name = "IPv4"
|
||||
case "IP6 (IP version 6)":
|
||||
afrs[i].Name = "IPv6"
|
||||
case "AFI for L2VPN information":
|
||||
afrs[i].Name = "L2VPN"
|
||||
case "E.164 with NSAP format subaddress":
|
||||
afrs[i].Name = "E164withSubaddress"
|
||||
case "MT IP: Multi-Topology IP version 4":
|
||||
afrs[i].Name = "MTIPv4"
|
||||
case "MAC/24":
|
||||
afrs[i].Name = "MACFinal24bits"
|
||||
case "MAC/40":
|
||||
afrs[i].Name = "MACFinal40bits"
|
||||
case "IPv6/64":
|
||||
afrs[i].Name = "IPv6Initial64bits"
|
||||
default:
|
||||
n := strings.Index(s, "(")
|
||||
if n > 0 {
|
||||
s = s[:n]
|
||||
}
|
||||
n = strings.Index(s, ":")
|
||||
if n > 0 {
|
||||
s = s[:n]
|
||||
}
|
||||
afrs[i].Name = sr.Replace(s)
|
||||
}
|
||||
afrs[i].Value, _ = strconv.Atoi(afr.Value)
|
||||
}
|
||||
return afrs
|
||||
}
|
44
vendor/golang.org/x/net/internal/socket/defs_darwin.go
generated
vendored
44
vendor/golang.org/x/net/internal/socket/defs_darwin.go
generated
vendored
@ -1,44 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
||||
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
||||
|
||||
package socket
|
||||
|
||||
/*
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
const (
|
||||
sysAF_UNSPEC = C.AF_UNSPEC
|
||||
sysAF_INET = C.AF_INET
|
||||
sysAF_INET6 = C.AF_INET6
|
||||
|
||||
sysSOCK_RAW = C.SOCK_RAW
|
||||
)
|
||||
|
||||
type iovec C.struct_iovec
|
||||
|
||||
type msghdr C.struct_msghdr
|
||||
|
||||
type cmsghdr C.struct_cmsghdr
|
||||
|
||||
type sockaddrInet C.struct_sockaddr_in
|
||||
|
||||
type sockaddrInet6 C.struct_sockaddr_in6
|
||||
|
||||
const (
|
||||
sizeofIovec = C.sizeof_struct_iovec
|
||||
sizeofMsghdr = C.sizeof_struct_msghdr
|
||||
sizeofCmsghdr = C.sizeof_struct_cmsghdr
|
||||
|
||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
||||
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
||||
)
|
44
vendor/golang.org/x/net/internal/socket/defs_dragonfly.go
generated
vendored
44
vendor/golang.org/x/net/internal/socket/defs_dragonfly.go
generated
vendored
@ -1,44 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
||||
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
||||
|
||||
package socket
|
||||
|
||||
/*
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
const (
|
||||
sysAF_UNSPEC = C.AF_UNSPEC
|
||||
sysAF_INET = C.AF_INET
|
||||
sysAF_INET6 = C.AF_INET6
|
||||
|
||||
sysSOCK_RAW = C.SOCK_RAW
|
||||
)
|
||||
|
||||
type iovec C.struct_iovec
|
||||
|
||||
type msghdr C.struct_msghdr
|
||||
|
||||
type cmsghdr C.struct_cmsghdr
|
||||
|
||||
type sockaddrInet C.struct_sockaddr_in
|
||||
|
||||
type sockaddrInet6 C.struct_sockaddr_in6
|
||||
|
||||
const (
|
||||
sizeofIovec = C.sizeof_struct_iovec
|
||||
sizeofMsghdr = C.sizeof_struct_msghdr
|
||||
sizeofCmsghdr = C.sizeof_struct_cmsghdr
|
||||
|
||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
||||
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
||||
)
|
44
vendor/golang.org/x/net/internal/socket/defs_freebsd.go
generated
vendored
44
vendor/golang.org/x/net/internal/socket/defs_freebsd.go
generated
vendored
@ -1,44 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
||||
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
||||
|
||||
package socket
|
||||
|
||||
/*
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
const (
|
||||
sysAF_UNSPEC = C.AF_UNSPEC
|
||||
sysAF_INET = C.AF_INET
|
||||
sysAF_INET6 = C.AF_INET6
|
||||
|
||||
sysSOCK_RAW = C.SOCK_RAW
|
||||
)
|
||||
|
||||
type iovec C.struct_iovec
|
||||
|
||||
type msghdr C.struct_msghdr
|
||||
|
||||
type cmsghdr C.struct_cmsghdr
|
||||
|
||||
type sockaddrInet C.struct_sockaddr_in
|
||||
|
||||
type sockaddrInet6 C.struct_sockaddr_in6
|
||||
|
||||
const (
|
||||
sizeofIovec = C.sizeof_struct_iovec
|
||||
sizeofMsghdr = C.sizeof_struct_msghdr
|
||||
sizeofCmsghdr = C.sizeof_struct_cmsghdr
|
||||
|
||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
||||
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
||||
)
|
49
vendor/golang.org/x/net/internal/socket/defs_linux.go
generated
vendored
49
vendor/golang.org/x/net/internal/socket/defs_linux.go
generated
vendored
@ -1,49 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
||||
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
||||
|
||||
package socket
|
||||
|
||||
/*
|
||||
#include <linux/in.h>
|
||||
#include <linux/in6.h>
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <sys/socket.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
const (
|
||||
sysAF_UNSPEC = C.AF_UNSPEC
|
||||
sysAF_INET = C.AF_INET
|
||||
sysAF_INET6 = C.AF_INET6
|
||||
|
||||
sysSOCK_RAW = C.SOCK_RAW
|
||||
)
|
||||
|
||||
type iovec C.struct_iovec
|
||||
|
||||
type msghdr C.struct_msghdr
|
||||
|
||||
type mmsghdr C.struct_mmsghdr
|
||||
|
||||
type cmsghdr C.struct_cmsghdr
|
||||
|
||||
type sockaddrInet C.struct_sockaddr_in
|
||||
|
||||
type sockaddrInet6 C.struct_sockaddr_in6
|
||||
|
||||
const (
|
||||
sizeofIovec = C.sizeof_struct_iovec
|
||||
sizeofMsghdr = C.sizeof_struct_msghdr
|
||||
sizeofMmsghdr = C.sizeof_struct_mmsghdr
|
||||
sizeofCmsghdr = C.sizeof_struct_cmsghdr
|
||||
|
||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
||||
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
||||
)
|
47
vendor/golang.org/x/net/internal/socket/defs_netbsd.go
generated
vendored
47
vendor/golang.org/x/net/internal/socket/defs_netbsd.go
generated
vendored
@ -1,47 +0,0 @@
|
||||
// Copyright 2017 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build ignore
|
||||
|
||||
// +godefs map struct_in_addr [4]byte /* in_addr */
|
||||
// +godefs map struct_in6_addr [16]byte /* in6_addr */
|
||||
|
||||
package socket
|
||||
|
||||
/*
|
||||
#include <sys/socket.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
*/
|
||||
import "C"
|
||||
|
||||
const (
|
||||
sysAF_UNSPEC = C.AF_UNSPEC
|
||||
sysAF_INET = C.AF_INET
|
||||
sysAF_INET6 = C.AF_INET6
|
||||
|
||||
sysSOCK_RAW = C.SOCK_RAW
|
||||
)
|
||||
|
||||
type iovec C.struct_iovec
|
||||
|
||||
type msghdr C.struct_msghdr
|
||||
|
||||
type mmsghdr C.struct_mmsghdr
|
||||
|
||||
type cmsghdr C.struct_cmsghdr
|
||||
|
||||
type sockaddrInet C.struct_sockaddr_in
|
||||
|
||||
type sockaddrInet6 C.struct_sockaddr_in6
|
||||
|
||||
const (
|
||||
sizeofIovec = C.sizeof_struct_iovec
|
||||
sizeofMsghdr = C.sizeof_struct_msghdr
|
||||
sizeofMmsghdr = C.sizeof_struct_mmsghdr
|
||||
sizeofCmsghdr = C.sizeof_struct_cmsghdr
|
||||
|
||||
sizeofSockaddrInet = C.sizeof_struct_sockaddr_in
|
||||
sizeofSockaddrInet6 = C.sizeof_struct_sockaddr_in6
|
||||
)
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user