Compare commits
97 Commits
Author | SHA1 | Date | |
---|---|---|---|
ad10b6fa91 | |||
219eb9e046 | |||
f7a2fc97e4 | |||
6957f6ca4e | |||
02bfece2e9 | |||
5af9ff493e | |||
44d92c19de | |||
5e0fbd8374 | |||
a78853f29f | |||
2d2583ee33 | |||
f4332fec59 | |||
ed16760739 | |||
30776ff858 | |||
2a48d68937 | |||
117e30ff21 | |||
486ef96e6f | |||
8a0e3fe10e | |||
ca419073e4 | |||
47a9fd80c8 | |||
112288ecb2 | |||
32fc3ee9d3 | |||
c7e2cf7602 | |||
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 |
@ -1,10 +1,11 @@
|
|||||||
language: go
|
language: go
|
||||||
sudo: required
|
sudo: required
|
||||||
dist: trusty
|
dist: xenial
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- 1.11.x
|
- 1.11.x
|
||||||
- 1.12.x
|
- 1.12.x
|
||||||
|
- 1.13.x
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
@ -17,6 +18,7 @@ env:
|
|||||||
- TARGET=arm64
|
- TARGET=arm64
|
||||||
- TARGET=ppc64le
|
- TARGET=ppc64le
|
||||||
- TARGET=s390x
|
- TARGET=s390x
|
||||||
|
- TARGET=mips64le
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
fast_finish: true
|
fast_finish: true
|
||||||
|
@ -72,10 +72,11 @@ vagrant ssh
|
|||||||
# you're now in a shell in a virtual machine
|
# you're now in a shell in a virtual machine
|
||||||
sudo su
|
sudo su
|
||||||
go get github.com/onsi/ginkgo/ginkgo
|
go get github.com/onsi/ginkgo/ginkgo
|
||||||
|
go install github.com/containernetworking/cni/cnitool
|
||||||
cd /go/src/github.com/containernetworking/plugins
|
cd /go/src/github.com/containernetworking/plugins
|
||||||
|
|
||||||
# to run the full test suite
|
# to run the full test suite
|
||||||
./test.sh
|
./test_linux.sh
|
||||||
|
|
||||||
# to focus on a particular test suite
|
# to focus on a particular test suite
|
||||||
cd plugins/main/loopback
|
cd plugins/main/loopback
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
# Owners
|
# Owners
|
||||||
This is the official list of the CNI network plugins owners:
|
This is the official list of the CNI network plugins owners:
|
||||||
|
- Bruce Ma <brucema19901024@gmail.com> (@mars1024)
|
||||||
- Bryan Boreham <bryan@weave.works> (@bboreham)
|
- 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)
|
- Dan Williams <dcbw@redhat.com> (@dcbw)
|
||||||
- Gabe Rosenhouse <grosenhouse@pivotal.io> (@rosenhouse)
|
- Gabe Rosenhouse <grosenhouse@pivotal.io> (@rosenhouse)
|
||||||
- Matt Dupre <matt@tigera.io> (@matthewdupre)
|
- 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)
|
||||||
|
11
go.mod
11
go.mod
@ -7,8 +7,8 @@ require (
|
|||||||
github.com/Microsoft/hcsshim v0.8.6
|
github.com/Microsoft/hcsshim v0.8.6
|
||||||
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae
|
github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae
|
||||||
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44
|
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44
|
||||||
github.com/containernetworking/cni v0.7.0
|
github.com/containernetworking/cni v0.7.1
|
||||||
github.com/coreos/go-iptables v0.4.2
|
github.com/coreos/go-iptables v0.4.5
|
||||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7
|
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7
|
||||||
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c
|
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c
|
||||||
github.com/d2g/dhcp4client v1.0.0
|
github.com/d2g/dhcp4client v1.0.0
|
||||||
@ -17,10 +17,6 @@ require (
|
|||||||
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c
|
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c
|
||||||
github.com/golang/protobuf v1.3.1 // indirect
|
github.com/golang/protobuf v1.3.1 // indirect
|
||||||
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56
|
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/mattn/go-shellwords v1.0.3
|
||||||
github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b
|
github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b
|
||||||
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a
|
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/net v0.0.0-20181011144130-49bb7cea24b1 // indirect
|
||||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f
|
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f
|
||||||
gopkg.in/airbrake/gobrake.v2 v2.0.9 // indirect
|
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/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/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 h1:y853v6rXx+zefEcjET3JuKAqvhj+FKflQijjeaSv2iA=
|
||||||
github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
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.1 h1:fE3r16wpSEyaqY4Z4oFrLMmIGfBYIKpPrHK31EJ9FzE=
|
||||||
github.com/containernetworking/cni v0.7.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
|
github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
|
||||||
github.com/coreos/go-iptables v0.4.2 h1:KH0EwId05JwWIfb96gWvkiT2cbuOu8ygqUaB+yPAwIg=
|
github.com/coreos/go-iptables v0.4.5 h1:DpHb9vJrZQEFMcVLFKAAGMUVX0XoRC0ptCthinRYm38=
|
||||||
github.com/coreos/go-iptables v0.4.2/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
|
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 h1:u9SHYsPQNyt5tgDm3YN7+9dYrpK96E5wFilTFWIDZOM=
|
||||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
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=
|
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/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 h1:742eGXur0715JMq73aD95/FU0XpVKXqNuTnEfXsLOYQ=
|
||||||
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
|
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 h1:K/VxK7SZ+cvuPgFSLKi5QPI9Vr/ipOf4C1gN+ntueUk=
|
||||||
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
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=
|
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=
|
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 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo=
|
||||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
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 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0=
|
||||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
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) {
|
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()))
|
By(fmt.Sprintf("adding %s to %s\n\n", "chained-bridge-bandwidth", contNS1.ShortName()))
|
||||||
chainedBridgeBandwidthEnv.runInNS(hostNS, cnitoolBin, "add", "network-chain-test", contNS1.LongName())
|
chainedBridgeBandwidthEnv.runInNS(hostNS, cnitoolBin, "add", "network-chain-test", contNS1.LongName())
|
||||||
chainedBridgeIP := ipRegexp.FindString(chainedBridgeBandwidthEnv.runInNS(contNS1, "ip", "addr"))
|
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()))
|
By(fmt.Sprintf("adding %s to %s\n\n", "basic-bridge", contNS2.ShortName()))
|
||||||
basicBridgeEnv.runInNS(hostNS, cnitoolBin, "add", "network-chain-test", contNS2.LongName())
|
basicBridgeEnv.runInNS(hostNS, cnitoolBin, "add", "network-chain-test", contNS2.LongName())
|
||||||
|
@ -4,12 +4,12 @@
|
|||||||
"plugins": [
|
"plugins": [
|
||||||
{
|
{
|
||||||
"type": "bridge",
|
"type": "bridge",
|
||||||
"bridge": "test-bridge-0",
|
"bridge": "test-bridge-1",
|
||||||
"isDefaultGateway": true,
|
"isDefaultGateway": true,
|
||||||
"ipam": {
|
"ipam": {
|
||||||
"type": "host-local",
|
"type": "host-local",
|
||||||
"subnet": "10.11.2.0/24",
|
"subnet": "10.12.2.0/24",
|
||||||
"dataDir": "/tmp/foo"
|
"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"
|
||||||
"github.com/Microsoft/hcsshim/hcn"
|
"github.com/Microsoft/hcsshim/hcn"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
"github.com/juju/errors"
|
"github.com/containernetworking/plugins/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -64,14 +65,14 @@ func GenerateHnsEndpoint(epInfo *EndpointInfo, n *NetConf) (*hcsshim.HNSEndpoint
|
|||||||
// run the IPAM plugin and get back the config to apply
|
// run the IPAM plugin and get back the config to apply
|
||||||
hnsEndpoint, err := hcsshim.GetHNSEndpointByName(epInfo.EndpointName)
|
hnsEndpoint, err := hcsshim.GetHNSEndpointByName(epInfo.EndpointName)
|
||||||
if err != nil && !hcsshim.IsNotExist(err) {
|
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 != nil {
|
||||||
if hnsEndpoint.VirtualNetwork != epInfo.NetworkId {
|
if hnsEndpoint.VirtualNetwork != epInfo.NetworkId {
|
||||||
_, err = hnsEndpoint.Delete()
|
_, err = hnsEndpoint.Delete()
|
||||||
if err != nil {
|
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
|
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
|
// run the IPAM plugin and get back the config to apply
|
||||||
hcnEndpoint, err := hcn.GetEndpointByName(epInfo.EndpointName)
|
hcnEndpoint, err := hcn.GetEndpointByName(epInfo.EndpointName)
|
||||||
if err != nil && !hcn.IsNotFoundError(err) {
|
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 {
|
if hcnEndpoint != nil {
|
||||||
@ -108,12 +109,10 @@ func GenerateHcnEndpoint(epInfo *EndpointInfo, n *NetConf) (*hcn.HostComputeEndp
|
|||||||
if !strings.EqualFold(hcnEndpoint.HostComputeNetwork, epInfo.NetworkId) {
|
if !strings.EqualFold(hcnEndpoint.HostComputeNetwork, epInfo.NetworkId) {
|
||||||
err = hcnEndpoint.Delete()
|
err = hcnEndpoint.Delete()
|
||||||
if err != nil {
|
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)
|
||||||
hcnEndpoint = nil
|
|
||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
} 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 {
|
if err != nil {
|
||||||
return nil, errors.Annotatef(err, "failed to Remove Endpoint after AddNamespaceEndpoint failure")
|
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
|
return hcnEndpoint, nil
|
||||||
|
|
||||||
|
@ -20,10 +20,11 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/Microsoft/hcsshim/hcn"
|
"github.com/Microsoft/hcsshim/hcn"
|
||||||
"github.com/buger/jsonparser"
|
"github.com/buger/jsonparser"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NetConf is the CNI spec
|
// NetConf is the CNI spec
|
||||||
@ -46,8 +47,16 @@ type RuntimeDNS struct {
|
|||||||
Search []string `json:"searches,omitempty"`
|
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 {
|
type RuntimeConfig struct {
|
||||||
DNS RuntimeDNS `json:"dns"`
|
DNS RuntimeDNS `json:"dns"`
|
||||||
|
PortMaps []PortMapEntry `json:"portMappings,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type policy struct {
|
type policy struct {
|
||||||
@ -207,3 +216,21 @@ func (n *NetConf) ApplyDefaultPAPolicy(paAddress string) {
|
|||||||
Value: []byte(`{"Type": "PA", "PA": "` + paAddress + `"}`),
|
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() {
|
Describe("MarshalPolicies", func() {
|
||||||
Context("when not set by user", func() {
|
Context("when not set by user", func() {
|
||||||
It("sets it by adding a policy", func() {
|
It("sets it by adding a policy", func() {
|
||||||
|
@ -21,10 +21,12 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
|
||||||
"github.com/containernetworking/plugins/pkg/utils/hwaddr"
|
|
||||||
"github.com/safchain/ethtool"
|
"github.com/safchain/ethtool"
|
||||||
"github.com/vishvananda/netlink"
|
"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 (
|
var (
|
||||||
@ -158,6 +160,9 @@ func SetupVethWithName(contVethName, hostVethName string, mtu int, hostNS ns.Net
|
|||||||
if err = netlink.LinkSetUp(hostVeth); err != nil {
|
if err = netlink.LinkSetUp(hostVeth); err != nil {
|
||||||
return fmt.Errorf("failed to set %q up: %v", hostVethName, err)
|
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
|
return nil
|
||||||
})
|
})
|
||||||
if err != 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 {
|
func DelLinkByName(ifName string) error {
|
||||||
iface, err := netlink.LinkByName(ifName)
|
iface, err := netlink.LinkByName(ifName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == "Link not found" {
|
if _, ok := err.(netlink.LinkNotFoundError); ok {
|
||||||
return ErrLinkNotFound
|
return ErrLinkNotFound
|
||||||
}
|
}
|
||||||
return fmt.Errorf("failed to lookup %q: %v", ifName, err)
|
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) {
|
func DelLinkByNameAddr(ifName string) ([]*net.IPNet, error) {
|
||||||
iface, err := netlink.LinkByName(ifName)
|
iface, err := netlink.LinkByName(ifName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != nil && err.Error() == "Link not found" {
|
if _, ok := err.(netlink.LinkNotFoundError); ok {
|
||||||
return nil, ErrLinkNotFound
|
return nil, ErrLinkNotFound
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("failed to lookup %q: %v", ifName, err)
|
return nil, fmt.Errorf("failed to lookup %q: %v", ifName, err)
|
||||||
|
@ -178,7 +178,16 @@ func (ns *netNS) Do(toRun func(NetNS) error) error {
|
|||||||
if err = ns.Set(); err != nil {
|
if err = ns.Set(); err != nil {
|
||||||
return fmt.Errorf("error switching to ns %v: %v", ns.file.Name(), err)
|
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)
|
return toRun(hostNS)
|
||||||
}
|
}
|
||||||
@ -193,6 +202,10 @@ func (ns *netNS) Do(toRun func(NetNS) error) error {
|
|||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
wg.Add(1)
|
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
|
var innerError error
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
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"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
"golang.org/x/sys/unix"
|
"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
|
// Creates a new persistent (bind-mounted) network namespace and returns an object
|
||||||
// representing that namespace, without switching to it.
|
// representing that namespace, without switching to it.
|
||||||
func NewNS() (ns.NetNS, error) {
|
func NewNS() (ns.NetNS, error) {
|
||||||
|
|
||||||
|
nsRunDir := getNsRunDir()
|
||||||
|
|
||||||
b := make([]byte, 16)
|
b := make([]byte, 16)
|
||||||
_, err := rand.Reader.Read(b)
|
_, err := rand.Reader.Read(b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -135,7 +154,7 @@ func NewNS() (ns.NetNS, error) {
|
|||||||
func UnmountNS(ns ns.NetNS) error {
|
func UnmountNS(ns ns.NetNS) error {
|
||||||
nsPath := ns.Path()
|
nsPath := ns.Path()
|
||||||
// Only unmount if it's been bind-mounted (don't touch namespaces in /proc...)
|
// 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 {
|
if err := unix.Unmount(nsPath, 0); err != nil {
|
||||||
return fmt.Errorf("failed to unmount NS: at %s: %v", nsPath, err)
|
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) {
|
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)
|
fullName = filepath.Clean(fullName)
|
||||||
data, err := ioutil.ReadFile(fullName)
|
data, err := ioutil.ReadFile(fullName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -46,7 +46,7 @@ func getSysctl(name string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setSysctl(name, value 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)
|
fullName = filepath.Clean(fullName)
|
||||||
if err := ioutil.WriteFile(fullName, []byte(value), 0644); err != nil {
|
if err := ioutil.WriteFile(fullName, []byte(value), 0644); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -54,3 +54,27 @@ func setSysctl(name, value string) (string, error) {
|
|||||||
|
|
||||||
return getSysctl(name)
|
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")
|
||||||
|
}
|
@ -65,12 +65,11 @@ options four
|
|||||||
|
|
||||||
func parse(contents string) (*types.DNS, error) {
|
func parse(contents string) (*types.DNS, error) {
|
||||||
f, err := ioutil.TempFile("", "host_local_resolv")
|
f, err := ioutil.TempFile("", "host_local_resolv")
|
||||||
defer f.Close()
|
|
||||||
defer os.Remove(f.Name())
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
defer f.Close()
|
||||||
|
defer os.Remove(f.Name())
|
||||||
|
|
||||||
if _, err := f.WriteString(contents); err != nil {
|
if _, err := f.WriteString(contents); err != nil {
|
||||||
return nil, err
|
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:
|
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')
|
* `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
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(n.RuntimeConfig.IPs) != 0 {
|
// load IP from CNI_ARGS
|
||||||
// args IP overwrites IP, so clear IPAM Config
|
if envArgs != "" {
|
||||||
n.IPAM.Addresses = make([]Address, 0, len(n.RuntimeConfig.IPs))
|
e := IPAMEnvArgs{}
|
||||||
for _, addr := range n.RuntimeConfig.IPs {
|
err := types.LoadArgs(envArgs, &e)
|
||||||
n.IPAM.Addresses = append(n.IPAM.Addresses, Address{AddressStr: addr})
|
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 {
|
if n.IPAM == nil {
|
||||||
return nil, "", fmt.Errorf("IPAM config missing 'ipam' key")
|
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
|
// CNI spec 0.2.0 and below supported only one v4 and v6 address
|
||||||
if numV4 > 1 || numV6 > 1 {
|
if numV4 > 1 || numV6 > 1 {
|
||||||
for _, v := range types020.SupportedVersions {
|
for _, v := range types020.SupportedVersions {
|
||||||
|
@ -404,6 +404,82 @@ var _ = Describe("static Operations", func() {
|
|||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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 {
|
func mustCIDR(s string) net.IPNet {
|
||||||
|
@ -14,6 +14,7 @@ If the bridge is missing, the plugin will create one on first use and, if gatewa
|
|||||||
## Example configuration
|
## Example configuration
|
||||||
```
|
```
|
||||||
{
|
{
|
||||||
|
"cniVersion": "0.3.1",
|
||||||
"name": "mynet",
|
"name": "mynet",
|
||||||
"type": "bridge",
|
"type": "bridge",
|
||||||
"bridge": "mynet0",
|
"bridge": "mynet0",
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/j-keck/arping"
|
"github.com/j-keck/arping"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
@ -35,6 +36,7 @@ import (
|
|||||||
"github.com/containernetworking/plugins/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
"github.com/containernetworking/plugins/pkg/utils"
|
"github.com/containernetworking/plugins/pkg/utils"
|
||||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
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
|
// 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 {
|
if err := json.Unmarshal(bytes, n); err != nil {
|
||||||
return nil, "", fmt.Errorf("failed to load netconf: %v", err)
|
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
|
return n, n.CNIVersion, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,7 +226,9 @@ func ensureBridge(brName string, mtu int, promiscMode, vlanFiltering bool) (*net
|
|||||||
// default packet limit
|
// default packet limit
|
||||||
TxQLen: -1,
|
TxQLen: -1,
|
||||||
},
|
},
|
||||||
VlanFiltering: &vlanFiltering,
|
}
|
||||||
|
if vlanFiltering {
|
||||||
|
br.VlanFiltering = &vlanFiltering
|
||||||
}
|
}
|
||||||
|
|
||||||
err := netlink.LinkAdd(br)
|
err := netlink.LinkAdd(br)
|
||||||
@ -242,6 +249,9 @@ func ensureBridge(brName string, mtu int, promiscMode, vlanFiltering bool) (*net
|
|||||||
return nil, err
|
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 {
|
if err := netlink.LinkSetUp(br); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -439,11 +449,6 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
// Configure the container hardware address and IP address(es)
|
// Configure the container hardware address and IP address(es)
|
||||||
if err := netns.Do(func(_ ns.NetNS) error {
|
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
|
// Disable IPv6 DAD just in case hairpin mode is enabled on the
|
||||||
// bridge. Hairpin mode causes echos of neighbor solicitation
|
// bridge. Hairpin mode causes echos of neighbor solicitation
|
||||||
// packets, which causes DAD failures.
|
// packets, which causes DAD failures.
|
||||||
@ -460,8 +465,36 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
if err := ipam.ConfigureIface(args.IfName, result); err != nil {
|
if err := ipam.ConfigureIface(args.IfName, result); err != nil {
|
||||||
return err
|
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
|
// Send a gratuitous arp
|
||||||
|
if err := netns.Do(func(_ ns.NetNS) error {
|
||||||
|
contVeth, err := net.InterfaceByName(args.IfName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
for _, ipc := range result.IPs {
|
for _, ipc := range result.IPs {
|
||||||
if ipc.Version == "4" {
|
if ipc.Version == "4" {
|
||||||
_ = arping.GratuitousArpOverIface(ipc.Address.IP, *contVeth)
|
_ = arping.GratuitousArpOverIface(ipc.Address.IP, *contVeth)
|
||||||
|
@ -27,7 +27,7 @@ import (
|
|||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"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/cni/pkg/types/current"
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
"github.com/containernetworking/plugins/pkg/testutils"
|
"github.com/containernetworking/plugins/pkg/testutils"
|
||||||
@ -1125,6 +1125,7 @@ var _ = Describe("bridge Operations", func() {
|
|||||||
AfterEach(func() {
|
AfterEach(func() {
|
||||||
Expect(os.RemoveAll(dataDir)).To(Succeed())
|
Expect(os.RemoveAll(dataDir)).To(Succeed())
|
||||||
Expect(originalNS.Close()).To(Succeed())
|
Expect(originalNS.Close()).To(Succeed())
|
||||||
|
Expect(testutils.UnmountNS(originalNS)).To(Succeed())
|
||||||
})
|
})
|
||||||
|
|
||||||
It("creates a bridge", func() {
|
It("creates a bridge", func() {
|
||||||
@ -1644,4 +1645,48 @@ var _ = Describe("bridge Operations", func() {
|
|||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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.
|
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
|
## Example configuration
|
||||||
|
|
||||||
A sample configuration with `device` property looks like:
|
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"
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
@ -50,6 +50,9 @@ type NetConf struct {
|
|||||||
HWAddr string `json:"hwaddr"` // MAC Address of target network interface
|
HWAddr string `json:"hwaddr"` // MAC Address of target network interface
|
||||||
KernelPath string `json:"kernelpath"` // Kernelpath of the device
|
KernelPath string `json:"kernelpath"` // Kernelpath of the device
|
||||||
PCIAddr string `json:"pciBusID"` // PCI Address of target network device
|
PCIAddr string `json:"pciBusID"` // PCI Address of target network device
|
||||||
|
RuntimeConfig struct {
|
||||||
|
DeviceID string `json:"deviceID,omitempty"`
|
||||||
|
} `json:"runtimeConfig,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -64,9 +67,16 @@ func loadConf(bytes []byte) (*NetConf, error) {
|
|||||||
if err := json.Unmarshal(bytes, n); err != nil {
|
if err := json.Unmarshal(bytes, n); err != nil {
|
||||||
return nil, fmt.Errorf("failed to load netconf: %v", err)
|
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 == "" {
|
if n.Device == "" && n.HWAddr == "" && n.KernelPath == "" && n.PCIAddr == "" {
|
||||||
return nil, fmt.Errorf(`specify either "device", "hwaddr", "kernelpath" or "pciBusID"`)
|
return nil, fmt.Errorf(`specify either "device", "hwaddr", "kernelpath" or "pciBusID"`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return n, nil
|
return n, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,7 +306,12 @@ func getLink(devname, hwaddr, kernelpath, pciaddr string) (netlink.Link, error)
|
|||||||
} else if len(pciaddr) > 0 {
|
} else if len(pciaddr) > 0 {
|
||||||
netDir := filepath.Join(sysBusPCI, pciaddr, "net")
|
netDir := filepath.Join(sysBusPCI, pciaddr, "net")
|
||||||
if _, err := os.Lstat(netDir); err != nil {
|
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)
|
fInfo, err := ioutil.ReadDir(netDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -233,7 +233,8 @@ var _ = Describe("base functionality", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
AfterEach(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() {
|
It("Works with a valid config without IPAM", func() {
|
||||||
|
@ -297,6 +297,7 @@ var _ = Describe("ipvlan Operations", func() {
|
|||||||
|
|
||||||
AfterEach(func() {
|
AfterEach(func() {
|
||||||
Expect(originalNS.Close()).To(Succeed())
|
Expect(originalNS.Close()).To(Succeed())
|
||||||
|
Expect(testutils.UnmountNS(originalNS)).To(Succeed())
|
||||||
})
|
})
|
||||||
|
|
||||||
It("creates an ipvlan link in a non-default namespace", func() {
|
It("creates an ipvlan link in a non-default namespace", func() {
|
||||||
|
@ -15,9 +15,15 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
"github.com/containernetworking/cni/pkg/version"
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
|
|
||||||
@ -25,9 +31,34 @@ import (
|
|||||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
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 {
|
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
|
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)
|
link, err := netlink.LinkByName(args.IfName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err // not tested
|
return err // not tested
|
||||||
@ -38,14 +69,69 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
return err // not tested
|
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
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err // not tested
|
return err // not tested
|
||||||
}
|
}
|
||||||
|
|
||||||
result := current.Result{}
|
var result types.Result
|
||||||
return result.Print()
|
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 {
|
func cmdDel(args *skel.CmdArgs) error {
|
||||||
@ -78,6 +164,18 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func cmdCheck(args *skel.CmdArgs) error {
|
func cmdCheck(args *skel.CmdArgs) error {
|
||||||
// TODO: implement
|
args.IfName = "lo" // ignore config, this only works for loopback
|
||||||
return nil
|
|
||||||
|
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
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
@ -49,17 +49,16 @@ var _ = Describe("Loopback", func() {
|
|||||||
fmt.Sprintf("CNI_ARGS=%s", "none"),
|
fmt.Sprintf("CNI_ARGS=%s", "none"),
|
||||||
fmt.Sprintf("CNI_PATH=%s", "/some/test/path"),
|
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() {
|
AfterEach(func() {
|
||||||
Expect(networkNS.Close()).To(Succeed())
|
Expect(networkNS.Close()).To(Succeed())
|
||||||
|
Expect(testutils.UnmountNS(networkNS)).To(Succeed())
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("when given a network namespace", func() {
|
Context("when given a network namespace", func() {
|
||||||
It("sets the lo device to UP", func() {
|
It("sets the lo device to UP", func() {
|
||||||
|
|
||||||
Skip("TODO: add network name")
|
|
||||||
command.Env = append(environ, fmt.Sprintf("CNI_COMMAND=%s", "ADD"))
|
command.Env = append(environ, fmt.Sprintf("CNI_COMMAND=%s", "ADD"))
|
||||||
|
|
||||||
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
|
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
|
||||||
@ -80,8 +79,6 @@ var _ = Describe("Loopback", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
It("sets the lo device to DOWN", func() {
|
It("sets the lo device to DOWN", func() {
|
||||||
|
|
||||||
Skip("TODO: add network name")
|
|
||||||
command.Env = append(environ, fmt.Sprintf("CNI_COMMAND=%s", "DEL"))
|
command.Env = append(environ, fmt.Sprintf("CNI_COMMAND=%s", "DEL"))
|
||||||
|
|
||||||
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
|
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
|
||||||
|
@ -23,9 +23,9 @@ Since each macvlan interface has its own MAC address, it makes it easy to use wi
|
|||||||
|
|
||||||
* `name` (string, required): the name of the network
|
* `name` (string, required): the name of the network
|
||||||
* `type` (string, required): "macvlan"
|
* `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".
|
* `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.
|
* `ipam` (dictionary, required): IPAM configuration to be used for this network. For interface only without ip address, create empty dictionary.
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
@ -85,9 +85,27 @@ func loadConf(bytes []byte) (*NetConf, string, error) {
|
|||||||
}
|
}
|
||||||
n.Master = defaultRouteInterface
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
return n, n.CNIVersion, nil
|
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) {
|
func modeFromString(s string) (netlink.MacvlanMode, error) {
|
||||||
switch s {
|
switch s {
|
||||||
case "", "bridge":
|
case "", "bridge":
|
||||||
|
@ -123,6 +123,7 @@ var _ = Describe("macvlan Operations", func() {
|
|||||||
|
|
||||||
AfterEach(func() {
|
AfterEach(func() {
|
||||||
Expect(originalNS.Close()).To(Succeed())
|
Expect(originalNS.Close()).To(Succeed())
|
||||||
|
Expect(testutils.UnmountNS(originalNS)).To(Succeed())
|
||||||
})
|
})
|
||||||
|
|
||||||
It("creates an macvlan link in a non-default namespace", func() {
|
It("creates an macvlan link in a non-default namespace", func() {
|
||||||
|
@ -228,7 +228,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
defer netns.Close()
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -247,12 +247,23 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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
|
result.DNS = conf.DNS
|
||||||
result.Interfaces = []*current.Interface{hostInterface, containerInterface}
|
}
|
||||||
|
|
||||||
return types.PrintResult(result, conf.CNIVersion)
|
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 {
|
func cmdDel(args *skel.CmdArgs) error {
|
||||||
conf := NetConf{}
|
conf := NetConf{}
|
||||||
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
|
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
|
||||||
|
@ -17,6 +17,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
@ -98,9 +99,10 @@ var _ = Describe("ptp Operations", func() {
|
|||||||
|
|
||||||
AfterEach(func() {
|
AfterEach(func() {
|
||||||
Expect(originalNS.Close()).To(Succeed())
|
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"
|
const IFNAME = "ptp0"
|
||||||
|
|
||||||
targetNs, err := testutils.NewNS()
|
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].Mac).To(Equal(wantMac))
|
||||||
Expect(res.Interfaces[1].Sandbox).To(Equal(targetNs.Path()))
|
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
|
// Call the plugins with the DEL command, deleting the veth endpoints
|
||||||
err = originalNS.Do(func(ns.NetNS) error {
|
err = originalNS.Do(func(ns.NetNS) error {
|
||||||
defer GinkgoRecover()
|
defer GinkgoRecover()
|
||||||
@ -327,7 +332,16 @@ var _ = Describe("ptp Operations", func() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
It("configures and deconfigures a ptp link with ADD/DEL", 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",
|
"cniVersion": "0.3.1",
|
||||||
"name": "mynet",
|
"name": "mynet",
|
||||||
"type": "ptp",
|
"type": "ptp",
|
||||||
@ -336,10 +350,11 @@ var _ = Describe("ptp Operations", func() {
|
|||||||
"ipam": {
|
"ipam": {
|
||||||
"type": "host-local",
|
"type": "host-local",
|
||||||
"subnet": "10.1.2.0/24"
|
"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() {
|
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() {
|
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)
|
return nil, "", fmt.Errorf("failed to load netconf: %v", err)
|
||||||
}
|
}
|
||||||
if n.Master == "" {
|
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 {
|
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
|
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) {
|
func createVlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Interface, error) {
|
||||||
vlan := ¤t.Interface{}
|
vlan := ¤t.Interface{}
|
||||||
|
|
||||||
@ -76,10 +94,6 @@ func createVlan(conf *NetConf, ifName string, netns ns.NetNS) (*current.Interfac
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if conf.MTU <= 0 {
|
|
||||||
conf.MTU = m.Attrs().MTU
|
|
||||||
}
|
|
||||||
|
|
||||||
v := &netlink.Vlan{
|
v := &netlink.Vlan{
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: netlink.LinkAttrs{
|
||||||
MTU: conf.MTU,
|
MTU: conf.MTU,
|
||||||
@ -193,7 +207,7 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
err = ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
|
err = ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
|
||||||
err = ip.DelLinkByName(args.IfName)
|
err = ip.DelLinkByName(args.IfName)
|
||||||
if err != nil && err != ip.ErrLinkNotFound {
|
if err != nil && err == ip.ErrLinkNotFound {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
@ -121,6 +121,7 @@ var _ = Describe("vlan Operations", func() {
|
|||||||
|
|
||||||
AfterEach(func() {
|
AfterEach(func() {
|
||||||
Expect(originalNS.Close()).To(Succeed())
|
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() {
|
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
|
return nil
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
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() {
|
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())
|
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,
|
"loopbackDSR": true,
|
||||||
"capabilities": {
|
"capabilities": {
|
||||||
"dns": true
|
"dns": true,
|
||||||
|
"portMappings": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -55,3 +56,4 @@ With win-bridge plugin, all containers (on the same host) are plugged into an L2
|
|||||||
* `loopbackDSR` (bool, optional): If true, will add a policy to allow the interface to support loopback direct server return.
|
* `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.
|
* `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"
|
||||||
"github.com/Microsoft/hcsshim/hcn"
|
"github.com/Microsoft/hcsshim/hcn"
|
||||||
"github.com/juju/errors"
|
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
"github.com/containernetworking/cni/pkg/version"
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
|
|
||||||
|
"github.com/containernetworking/plugins/pkg/errors"
|
||||||
"github.com/containernetworking/plugins/pkg/hns"
|
"github.com/containernetworking/plugins/pkg/hns"
|
||||||
"github.com/containernetworking/plugins/pkg/ipam"
|
"github.com/containernetworking/plugins/pkg/ipam"
|
||||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
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")
|
return nil, errors.Annotatef(err, "error while NewResultFromResult")
|
||||||
} else {
|
} else {
|
||||||
if len(result.IPs) == 0 {
|
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.IpAddress = result.IPs[0].Address.IP
|
||||||
epInfo.Gateway = result.IPs[0].Address.IP.Mask(result.IPs[0].Address.Mask)
|
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)
|
n.ApplyOutboundNatPolicy(n.IPMasqNetwork)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add HostPort mapping if any present
|
||||||
|
n.ApplyPortMappingPolicy(n.RuntimeConfig.PortMaps)
|
||||||
|
|
||||||
epInfo.DNS = n.GetDNS()
|
epInfo.DNS = n.GetDNS()
|
||||||
|
|
||||||
return epInfo, nil
|
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)
|
epName := hns.ConstructEndpointName(args.ContainerID, args.Netns, n.Name)
|
||||||
|
|
||||||
hnsEndpoint, err := hns.ProvisionEndpoint(epName, hnsNetwork.Id, args.ContainerID, args.Netns, func() (*hcsshim.HNSEndpoint, error) {
|
hnsEndpoint, err := hns.ProvisionEndpoint(epName, hnsNetwork.Id, args.ContainerID, args.Netns, func() (*hcsshim.HNSEndpoint, error) {
|
||||||
epInfo, err := ProcessEndpointArgs(args, n)
|
epInfo, err := ProcessEndpointArgs(args, n)
|
||||||
epInfo.NetworkId = hnsNetwork.Id
|
epInfo.NetworkId = hnsNetwork.Id
|
||||||
@ -130,7 +132,6 @@ func cmdHnsAdd(args *skel.CmdArgs, n *NetConf) (*current.Result, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return result, nil
|
return result, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func cmdHcnAdd(args *skel.CmdArgs, n *NetConf) (*current.Result, error) {
|
func cmdHcnAdd(args *skel.CmdArgs, n *NetConf) (*current.Result, error) {
|
||||||
@ -194,7 +195,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if result == nil {
|
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)
|
return types.PrintResult(result, cniVersion)
|
||||||
}
|
}
|
||||||
|
@ -21,13 +21,13 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Microsoft/hcsshim"
|
"github.com/Microsoft/hcsshim"
|
||||||
"github.com/juju/errors"
|
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
"github.com/containernetworking/cni/pkg/version"
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
|
|
||||||
|
"github.com/containernetworking/plugins/pkg/errors"
|
||||||
"github.com/containernetworking/plugins/pkg/hns"
|
"github.com/containernetworking/plugins/pkg/hns"
|
||||||
"github.com/containernetworking/plugins/pkg/ipam"
|
"github.com/containernetworking/plugins/pkg/ipam"
|
||||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
||||||
@ -100,12 +100,12 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(result.IPs) == 0 {
|
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()
|
ipAddr := result.IPs[0].Address.IP.To4()
|
||||||
if ipAddr == nil {
|
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
|
// 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() {
|
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() {
|
It("Should work with multiple host veth interfaces", func() {
|
||||||
conf := `{
|
conf := `{
|
||||||
@ -874,8 +889,8 @@ var _ = Describe("bandwidth test", func() {
|
|||||||
|
|
||||||
Context("when chaining bandwidth plugin with PTP using 0.3.0 config", func() {
|
Context("when chaining bandwidth plugin with PTP using 0.3.0 config", func() {
|
||||||
var ptpConf string
|
var ptpConf string
|
||||||
var rateInBits int
|
var rateInBits uint64
|
||||||
var burstInBits int
|
var burstInBits uint64
|
||||||
var packetInBytes int
|
var packetInBytes int
|
||||||
var containerWithoutTbfNS ns.NetNS
|
var containerWithoutTbfNS ns.NetNS
|
||||||
var containerWithTbfNS ns.NetNS
|
var containerWithTbfNS ns.NetNS
|
||||||
@ -889,7 +904,7 @@ var _ = Describe("bandwidth test", func() {
|
|||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
rateInBytes := 1000
|
rateInBytes := 1000
|
||||||
rateInBits = rateInBytes * 8
|
rateInBits = uint64(rateInBytes * 8)
|
||||||
burstInBits = rateInBits * 2
|
burstInBits = rateInBits * 2
|
||||||
packetInBytes = rateInBytes * 25
|
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() {
|
Context("when chaining bandwidth plugin with PTP using 0.4.0 config", func() {
|
||||||
var ptpConf string
|
var ptpConf string
|
||||||
var rateInBits int
|
var rateInBits uint64
|
||||||
var burstInBits int
|
var burstInBits uint64
|
||||||
var packetInBytes int
|
var packetInBytes int
|
||||||
var containerWithoutTbfNS ns.NetNS
|
var containerWithoutTbfNS ns.NetNS
|
||||||
var containerWithTbfNS ns.NetNS
|
var containerWithTbfNS ns.NetNS
|
||||||
@ -1034,7 +1049,7 @@ var _ = Describe("bandwidth test", func() {
|
|||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
rateInBytes := 1000
|
rateInBytes := 1000
|
||||||
rateInBits = rateInBytes * 8
|
rateInBits = uint64(rateInBytes * 8)
|
||||||
burstInBits = rateInBits * 2
|
burstInBits = rateInBits * 2
|
||||||
packetInBytes = rateInBytes * 25
|
packetInBytes = rateInBytes * 25
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ func TeardownIfb(deviceName string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateIngressQdisc(rateInBits, burstInBits int, hostDeviceName string) error {
|
func CreateIngressQdisc(rateInBits, burstInBits uint64, hostDeviceName string) error {
|
||||||
hostDevice, err := netlink.LinkByName(hostDeviceName)
|
hostDevice, err := netlink.LinkByName(hostDeviceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("get host device: %s", err)
|
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)
|
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)
|
ifbDevice, err := netlink.LinkByName(ifbDeviceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("get ifb device: %s", err)
|
return fmt.Errorf("get ifb device: %s", err)
|
||||||
@ -113,7 +113,7 @@ func CreateEgressQdisc(rateInBits, burstInBits int, hostDeviceName string, ifbDe
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTBF(rateInBits, burstInBits, linkIndex int) error {
|
func createTBF(rateInBits, burstInBits uint64, linkIndex int) error {
|
||||||
// Equivalent to
|
// Equivalent to
|
||||||
// tc qdisc add dev link root tbf
|
// tc qdisc add dev link root tbf
|
||||||
// rate netConf.BandwidthLimits.Rate
|
// rate netConf.BandwidthLimits.Rate
|
||||||
|
@ -17,6 +17,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
@ -37,11 +38,11 @@ const ifbDevicePrefix = "bwp"
|
|||||||
// BandwidthEntry corresponds to a single entry in the bandwidth argument,
|
// BandwidthEntry corresponds to a single entry in the bandwidth argument,
|
||||||
// see CONVENTIONS.md
|
// see CONVENTIONS.md
|
||||||
type BandwidthEntry struct {
|
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
|
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 int `json:"ingressBurst"` //Bandwidth burst in bits for traffic through container. 0 for no limit. If ingressBurst is set, ingressRate 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
|
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 int `json:"egressBurst"` //Bandwidth burst in bits for traffic through container. 0 for no limit. If egressBurst is set, egressRate 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 {
|
func (bw *BandwidthEntry) isZero() bool {
|
||||||
@ -101,7 +102,7 @@ func getBandwidth(conf *PluginConf) *BandwidthEntry {
|
|||||||
return conf.BandwidthEntry
|
return conf.BandwidthEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateRateAndBurst(rate int, burst int) error {
|
func validateRateAndBurst(rate, burst uint64) error {
|
||||||
switch {
|
switch {
|
||||||
case burst < 0 || rate < 0:
|
case burst < 0 || rate < 0:
|
||||||
return fmt.Errorf("rate and burst must be a positive integer")
|
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")
|
return fmt.Errorf("if rate is set, burst must also be set")
|
||||||
case rate == 0 && burst != 0:
|
case rate == 0 && burst != 0:
|
||||||
return fmt.Errorf("if burst is set, rate must also be set")
|
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
|
return nil
|
||||||
|
@ -27,7 +27,6 @@ import (
|
|||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
"github.com/containernetworking/cni/pkg/version"
|
"github.com/containernetworking/cni/pkg/version"
|
||||||
|
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
|
||||||
bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
|
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)
|
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.
|
// Parse previous result.
|
||||||
if conf.RawPrevResult == nil {
|
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.
|
// 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)
|
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
|
return &conf, result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,6 +116,10 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if conf.PrevResult == nil {
|
||||||
|
return fmt.Errorf("missing prevResult from earlier plugin")
|
||||||
|
}
|
||||||
|
|
||||||
backend, err := getBackend(conf)
|
backend, err := getBackend(conf)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -142,12 +146,6 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
return err
|
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
|
// Runtime errors are ignored
|
||||||
if err := backend.Del(conf, result); err != nil {
|
if err := backend.Del(conf, result); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -167,8 +165,8 @@ func cmdCheck(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure we have previous result.
|
// Ensure we have previous result.
|
||||||
if result == nil {
|
if conf.PrevResult == nil {
|
||||||
return fmt.Errorf("Required prevResult missing")
|
return fmt.Errorf("missing prevResult from earlier plugin")
|
||||||
}
|
}
|
||||||
|
|
||||||
backend, err := getBackend(conf)
|
backend, err := getBackend(conf)
|
||||||
|
@ -270,6 +270,13 @@ var _ = Describe("firewall plugin iptables backend", func() {
|
|||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
validateFullRuleset(fullConf)
|
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
|
return nil
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types/current"
|
"github.com/containernetworking/cni/pkg/types/current"
|
||||||
|
"github.com/containernetworking/plugins/pkg/utils"
|
||||||
"github.com/coreos/go-iptables/iptables"
|
"github.com/coreos/go-iptables/iptables"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -32,20 +33,6 @@ func getPrivChainRules(ip string) [][]string {
|
|||||||
return rules
|
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 {
|
func generateFilterRule(privChainName string) []string {
|
||||||
return []string{"-m", "comment", "--comment", "CNI firewall plugin rules", "-j", privChainName}
|
return []string{"-m", "comment", "--comment", "CNI firewall plugin rules", "-j", privChainName}
|
||||||
}
|
}
|
||||||
@ -73,10 +60,10 @@ func (ib *iptablesBackend) setupChains(ipt *iptables.IPTables) error {
|
|||||||
adminRule := generateFilterRule(ib.adminChainName)
|
adminRule := generateFilterRule(ib.adminChainName)
|
||||||
|
|
||||||
// Ensure our private chains exist
|
// 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
|
return err
|
||||||
}
|
}
|
||||||
if err := ensureChain(ipt, "filter", ib.adminChainName); err != nil {
|
if err := utils.EnsureChain(ipt, "filter", ib.adminChainName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,10 +147,10 @@ func (ib *iptablesBackend) checkRules(conf *FirewallNetConf, result *current.Res
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure our private chains exist
|
// 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
|
return err
|
||||||
}
|
}
|
||||||
if err := ensureChain(ipt, "filter", ib.adminChainName); err != nil {
|
if err := utils.EnsureChain(ipt, "filter", ib.adminChainName); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ Additionally, for the bridge plugin, `isGateway` will be set to `true`, if not p
|
|||||||
|
|
||||||
## Windows Support (Experimental)
|
## 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).
|
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:
|
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)
|
* `name` (string, required): the name of the network (must match the name in Flannel config / name of the HNS network)
|
||||||
|
@ -72,7 +72,7 @@ will masquerade traffic as needed.
|
|||||||
The DNAT rule rewrites the destination port and address of new connections.
|
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
|
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
|
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
|
8080 and 8043 on the host forwarded to ports 80 and 443 in the container, the
|
||||||
rules look like this:
|
rules look like this:
|
||||||
|
|
||||||
@ -86,10 +86,10 @@ rules look like this:
|
|||||||
- `-j MARK --set-xmark 0x2000/0x2000`
|
- `-j MARK --set-xmark 0x2000/0x2000`
|
||||||
|
|
||||||
`CNI-DN-xxxxxx` chain:
|
`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 -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 --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 -s 127.0.0.1 --dport 8043 -j CNI-HOSTPORT-SETMARK`
|
||||||
- `-p tcp --dport 8043 -j DNAT --to-destination 172.16.30.2:443`
|
- `-p tcp --dport 8043 -j DNAT --to-destination 172.16.30.2:443`
|
||||||
|
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containernetworking/plugins/pkg/utils"
|
||||||
"github.com/coreos/go-iptables/iptables"
|
"github.com/coreos/go-iptables/iptables"
|
||||||
"github.com/mattn/go-shellwords"
|
"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.
|
// setup idempotently creates the chain. It will not error if the chain exists.
|
||||||
func (c *chain) setup(ipt *iptables.IPTables) error {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if !exists {
|
|
||||||
if err := ipt.NewChain(c.table, c.name); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the rules to the chain
|
// Add the rules to the chain
|
||||||
for _, rule := range c.rules {
|
for _, rule := range c.rules {
|
||||||
@ -74,7 +70,7 @@ func (c *chain) teardown(ipt *iptables.IPTables) error {
|
|||||||
// flush the chain
|
// flush the chain
|
||||||
// This will succeed *and create the chain* if it does not exist.
|
// This will succeed *and create the chain* if it does not exist.
|
||||||
// If the chain doesn't exist, the next checks will fail.
|
// 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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,17 +90,15 @@ func (c *chain) teardown(ipt *iptables.IPTables) error {
|
|||||||
}
|
}
|
||||||
chainParts = chainParts[2:] // List results always include an -A CHAINNAME
|
chainParts = chainParts[2:] // List results always include an -A CHAINNAME
|
||||||
|
|
||||||
if err := ipt.Delete(c.table, entryChain, chainParts...); err != nil {
|
if err := utils.DeleteRule(ipt, c.table, entryChain, chainParts...); err != nil {
|
||||||
return fmt.Errorf("Failed to delete referring rule %s %s: %v", c.table, entryChainRule, err)
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ipt.DeleteChain(c.table, c.name); err != nil {
|
return utils.DeleteChain(ipt, c.table, c.name)
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// insertUnique will add a rule to a chain if it does not already exist.
|
// 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.
|
// check the chain.
|
||||||
func (c *chain) check(ipt *iptables.IPTables) error {
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/containernetworking/plugins/pkg/ns"
|
"github.com/containernetworking/plugins/pkg/ns"
|
||||||
"github.com/containernetworking/plugins/pkg/testutils"
|
"github.com/containernetworking/plugins/pkg/testutils"
|
||||||
@ -32,6 +33,7 @@ const TABLE = "filter" // We'll monkey around here
|
|||||||
var _ = Describe("chain tests", func() {
|
var _ = Describe("chain tests", func() {
|
||||||
var testChain chain
|
var testChain chain
|
||||||
var ipt *iptables.IPTables
|
var ipt *iptables.IPTables
|
||||||
|
var testNs ns.NetNS
|
||||||
var cleanup func()
|
var cleanup func()
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
@ -41,7 +43,7 @@ var _ = Describe("chain tests", func() {
|
|||||||
currNs, err := ns.GetCurrentNS()
|
currNs, err := ns.GetCurrentNS()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
testNs, err := testutils.NewNS()
|
testNs, err = testutils.NewNS()
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
tlChainName := fmt.Sprintf("cni-test-%d", rand.Intn(10000000))
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
@ -61,8 +61,8 @@ type PortMapConf struct {
|
|||||||
// These are fields parsed out of the config or the environment;
|
// These are fields parsed out of the config or the environment;
|
||||||
// included here for convenience
|
// included here for convenience
|
||||||
ContainerID string `json:"-"`
|
ContainerID string `json:"-"`
|
||||||
ContIPv4 net.IP `json:"-"`
|
ContIPv4 net.IPNet `json:"-"`
|
||||||
ContIPv6 net.IP `json:"-"`
|
ContIPv6 net.IPNet `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// The default mark bit to signal that masquerading is required
|
// The default mark bit to signal that masquerading is required
|
||||||
@ -85,13 +85,13 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
netConf.ContainerID = args.ContainerID
|
netConf.ContainerID = args.ContainerID
|
||||||
|
|
||||||
if netConf.ContIPv4 != nil {
|
if netConf.ContIPv4.IP != nil {
|
||||||
if err := forwardPorts(netConf, netConf.ContIPv4); err != nil {
|
if err := forwardPorts(netConf, netConf.ContIPv4); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if netConf.ContIPv6 != nil {
|
if netConf.ContIPv6.IP != nil {
|
||||||
if err := forwardPorts(netConf, netConf.ContIPv6); err != nil {
|
if err := forwardPorts(netConf, netConf.ContIPv6); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -138,13 +138,13 @@ func cmdCheck(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
conf.ContainerID = args.ContainerID
|
conf.ContainerID = args.ContainerID
|
||||||
|
|
||||||
if conf.ContIPv4 != nil {
|
if conf.ContIPv4.IP != nil {
|
||||||
if err := checkPorts(conf, conf.ContIPv4); err != nil {
|
if err := checkPorts(conf, conf.ContIPv4); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if conf.ContIPv6 != nil {
|
if conf.ContIPv6.IP != nil {
|
||||||
if err := checkPorts(conf, conf.ContIPv6); err != nil {
|
if err := checkPorts(conf, conf.ContIPv6); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -205,9 +205,9 @@ func parseConfig(stdin []byte, ifName string) (*PortMapConf, *current.Result, er
|
|||||||
|
|
||||||
if conf.PrevResult != nil {
|
if conf.PrevResult != nil {
|
||||||
for _, ip := range result.IPs {
|
for _, ip := range result.IPs {
|
||||||
if ip.Version == "6" && conf.ContIPv6 != nil {
|
if ip.Version == "6" && conf.ContIPv6.IP != nil {
|
||||||
continue
|
continue
|
||||||
} else if ip.Version == "4" && conf.ContIPv4 != nil {
|
} else if ip.Version == "4" && conf.ContIPv4.IP != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,9 +223,9 @@ func parseConfig(stdin []byte, ifName string) (*PortMapConf, *current.Result, er
|
|||||||
}
|
}
|
||||||
switch ip.Version {
|
switch ip.Version {
|
||||||
case "6":
|
case "6":
|
||||||
conf.ContIPv6 = ip.Address.IP
|
conf.ContIPv6 = ip.Address
|
||||||
case "4":
|
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"
|
const OldTopLevelSNATChainName = "CNI-HOSTPORT-SNAT"
|
||||||
|
|
||||||
// forwardPorts establishes port forwarding to a given container IP.
|
// forwardPorts establishes port forwarding to a given container IP.
|
||||||
// containerIP can be either v4 or v6.
|
// containerNet.IP can be either v4 or v6.
|
||||||
func forwardPorts(config *PortMapConf, containerIP net.IP) error {
|
func forwardPorts(config *PortMapConf, containerNet net.IPNet) error {
|
||||||
isV6 := (containerIP.To4() == nil)
|
isV6 := (containerNet.IP.To4() == nil)
|
||||||
|
|
||||||
var ipt *iptables.IPTables
|
var ipt *iptables.IPTables
|
||||||
var err error
|
var err error
|
||||||
@ -86,7 +86,7 @@ func forwardPorts(config *PortMapConf, containerIP net.IP) error {
|
|||||||
if !isV6 {
|
if !isV6 {
|
||||||
// Set the route_localnet bit on the host interface, so that
|
// Set the route_localnet bit on the host interface, so that
|
||||||
// 127/8 can cross a routing boundary.
|
// 127/8 can cross a routing boundary.
|
||||||
hostIfName := getRoutableHostIF(containerIP)
|
hostIfName := getRoutableHostIF(containerNet.IP)
|
||||||
if hostIfName != "" {
|
if hostIfName != "" {
|
||||||
if err := enableLocalnetRouting(hostIfName); err != nil {
|
if err := enableLocalnetRouting(hostIfName); err != nil {
|
||||||
return fmt.Errorf("unable to enable route_localnet: %v", err)
|
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)
|
dnatChain := genDnatChain(config.Name, config.ContainerID)
|
||||||
// First, idempotently tear down this chain in case there was some
|
// First, idempotently tear down this chain in case there was some
|
||||||
// sort of collision or bad state.
|
// sort of collision or bad state.
|
||||||
fillDnatRules(&dnatChain, config, containerIP)
|
fillDnatRules(&dnatChain, config, containerNet)
|
||||||
if err := dnatChain.setup(ipt); err != nil {
|
if err := dnatChain.setup(ipt); err != nil {
|
||||||
return fmt.Errorf("unable to setup DNAT: %v", err)
|
return fmt.Errorf("unable to setup DNAT: %v", err)
|
||||||
}
|
}
|
||||||
@ -112,10 +112,10 @@ func forwardPorts(config *PortMapConf, containerIP net.IP) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkPorts(config *PortMapConf, containerIP net.IP) error {
|
func checkPorts(config *PortMapConf, containerNet net.IPNet) error {
|
||||||
|
|
||||||
dnatChain := genDnatChain(config.Name, config.ContainerID)
|
dnatChain := genDnatChain(config.Name, config.ContainerID)
|
||||||
fillDnatRules(&dnatChain, config, containerIP)
|
fillDnatRules(&dnatChain, config, containerNet)
|
||||||
|
|
||||||
ip4t := maybeGetIptables(false)
|
ip4t := maybeGetIptables(false)
|
||||||
ip6t := maybeGetIptables(true)
|
ip6t := maybeGetIptables(true)
|
||||||
@ -124,7 +124,7 @@ func checkPorts(config *PortMapConf, containerIP net.IP) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ip4t != nil {
|
if ip4t != nil {
|
||||||
exists, err := chainExists(ip4t, dnatChain.table, dnatChain.name)
|
exists, err := utils.ChainExists(ip4t, dnatChain.table, dnatChain.name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -137,7 +137,7 @@ func checkPorts(config *PortMapConf, containerIP net.IP) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ip6t != nil {
|
if ip6t != nil {
|
||||||
exists, err := chainExists(ip6t, dnatChain.table, dnatChain.name)
|
exists, err := utils.ChainExists(ip6t, dnatChain.table, dnatChain.name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -180,8 +180,8 @@ func genDnatChain(netName, containerID string) chain {
|
|||||||
|
|
||||||
// dnatRules generates the destination NAT rules, one per port, to direct
|
// dnatRules generates the destination NAT rules, one per port, to direct
|
||||||
// traffic from hostip:hostport to podip:podport
|
// traffic from hostip:hostport to podip:podport
|
||||||
func fillDnatRules(c *chain, config *PortMapConf, containerIP net.IP) {
|
func fillDnatRules(c *chain, config *PortMapConf, containerNet net.IPNet) {
|
||||||
isV6 := (containerIP.To4() == nil)
|
isV6 := (containerNet.IP.To4() == nil)
|
||||||
comment := trimComment(fmt.Sprintf(`dnat name: "%s" id: "%s"`, config.Name, config.ContainerID))
|
comment := trimComment(fmt.Sprintf(`dnat name: "%s" id: "%s"`, config.Name, config.ContainerID))
|
||||||
entries := config.RuntimeConfig.PortMaps
|
entries := config.RuntimeConfig.PortMaps
|
||||||
setMarkChainName := SetMarkChainName
|
setMarkChainName := SetMarkChainName
|
||||||
@ -224,6 +224,16 @@ func fillDnatRules(c *chain, config *PortMapConf, containerIP net.IP) {
|
|||||||
// the ordering is important here; the mark rules must be first.
|
// the ordering is important here; the mark rules must be first.
|
||||||
c.rules = make([][]string, 0, 3*len(entries))
|
c.rules = make([][]string, 0, 3*len(entries))
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
|
// If a HostIP is given, only process the entry if host and container address families match
|
||||||
|
if entry.HostIP != "" {
|
||||||
|
hostIP := net.ParseIP(entry.HostIP)
|
||||||
|
isHostV6 := (hostIP.To4() == nil)
|
||||||
|
|
||||||
|
if isV6 != isHostV6 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ruleBase := []string{
|
ruleBase := []string{
|
||||||
"-p", entry.Protocol,
|
"-p", entry.Protocol,
|
||||||
"--dport", strconv.Itoa(entry.HostPort)}
|
"--dport", strconv.Itoa(entry.HostPort)}
|
||||||
@ -239,7 +249,7 @@ func fillDnatRules(c *chain, config *PortMapConf, containerIP net.IP) {
|
|||||||
copy(hpRule, ruleBase)
|
copy(hpRule, ruleBase)
|
||||||
|
|
||||||
hpRule = append(hpRule,
|
hpRule = append(hpRule,
|
||||||
"-s", containerIP.String(),
|
"-s", containerNet.String(),
|
||||||
"-j", setMarkChainName,
|
"-j", setMarkChainName,
|
||||||
)
|
)
|
||||||
c.rules = append(c.rules, hpRule)
|
c.rules = append(c.rules, hpRule)
|
||||||
@ -262,7 +272,7 @@ func fillDnatRules(c *chain, config *PortMapConf, containerIP net.IP) {
|
|||||||
copy(dnatRule, ruleBase)
|
copy(dnatRule, ruleBase)
|
||||||
dnatRule = append(dnatRule,
|
dnatRule = append(dnatRule,
|
||||||
"-j", "DNAT",
|
"-j", "DNAT",
|
||||||
"--to-destination", fmtIpPort(containerIP, entry.ContainerPort),
|
"--to-destination", fmtIpPort(containerNet.IP, entry.ContainerPort),
|
||||||
)
|
)
|
||||||
c.rules = append(c.rules, dnatRule)
|
c.rules = append(c.rules, dnatRule)
|
||||||
}
|
}
|
||||||
|
@ -96,6 +96,7 @@ var _ = Describe("portmap integration tests", func() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Describe("Creating an interface in a namespace with the ptp plugin", func() {
|
||||||
// This needs to be done using Ginkgo's asynchronous testing mode.
|
// This needs to be done using Ginkgo's asynchronous testing mode.
|
||||||
It("forwards a TCP port on ipv4", func(done Done) {
|
It("forwards a TCP port on ipv4", func(done Done) {
|
||||||
var err error
|
var err error
|
||||||
@ -171,6 +172,18 @@ var _ = Describe("portmap integration tests", func() {
|
|||||||
cmd.Stdout = GinkgoWriter
|
cmd.Stdout = GinkgoWriter
|
||||||
Expect(cmd.Run()).To(Succeed())
|
Expect(cmd.Run()).To(Succeed())
|
||||||
|
|
||||||
|
// dump ip routes output for debugging
|
||||||
|
cmd = exec.Command("ip", "route")
|
||||||
|
cmd.Stderr = GinkgoWriter
|
||||||
|
cmd.Stdout = GinkgoWriter
|
||||||
|
Expect(cmd.Run()).To(Succeed())
|
||||||
|
|
||||||
|
// dump ip addresses output for debugging
|
||||||
|
cmd = exec.Command("ip", "addr")
|
||||||
|
cmd.Stderr = GinkgoWriter
|
||||||
|
cmd.Stdout = GinkgoWriter
|
||||||
|
Expect(cmd.Run()).To(Succeed())
|
||||||
|
|
||||||
// Sanity check: verify that the container is reachable directly
|
// Sanity check: verify that the container is reachable directly
|
||||||
contOK := testEchoServer(contIP.String(), containerPort, "")
|
contOK := testEchoServer(contIP.String(), containerPort, "")
|
||||||
|
|
||||||
@ -210,6 +223,7 @@ var _ = Describe("portmap integration tests", func() {
|
|||||||
|
|
||||||
}, TIMEOUT*9)
|
}, TIMEOUT*9)
|
||||||
})
|
})
|
||||||
|
})
|
||||||
|
|
||||||
// testEchoServer returns true if we found an echo server on the port
|
// testEchoServer returns true if we found an echo server on the port
|
||||||
func testEchoServer(address string, port int, netns string) bool {
|
func testEchoServer(address string, port int, netns string) bool {
|
||||||
|
@ -16,7 +16,8 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
|
||||||
|
"github.com/containernetworking/cni/pkg/types"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
@ -77,8 +78,10 @@ var _ = Describe("portmapping configuration", func() {
|
|||||||
Expect(c.SNAT).To(Equal(&fvar))
|
Expect(c.SNAT).To(Equal(&fvar))
|
||||||
Expect(c.Name).To(Equal("test"))
|
Expect(c.Name).To(Equal("test"))
|
||||||
|
|
||||||
Expect(c.ContIPv4).To(Equal(net.ParseIP("10.0.0.2")))
|
n, err := types.ParseCIDR("10.0.0.2/24")
|
||||||
Expect(c.ContIPv6).To(Equal(net.ParseIP("2001:db8:1::2")))
|
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() {
|
It("Correctly parses a DEL config", func() {
|
||||||
@ -186,7 +189,8 @@ var _ = Describe("portmapping configuration", func() {
|
|||||||
entryChains: []string{"CNI-HOSTPORT-DNAT"},
|
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{
|
Expect(ch.entryRules).To(Equal([][]string{
|
||||||
{"-m", "comment", "--comment",
|
{"-m", "comment", "--comment",
|
||||||
@ -204,16 +208,16 @@ var _ = Describe("portmapping configuration", func() {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
Expect(ch.rules).To(Equal([][]string{
|
Expect(ch.rules).To(Equal([][]string{
|
||||||
{"-p", "tcp", "--dport", "8080", "-s", "10.0.0.2", "-j", "CNI-HOSTPORT-SETMARK"},
|
{"-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", "-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", "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", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||||
{"-p", "tcp", "--dport", "8081", "-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", "-s", "10.0.0.2", "-j", "CNI-HOSTPORT-SETMARK"},
|
{"-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", "-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", "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", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
|
||||||
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "10.0.0.2:82"},
|
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "10.0.0.2:82"},
|
||||||
}))
|
}))
|
||||||
@ -221,16 +225,17 @@ var _ = Describe("portmapping configuration", func() {
|
|||||||
ch.rules = nil
|
ch.rules = nil
|
||||||
ch.entryRules = 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{
|
Expect(ch.rules).To(Equal([][]string{
|
||||||
{"-p", "tcp", "--dport", "8080", "-s", "2001:db8::2", "-j", "CNI-HOSTPORT-SETMARK"},
|
{"-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", "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", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "[2001:db8::2]:80"},
|
||||||
{"-p", "udp", "--dport", "8080", "-s", "2001:db8::2", "-j", "CNI-HOSTPORT-SETMARK"},
|
{"-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", "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"},
|
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "[2001:db8::2]:82"},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
@ -240,7 +245,8 @@ var _ = Describe("portmapping configuration", func() {
|
|||||||
fvar := false
|
fvar := false
|
||||||
conf.SNAT = &fvar
|
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{
|
Expect(ch.rules).To(Equal([][]string{
|
||||||
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
|
{"-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", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
|
||||||
@ -276,9 +282,10 @@ var _ = Describe("portmapping configuration", func() {
|
|||||||
conf.ContainerID = containerID
|
conf.ContainerID = containerID
|
||||||
|
|
||||||
ch = genDnatChain(conf.Name, 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{
|
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", "-s", "127.0.0.1", "-j", "PLZ-SET-MARK"},
|
||||||
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
|
{"-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" {
|
if ipCfg.Version == "4" {
|
||||||
src.Mask = net.CIDRMask(32, 32)
|
src.Mask = net.CIDRMask(32, 32)
|
||||||
} else {
|
} else {
|
||||||
src.Mask = net.CIDRMask(64, 64)
|
src.Mask = net.CIDRMask(128, 128)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Source to use %s", src.String())
|
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)
|
dest.Mask = net.CIDRMask(0, 32)
|
||||||
} else {
|
} else {
|
||||||
dest.IP = net.IPv6zero
|
dest.IP = net.IPv6zero
|
||||||
dest.Mask = net.CIDRMask(0, 64)
|
dest.Mask = net.CIDRMask(0, 128)
|
||||||
}
|
}
|
||||||
|
|
||||||
route := netlink.Route{
|
route := netlink.Route{
|
||||||
@ -296,6 +296,10 @@ func doRoutes(ipCfgs []*current.IPConfig, origRoutes []*types.Route, iface strin
|
|||||||
|
|
||||||
route.Table = table
|
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
|
// We use route replace in case the route already exists, which
|
||||||
// is possible for the default gateway we added above.
|
// is possible for the default gateway we added above.
|
||||||
err = netlink.RouteReplace(&route)
|
err = netlink.RouteReplace(&route)
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/j-keck/arping"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/containernetworking/cni/pkg/skel"
|
||||||
@ -179,7 +180,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = current.NewResultFromResult(tuningConf.PrevResult)
|
result, err := current.NewResultFromResult(tuningConf.PrevResult)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -208,6 +209,13 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
if err = changeMacAddr(args.IfName, tuningConf.Mac); err != nil {
|
if err = changeMacAddr(args.IfName, tuningConf.Mac); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, ipc := range result.IPs {
|
||||||
|
if ipc.Version == "4" {
|
||||||
|
_ = arping.GratuitousArpOverIfaceByName(ipc.Address.IP, args.IfName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
updateResultsMacAddr(*tuningConf, args.IfName, tuningConf.Mac)
|
updateResultsMacAddr(*tuningConf, args.IfName, tuningConf.Mac)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ $DOCKER run -ti -v ${SRC_DIR}:/go/src/github.com/containernetworking/plugins --r
|
|||||||
apk --no-cache add bash tar;
|
apk --no-cache add bash tar;
|
||||||
cd /go/src/github.com/containernetworking/plugins; umask 0022;
|
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}/*; \
|
rm -f ${OUTPUT_DIR}/*; \
|
||||||
CGO_ENABLED=0 GOARCH=\$arch ./build_linux.sh ${BUILDFLAGS}; \
|
CGO_ENABLED=0 GOARCH=\$arch ./build_linux.sh ${BUILDFLAGS}; \
|
||||||
for format in tgz; do \
|
for format in tgz; do \
|
||||||
|
@ -57,3 +57,7 @@ if [ -n "${vetRes}" ]; then
|
|||||||
echo -e "govet checking failed:\n${vetRes}"
|
echo -e "govet checking failed:\n${vetRes}"
|
||||||
exit 255
|
exit 255
|
||||||
fi
|
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)
|
||||||
|
1
vendor/github.com/containernetworking/cni/libcni/api.go
generated
vendored
1
vendor/github.com/containernetworking/cni/libcni/api.go
generated
vendored
@ -69,6 +69,7 @@ type CNI interface {
|
|||||||
AddNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
|
AddNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
|
||||||
CheckNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
|
CheckNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
|
||||||
DelNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
|
DelNetworkList(ctx context.Context, net *NetworkConfigList, rt *RuntimeConf) error
|
||||||
|
GetNetworkListCachedResult(net *NetworkConfigList, rt *RuntimeConf) (types.Result, error)
|
||||||
|
|
||||||
AddNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
|
AddNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) (types.Result, error)
|
||||||
CheckNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error
|
CheckNetwork(ctx context.Context, net *NetworkConfig, rt *RuntimeConf) error
|
||||||
|
56
vendor/github.com/containernetworking/cni/pkg/invoke/args.go
generated
vendored
56
vendor/github.com/containernetworking/cni/pkg/invoke/args.go
generated
vendored
@ -15,6 +15,7 @@
|
|||||||
package invoke
|
package invoke
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -22,6 +23,8 @@ import (
|
|||||||
type CNIArgs interface {
|
type CNIArgs interface {
|
||||||
// For use with os/exec; i.e., return nil to inherit the
|
// For use with os/exec; i.e., return nil to inherit the
|
||||||
// environment from this process
|
// environment from this process
|
||||||
|
// For use in delegation; inherit the environment from this
|
||||||
|
// process and allow overrides
|
||||||
AsEnv() []string
|
AsEnv() []string
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,17 +60,17 @@ func (args *Args) AsEnv() []string {
|
|||||||
pluginArgsStr = stringify(args.PluginArgs)
|
pluginArgsStr = stringify(args.PluginArgs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure that the custom values are first, so any value present in
|
// Duplicated values which come first will be overrided, so we must put the
|
||||||
// the process environment won't override them.
|
// custom values in the end to avoid being overrided by the process environments.
|
||||||
env = append([]string{
|
env = append(env,
|
||||||
"CNI_COMMAND="+args.Command,
|
"CNI_COMMAND="+args.Command,
|
||||||
"CNI_CONTAINERID="+args.ContainerID,
|
"CNI_CONTAINERID="+args.ContainerID,
|
||||||
"CNI_NETNS="+args.NetNS,
|
"CNI_NETNS="+args.NetNS,
|
||||||
"CNI_ARGS="+pluginArgsStr,
|
"CNI_ARGS="+pluginArgsStr,
|
||||||
"CNI_IFNAME="+args.IfName,
|
"CNI_IFNAME="+args.IfName,
|
||||||
"CNI_PATH="+args.Path,
|
"CNI_PATH="+args.Path,
|
||||||
}, env...)
|
)
|
||||||
return env
|
return dedupEnv(env)
|
||||||
}
|
}
|
||||||
|
|
||||||
// taken from rkt/networking/net_plugin.go
|
// taken from rkt/networking/net_plugin.go
|
||||||
@ -80,3 +83,46 @@ func stringify(pluginArgs [][2]string) string {
|
|||||||
|
|
||||||
return strings.Join(entries, ";")
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"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 {
|
if exec == nil {
|
||||||
exec = defaultExec
|
exec = defaultExec
|
||||||
}
|
}
|
||||||
|
|
||||||
if os.Getenv("CNI_COMMAND") != expectedCommand {
|
|
||||||
return "", nil, fmt.Errorf("CNI_COMMAND is not " + expectedCommand)
|
|
||||||
}
|
|
||||||
|
|
||||||
paths := filepath.SplitList(os.Getenv("CNI_PATH"))
|
paths := filepath.SplitList(os.Getenv("CNI_PATH"))
|
||||||
pluginPath, err := exec.FindInPath(delegatePlugin, paths)
|
pluginPath, err := exec.FindInPath(delegatePlugin, paths)
|
||||||
if err != nil {
|
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
|
// DelegateAdd calls the given delegate plugin with the CNI ADD action and
|
||||||
// JSON configuration
|
// JSON configuration
|
||||||
func DelegateAdd(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) (types.Result, error) {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// DelegateCheck calls the given delegate plugin with the CNI CHECK action and
|
||||||
// JSON configuration
|
// JSON configuration
|
||||||
func DelegateCheck(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error {
|
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 {
|
if err != nil {
|
||||||
return err
|
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
|
// DelegateDel calls the given delegate plugin with the CNI DEL action and
|
||||||
// JSON configuration
|
// JSON configuration
|
||||||
func DelegateDel(ctx context.Context, delegatePlugin string, netconf []byte, exec Exec) error {
|
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 {
|
if err != nil {
|
||||||
return err
|
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,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
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
|
// IsNotExist returns true if the error is due to the chain or rule not existing
|
||||||
func (e *Error) IsNotExist() bool {
|
func (e *Error) IsNotExist() bool {
|
||||||
return e.ExitStatus() == 1 &&
|
if e.ExitStatus() != 1 {
|
||||||
(e.msg == fmt.Sprintf("%s: Bad rule (does a matching rule exist in that chain?).\n", getIptablesCommand(e.proto)) ||
|
return false
|
||||||
e.msg == fmt.Sprintf("%s: No chain/target/match by that name.\n", getIptablesCommand(e.proto)))
|
}
|
||||||
|
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
|
// Protocol to differentiate between IPv4 and IPv6
|
||||||
@ -101,7 +105,13 @@ func NewWithProtocol(proto Protocol) (*IPTables, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
vstring, err := getIptablesVersionString(path)
|
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)
|
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)
|
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)))
|
|
||||||
}
|
|
6
vendor/modules.txt
vendored
6
vendor/modules.txt
vendored
@ -24,7 +24,7 @@ github.com/Microsoft/hcsshim/internal/safefile
|
|||||||
github.com/alexflint/go-filemutex
|
github.com/alexflint/go-filemutex
|
||||||
# github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44
|
# github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44
|
||||||
github.com/buger/jsonparser
|
github.com/buger/jsonparser
|
||||||
# github.com/containernetworking/cni v0.7.0
|
# github.com/containernetworking/cni v0.7.1
|
||||||
github.com/containernetworking/cni/pkg/types
|
github.com/containernetworking/cni/pkg/types
|
||||||
github.com/containernetworking/cni/pkg/types/current
|
github.com/containernetworking/cni/pkg/types/current
|
||||||
github.com/containernetworking/cni/pkg/invoke
|
github.com/containernetworking/cni/pkg/invoke
|
||||||
@ -32,7 +32,7 @@ github.com/containernetworking/cni/pkg/skel
|
|||||||
github.com/containernetworking/cni/pkg/version
|
github.com/containernetworking/cni/pkg/version
|
||||||
github.com/containernetworking/cni/pkg/types/020
|
github.com/containernetworking/cni/pkg/types/020
|
||||||
github.com/containernetworking/cni/libcni
|
github.com/containernetworking/cni/libcni
|
||||||
# github.com/coreos/go-iptables v0.4.2
|
# github.com/coreos/go-iptables v0.4.5
|
||||||
github.com/coreos/go-iptables/iptables
|
github.com/coreos/go-iptables/iptables
|
||||||
# github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7
|
# github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7
|
||||||
github.com/coreos/go-systemd/activation
|
github.com/coreos/go-systemd/activation
|
||||||
@ -48,8 +48,6 @@ github.com/d2g/dhcp4server/leasepool/memorypool
|
|||||||
github.com/godbus/dbus
|
github.com/godbus/dbus
|
||||||
# github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56
|
# github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56
|
||||||
github.com/j-keck/arping
|
github.com/j-keck/arping
|
||||||
# github.com/juju/errors v0.0.0-20180806074554-22422dad46e1
|
|
||||||
github.com/juju/errors
|
|
||||||
# github.com/mattn/go-shellwords v1.0.3
|
# github.com/mattn/go-shellwords v1.0.3
|
||||||
github.com/mattn/go-shellwords
|
github.com/mattn/go-shellwords
|
||||||
# github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b
|
# github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b
|
||||||
|
Reference in New Issue
Block a user