Merge pull request #537 from dcbw/100

Port plugins to CNI 1.0.0 and increase old verison test coverage
This commit is contained in:
Dan Williams 2021-03-03 10:51:56 -06:00 committed by GitHub
commit d385120175
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
99 changed files with 8989 additions and 8141 deletions

4
go.mod
View File

@ -7,7 +7,7 @@ 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.8.1 github.com/containernetworking/cni v0.8.1-0.20201216164644-62e54113f44a
github.com/coreos/go-iptables v0.5.0 github.com/coreos/go-iptables v0.5.0
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
@ -17,7 +17,7 @@ require (
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c
github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56 github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56
github.com/mattn/go-shellwords v1.0.3 github.com/mattn/go-shellwords v1.0.3
github.com/onsi/ginkgo v1.12.1 github.com/onsi/ginkgo v1.13.0
github.com/onsi/gomega v1.10.3 github.com/onsi/gomega v1.10.3
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8
github.com/sirupsen/logrus v1.0.6 // indirect github.com/sirupsen/logrus v1.0.6 // indirect

15
go.sum
View File

@ -6,8 +6,8 @@ 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.8.1 h1:7zpDnQ3T3s4ucOuJ/ZCLrYBxzkg0AELFfII3Epo9TmI= github.com/containernetworking/cni v0.8.1-0.20201216164644-62e54113f44a h1:EDko/CXJ2CYF5pFdlTO/qHwkD0IxpSQY+FcTll6A/F4=
github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= github.com/containernetworking/cni v0.8.1-0.20201216164644-62e54113f44a/go.mod h1:AKuhXbN5EzmD4yTNtfSsX3tPcmtrBI6QcRV0NiNt15Y=
github.com/coreos/go-iptables v0.5.0 h1:mw6SAibtHKZcNzAsOxjoHIG0gy5YFHhypWSSNc6EjbQ= github.com/coreos/go-iptables v0.5.0 h1:mw6SAibtHKZcNzAsOxjoHIG0gy5YFHhypWSSNc6EjbQ=
github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= github.com/coreos/go-iptables v0.5.0/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=
@ -24,6 +24,8 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c h1:RBUpb2b14UnmRHNd2uHz20ZHLDK+SW5Us/vWF5IHRaY= github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c h1:RBUpb2b14UnmRHNd2uHz20ZHLDK+SW5Us/vWF5IHRaY=
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -48,13 +50,17 @@ github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ= github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.13.0 h1:M76yO2HkZASFjXL0HSoZJ1AYEmQxNJmY41Jx1zNUq1Y=
github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA= github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA=
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 h1:2c1EFnZHIPCW8qKWgHMH/fX2PkSabFc5mrVzfUNdg5U= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8 h1:2c1EFnZHIPCW8qKWgHMH/fX2PkSabFc5mrVzfUNdg5U=
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
github.com/sirupsen/logrus v1.0.6 h1:hcP1GmhGigz/O7h1WVUM5KklBp1JoNS9FggWKdj/j3s= github.com/sirupsen/logrus v1.0.6 h1:hcP1GmhGigz/O7h1WVUM5KklBp1JoNS9FggWKdj/j3s=
github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -69,6 +75,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnk
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARVd/2vfGcmI45D2gj45M= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 h1:wBouT66WTYFXdxfVdz9sVWARVd/2vfGcmI45D2gj45M=
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -76,13 +83,17 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637 h1:O5hKNaGxIT4A8OTMnuh6UpmBdI3SAPxlZ3g0olDrJVM= golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637 h1:O5hKNaGxIT4A8OTMnuh6UpmBdI3SAPxlZ3g0olDrJVM=
golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k= golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@ -23,7 +23,7 @@ import (
"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" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/errors" "github.com/containernetworking/plugins/pkg/errors"
) )
@ -286,28 +286,20 @@ func ConstructResult(hnsNetwork *hcsshim.HNSNetwork, hnsEndpoint *hcsshim.HNSEnd
return nil, errors.Annotatef(err, "failed to parse CIDR from %s", hnsNetwork.Subnets[0].AddressPrefix) return nil, errors.Annotatef(err, "failed to parse CIDR from %s", hnsNetwork.Subnets[0].AddressPrefix)
} }
var ipVersion string
if ipv4 := hnsEndpoint.IPAddress.To4(); ipv4 != nil {
ipVersion = "4"
} else if ipv6 := hnsEndpoint.IPAddress.To16(); ipv6 != nil {
ipVersion = "6"
} else {
return nil, fmt.Errorf("IPAddress of HNSEndpoint %s isn't a valid ipv4 or ipv6 Address", hnsEndpoint.Name)
}
resultIPConfig := &current.IPConfig{ resultIPConfig := &current.IPConfig{
Version: ipVersion,
Address: net.IPNet{ Address: net.IPNet{
IP: hnsEndpoint.IPAddress, IP: hnsEndpoint.IPAddress,
Mask: ipSubnet.Mask}, Mask: ipSubnet.Mask},
Gateway: net.ParseIP(hnsEndpoint.GatewayAddress), Gateway: net.ParseIP(hnsEndpoint.GatewayAddress),
} }
result := &current.Result{} result := &current.Result{
result.Interfaces = []*current.Interface{resultInterface} CNIVersion: current.ImplementedSpecVersion,
result.IPs = []*current.IPConfig{resultIPConfig} Interfaces: []*current.Interface{resultInterface},
result.DNS = types.DNS{ IPs: []*current.IPConfig{resultIPConfig},
Search: strings.Split(hnsEndpoint.DNSSuffix, ","), DNS: types.DNS{
Nameservers: strings.Split(hnsEndpoint.DNSServerList, ","), Search: strings.Split(hnsEndpoint.DNSSuffix, ","),
Nameservers: strings.Split(hnsEndpoint.DNSServerList, ","),
},
} }
return result, nil return result, nil
@ -341,29 +333,21 @@ func ConstructHcnResult(hcnNetwork *hcn.HostComputeNetwork, hcnEndpoint *hcn.Hos
return nil, err return nil, err
} }
var ipVersion string
ipAddress := net.ParseIP(hcnEndpoint.IpConfigurations[0].IpAddress) ipAddress := net.ParseIP(hcnEndpoint.IpConfigurations[0].IpAddress)
if ipv4 := ipAddress.To4(); ipv4 != nil {
ipVersion = "4"
} else if ipv6 := ipAddress.To16(); ipv6 != nil {
ipVersion = "6"
} else {
return nil, fmt.Errorf("[win-cni] The IPAddress of hnsEndpoint isn't a valid ipv4 or ipv6 Address.")
}
resultIPConfig := &current.IPConfig{ resultIPConfig := &current.IPConfig{
Version: ipVersion,
Address: net.IPNet{ Address: net.IPNet{
IP: ipAddress, IP: ipAddress,
Mask: ipSubnet.Mask}, Mask: ipSubnet.Mask},
Gateway: net.ParseIP(hcnEndpoint.Routes[0].NextHop), Gateway: net.ParseIP(hcnEndpoint.Routes[0].NextHop),
} }
result := &current.Result{} result := &current.Result{
result.Interfaces = []*current.Interface{resultInterface} CNIVersion: current.ImplementedSpecVersion,
result.IPs = []*current.IPConfig{resultIPConfig} Interfaces: []*current.Interface{resultInterface},
result.DNS = types.DNS{ IPs: []*current.IPConfig{resultIPConfig},
Search: hcnEndpoint.Dns.Search, DNS: types.DNS{
Nameservers: hcnEndpoint.Dns.ServerList, Search: hcnEndpoint.Dns.Search,
Nameservers: hcnEndpoint.Dns.ServerList,
},
} }
return result, nil return result, nil

View File

@ -18,7 +18,7 @@ import (
"bytes" "bytes"
"io/ioutil" "io/ioutil"
"github.com/containernetworking/cni/pkg/types/current" current "github.com/containernetworking/cni/pkg/types/100"
) )
func EnableIP4Forward() error { func EnableIP4Forward() error {
@ -36,12 +36,13 @@ func EnableForward(ips []*current.IPConfig) error {
v6 := false v6 := false
for _, ip := range ips { for _, ip := range ips {
if ip.Version == "4" && !v4 { isV4 := ip.Address.IP.To4() != nil
if isV4 && !v4 {
if err := EnableIP4Forward(); err != nil { if err := EnableIP4Forward(); err != nil {
return err return err
} }
v4 = true v4 = true
} else if ip.Version == "6" && !v6 { } else if !isV4 && !v6 {
if err := EnableIP6Forward(); err != nil { if err := EnableIP6Forward(); err != nil {
return err return err
} }

View File

@ -21,7 +21,7 @@ import (
"net" "net"
"github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/vishvananda/netlink" "github.com/vishvananda/netlink"
) )
@ -57,15 +57,10 @@ func ValidateExpectedInterfaceIPs(ifName string, resultIPs []*current.IPConfig)
findGwy := &netlink.Route{Dst: ourPrefix} findGwy := &netlink.Route{Dst: ourPrefix}
routeFilter := netlink.RT_FILTER_DST routeFilter := netlink.RT_FILTER_DST
var family int
switch { family := netlink.FAMILY_V6
case ips.Version == "4": if ips.Address.IP.To4() != nil {
family = netlink.FAMILY_V4 family = netlink.FAMILY_V4
case ips.Version == "6":
family = netlink.FAMILY_V6
default:
return fmt.Errorf("Invalid IP Version %v for interface %v", ips.Version, ifName)
} }
gwy, err := netlink.RouteListFiltered(family, findGwy, routeFilter) gwy, err := netlink.RouteListFiltered(family, findGwy, routeFilter)

View File

@ -19,7 +19,7 @@ import (
"net" "net"
"os" "os"
"github.com/containernetworking/cni/pkg/types/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ip" "github.com/containernetworking/plugins/pkg/ip"
"github.com/containernetworking/plugins/pkg/utils/sysctl" "github.com/containernetworking/plugins/pkg/utils/sysctl"
@ -61,7 +61,7 @@ func ConfigureIface(ifName string, res *current.Result) error {
// Make sure sysctl "disable_ipv6" is 0 if we are about to add // Make sure sysctl "disable_ipv6" is 0 if we are about to add
// an IPv6 address to the interface // an IPv6 address to the interface
if !has_enabled_ipv6 && ipc.Version == "6" { if !has_enabled_ipv6 && ipc.Address.IP.To4() == nil {
// Enabled IPv6 for loopback "lo" and the interface // Enabled IPv6 for loopback "lo" and the interface
// being configured // being configured
for _, iface := range [2]string{"lo", ifName} { for _, iface := range [2]string{"lo", ifName} {

View File

@ -19,7 +19,7 @@ import (
"syscall" "syscall"
"github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils" "github.com/containernetworking/plugins/pkg/testutils"
@ -109,13 +109,11 @@ var _ = Describe("ConfigureIface", func() {
}, },
IPs: []*current.IPConfig{ IPs: []*current.IPConfig{
{ {
Version: "4",
Interface: current.Int(0), Interface: current.Int(0),
Address: *ipv4, Address: *ipv4,
Gateway: ipgw4, Gateway: ipgw4,
}, },
{ {
Version: "6",
Interface: current.Int(0), Interface: current.Int(0),
Address: *ipv6, Address: *ipv6,
Gateway: ipgw6, Gateway: ipgw6,
@ -281,12 +279,10 @@ var _ = Describe("ConfigureIface", func() {
}, },
IPs: []*current.IPConfig{ IPs: []*current.IPConfig{
{ {
Version: "4",
Address: *ipv4, Address: *ipv4,
Gateway: ipgw4, Gateway: ipgw4,
}, },
{ {
Version: "6",
Address: *ipv6, Address: *ipv6,
Gateway: ipgw6, Gateway: ipgw6,
}, },

View File

@ -17,13 +17,24 @@ package testutils
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"net"
"os/exec" "os/exec"
"strconv" "strconv"
"syscall" "syscall"
) )
// Ping shells out to the `ping` command. Returns nil if successful. // Ping shells out to the `ping` command. Returns nil if successful.
func Ping(saddr, daddr string, isV6 bool, timeoutSec int) error { func Ping(saddr, daddr string, timeoutSec int) error {
ip := net.ParseIP(saddr)
if ip == nil {
return fmt.Errorf("failed to parse IP %q", saddr)
}
bin := "ping6"
if ip.To4() != nil {
bin = "ping"
}
args := []string{ args := []string{
"-c", "1", "-c", "1",
"-W", strconv.Itoa(timeoutSec), "-W", strconv.Itoa(timeoutSec),
@ -31,11 +42,6 @@ func Ping(saddr, daddr string, isV6 bool, timeoutSec int) error {
daddr, daddr,
} }
bin := "ping"
if isV6 {
bin = "ping6"
}
cmd := exec.Command(bin, args...) cmd := exec.Command(bin, args...)
var stderr bytes.Buffer var stderr bytes.Buffer
cmd.Stderr = &stderr cmd.Stderr = &stderr

54
pkg/testutils/testing.go Normal file
View File

@ -0,0 +1,54 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package testutils
import (
"github.com/containernetworking/cni/pkg/version"
)
// AllSpecVersions contains all CNI spec version numbers
var AllSpecVersions = [...]string{"0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0", "1.0.0"}
// SpecVersionHasIPVersion returns true if the given CNI specification version
// includes the "version" field in the IP address elements
func SpecVersionHasIPVersion(ver string) bool {
for _, i := range []string{"0.3.0", "0.3.1", "0.4.0"} {
if ver == i {
return true
}
}
return false
}
// SpecVersionHasCHECK returns true if the given CNI specification version
// supports the CHECK command
func SpecVersionHasCHECK(ver string) bool {
ok, _ := version.GreaterThanOrEqualTo(ver, "0.4.0")
return ok
}
// SpecVersionHasChaining returns true if the given CNI specification version
// supports plugin chaining
func SpecVersionHasChaining(ver string) bool {
ok, _ := version.GreaterThanOrEqualTo(ver, "0.3.0")
return ok
}
// SpecVersionHasMultipleIPs returns true if the given CNI specification version
// supports more than one IP address of each family
func SpecVersionHasMultipleIPs(ver string) bool {
ok, _ := version.GreaterThanOrEqualTo(ver, "0.3.0")
return ok
}

View File

@ -30,7 +30,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/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/coreos/go-systemd/activation" "github.com/coreos/go-systemd/activation"
) )
@ -43,13 +43,15 @@ type DHCP struct {
leases map[string]*DHCPLease leases map[string]*DHCPLease
hostNetnsPrefix string hostNetnsPrefix string
clientTimeout time.Duration clientTimeout time.Duration
clientResendMax time.Duration
broadcast bool broadcast bool
} }
func newDHCP(clientTimeout time.Duration) *DHCP { func newDHCP(clientTimeout, clientResendMax time.Duration) *DHCP {
return &DHCP{ return &DHCP{
leases: make(map[string]*DHCPLease), leases: make(map[string]*DHCPLease),
clientTimeout: clientTimeout, clientTimeout: clientTimeout,
clientResendMax: clientResendMax,
} }
} }
@ -67,7 +69,7 @@ func (d *DHCP) Allocate(args *skel.CmdArgs, result *current.Result) error {
clientID := generateClientID(args.ContainerID, conf.Name, args.IfName) clientID := generateClientID(args.ContainerID, conf.Name, args.IfName)
hostNetns := d.hostNetnsPrefix + args.Netns hostNetns := d.hostNetnsPrefix + args.Netns
l, err := AcquireLease(clientID, hostNetns, args.IfName, d.clientTimeout, d.broadcast) l, err := AcquireLease(clientID, hostNetns, args.IfName, d.clientTimeout, d.clientResendMax, d.broadcast)
if err != nil { if err != nil {
return err return err
} }
@ -81,7 +83,6 @@ func (d *DHCP) Allocate(args *skel.CmdArgs, result *current.Result) error {
d.setLease(clientID, l) d.setLease(clientID, l)
result.IPs = []*current.IPConfig{{ result.IPs = []*current.IPConfig{{
Version: "4",
Address: *ipn, Address: *ipn,
Gateway: l.Gateway(), Gateway: l.Gateway(),
}} }}
@ -162,7 +163,7 @@ func getListener(socketPath string) (net.Listener, error) {
func runDaemon( func runDaemon(
pidfilePath, hostPrefix, socketPath string, pidfilePath, hostPrefix, socketPath string,
dhcpClientTimeout time.Duration, broadcast bool, dhcpClientTimeout time.Duration, resendMax time.Duration, broadcast bool,
) error { ) error {
// since other goroutines (on separate threads) will change namespaces, // since other goroutines (on separate threads) will change namespaces,
// ensure the RPC server does not get scheduled onto those // ensure the RPC server does not get scheduled onto those
@ -183,7 +184,7 @@ func runDaemon(
return fmt.Errorf("Error getting listener: %v", err) return fmt.Errorf("Error getting listener: %v", err)
} }
dhcp := newDHCP(dhcpClientTimeout) dhcp := newDHCP(dhcpClientTimeout, resendMax)
dhcp.hostNetnsPrefix = hostPrefix dhcp.hostNetnsPrefix = hostPrefix
dhcp.broadcast = broadcast dhcp.broadcast = broadcast
rpc.Register(dhcp) rpc.Register(dhcp)

View File

@ -23,7 +23,7 @@ import (
"time" "time"
"github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils" "github.com/containernetworking/plugins/pkg/testutils"

View File

@ -15,7 +15,9 @@
package main package main
import ( import (
"bytes"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"net" "net"
"os" "os"
@ -25,7 +27,7 @@ import (
"time" "time"
"github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils" "github.com/containernetworking/plugins/pkg/testutils"
@ -208,6 +210,11 @@ var _ = Describe("DHCP Operations", func() {
dhcpPluginPath, err := exec.LookPath("dhcp") dhcpPluginPath, err := exec.LookPath("dhcp")
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
clientCmd = exec.Command(dhcpPluginPath, "daemon", "-socketpath", socketPath) clientCmd = exec.Command(dhcpPluginPath, "daemon", "-socketpath", socketPath)
// copy dhcp client's stdout/stderr to test stdout
clientCmd.Stdout = os.Stdout
clientCmd.Stderr = os.Stderr
err = clientCmd.Start() err = clientCmd.Start()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(clientCmd.Process).NotTo(BeNil()) Expect(clientCmd.Process).NotTo(BeNil())
@ -226,118 +233,127 @@ var _ = Describe("DHCP Operations", func() {
clientCmd.Wait() clientCmd.Wait()
Expect(originalNS.Close()).To(Succeed()) Expect(originalNS.Close()).To(Succeed())
Expect(testutils.UnmountNS(originalNS)).To(Succeed())
Expect(targetNS.Close()).To(Succeed()) Expect(targetNS.Close()).To(Succeed())
defer os.RemoveAll(tmpDir) Expect(testutils.UnmountNS(targetNS)).To(Succeed())
Expect(os.RemoveAll(tmpDir)).To(Succeed())
}) })
It("configures and deconfigures a link with ADD/DEL", func() { for _, ver := range testutils.AllSpecVersions {
conf := fmt.Sprintf(`{ // Redefine ver inside for scope so real value is picked up by each dynamically defined It()
"cniVersion": "0.3.1", // See Gingkgo's "Patterns for dynamically generating tests" documentation.
"name": "mynet", ver := ver
"type": "ipvlan",
"ipam": {
"type": "dhcp",
"daemonSocketPath": "%s"
}
}`, socketPath)
args := &skel.CmdArgs{ It(fmt.Sprintf("[%s] configures and deconfigures a link with ADD/DEL", ver), func() {
ContainerID: "dummy", conf := fmt.Sprintf(`{
Netns: targetNS.Path(), "cniVersion": "%s",
IfName: contVethName, "name": "mynet",
StdinData: []byte(conf), "type": "ipvlan",
} "ipam": {
"type": "dhcp",
"daemonSocketPath": "%s"
}
}`, ver, socketPath)
var addResult *current.Result args := &skel.CmdArgs{
err := originalNS.Do(func(ns.NetNS) error { ContainerID: "dummy",
defer GinkgoRecover() Netns: targetNS.Path(),
IfName: contVethName,
StdinData: []byte(conf),
}
r, _, err := testutils.CmdAddWithArgs(args, func() error { var addResult *types100.Result
return cmdAdd(args) err := originalNS.Do(func(ns.NetNS) error {
})
Expect(err).NotTo(HaveOccurred())
addResult, err = current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(len(addResult.IPs)).To(Equal(1))
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24"))
return nil
})
Expect(err).NotTo(HaveOccurred())
err = originalNS.Do(func(ns.NetNS) error {
return testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
})
Expect(err).NotTo(HaveOccurred())
})
It("correctly handles multiple DELs for the same container", func() {
conf := fmt.Sprintf(`{
"cniVersion": "0.3.1",
"name": "mynet",
"type": "ipvlan",
"ipam": {
"type": "dhcp",
"daemonSocketPath": "%s"
}
}`, socketPath)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: contVethName,
StdinData: []byte(conf),
}
var addResult *current.Result
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
r, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
addResult, err = current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(len(addResult.IPs)).To(Equal(1))
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24"))
return nil
})
Expect(err).NotTo(HaveOccurred())
wg := sync.WaitGroup{}
wg.Add(3)
started := sync.WaitGroup{}
started.Add(3)
for i := 0; i < 3; i++ {
go func() {
defer GinkgoRecover() defer GinkgoRecover()
// Wait until all goroutines are running r, _, err := testutils.CmdAddWithArgs(args, func() error {
started.Done() return cmdAdd(args)
started.Wait()
err = originalNS.Do(func(ns.NetNS) error {
return testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
wg.Done()
}()
}
wg.Wait()
err = originalNS.Do(func(ns.NetNS) error { addResult, err = types100.GetResult(r)
return testutils.CmdDelWithArgs(args, func() error { Expect(err).NotTo(HaveOccurred())
return cmdDel(args) Expect(len(addResult.IPs)).To(Equal(1))
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24"))
return nil
}) })
Expect(err).NotTo(HaveOccurred())
err = originalNS.Do(func(ns.NetNS) error {
return testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
})
Expect(err).NotTo(HaveOccurred())
}) })
Expect(err).NotTo(HaveOccurred())
}) It(fmt.Sprintf("[%s] correctly handles multiple DELs for the same container", ver), func() {
conf := fmt.Sprintf(`{
"cniVersion": "%s",
"name": "mynet",
"type": "ipvlan",
"ipam": {
"type": "dhcp",
"daemonSocketPath": "%s"
}
}`, ver, socketPath)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: contVethName,
StdinData: []byte(conf),
}
var addResult *types100.Result
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
r, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
addResult, err = types100.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(len(addResult.IPs)).To(Equal(1))
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24"))
return nil
})
Expect(err).NotTo(HaveOccurred())
wg := sync.WaitGroup{}
wg.Add(3)
started := sync.WaitGroup{}
started.Add(3)
for i := 0; i < 3; i++ {
go func() {
defer GinkgoRecover()
// Wait until all goroutines are running
started.Done()
started.Wait()
err = originalNS.Do(func(ns.NetNS) error {
return testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
})
Expect(err).NotTo(HaveOccurred())
wg.Done()
}()
}
wg.Wait()
err = originalNS.Do(func(ns.NetNS) error {
return testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
})
Expect(err).NotTo(HaveOccurred())
})
}
}) })
const ( const (
@ -508,7 +524,19 @@ var _ = Describe("DHCP Lease Unavailable Operations", func() {
// Start the DHCP client daemon // Start the DHCP client daemon
dhcpPluginPath, err := exec.LookPath("dhcp") dhcpPluginPath, err := exec.LookPath("dhcp")
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
clientCmd = exec.Command(dhcpPluginPath, "daemon", "-socketpath", socketPath) // Use very short timeouts for lease-unavailable operations because
// the same test is run many times, and the delays will exceed the
// `go test` timeout with default delays. Since our DHCP server
// and client daemon are local processes anyway, we can depend on
// them to respond very quickly.
clientCmd = exec.Command(dhcpPluginPath, "daemon", "-socketpath", socketPath, "-timeout", "2s", "-resendmax", "8s")
// copy dhcp client's stdout/stderr to test stdout
var b bytes.Buffer
mw := io.MultiWriter(os.Stdout, &b)
clientCmd.Stdout = mw
clientCmd.Stderr = mw
err = clientCmd.Start() err = clientCmd.Start()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(clientCmd.Process).NotTo(BeNil()) Expect(clientCmd.Process).NotTo(BeNil())
@ -527,92 +555,101 @@ var _ = Describe("DHCP Lease Unavailable Operations", func() {
clientCmd.Wait() clientCmd.Wait()
Expect(originalNS.Close()).To(Succeed()) Expect(originalNS.Close()).To(Succeed())
Expect(testutils.UnmountNS(originalNS)).To(Succeed())
Expect(targetNS.Close()).To(Succeed()) Expect(targetNS.Close()).To(Succeed())
defer os.RemoveAll(tmpDir) Expect(testutils.UnmountNS(targetNS)).To(Succeed())
Expect(os.RemoveAll(tmpDir)).To(Succeed())
}) })
It("Configures multiple links with multiple ADD with second lease unavailable", func() { for _, ver := range testutils.AllSpecVersions {
conf := fmt.Sprintf(`{ // Redefine ver inside for scope so real value is picked up by each dynamically defined It()
"cniVersion": "0.3.1", // See Gingkgo's "Patterns for dynamically generating tests" documentation.
"name": "mynet", ver := ver
"type": "bridge",
"bridge": "%s",
"ipam": {
"type": "dhcp",
"daemonSocketPath": "%s"
}
}`, hostBridgeName, socketPath)
args := &skel.CmdArgs{ It(fmt.Sprintf("[%s] configures multiple links with multiple ADD with second lease unavailable", ver), func() {
ContainerID: "dummy", conf := fmt.Sprintf(`{
Netns: targetNS.Path(), "cniVersion": "%s",
IfName: contVethName0, "name": "mynet",
StdinData: []byte(conf), "type": "bridge",
} "bridge": "%s",
"ipam": {
"type": "dhcp",
"daemonSocketPath": "%s"
}
}`, ver, hostBridgeName, socketPath)
var addResult *current.Result args := &skel.CmdArgs{
err := originalNS.Do(func(ns.NetNS) error { ContainerID: "dummy",
defer GinkgoRecover() Netns: targetNS.Path(),
IfName: contVethName0,
StdinData: []byte(conf),
}
r, _, err := testutils.CmdAddWithArgs(args, func() error { var addResult *types100.Result
return cmdAdd(args) err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
r, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
addResult, err = types100.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(len(addResult.IPs)).To(Equal(1))
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24"))
return nil
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
addResult, err = current.GetResult(r) args = &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: contVethName1,
StdinData: []byte(conf),
}
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
_, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).To(HaveOccurred())
println(err.Error())
Expect(err.Error()).To(Equal("error calling DHCP.Allocate: no more tries"))
return nil
})
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(len(addResult.IPs)).To(Equal(1))
Expect(addResult.IPs[0].Address.String()).To(Equal("192.168.1.5/24"))
return nil
})
Expect(err).NotTo(HaveOccurred())
args = &skel.CmdArgs{ args = &skel.CmdArgs{
ContainerID: "dummy", ContainerID: "dummy",
Netns: targetNS.Path(), Netns: targetNS.Path(),
IfName: contVethName1, IfName: contVethName1,
StdinData: []byte(conf), StdinData: []byte(conf),
} }
err = originalNS.Do(func(ns.NetNS) error { err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover() return testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
_, _, err := testutils.CmdAddWithArgs(args, func() error { })
return cmdAdd(args)
}) })
Expect(err).To(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
println(err.Error())
Expect(err.Error()).To(Equal("error calling DHCP.Allocate: no more tries"))
return nil
})
Expect(err).NotTo(HaveOccurred())
args = &skel.CmdArgs{ args = &skel.CmdArgs{
ContainerID: "dummy", ContainerID: "dummy",
Netns: targetNS.Path(), Netns: targetNS.Path(),
IfName: contVethName1, IfName: contVethName0,
StdinData: []byte(conf), StdinData: []byte(conf),
} }
err = originalNS.Do(func(ns.NetNS) error { err = originalNS.Do(func(ns.NetNS) error {
return testutils.CmdDelWithArgs(args, func() error { return testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args) return cmdDel(args)
})
}) })
Expect(err).NotTo(HaveOccurred())
}) })
Expect(err).NotTo(HaveOccurred()) }
args = &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: contVethName0,
StdinData: []byte(conf),
}
err = originalNS.Do(func(ns.NetNS) error {
return testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
})
Expect(err).NotTo(HaveOccurred())
})
}) })

View File

@ -57,6 +57,7 @@ type DHCPLease struct {
rebindingTime time.Time rebindingTime time.Time
expireTime time.Time expireTime time.Time
timeout time.Duration timeout time.Duration
resendMax time.Duration
broadcast bool broadcast bool
stopping uint32 stopping uint32
stop chan struct{} stop chan struct{}
@ -68,13 +69,14 @@ type DHCPLease struct {
// calling DHCPLease.Stop() // calling DHCPLease.Stop()
func AcquireLease( func AcquireLease(
clientID, netns, ifName string, clientID, netns, ifName string,
timeout time.Duration, broadcast bool, timeout, resendMax time.Duration, broadcast bool,
) (*DHCPLease, error) { ) (*DHCPLease, error) {
errCh := make(chan error, 1) errCh := make(chan error, 1)
l := &DHCPLease{ l := &DHCPLease{
clientID: clientID, clientID: clientID,
stop: make(chan struct{}), stop: make(chan struct{}),
timeout: timeout, timeout: timeout,
resendMax: resendMax,
broadcast: broadcast, broadcast: broadcast,
} }
@ -139,7 +141,7 @@ func (l *DHCPLease) acquire() error {
opts[dhcp4.OptionClientIdentifier] = []byte(l.clientID) opts[dhcp4.OptionClientIdentifier] = []byte(l.clientID)
opts[dhcp4.OptionParameterRequestList] = []byte{byte(dhcp4.OptionRouter), byte(dhcp4.OptionSubnetMask)} opts[dhcp4.OptionParameterRequestList] = []byte{byte(dhcp4.OptionRouter), byte(dhcp4.OptionSubnetMask)}
pkt, err := backoffRetry(func() (*dhcp4.Packet, error) { pkt, err := backoffRetry(l.resendMax, func() (*dhcp4.Packet, error) {
ok, ack, err := DhcpRequest(c, opts) ok, ack, err := DhcpRequest(c, opts)
switch { switch {
case err != nil: case err != nil:
@ -258,7 +260,7 @@ func (l *DHCPLease) renew() error {
opts := make(dhcp4.Options) opts := make(dhcp4.Options)
opts[dhcp4.OptionClientIdentifier] = []byte(l.clientID) opts[dhcp4.OptionClientIdentifier] = []byte(l.clientID)
pkt, err := backoffRetry(func() (*dhcp4.Packet, error) { pkt, err := backoffRetry(l.resendMax, func() (*dhcp4.Packet, error) {
ok, ack, err := DhcpRenew(c, *l.ack, opts) ok, ack, err := DhcpRenew(c, *l.ack, opts)
switch { switch {
case err != nil: case err != nil:
@ -340,7 +342,7 @@ func jitter(span time.Duration) time.Duration {
return time.Duration(float64(span) * (2.0*rand.Float64() - 1.0)) return time.Duration(float64(span) * (2.0*rand.Float64() - 1.0))
} }
func backoffRetry(f func() (*dhcp4.Packet, error)) (*dhcp4.Packet, error) { func backoffRetry(resendMax time.Duration, f func() (*dhcp4.Packet, error)) (*dhcp4.Packet, error) {
var baseDelay time.Duration = resendDelay0 var baseDelay time.Duration = resendDelay0
var sleepTime time.Duration var sleepTime time.Duration
@ -358,7 +360,7 @@ func backoffRetry(f func() (*dhcp4.Packet, error)) (*dhcp4.Packet, error) {
time.Sleep(sleepTime) time.Sleep(sleepTime)
if baseDelay < resendDelayMax { if baseDelay < resendMax {
baseDelay *= 2 baseDelay *= 2
} else { } else {
break break

View File

@ -26,7 +26,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/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/cni/pkg/version"
bv "github.com/containernetworking/plugins/pkg/utils/buildversion" bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
) )
@ -40,19 +40,21 @@ func main() {
var socketPath string var socketPath string
var broadcast bool var broadcast bool
var timeout time.Duration var timeout time.Duration
var resendMax time.Duration
daemonFlags := flag.NewFlagSet("daemon", flag.ExitOnError) daemonFlags := flag.NewFlagSet("daemon", flag.ExitOnError)
daemonFlags.StringVar(&pidfilePath, "pidfile", "", "optional path to write daemon PID to") daemonFlags.StringVar(&pidfilePath, "pidfile", "", "optional path to write daemon PID to")
daemonFlags.StringVar(&hostPrefix, "hostprefix", "", "optional prefix to host root") daemonFlags.StringVar(&hostPrefix, "hostprefix", "", "optional prefix to host root")
daemonFlags.StringVar(&socketPath, "socketpath", "", "optional dhcp server socketpath") daemonFlags.StringVar(&socketPath, "socketpath", "", "optional dhcp server socketpath")
daemonFlags.BoolVar(&broadcast, "broadcast", false, "broadcast DHCP leases") daemonFlags.BoolVar(&broadcast, "broadcast", false, "broadcast DHCP leases")
daemonFlags.DurationVar(&timeout, "timeout", 10*time.Second, "optional dhcp client timeout duration") daemonFlags.DurationVar(&timeout, "timeout", 10*time.Second, "optional dhcp client timeout duration")
daemonFlags.DurationVar(&resendMax, "resendmax", resendDelayMax, "optional dhcp client resend max duration")
daemonFlags.Parse(os.Args[2:]) daemonFlags.Parse(os.Args[2:])
if socketPath == "" { if socketPath == "" {
socketPath = defaultSocketPath socketPath = defaultSocketPath
} }
if err := runDaemon(pidfilePath, hostPrefix, socketPath, timeout, broadcast); err != nil { if err := runDaemon(pidfilePath, hostPrefix, socketPath, timeout, resendMax, broadcast); err != nil {
log.Printf(err.Error()) log.Printf(err.Error())
os.Exit(1) os.Exit(1)
} }
@ -69,7 +71,7 @@ func cmdAdd(args *skel.CmdArgs) error {
return err return err
} }
result := &current.Result{} result := &current.Result{CNIVersion: current.ImplementedSpecVersion}
if err := rpcCall("DHCP.Allocate", args, result); err != nil { if err := rpcCall("DHCP.Allocate", args, result); err != nil {
return err return err
} }
@ -96,7 +98,7 @@ func cmdCheck(args *skel.CmdArgs) error {
return err return err
} }
result := &current.Result{} result := &current.Result{CNIVersion: current.ImplementedSpecVersion}
if err := rpcCall("DHCP.Allocate", args, result); err != nil { if err := rpcCall("DHCP.Allocate", args, result); err != nil {
return err return err
} }

View File

@ -21,7 +21,7 @@ import (
"os" "os"
"strconv" "strconv"
"github.com/containernetworking/cni/pkg/types/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ip" "github.com/containernetworking/plugins/pkg/ip"
"github.com/containernetworking/plugins/plugins/ipam/host-local/backend" "github.com/containernetworking/plugins/plugins/ipam/host-local/backend"
) )
@ -108,13 +108,8 @@ func (a *IPAllocator) Get(id string, ifname string, requestedIP net.IP) (*curren
if reservedIP == nil { if reservedIP == nil {
return nil, fmt.Errorf("no IP addresses available in range set: %s", a.rangeset.String()) return nil, fmt.Errorf("no IP addresses available in range set: %s", a.rangeset.String())
} }
version := "4"
if reservedIP.IP.To4() == nil {
version = "6"
}
return &current.IPConfig{ return &current.IPConfig{
Version: version,
Address: *reservedIP, Address: *reservedIP,
Gateway: gw, Gateway: gw,
}, nil }, nil

View File

@ -19,7 +19,7 @@ import (
"net" "net"
"github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/current" current "github.com/containernetworking/cni/pkg/types/100"
fakestore "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/testing" fakestore "github.com/containernetworking/plugins/plugins/ipam/host-local/backend/testing"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"

View File

@ -20,7 +20,7 @@ import (
"net" "net"
"github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/020" "github.com/containernetworking/cni/pkg/version"
) )
// The top-level network config - IPAM plugins are passed the full configuration // The top-level network config - IPAM plugins are passed the full configuration
@ -136,10 +136,8 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) {
// 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 { if ok, _ := version.GreaterThanOrEqualTo(n.CNIVersion, "0.3.0"); !ok {
if n.CNIVersion == v { return nil, "", fmt.Errorf("CNI version %v does not support more than 1 address per family", n.CNIVersion)
return nil, "", fmt.Errorf("CNI version %v does not support more than 1 address per family", n.CNIVersion)
}
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -25,7 +25,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/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/cni/pkg/version"
) )
@ -62,7 +62,7 @@ func cmdAdd(args *skel.CmdArgs) error {
return err return err
} }
result := &current.Result{} result := &current.Result{CNIVersion: current.ImplementedSpecVersion}
if ipamConf.ResolvConf != "" { if ipamConf.ResolvConf != "" {
dns, err := parseResolvConf(ipamConf.ResolvConf) dns, err := parseResolvConf(ipamConf.ResolvConf)

View File

@ -22,8 +22,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"
types020 "github.com/containernetworking/cni/pkg/types/020" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/types/current"
"github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/cni/pkg/version"
bv "github.com/containernetworking/plugins/pkg/utils/buildversion" bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
) )
@ -225,20 +224,16 @@ func LoadIPAMConfig(bytes []byte, envArgs string) (*IPAMConfig, string, error) {
} }
if n.IPAM.Addresses[i].Address.IP.To4() != nil { if n.IPAM.Addresses[i].Address.IP.To4() != nil {
n.IPAM.Addresses[i].Version = "4"
numV4++ numV4++
} else { } else {
n.IPAM.Addresses[i].Version = "6"
numV6++ numV6++
} }
} }
// 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 { if ok, _ := version.GreaterThanOrEqualTo(n.CNIVersion, "0.3.0"); !ok {
if n.CNIVersion == v { return nil, "", fmt.Errorf("CNI version %v does not support more than 1 address per family", n.CNIVersion)
return nil, "", fmt.Errorf("CNI version %v does not support more than 1 address per family", n.CNIVersion)
}
} }
} }
@ -254,14 +249,16 @@ func cmdAdd(args *skel.CmdArgs) error {
return err return err
} }
result := &current.Result{} result := &current.Result{
result.DNS = ipamConf.DNS CNIVersion: current.ImplementedSpecVersion,
result.Routes = ipamConf.Routes DNS: ipamConf.DNS,
Routes: ipamConf.Routes,
}
for _, v := range ipamConf.Addresses { for _, v := range ipamConf.Addresses {
result.IPs = append(result.IPs, &current.IPConfig{ result.IPs = append(result.IPs, &current.IPConfig{
Version: v.Version,
Address: v.Address, Address: v.Address,
Gateway: v.Gateway}) Gateway: v.Gateway,
})
} }
return types.PrintResult(result, confVersion) return types.PrintResult(result, confVersion)

View File

@ -15,12 +15,13 @@
package main package main
import ( import (
"fmt"
"net" "net"
"strings" "strings"
"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/100"
"github.com/containernetworking/plugins/pkg/testutils" "github.com/containernetworking/plugins/pkg/testutils"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
@ -28,18 +29,101 @@ import (
) )
var _ = Describe("static Operations", func() { var _ = Describe("static Operations", func() {
It("allocates and releases addresses with ADD/DEL", func() { for _, ver := range testutils.AllSpecVersions {
const ifname string = "eth0" // Redefine ver inside for scope so real value is picked up by each dynamically defined It()
const nspath string = "/some/where" // See Gingkgo's "Patterns for dynamically generating tests" documentation.
ver := ver
conf := `{ It(fmt.Sprintf("[%s] allocates and releases addresses with ADD/DEL", ver), func() {
"cniVersion": "0.3.1", const ifname string = "eth0"
"name": "mynet", const nspath string = "/some/where"
"type": "ipvlan",
"master": "foo0", conf := fmt.Sprintf(`{
"ipam": { "cniVersion": "%s",
"type": "static", "name": "mynet",
"addresses": [ { "type": "ipvlan",
"master": "foo0",
"ipam": {
"type": "static",
"addresses": [ {
"address": "10.10.0.1/24",
"gateway": "10.10.0.254"
},
{
"address": "3ffe:ffff:0:01ff::1/64",
"gateway": "3ffe:ffff:0::1"
}],
"routes": [
{ "dst": "0.0.0.0/0" },
{ "dst": "192.168.0.0/16", "gw": "10.10.5.1" },
{ "dst": "3ffe:ffff:0:01ff::1/64" }],
"dns": {
"nameservers" : ["8.8.8.8"],
"domain": "example.com",
"search": [ "example.com" ]
}
}
}`, ver)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: nspath,
IfName: ifname,
StdinData: []byte(conf),
}
// Allocate the IP
r, raw, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
if testutils.SpecVersionHasIPVersion(ver) {
Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0))
}
result, err := types100.GetResult(r)
Expect(err).NotTo(HaveOccurred())
// Gomega is cranky about slices with different caps
Expect(*result.IPs[0]).To(Equal(
types100.IPConfig{
Address: mustCIDR("10.10.0.1/24"),
Gateway: net.ParseIP("10.10.0.254"),
}))
Expect(*result.IPs[1]).To(Equal(
types100.IPConfig{
Address: mustCIDR("3ffe:ffff:0:01ff::1/64"),
Gateway: net.ParseIP("3ffe:ffff:0::1"),
},
))
Expect(len(result.IPs)).To(Equal(2))
Expect(result.Routes).To(Equal([]*types.Route{
{Dst: mustCIDR("0.0.0.0/0")},
{Dst: mustCIDR("192.168.0.0/16"), GW: net.ParseIP("10.10.5.1")},
{Dst: mustCIDR("3ffe:ffff:0:01ff::1/64")},
}))
// Release the IP
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
})
It(fmt.Sprintf("[%s] doesn't error when passed an unknown ID on DEL", ver), func() {
const ifname string = "eth0"
const nspath string = "/some/where"
conf := fmt.Sprintf(`{
"cniVersion": "%s",
"name": "mynet",
"type": "ipvlan",
"master": "foo0",
"ipam": {
"type": "static",
"addresses": [ {
"address": "10.10.0.1/24", "address": "10.10.0.1/24",
"gateway": "10.10.0.254" "gateway": "10.10.0.254"
}, },
@ -47,439 +131,370 @@ var _ = Describe("static Operations", func() {
"address": "3ffe:ffff:0:01ff::1/64", "address": "3ffe:ffff:0:01ff::1/64",
"gateway": "3ffe:ffff:0::1" "gateway": "3ffe:ffff:0::1"
}], }],
"routes": [ "routes": [
{ "dst": "0.0.0.0/0" }, { "dst": "0.0.0.0/0" },
{ "dst": "192.168.0.0/16", "gw": "10.10.5.1" }, { "dst": "192.168.0.0/16", "gw": "10.10.5.1" },
{ "dst": "3ffe:ffff:0:01ff::1/64" }], { "dst": "3ffe:ffff:0:01ff::1/64" }],
"dns": { "dns": {
"nameservers" : ["8.8.8.8"], "nameservers" : ["8.8.8.8"],
"domain": "example.com", "domain": "example.com",
"search": [ "example.com" ] "search": [ "example.com" ]
}
} }
}`, ver)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: nspath,
IfName: ifname,
StdinData: []byte(conf),
} }
}`
args := &skel.CmdArgs{ // Release the IP
ContainerID: "dummy", err := testutils.CmdDelWithArgs(args, func() error {
Netns: nspath, return cmdDel(args)
IfName: ifname, })
StdinData: []byte(conf), Expect(err).NotTo(HaveOccurred())
}
// 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) It(fmt.Sprintf("[%s] allocates and releases addresses with ADD/DEL, with ENV variables", ver), func() {
Expect(err).NotTo(HaveOccurred()) const ifname string = "eth0"
const nspath string = "/some/where"
// Gomega is cranky about slices with different caps conf := fmt.Sprintf(`{
Expect(*result.IPs[0]).To(Equal( "cniVersion": "%s",
current.IPConfig{ "name": "mynet",
Version: "4", "type": "ipvlan",
Address: mustCIDR("10.10.0.1/24"), "master": "foo0",
Gateway: net.ParseIP("10.10.0.254"), "ipam": {
"type": "static",
"routes": [
{ "dst": "0.0.0.0/0" },
{ "dst": "192.168.0.0/16", "gw": "10.10.5.1" }],
"dns": {
"nameservers" : ["8.8.8.8"],
"domain": "example.com",
"search": [ "example.com" ]
}
}
}`, ver)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: nspath,
IfName: ifname,
StdinData: []byte(conf),
Args: "IP=10.10.0.1/24;GATEWAY=10.10.0.254",
}
// Allocate the IP
r, raw, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
if testutils.SpecVersionHasIPVersion(ver) {
Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0))
}
result, err := types100.GetResult(r)
Expect(err).NotTo(HaveOccurred())
// Gomega is cranky about slices with different caps
Expect(*result.IPs[0]).To(Equal(
types100.IPConfig{
Address: mustCIDR("10.10.0.1/24"),
Gateway: net.ParseIP("10.10.0.254"),
}))
Expect(len(result.IPs)).To(Equal(1))
Expect(result.Routes).To(Equal([]*types.Route{
{Dst: mustCIDR("0.0.0.0/0")},
{Dst: mustCIDR("192.168.0.0/16"), GW: net.ParseIP("10.10.5.1")},
})) }))
Expect(*result.IPs[1]).To(Equal( // Release the IP
current.IPConfig{ err = testutils.CmdDelWithArgs(args, func() error {
Version: "6", return cmdDel(args)
Address: mustCIDR("3ffe:ffff:0:01ff::1/64"), })
Gateway: net.ParseIP("3ffe:ffff:0::1"), Expect(err).NotTo(HaveOccurred())
},
))
Expect(len(result.IPs)).To(Equal(2))
Expect(result.Routes).To(Equal([]*types.Route{
{Dst: mustCIDR("0.0.0.0/0")},
{Dst: mustCIDR("192.168.0.0/16"), GW: net.ParseIP("10.10.5.1")},
{Dst: mustCIDR("3ffe:ffff:0:01ff::1/64")},
}))
// Release the IP
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
}) })
Expect(err).NotTo(HaveOccurred())
})
It("doesn't error when passed an unknown ID on DEL", func() { It(fmt.Sprintf("[%s] allocates and releases multiple addresses with ADD/DEL, with ENV variables", ver), func() {
const ifname string = "eth0" const ifname string = "eth0"
const nspath string = "/some/where" const nspath string = "/some/where"
conf := `{ conf := fmt.Sprintf(`{
"cniVersion": "0.3.0", "cniVersion": "%s",
"name": "mynet", "name": "mynet",
"type": "ipvlan", "type": "ipvlan",
"master": "foo0", "master": "foo0",
"ipam": { "ipam": {
"type": "static", "type": "static"
"addresses": [ { }
"address": "10.10.0.1/24", }`, ver)
"gateway": "10.10.0.254"
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: nspath,
IfName: ifname,
StdinData: []byte(conf),
Args: "IP=10.10.0.1/24,11.11.0.1/24;GATEWAY=10.10.0.254",
}
// Allocate the IP
r, raw, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
if !testutils.SpecVersionHasMultipleIPs(ver) {
errStr := fmt.Sprintf("CNI version %s does not support more than 1 address per family", ver)
Expect(err).To(MatchError(errStr))
return
}
Expect(err).NotTo(HaveOccurred())
if testutils.SpecVersionHasIPVersion(ver) {
Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0))
}
result, err := types100.GetResult(r)
Expect(err).NotTo(HaveOccurred())
// Gomega is cranky about slices with different caps
Expect(*result.IPs[0]).To(Equal(
types100.IPConfig{
Address: mustCIDR("10.10.0.1/24"),
Gateway: net.ParseIP("10.10.0.254"),
}))
Expect(*result.IPs[1]).To(Equal(
types100.IPConfig{
Address: mustCIDR("11.11.0.1/24"),
Gateway: nil,
}))
Expect(len(result.IPs)).To(Equal(2))
// Release the IP
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
})
It(fmt.Sprintf("[%s] allocates and releases multiple addresses with ADD/DEL, from RuntimeConfig", ver), func() {
const ifname string = "eth0"
const nspath string = "/some/where"
conf := fmt.Sprintf(`{
"cniVersion": "%s",
"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": {
"address": "3ffe:ffff:0:01ff::1/64",
"gateway": "3ffe:ffff:0::1"
}],
"routes": [
{ "dst": "0.0.0.0/0" },
{ "dst": "192.168.0.0/16", "gw": "10.10.5.1" },
{ "dst": "3ffe:ffff:0:01ff::1/64" }],
"dns": {
"nameservers" : ["8.8.8.8"],
"domain": "example.com",
"search": [ "example.com" ]
}}}`
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: nspath,
IfName: ifname,
StdinData: []byte(conf),
}
// Release the IP
err := testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
})
It("allocates and releases addresses with ADD/DEL, with ENV variables", func() {
const ifname string = "eth0"
const nspath string = "/some/where"
conf := `{
"cniVersion": "0.3.1",
"name": "mynet",
"type": "ipvlan",
"master": "foo0",
"ipam": {
"type": "static",
"routes": [
{ "dst": "0.0.0.0/0" },
{ "dst": "192.168.0.0/16", "gw": "10.10.5.1" }],
"dns": {
"nameservers" : ["8.8.8.8"],
"domain": "example.com",
"search": [ "example.com" ]
}
}
}`
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: nspath,
IfName: ifname,
StdinData: []byte(conf),
Args: "IP=10.10.0.1/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())
// Gomega is cranky about slices with different caps
Expect(*result.IPs[0]).To(Equal(
current.IPConfig{
Version: "4",
Address: mustCIDR("10.10.0.1/24"),
Gateway: net.ParseIP("10.10.0.254"),
}))
Expect(len(result.IPs)).To(Equal(1))
Expect(result.Routes).To(Equal([]*types.Route{
{Dst: mustCIDR("0.0.0.0/0")},
{Dst: mustCIDR("192.168.0.0/16"), GW: net.ParseIP("10.10.5.1")},
}))
// Release the IP
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
})
It("allocates and releases multiple addresses with ADD/DEL, with ENV variables", func() {
const ifname string = "eth0"
const nspath string = "/some/where"
conf := `{
"cniVersion": "0.3.1",
"name": "mynet",
"type": "ipvlan",
"master": "foo0",
"ipam": {
"type": "static"
}
}`
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: nspath,
IfName: ifname,
StdinData: []byte(conf),
Args: "IP=10.10.0.1/24,11.11.0.1/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())
// Gomega is cranky about slices with different caps
Expect(*result.IPs[0]).To(Equal(
current.IPConfig{
Version: "4",
Address: mustCIDR("10.10.0.1/24"),
Gateway: net.ParseIP("10.10.0.254"),
}))
Expect(*result.IPs[1]).To(Equal(
current.IPConfig{
Version: "4",
Address: mustCIDR("11.11.0.1/24"),
Gateway: nil,
}))
Expect(len(result.IPs)).To(Equal(2))
// Release the IP
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
})
It("allocates and releases multiple addresses with ADD/DEL, from RuntimeConfig", 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 := &skel.CmdArgs{
ContainerID: "dummy",
Netns: nspath,
IfName: ifname,
StdinData: []byte(conf),
}
// 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())
// Gomega is cranky about slices with different caps
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())
})
It("allocates and releases multiple addresses with ADD/DEL, from args", func() {
const ifname string = "eth0"
const nspath string = "/some/where"
conf := `{
"cniVersion": "0.3.1",
"name": "mynet",
"type": "ipvlan",
"master": "foo0",
"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" ]
}
},
"args": {
"cni": {
"ips" : ["10.10.0.1/24", "3ffe:ffff:0:01ff::1/64"] "ips" : ["10.10.0.1/24", "3ffe:ffff:0:01ff::1/64"]
} }
}`, ver)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: nspath,
IfName: ifname,
StdinData: []byte(conf),
} }
}`
args := &skel.CmdArgs{ // Allocate the IP
ContainerID: "dummy", r, raw, err := testutils.CmdAddWithArgs(args, func() error {
Netns: nspath, return cmdAdd(args)
IfName: ifname, })
StdinData: []byte(conf), Expect(err).NotTo(HaveOccurred())
} if testutils.SpecVersionHasIPVersion(ver) {
Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0))
// 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())
// Gomega is cranky about slices with different caps
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())
})
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{ result, err := types100.GetResult(r)
ContainerID: "dummy", Expect(err).NotTo(HaveOccurred())
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 // Gomega is cranky about slices with different caps
r, raw, err := testutils.CmdAddWithArgs(args, func() error { Expect(*result.IPs[0]).To(Equal(
return cmdAdd(args) types100.IPConfig{
}) Address: mustCIDR("10.10.0.1/24"),
Expect(err).NotTo(HaveOccurred()) }))
Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0)) Expect(*result.IPs[1]).To(Equal(
types100.IPConfig{
result, err := current.GetResult(r) Address: mustCIDR("3ffe:ffff:0:01ff::1/64"),
Expect(err).NotTo(HaveOccurred()) },
))
// only addresses in runtimeConfig configured because of its priorities Expect(len(result.IPs)).To(Equal(2))
Expect(*result.IPs[0]).To(Equal( Expect(result.Routes).To(Equal([]*types.Route{
current.IPConfig{ {Dst: mustCIDR("0.0.0.0/0"), GW: net.ParseIP("10.10.0.254")},
Version: "4", {Dst: mustCIDR("3ffe:ffff:0:01ff::1/64"), GW: net.ParseIP("3ffe:ffff:0::1")},
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 // Release the IP
err = testutils.CmdDelWithArgs(args, func() error { err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args) return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
}) })
Expect(err).NotTo(HaveOccurred())
})
It(fmt.Sprintf("[%s] allocates and releases multiple addresses with ADD/DEL, from args", ver), func() {
const ifname string = "eth0"
const nspath string = "/some/where"
conf := fmt.Sprintf(`{
"cniVersion": "%s",
"name": "mynet",
"type": "ipvlan",
"master": "foo0",
"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" ]
}
},
"args": {
"cni": {
"ips" : ["10.10.0.1/24", "3ffe:ffff:0:01ff::1/64"]
}
}
}`, ver)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: nspath,
IfName: ifname,
StdinData: []byte(conf),
}
// Allocate the IP
r, raw, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
if testutils.SpecVersionHasIPVersion(ver) {
Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0))
}
result, err := types100.GetResult(r)
Expect(err).NotTo(HaveOccurred())
// Gomega is cranky about slices with different caps
Expect(*result.IPs[0]).To(Equal(
types100.IPConfig{
Address: mustCIDR("10.10.0.1/24"),
}))
Expect(*result.IPs[1]).To(Equal(
types100.IPConfig{
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())
})
It(fmt.Sprintf("[%s] allocates and releases multiple addresses with ADD/DEL, from RuntimeConfig/ARGS/CNI_ARGS", ver), func() {
const ifname string = "eth0"
const nspath string = "/some/where"
conf := fmt.Sprintf(`{
"cniVersion": "%s",
"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"]
}
}
}`, ver)
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())
if testutils.SpecVersionHasIPVersion(ver) {
Expect(strings.Index(string(raw), "\"version\":")).Should(BeNumerically(">", 0))
}
result, err := types100.GetResult(r)
Expect(err).NotTo(HaveOccurred())
// only addresses in runtimeConfig configured because of its priorities
Expect(*result.IPs[0]).To(Equal(
types100.IPConfig{
Address: mustCIDR("10.10.0.1/24"),
}))
Expect(*result.IPs[1]).To(Equal(
types100.IPConfig{
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 {

View File

@ -29,7 +29,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/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/ip" "github.com/containernetworking/plugins/pkg/ip"
"github.com/containernetworking/plugins/pkg/ipam" "github.com/containernetworking/plugins/pkg/ipam"
@ -412,7 +412,14 @@ func cmdAdd(args *skel.CmdArgs) error {
} }
// Assume L2 interface only // Assume L2 interface only
result := &current.Result{CNIVersion: cniVersion, Interfaces: []*current.Interface{brInterface, hostInterface, containerInterface}} result := &current.Result{
CNIVersion: current.ImplementedSpecVersion,
Interfaces: []*current.Interface{
brInterface,
hostInterface,
containerInterface,
},
}
if isLayer3 { if isLayer3 {
// run the IPAM plugin and get back the config to apply // run the IPAM plugin and get back the config to apply
@ -453,7 +460,7 @@ func cmdAdd(args *skel.CmdArgs) error {
// 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.
for _, ipc := range result.IPs { for _, ipc := range result.IPs {
if ipc.Version == "6" && (n.HairpinMode || n.PromiscMode) { if ipc.Address.IP.To4() == nil && (n.HairpinMode || n.PromiscMode) {
if err := disableIPV6DAD(args.IfName); err != nil { if err := disableIPV6DAD(args.IfName); err != nil {
return err return err
} }
@ -496,7 +503,7 @@ func cmdAdd(args *skel.CmdArgs) error {
} }
for _, ipc := range result.IPs { for _, ipc := range result.IPs {
if ipc.Version == "4" { if ipc.Address.IP.To4() != nil {
_ = arping.GratuitousArpOverIface(ipc.Address.IP, *contVeth) _ = arping.GratuitousArpOverIface(ipc.Address.IP, *contVeth)
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -30,7 +30,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/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/ip" "github.com/containernetworking/plugins/pkg/ip"

File diff suppressed because it is too large Load Diff

View File

@ -24,7 +24,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/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/ip" "github.com/containernetworking/plugins/pkg/ip"

View File

@ -17,12 +17,17 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"net" "net"
"os"
"strings"
"syscall" "syscall"
"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/020"
"github.com/containernetworking/cni/pkg/types/040"
"github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils" "github.com/containernetworking/plugins/pkg/testutils"
@ -44,37 +49,33 @@ type Net struct {
IPAM *allocator.IPAMConfig `json:"ipam"` IPAM *allocator.IPAMConfig `json:"ipam"`
DNS types.DNS `json:"dns"` DNS types.DNS `json:"dns"`
RawPrevResult map[string]interface{} `json:"prevResult,omitempty"` RawPrevResult map[string]interface{} `json:"prevResult,omitempty"`
PrevResult current.Result `json:"-"` PrevResult types100.Result `json:"-"`
} }
func buildOneConfig(netName string, cniVersion string, master string, orig *Net, prevResult types.Result) (*Net, error) { func buildOneConfig(cniVersion string, master string, orig *Net, prevResult types.Result) (*Net, error) {
var err error
inject := map[string]interface{}{
"name": netName,
"cniVersion": cniVersion,
}
// Add previous plugin result
if prevResult != nil {
inject["prevResult"] = prevResult
}
if orig.IPAM == nil {
inject["master"] = master
}
// Ensure every config uses the same name and version
config := make(map[string]interface{})
confBytes, err := json.Marshal(orig) confBytes, err := json.Marshal(orig)
if err != nil { if err != nil {
return nil, err return nil, err
} }
config := make(map[string]interface{})
err = json.Unmarshal(confBytes, &config) err = json.Unmarshal(confBytes, &config)
if err != nil { if err != nil {
return nil, fmt.Errorf("unmarshal existing network bytes: %s", err) return nil, fmt.Errorf("unmarshal existing network bytes: %s", err)
} }
inject := map[string]interface{}{
"name": orig.Name,
"cniVersion": orig.CNIVersion,
}
// Add previous plugin result
if prevResult != nil && testutils.SpecVersionHasChaining(cniVersion) {
inject["prevResult"] = prevResult
}
if master != "" {
inject["master"] = master
}
for key, value := range inject { for key, value := range inject {
config[key] = value config[key] = value
} }
@ -93,121 +94,49 @@ func buildOneConfig(netName string, cniVersion string, master string, orig *Net,
} }
func ipvlanAddDelTest(conf, IFNAME string, originalNS ns.NetNS) { func ipvlanAddCheckDelTest(conf, masterName string, originalNS, targetNS ns.NetNS) {
targetNs, err := testutils.NewNS() // Unmarshal to pull out CNI spec version
rawConfig := make(map[string]interface{})
err := json.Unmarshal([]byte(conf), &rawConfig)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
defer targetNs.Close() cniVersion := rawConfig["cniVersion"].(string)
args := &skel.CmdArgs{ args := &skel.CmdArgs{
ContainerID: "dummy", ContainerID: "dummy",
Netns: targetNs.Path(), Netns: targetNS.Path(),
IfName: IFNAME, IfName: "ipvl0",
StdinData: []byte(conf), StdinData: []byte(conf),
} }
var result *current.Result var result types.Result
var macAddress string
err = originalNS.Do(func(ns.NetNS) error { err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
r, _, err := testutils.CmdAddWithArgs(args, func() error { result, _, err = testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args) return cmdAdd(args)
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
result, err = current.GetResult(r) t := newTesterByVersion(cniVersion)
Expect(err).NotTo(HaveOccurred()) macAddress = t.verifyResult(result, args.IfName)
Expect(len(result.Interfaces)).To(Equal(1))
Expect(result.Interfaces[0].Name).To(Equal(IFNAME))
Expect(len(result.IPs)).To(Equal(1))
return nil return nil
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
// Make sure ipvlan link exists in the target namespace // Make sure ipvlan link exists in the target namespace
err = targetNs.Do(func(ns.NetNS) error { err = targetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
link, err := netlink.LinkByName(IFNAME) link, err := netlink.LinkByName(args.IfName)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().Name).To(Equal(IFNAME)) Expect(link.Attrs().Name).To(Equal(args.IfName))
hwaddr, err := net.ParseMAC(result.Interfaces[0].Mac) if macAddress != "" {
Expect(err).NotTo(HaveOccurred()) hwaddr, err := net.ParseMAC(macAddress)
Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr)) Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr))
addrs, err := netlink.AddrList(link, syscall.AF_INET) }
Expect(err).NotTo(HaveOccurred())
Expect(len(addrs)).To(Equal(1))
return nil
})
Expect(err).NotTo(HaveOccurred())
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
// Make sure ipvlan link has been deleted
err = targetNs.Do(func(ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName(IFNAME)
Expect(err).To(HaveOccurred())
Expect(link).To(BeNil())
return nil
})
Expect(err).NotTo(HaveOccurred())
}
func ipvlanAddCheckDelTest(conf string, netName string, IFNAME string, originalNS ns.NetNS) {
targetNs, err := testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
defer targetNs.Close()
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNs.Path(),
IfName: IFNAME,
StdinData: []byte(conf),
}
var result *current.Result
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
r, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
result, err = current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(len(result.Interfaces)).To(Equal(1))
Expect(result.Interfaces[0].Name).To(Equal(IFNAME))
Expect(len(result.IPs)).To(Equal(1))
return nil
})
Expect(err).NotTo(HaveOccurred())
// Make sure ipvlan link exists in the target namespace
err = targetNs.Do(func(ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName(IFNAME)
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().Name).To(Equal(IFNAME))
hwaddr, err := net.ParseMAC(result.Interfaces[0].Mac)
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr))
addrs, err := netlink.AddrList(link, syscall.AF_INET) addrs, err := netlink.AddrList(link, syscall.AF_INET)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
@ -225,26 +154,26 @@ func ipvlanAddCheckDelTest(conf string, netName string, IFNAME string, originalN
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
} }
cniVersion := "0.4.0" // build chained/cached config for DEL
newConf, err := buildOneConfig(netName, cniVersion, MASTER_NAME, n, result) newConf, err := buildOneConfig(cniVersion, masterName, n, result)
Expect(err).NotTo(HaveOccurred())
confBytes, err := json.Marshal(newConf)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
confString, err := json.Marshal(newConf) args.StdinData = confBytes
Expect(err).NotTo(HaveOccurred()) GinkgoT().Logf(string(confBytes))
args.StdinData = confString if testutils.SpecVersionHasCHECK(cniVersion) {
// CNI Check on ipvlan in the target namespace
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
// CNI Check on ipvlan in the target namespace return testutils.CmdCheckWithArgs(args, func() error {
err = originalNS.Do(func(ns.NetNS) error { return cmdCheck(args)
defer GinkgoRecover() })
err := testutils.CmdCheckWithArgs(args, func() error {
return cmdCheck(args)
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
return nil }
})
Expect(err).NotTo(HaveOccurred())
err = originalNS.Do(func(ns.NetNS) error { err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
@ -258,10 +187,10 @@ func ipvlanAddCheckDelTest(conf string, netName string, IFNAME string, originalN
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
// Make sure ipvlan link has been deleted // Make sure ipvlan link has been deleted
err = targetNs.Do(func(ns.NetNS) error { err = targetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
link, err := netlink.LinkByName(IFNAME) link, err := netlink.LinkByName(args.IfName)
Expect(err).To(HaveOccurred()) Expect(err).To(HaveOccurred())
Expect(link).To(BeNil()) Expect(link).To(BeNil())
return nil return nil
@ -269,8 +198,70 @@ func ipvlanAddCheckDelTest(conf string, netName string, IFNAME string, originalN
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
} }
type tester interface {
// verifyResult minimally verifies the Result and returns the interface's MAC address
verifyResult(result types.Result, name string) string
}
type testerBase struct{}
type testerV10x testerBase
type testerV04x testerBase
type testerV02x testerBase
func newTesterByVersion(version string) tester {
switch {
case strings.HasPrefix(version, "1.0."):
return &testerV10x{}
case strings.HasPrefix(version, "0.4.") || strings.HasPrefix(version, "0.3."):
return &testerV04x{}
case strings.HasPrefix(version, "0.1.") || strings.HasPrefix(version, "0.2."):
return &testerV02x{}
}
Fail(fmt.Sprintf("unsupported config version %s", version))
return nil
}
// verifyResult minimally verifies the Result and returns the interface's MAC address
func (t *testerV10x) verifyResult(result types.Result, name string) string {
r, err := types100.GetResult(result)
Expect(err).NotTo(HaveOccurred())
Expect(len(r.Interfaces)).To(Equal(1))
Expect(r.Interfaces[0].Name).To(Equal(name))
Expect(len(r.IPs)).To(Equal(1))
return r.Interfaces[0].Mac
}
// verifyResult minimally verifies the Result and returns the interface's MAC address
func (t *testerV04x) verifyResult(result types.Result, name string) string {
r, err := types040.GetResult(result)
Expect(err).NotTo(HaveOccurred())
Expect(len(r.Interfaces)).To(Equal(1))
Expect(r.Interfaces[0].Name).To(Equal(name))
Expect(len(r.IPs)).To(Equal(1))
return r.Interfaces[0].Mac
}
// verifyResult minimally verifies the Result and returns the interface's MAC address
func (t *testerV02x) verifyResult(result types.Result, name string) string {
r, err := types020.GetResult(result)
Expect(err).NotTo(HaveOccurred())
Expect(r.IP4.IP).NotTo(BeNil())
Expect(r.IP4.IP.IP).NotTo(BeNil())
Expect(r.IP6).To(BeNil())
// 0.2 and earlier don't return MAC address
return ""
}
var _ = Describe("ipvlan Operations", func() { var _ = Describe("ipvlan Operations", func() {
var originalNS ns.NetNS var originalNS, targetNS ns.NetNS
var dataDir string
BeforeEach(func() { BeforeEach(func() {
// Create a new NetNS so we don't modify the host // Create a new NetNS so we don't modify the host
@ -278,6 +269,12 @@ var _ = Describe("ipvlan Operations", func() {
originalNS, err = testutils.NewNS() originalNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
targetNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
dataDir, err = ioutil.TempDir("", "ipvlan_test")
Expect(err).NotTo(HaveOccurred())
err = originalNS.Do(func(ns.NetNS) error { err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
@ -296,219 +293,170 @@ var _ = Describe("ipvlan Operations", func() {
}) })
AfterEach(func() { AfterEach(func() {
Expect(os.RemoveAll(dataDir)).To(Succeed())
Expect(originalNS.Close()).To(Succeed()) Expect(originalNS.Close()).To(Succeed())
Expect(testutils.UnmountNS(originalNS)).To(Succeed()) Expect(testutils.UnmountNS(originalNS)).To(Succeed())
Expect(targetNS.Close()).To(Succeed())
Expect(testutils.UnmountNS(targetNS)).To(Succeed())
}) })
It("creates an ipvlan link in a non-default namespace", func() { for _, ver := range testutils.AllSpecVersions {
conf := &NetConf{ // Redefine ver inside for scope so real value is picked up by each dynamically defined It()
NetConf: types.NetConf{ // See Gingkgo's "Patterns for dynamically generating tests" documentation.
CNIVersion: "0.3.1", ver := ver
Name: "testConfig",
Type: "ipvlan",
},
Master: MASTER_NAME,
Mode: "l2",
MTU: 1500,
}
// Create ipvlan in other namespace It(fmt.Sprintf("[%s] creates an ipvlan link in a non-default namespace", ver), func() {
targetNs, err := testutils.NewNS() conf := &NetConf{
Expect(err).NotTo(HaveOccurred()) NetConf: types.NetConf{
defer targetNs.Close() CNIVersion: ver,
Name: "testConfig",
Type: "ipvlan",
},
Master: MASTER_NAME,
Mode: "l2",
MTU: 1500,
}
err = originalNS.Do(func(ns.NetNS) error { err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
_, err := createIpvlan(conf, "foobar0", targetNS)
Expect(err).NotTo(HaveOccurred())
return nil
})
_, err := createIpvlan(conf, "foobar0", targetNs)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred()) // Make sure ipvlan link exists in the target namespace
err = targetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
// Make sure ipvlan link exists in the target namespace link, err := netlink.LinkByName("foobar0")
err = targetNs.Do(func(ns.NetNS) error { Expect(err).NotTo(HaveOccurred())
defer GinkgoRecover() Expect(link.Attrs().Name).To(Equal("foobar0"))
return nil
link, err := netlink.LinkByName("foobar0")
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().Name).To(Equal("foobar0"))
return nil
})
Expect(err).NotTo(HaveOccurred())
})
It("configures and deconfigures an iplvan link with ADD/DEL", func() {
const IFNAME = "ipvl0"
conf := fmt.Sprintf(`{
"cniVersion": "0.3.1",
"name": "mynet",
"type": "ipvlan",
"master": "%s",
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24"
}
}`, MASTER_NAME)
ipvlanAddDelTest(conf, IFNAME, originalNS)
})
It("configures and deconfigures an iplvan link with ADD/DEL when chained", func() {
const IFNAME = "ipvl0"
conf := fmt.Sprintf(`{
"cniVersion": "0.3.1",
"name": "mynet",
"type": "ipvlan",
"prevResult": {
"interfaces": [
{
"name": "%s"
}
],
"ips": [
{
"version": "4",
"address": "10.1.2.2/24",
"gateway": "10.1.2.1",
"interface": 0
}
],
"routes": []
}
}`, MASTER_NAME)
ipvlanAddDelTest(conf, IFNAME, originalNS)
})
It("deconfigures an unconfigured ipvlan link with DEL", func() {
const IFNAME = "ipvl0"
conf := fmt.Sprintf(`{
"cniVersion": "0.3.0",
"name": "mynet",
"type": "ipvlan",
"master": "%s",
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24"
}
}`, MASTER_NAME)
targetNs, err := testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
defer targetNs.Close()
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNs.Path(),
IfName: IFNAME,
StdinData: []byte(conf),
}
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
return nil
}) })
Expect(err).NotTo(HaveOccurred())
})
It("configures and deconfigures a cniVersion 0.4.0 iplvan link with ADD/CHECK/DEL", func() { It(fmt.Sprintf("[%s] configures and deconfigures an iplvan link with ADD/DEL", ver), func() {
const IFNAME = "ipvl0" conf := fmt.Sprintf(`{
"cniVersion": "%s",
"name": "mynet",
"type": "ipvlan",
"master": "%s",
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24",
"dataDir": "%s"
}
}`, ver, MASTER_NAME, dataDir)
conf := fmt.Sprintf(`{ ipvlanAddCheckDelTest(conf, "", originalNS, targetNS)
"cniVersion": "0.4.0", })
"name": "ipvlanTest1",
"type": "ipvlan",
"master": "%s",
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24"
}
}`, MASTER_NAME)
ipvlanAddCheckDelTest(conf, "ipvlanTest1", IFNAME, originalNS) if testutils.SpecVersionHasChaining(ver) {
}) It(fmt.Sprintf("[%s] configures and deconfigures an iplvan link with ADD/DEL when chained", ver), func() {
conf := fmt.Sprintf(`{
"cniVersion": "%s",
"name": "mynet",
"type": "ipvlan",
"prevResult": {
"interfaces": [
{
"name": "%s"
}
],
"ips": [
{
"version": "4",
"address": "10.1.2.2/24",
"gateway": "10.1.2.1",
"interface": 0
}
],
"routes": []
}
}`, ver, MASTER_NAME)
It("configures and deconfigures a cniVersion 0.4.0 iplvan link with ADD/CHECK/DEL when chained", func() { ipvlanAddCheckDelTest(conf, MASTER_NAME, originalNS, targetNS)
const IFNAME = "ipvl0" })
}
conf := fmt.Sprintf(`{ It(fmt.Sprintf("[%s] deconfigures an unconfigured ipvlan link with DEL", ver), func() {
"cniVersion": "0.4.0", conf := fmt.Sprintf(`{
"name": "ipvlanTest2", "cniVersion": "%s",
"type": "ipvlan", "name": "mynet",
"prevResult": { "type": "ipvlan",
"interfaces": [ "master": "%s",
{ "ipam": {
"name": "%s" "type": "host-local",
} "subnet": "10.1.2.0/24",
], "dataDir": "%s"
"ips": [ }
{ }`, ver, MASTER_NAME, dataDir)
"version": "4",
"address": "10.1.2.2/24",
"gateway": "10.1.2.1",
"interface": 0
}
],
"routes": []
}
}`, MASTER_NAME)
ipvlanAddCheckDelTest(conf, "ipvlanTest2", IFNAME, originalNS) args := &skel.CmdArgs{
}) ContainerID: "dummy",
Netns: targetNS.Path(),
It("configures and deconfigures a ipvlan link with ADD/DEL, without master config", func() { IfName: "ipvl0",
const IFNAME = "ipvl0" StdinData: []byte(conf),
conf := `{
"cniVersion": "0.3.1",
"name": "mynet",
"type": "ipvlan",
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24"
}
}`
targetNs, err := testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
defer targetNs.Close()
// Make MASTER_NAME as default route interface
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName(MASTER_NAME)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetUp(link)
Expect(err).NotTo(HaveOccurred())
var address = &net.IPNet{IP: net.IPv4(192, 0, 0, 1), Mask: net.CIDRMask(24, 32)}
var addr = &netlink.Addr{IPNet: address}
err = netlink.AddrAdd(link, addr)
Expect(err).NotTo(HaveOccurred())
// add default gateway into MASTER
dst := &net.IPNet{
IP: net.IPv4(0, 0, 0, 0),
Mask: net.CIDRMask(0, 0),
} }
ip := net.IPv4(192, 0, 0, 254)
route := netlink.Route{LinkIndex: link.Attrs().Index, Dst: dst, Gw: ip}
err = netlink.RouteAdd(&route)
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
ipvlanAddDelTest(conf, IFNAME, originalNS) 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(fmt.Sprintf("[%s] configures and deconfigures a ipvlan link with ADD/DEL, without master config", ver), func() {
conf := fmt.Sprintf(`{
"cniVersion": "%s",
"name": "mynet",
"type": "ipvlan",
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24",
"dataDir": "%s"
}
}`, ver, dataDir)
// Make MASTER_NAME as default route interface
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName(MASTER_NAME)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetUp(link)
Expect(err).NotTo(HaveOccurred())
var address = &net.IPNet{IP: net.IPv4(192, 0, 0, 1), Mask: net.CIDRMask(24, 32)}
var addr = &netlink.Addr{IPNet: address}
err = netlink.AddrAdd(link, addr)
Expect(err).NotTo(HaveOccurred())
// add default gateway into MASTER
dst := &net.IPNet{
IP: net.IPv4(0, 0, 0, 0),
Mask: net.CIDRMask(0, 0),
}
ip := net.IPv4(192, 0, 0, 254)
route := netlink.Route{LinkIndex: link.Attrs().Index, Dst: dst, Gw: ip}
err = netlink.RouteAdd(&route)
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
ipvlanAddCheckDelTest(conf, MASTER_NAME, originalNS, targetNS)
})
}
}) })

View File

@ -24,7 +24,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/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"
@ -109,12 +109,19 @@ func cmdAdd(args *skel.CmdArgs) error {
// loopback should pass it transparently // loopback should pass it transparently
result = conf.PrevResult result = conf.PrevResult
} else { } else {
loopbackInterface := &current.Interface{Name: args.IfName, Mac: "00:00:00:00:00:00", Sandbox: args.Netns} r := &current.Result{
r := &current.Result{CNIVersion: conf.CNIVersion, Interfaces: []*current.Interface{loopbackInterface}} CNIVersion: conf.CNIVersion,
Interfaces: []*current.Interface{
&current.Interface{
Name: args.IfName,
Mac: "00:00:00:00:00:00",
Sandbox: args.Netns,
},
},
}
if v4Addr != nil { if v4Addr != nil {
r.IPs = append(r.IPs, &current.IPConfig{ r.IPs = append(r.IPs, &current.IPConfig{
Version: "4",
Interface: current.Int(0), Interface: current.Int(0),
Address: *v4Addr, Address: *v4Addr,
}) })
@ -122,7 +129,6 @@ func cmdAdd(args *skel.CmdArgs) error {
if v6Addr != nil { if v6Addr != nil {
r.IPs = append(r.IPs, &current.IPConfig{ r.IPs = append(r.IPs, &current.IPConfig{
Version: "6",
Interface: current.Int(0), Interface: current.Int(0),
Address: *v6Addr, Address: *v6Addr,
}) })

View File

@ -28,6 +28,10 @@ import (
"github.com/onsi/gomega/gexec" "github.com/onsi/gomega/gexec"
) )
func generateConfig(cniVersion string) *strings.Reader {
return strings.NewReader(fmt.Sprintf(`{ "name": "loopback-test", "cniVersion": "%s" }`, cniVersion))
}
var _ = Describe("Loopback", func() { var _ = Describe("Loopback", func() {
var ( var (
networkNS ns.NetNS networkNS ns.NetNS
@ -49,7 +53,6 @@ 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(`{ "name": "loopback-test", "cniVersion": "0.1.0" }`)
}) })
AfterEach(func() { AfterEach(func() {
@ -57,45 +60,53 @@ var _ = Describe("Loopback", func() {
Expect(testutils.UnmountNS(networkNS)).To(Succeed()) Expect(testutils.UnmountNS(networkNS)).To(Succeed())
}) })
Context("when given a network namespace", func() { for _, ver := range testutils.AllSpecVersions {
It("sets the lo device to UP", func() { // Redefine ver inside for scope so real value is picked up by each dynamically defined It()
command.Env = append(environ, fmt.Sprintf("CNI_COMMAND=%s", "ADD")) // See Gingkgo's "Patterns for dynamically generating tests" documentation.
ver := ver
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) Context("when given a network namespace", func() {
Expect(err).NotTo(HaveOccurred()) It(fmt.Sprintf("[%s] sets the lo device to UP", ver), func() {
command.Stdin = generateConfig(ver)
command.Env = append(environ, fmt.Sprintf("CNI_COMMAND=%s", "ADD"))
Eventually(session).Should(gbytes.Say(`{.*}`)) session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
Eventually(session).Should(gexec.Exit(0)) Expect(err).NotTo(HaveOccurred())
var lo *net.Interface Eventually(session).Should(gbytes.Say(`{.*}`))
err = networkNS.Do(func(ns.NetNS) error { Eventually(session).Should(gexec.Exit(0))
var err error
lo, err = net.InterfaceByName("lo") var lo *net.Interface
return err err = networkNS.Do(func(ns.NetNS) error {
var err error
lo, err = net.InterfaceByName("lo")
return err
})
Expect(err).NotTo(HaveOccurred())
Expect(lo.Flags & net.FlagUp).To(Equal(net.FlagUp))
}) })
Expect(err).NotTo(HaveOccurred())
Expect(lo.Flags & net.FlagUp).To(Equal(net.FlagUp)) It(fmt.Sprintf("[%s] sets the lo device to DOWN", ver), func() {
}) command.Stdin = generateConfig(ver)
command.Env = append(environ, fmt.Sprintf("CNI_COMMAND=%s", "DEL"))
It("sets the lo device to DOWN", func() { session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
command.Env = append(environ, fmt.Sprintf("CNI_COMMAND=%s", "DEL")) Expect(err).NotTo(HaveOccurred())
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter) Eventually(session).Should(gbytes.Say(``))
Expect(err).NotTo(HaveOccurred()) Eventually(session).Should(gexec.Exit(0))
Eventually(session).Should(gbytes.Say(``)) var lo *net.Interface
Eventually(session).Should(gexec.Exit(0)) err = networkNS.Do(func(ns.NetNS) error {
var err error
lo, err = net.InterfaceByName("lo")
return err
})
Expect(err).NotTo(HaveOccurred())
var lo *net.Interface Expect(lo.Flags & net.FlagUp).NotTo(Equal(net.FlagUp))
err = networkNS.Do(func(ns.NetNS) error {
var err error
lo, err = net.InterfaceByName("lo")
return err
}) })
Expect(err).NotTo(HaveOccurred())
Expect(lo.Flags & net.FlagUp).NotTo(Equal(net.FlagUp))
}) })
}) }
}) })

View File

@ -26,7 +26,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/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/ip" "github.com/containernetworking/plugins/pkg/ip"
@ -256,7 +256,10 @@ func cmdAdd(args *skel.CmdArgs) error {
}() }()
// Assume L2 interface only // Assume L2 interface only
result := &current.Result{CNIVersion: cniVersion, Interfaces: []*current.Interface{macvlanInterface}} result := &current.Result{
CNIVersion: current.ImplementedSpecVersion,
Interfaces: []*current.Interface{macvlanInterface},
}
if isLayer3 { if isLayer3 {
// run the IPAM plugin and get back the config to apply // run the IPAM plugin and get back the config to apply
@ -301,7 +304,7 @@ func cmdAdd(args *skel.CmdArgs) error {
} }
for _, ipc := range result.IPs { for _, ipc := range result.IPs {
if ipc.Version == "4" { if ipc.Address.IP.To4() != nil {
_ = arping.GratuitousArpOverIface(ipc.Address.IP, *contVeth) _ = arping.GratuitousArpOverIface(ipc.Address.IP, *contVeth)
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -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/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/ip" "github.com/containernetworking/plugins/pkg/ip"
@ -108,7 +108,7 @@ func setupContainerVeth(netns ns.NetNS, ifName string, mtu int, pr *current.Resu
} }
addrBits := 32 addrBits := 32
if ipc.Version == "6" { if ipc.Address.IP.To4() == nil {
addrBits = 128 addrBits = 128
} }
@ -141,7 +141,7 @@ func setupContainerVeth(netns ns.NetNS, ifName string, mtu int, pr *current.Resu
// Send a gratuitous arp for all v4 addresses // Send a gratuitous arp for all v4 addresses
for _, ipc := range pr.IPs { for _, ipc := range pr.IPs {
if ipc.Version == "4" { if ipc.Address.IP.To4() != nil {
_ = arping.GratuitousArpOverIface(ipc.Address.IP, *contVeth) _ = arping.GratuitousArpOverIface(ipc.Address.IP, *contVeth)
} }
} }

View File

@ -17,11 +17,15 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"strings"
"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/020"
"github.com/containernetworking/cni/pkg/types/040"
"github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils" "github.com/containernetworking/plugins/pkg/testutils"
@ -41,7 +45,7 @@ type Net struct {
IPAM *allocator.IPAMConfig `json:"ipam"` IPAM *allocator.IPAMConfig `json:"ipam"`
DNS types.DNS `json:"dns"` DNS types.DNS `json:"dns"`
RawPrevResult map[string]interface{} `json:"prevResult,omitempty"` RawPrevResult map[string]interface{} `json:"prevResult,omitempty"`
PrevResult current.Result `json:"-"` PrevResult types100.Result `json:"-"`
} }
func buildOneConfig(netName string, cniVersion string, orig *Net, prevResult types.Result) (*Net, error) { func buildOneConfig(netName string, cniVersion string, orig *Net, prevResult types.Result) (*Net, error) {
@ -87,43 +91,166 @@ func buildOneConfig(netName string, cniVersion string, orig *Net, prevResult typ
} }
type tester interface {
// verifyResult minimally verifies the Result and returns the interface's IP addresses and MAC address
verifyResult(result types.Result, expectedIfName, expectedSandbox string, expectedDNS types.DNS) ([]resultIP, string)
}
type testerBase struct{}
type testerV10x testerBase
type testerV04x testerBase
type testerV03x testerBase
type testerV01xOr02x testerBase
func newTesterByVersion(version string) tester {
switch {
case strings.HasPrefix(version, "1.0."):
return &testerV10x{}
case strings.HasPrefix(version, "0.4."):
return &testerV04x{}
case strings.HasPrefix(version, "0.3."):
return &testerV03x{}
default:
return &testerV01xOr02x{}
}
}
type resultIP struct {
ip string
gw string
}
// verifyResult minimally verifies the Result and returns the interface's IP addresses and MAC address
func (t *testerV10x) verifyResult(result types.Result, expectedIfName, expectedSandbox string, expectedDNS types.DNS) ([]resultIP, string) {
r, err := types100.GetResult(result)
Expect(err).NotTo(HaveOccurred())
Expect(r.Interfaces).To(HaveLen(2))
Expect(r.Interfaces[0].Name).To(HavePrefix("veth"))
Expect(r.Interfaces[0].Mac).To(HaveLen(17))
Expect(r.Interfaces[0].Sandbox).To(BeEmpty())
Expect(r.Interfaces[1].Name).To(Equal(expectedIfName))
Expect(r.Interfaces[1].Sandbox).To(Equal(expectedSandbox))
Expect(r.DNS).To(Equal(expectedDNS))
// Grab IPs from container interface
ips := []resultIP{}
for _, ipc := range r.IPs {
if *ipc.Interface == 1 {
ips = append(ips, resultIP{
ip: ipc.Address.IP.String(),
gw: ipc.Gateway.String(),
})
}
}
return ips, r.Interfaces[1].Mac
}
func verify0403(result types.Result, expectedIfName, expectedSandbox string, expectedDNS types.DNS) ([]resultIP, string) {
r, err := types040.GetResult(result)
Expect(err).NotTo(HaveOccurred())
Expect(r.Interfaces).To(HaveLen(2))
Expect(r.Interfaces[0].Name).To(HavePrefix("veth"))
Expect(r.Interfaces[0].Mac).To(HaveLen(17))
Expect(r.Interfaces[0].Sandbox).To(BeEmpty())
Expect(r.Interfaces[1].Name).To(Equal(expectedIfName))
Expect(r.Interfaces[1].Sandbox).To(Equal(expectedSandbox))
Expect(r.DNS).To(Equal(expectedDNS))
// Grab IPs from container interface
ips := []resultIP{}
for _, ipc := range r.IPs {
if *ipc.Interface == 1 {
ips = append(ips, resultIP{
ip: ipc.Address.IP.String(),
gw: ipc.Gateway.String(),
})
}
}
return ips, r.Interfaces[1].Mac
}
// verifyResult minimally verifies the Result and returns the interface's IP addresses and MAC address
func (t *testerV04x) verifyResult(result types.Result, expectedIfName, expectedSandbox string, expectedDNS types.DNS) ([]resultIP, string) {
return verify0403(result, expectedIfName, expectedSandbox, expectedDNS)
}
// verifyResult minimally verifies the Result and returns the interface's IP addresses and MAC address
func (t *testerV03x) verifyResult(result types.Result, expectedIfName, expectedSandbox string, expectedDNS types.DNS) ([]resultIP, string) {
return verify0403(result, expectedIfName, expectedSandbox, expectedDNS)
}
// verifyResult minimally verifies the Result and returns the interface's IP addresses and MAC address
func (t *testerV01xOr02x) verifyResult(result types.Result, expectedIfName, expectedSandbox string, expectedDNS types.DNS) ([]resultIP, string) {
r, err := types020.GetResult(result)
Expect(err).NotTo(HaveOccurred())
ips := []resultIP{}
if r.IP4 != nil && r.IP4.IP.IP != nil {
ips = append(ips, resultIP{
ip: r.IP4.IP.IP.String(),
gw: r.IP4.Gateway.String(),
})
}
if r.IP6 != nil && r.IP6.IP.IP != nil {
ips = append(ips, resultIP{
ip: r.IP6.IP.IP.String(),
gw: r.IP6.Gateway.String(),
})
}
// 0.2 and earlier don't return MAC address
return ips, ""
}
var _ = Describe("ptp Operations", func() { var _ = Describe("ptp Operations", func() {
var originalNS ns.NetNS var originalNS, targetNS ns.NetNS
var dataDir string
BeforeEach(func() { BeforeEach(func() {
// Create a new NetNS so we don't modify the host // Create a new NetNS so we don't modify the host
var err error var err error
originalNS, err = testutils.NewNS() originalNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
targetNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
dataDir, err = ioutil.TempDir("", "ptp_test")
Expect(err).NotTo(HaveOccurred())
}) })
AfterEach(func() { AfterEach(func() {
Expect(os.RemoveAll(dataDir)).To(Succeed())
Expect(originalNS.Close()).To(Succeed()) Expect(originalNS.Close()).To(Succeed())
Expect(testutils.UnmountNS(originalNS)).To(Succeed()) Expect(testutils.UnmountNS(originalNS)).To(Succeed())
Expect(targetNS.Close()).To(Succeed())
Expect(testutils.UnmountNS(targetNS)).To(Succeed())
}) })
doTest := func(conf string, numIPs int, expectedDNSConf types.DNS) { doTest := func(conf, cniVersion string, numIPs int, expectedDNSConf types.DNS, targetNS ns.NetNS) {
const IFNAME = "ptp0" const IFNAME = "ptp0"
targetNs, err := testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
defer targetNs.Close()
args := &skel.CmdArgs{ args := &skel.CmdArgs{
ContainerID: "dummy", ContainerID: "dummy",
Netns: targetNs.Path(), Netns: targetNS.Path(),
IfName: IFNAME, IfName: IFNAME,
StdinData: []byte(conf), StdinData: []byte(conf),
} }
var resI types.Result var result types.Result
var res *current.Result
// Execute the plugin with the ADD command, creating the veth endpoints // Execute the plugin with the ADD command, creating the veth endpoints
err = originalNS.Do(func(ns.NetNS) error { err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
resI, _, err = testutils.CmdAddWithArgs(args, func() error { var err error
result, _, err = testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args) return cmdAdd(args)
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
@ -131,32 +258,25 @@ var _ = Describe("ptp Operations", func() {
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
res, err = current.NewResultFromResult(resI) t := newTesterByVersion(cniVersion)
Expect(err).NotTo(HaveOccurred()) ips, mac := t.verifyResult(result, IFNAME, targetNS.Path(), expectedDNSConf)
Expect(len(ips)).To(Equal(numIPs))
// Make sure ptp link exists in the target namespace // Make sure ptp link exists in the target namespace
// Then, ping the gateway // Then, ping the gateway
seenIPs := 0 err = targetNS.Do(func(ns.NetNS) error {
wantMac := ""
err = targetNs.Do(func(ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
link, err := netlink.LinkByName(IFNAME) link, err := netlink.LinkByName(IFNAME)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
wantMac = link.Attrs().HardwareAddr.String() if mac != "" {
Expect(mac).To(Equal(link.Attrs().HardwareAddr.String()))
}
for _, ipc := range res.IPs { for _, ipc := range ips {
if *ipc.Interface != 1 { fmt.Fprintln(GinkgoWriter, "ping", ipc.ip, "->", ipc.gw)
continue if err := testutils.Ping(ipc.ip, ipc.gw, 30); err != nil {
} return fmt.Errorf("ping %s -> %s failed: %s", ipc.ip, ipc.gw, err)
seenIPs += 1
saddr := ipc.Address.IP.String()
daddr := ipc.Gateway.String()
fmt.Fprintln(GinkgoWriter, "ping", saddr, "->", daddr)
if err := testutils.Ping(saddr, daddr, (ipc.Version == "6"), 30); err != nil {
return fmt.Errorf("ping %s -> %s failed: %s", saddr, daddr, err)
} }
} }
@ -164,121 +284,6 @@ var _ = Describe("ptp Operations", func() {
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(seenIPs).To(Equal(numIPs))
// make sure the interfaces are correct
Expect(res.Interfaces).To(HaveLen(2))
Expect(res.Interfaces[0].Name).To(HavePrefix("veth"))
Expect(res.Interfaces[0].Mac).To(HaveLen(17))
Expect(res.Interfaces[0].Sandbox).To(BeEmpty())
Expect(res.Interfaces[1].Name).To(Equal(IFNAME))
Expect(res.Interfaces[1].Mac).To(Equal(wantMac))
Expect(res.Interfaces[1].Sandbox).To(Equal(targetNs.Path()))
// make sure DNS is correct
Expect(res.DNS).To(Equal(expectedDNSConf))
// Call the plugins with the DEL command, deleting the veth endpoints
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
err := testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
// Make sure ptp link has been deleted
err = targetNs.Do(func(ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName(IFNAME)
Expect(err).To(HaveOccurred())
Expect(link).To(BeNil())
return nil
})
Expect(err).NotTo(HaveOccurred())
}
doTestv4 := func(conf string, netName string, numIPs int) {
const IFNAME = "ptp0"
targetNs, err := testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
defer targetNs.Close()
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNs.Path(),
IfName: IFNAME,
StdinData: []byte(conf),
}
var resI types.Result
var res *current.Result
// Execute the plugin with the ADD command, creating the veth endpoints
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
resI, _, err = testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
res, err = current.NewResultFromResult(resI)
Expect(err).NotTo(HaveOccurred())
// Make sure ptp link exists in the target namespace
// Then, ping the gateway
seenIPs := 0
wantMac := ""
err = targetNs.Do(func(ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName(IFNAME)
Expect(err).NotTo(HaveOccurred())
wantMac = link.Attrs().HardwareAddr.String()
for _, ipc := range res.IPs {
if *ipc.Interface != 1 {
continue
}
seenIPs += 1
saddr := ipc.Address.IP.String()
daddr := ipc.Gateway.String()
fmt.Fprintln(GinkgoWriter, "ping", saddr, "->", daddr)
if err := testutils.Ping(saddr, daddr, (ipc.Version == "6"), 30); err != nil {
return fmt.Errorf("ping %s -> %s failed: %s", saddr, daddr, err)
}
}
return nil
})
Expect(err).NotTo(HaveOccurred())
Expect(seenIPs).To(Equal(numIPs))
// make sure the interfaces are correct
Expect(res.Interfaces).To(HaveLen(2))
Expect(res.Interfaces[0].Name).To(HavePrefix("veth"))
Expect(res.Interfaces[0].Mac).To(HaveLen(17))
Expect(res.Interfaces[0].Sandbox).To(BeEmpty())
Expect(res.Interfaces[1].Name).To(Equal(IFNAME))
Expect(res.Interfaces[1].Mac).To(Equal(wantMac))
Expect(res.Interfaces[1].Sandbox).To(Equal(targetNs.Path()))
// call CmdCheck // call CmdCheck
n := &Net{} n := &Net{}
err = json.Unmarshal([]byte(conf), &n) err = json.Unmarshal([]byte(conf), &n)
@ -287,8 +292,7 @@ var _ = Describe("ptp Operations", func() {
n.IPAM, _, err = allocator.LoadIPAMConfig([]byte(conf), "") n.IPAM, _, err = allocator.LoadIPAMConfig([]byte(conf), "")
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
cniVersion := "0.4.0" newConf, err := buildOneConfig(n.Name, cniVersion, n, result)
newConf, err := buildOneConfig(netName, cniVersion, n, res)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
confString, err := json.Marshal(newConf) confString, err := json.Marshal(newConf)
@ -299,11 +303,13 @@ var _ = Describe("ptp Operations", func() {
// CNI Check host-device in the target namespace // CNI Check host-device in the target namespace
err = originalNS.Do(func(ns.NetNS) error { err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
var err error return testutils.CmdCheckWithArgs(args, func() error { return cmdCheck(args) })
err = testutils.CmdCheckWithArgs(args, func() error { return cmdCheck(args) })
return err
}) })
Expect(err).NotTo(HaveOccurred()) if testutils.SpecVersionHasCHECK(cniVersion) {
Expect(err).NotTo(HaveOccurred())
} else {
Expect(err).To(MatchError("config version does not allow CHECK"))
}
args.StdinData = []byte(conf) args.StdinData = []byte(conf)
@ -320,7 +326,7 @@ var _ = Describe("ptp Operations", func() {
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
// Make sure ptp link has been deleted // Make sure ptp link has been deleted
err = targetNs.Do(func(ns.NetNS) error { err = targetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
link, err := netlink.LinkByName(IFNAME) link, err := netlink.LinkByName(IFNAME)
@ -331,227 +337,200 @@ var _ = Describe("ptp Operations", func() {
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
} }
It("configures and deconfigures a ptp link with ADD/DEL", func() { for _, ver := range testutils.AllSpecVersions {
dnsConf := types.DNS{ // Redefine ver inside for scope so real value is picked up by each dynamically defined It()
Nameservers: []string{"10.1.2.123"}, // See Gingkgo's "Patterns for dynamically generating tests" documentation.
Domain: "some.domain.test", ver := ver
Search: []string{"search.test"},
Options: []string{"option1:foo"},
}
dnsConfBytes, err := json.Marshal(dnsConf)
Expect(err).NotTo(HaveOccurred())
conf := fmt.Sprintf(`{ It(fmt.Sprintf("[%s] configures and deconfigures a ptp link with ADD/DEL", ver), func() {
"cniVersion": "0.3.1", dnsConf := types.DNS{
"name": "mynet", Nameservers: []string{"10.1.2.123"},
"type": "ptp", Domain: "some.domain.test",
"ipMasq": true, Search: []string{"search.test"},
"mtu": 5000, Options: []string{"option1:foo"},
"ipam": { }
"type": "host-local", dnsConfBytes, err := json.Marshal(dnsConf)
"subnet": "10.1.2.0/24"
},
"dns": %s
}`, string(dnsConfBytes))
doTest(conf, 1, dnsConf)
})
It("configures and deconfigures a dual-stack ptp link with ADD/DEL", func() {
conf := `{
"cniVersion": "0.3.1",
"name": "mynet",
"type": "ptp",
"ipMasq": true,
"mtu": 5000,
"ipam": {
"type": "host-local",
"ranges": [
[{ "subnet": "10.1.2.0/24"}],
[{ "subnet": "2001:db8:1::0/66"}]
]
}
}`
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()) Expect(err).NotTo(HaveOccurred())
conf := fmt.Sprintf(`{ conf := fmt.Sprintf(`{
"cniVersion": "0.3.1", "cniVersion": "%s",
"name": "mynet", "name": "mynet",
"type": "ptp", "type": "ptp",
"ipMasq": true, "ipMasq": true,
"mtu": 5000, "mtu": 5000,
"ipam": { "ipam": {
"type": "host-local", "type": "host-local",
"subnet": "10.1.2.0/24", "subnet": "10.1.2.0/24",
"resolvConf": "%s" "dataDir": "%s"
}, },
"dns": %s "dns": %s
}`, resolvConfPath, string(dnsConfBytes)) }`, ver, dataDir, string(dnsConfBytes))
doTest(conf, 1, ptpDNSConf) doTest(conf, ver, 1, dnsConf, targetNS)
} })
})
It("overrides IPAM DNS settings if any empty list DNS settings provided", func() { It(fmt.Sprintf("[%s] configures and deconfigures a dual-stack ptp link with ADD/DEL", ver), func() {
ipamDNSConf := types.DNS{ conf := fmt.Sprintf(`{
Nameservers: []string{"10.1.2.123"}, "cniVersion": "%s",
Domain: "some.domain.test", "name": "mynet",
Search: []string{"search.test"}, "type": "ptp",
Options: []string{"option1:foo"}, "ipMasq": true,
} "mtu": 5000,
resolvConfPath, err := testutils.TmpResolvConf(ipamDNSConf) "ipam": {
Expect(err).NotTo(HaveOccurred()) "type": "host-local",
defer os.RemoveAll(resolvConfPath) "ranges": [
[{ "subnet": "10.1.2.0/24"}],
[{ "subnet": "2001:db8:1::0/66"}]
],
"dataDir": "%s"
}
}`, ver, dataDir)
conf := fmt.Sprintf(`{ doTest(conf, ver, 2, types.DNS{}, targetNS)
"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(fmt.Sprintf("[%s] does not override IPAM DNS settings if no DNS settings provided", ver), 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)
It("deconfigures an unconfigured ptp link with DEL", func() { conf := fmt.Sprintf(`{
const IFNAME = "ptp0" "cniVersion": "%s",
"name": "mynet",
"type": "ptp",
"ipMasq": true,
"mtu": 5000,
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24",
"resolvConf": "%s",
"dataDir": "%s"
}
}`, ver, resolvConfPath, dataDir)
conf := `{ doTest(conf, ver, 1, ipamDNSConf, targetNS)
"cniVersion": "0.3.0", })
"name": "mynet",
"type": "ptp",
"ipMasq": true,
"mtu": 5000,
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24"
}
}`
targetNs, err := testutils.NewNS() It(fmt.Sprintf("[%s] overrides IPAM DNS settings if any DNS settings provided", ver), func() {
Expect(err).NotTo(HaveOccurred()) ipamDNSConf := types.DNS{
defer targetNs.Close() 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)
args := &skel.CmdArgs{ for _, ptpDNSConf := range []types.DNS{
ContainerID: "dummy", {
Netns: targetNs.Path(), Nameservers: []string{"10.1.2.234"},
IfName: IFNAME, },
StdinData: []byte(conf), {
} Domain: "someother.domain.test",
},
{
Search: []string{"search.elsewhere.test"},
},
{
Options: []string{"option2:bar"},
},
} {
dnsConfBytes, err := json.Marshal(ptpDNSConf)
Expect(err).NotTo(HaveOccurred())
// Call the plugins with the DEL command. It should not error even though the veth doesn't exist. conf := fmt.Sprintf(`{
err = originalNS.Do(func(ns.NetNS) error { "cniVersion": "%s",
defer GinkgoRecover() "name": "mynet",
"type": "ptp",
"ipMasq": true,
"mtu": 5000,
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24",
"resolvConf": "%s",
"dataDir": "%s"
},
"dns": %s
}`, ver, resolvConfPath, dataDir, string(dnsConfBytes))
err := testutils.CmdDelWithArgs(args, func() error { doTest(conf, ver, 1, ptpDNSConf, targetNS)
return cmdDel(args) }
})
It(fmt.Sprintf("[%s] overrides IPAM DNS settings if any empty list DNS settings provided", ver), 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": "%s",
"name": "mynet",
"type": "ptp",
"ipMasq": true,
"mtu": 5000,
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24",
"dataDir": "%s",
"resolvConf": "%s"
},
"dns": {
"nameservers": [],
"search": [],
"options": []
}
}`, ver, dataDir, resolvConfPath)
doTest(conf, ver, 1, types.DNS{}, targetNS)
})
It(fmt.Sprintf("[%s] deconfigures an unconfigured ptp link with DEL", ver), func() {
const IFNAME = "ptp0"
conf := fmt.Sprintf(`{
"cniVersion": "%s",
"name": "mynet",
"type": "ptp",
"ipMasq": true,
"mtu": 5000,
"ipam": {
"type": "host-local",
"dataDir": "%s",
"subnet": "10.1.2.0/24"
}
}`, ver, dataDir)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: IFNAME,
StdinData: []byte(conf),
}
// Call the plugins with the DEL command. It should not error even though the veth doesn't exist.
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()) Expect(err).NotTo(HaveOccurred())
return nil
}) })
Expect(err).NotTo(HaveOccurred()) }
})
It("configures and deconfigures a CNI V4 ptp link with ADD/DEL", func() {
conf := `{
"cniVersion": "0.4.0",
"name": "ptpNetv4",
"type": "ptp",
"ipMasq": true,
"mtu": 5000,
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24"
}
}`
doTestv4(conf, "ptpNetv4", 1)
})
It("configures and deconfigures a CNI V4 dual-stack ptp link with ADD/DEL", func() {
conf := `{
"cniVersion": "0.4.0",
"name": "ptpNetv4ds",
"type": "ptp",
"ipMasq": true,
"mtu": 5000,
"ipam": {
"type": "host-local",
"ranges": [
[{ "subnet": "10.1.2.0/24"}],
[{ "subnet": "2001:db8:1::0/66"}]
]
}
}`
doTestv4(conf, "ptpNetv4ds", 2)
})
}) })

View File

@ -24,7 +24,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/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/ip" "github.com/containernetworking/plugins/pkg/ip"

View File

@ -17,12 +17,17 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io/ioutil"
"net" "net"
"os"
"strings"
"syscall" "syscall"
"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/020"
"github.com/containernetworking/cni/pkg/types/040"
"github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils" "github.com/containernetworking/plugins/pkg/testutils"
@ -45,7 +50,7 @@ type Net struct {
IPAM *allocator.IPAMConfig `json:"ipam"` IPAM *allocator.IPAMConfig `json:"ipam"`
DNS types.DNS `json:"dns"` DNS types.DNS `json:"dns"`
RawPrevResult map[string]interface{} `json:"prevResult,omitempty"` RawPrevResult map[string]interface{} `json:"prevResult,omitempty"`
PrevResult current.Result `json:"-"` PrevResult types100.Result `json:"-"`
} }
func buildOneConfig(netName string, cniVersion string, orig *Net, prevResult types.Result) (*Net, error) { func buildOneConfig(netName string, cniVersion string, orig *Net, prevResult types.Result) (*Net, error) {
@ -91,14 +96,90 @@ func buildOneConfig(netName string, cniVersion string, orig *Net, prevResult typ
} }
type tester interface {
// verifyResult minimally verifies the Result and returns the interface's MAC address
verifyResult(result types.Result, name string) string
}
type testerBase struct{}
type testerV10x testerBase
type testerV04x testerBase
type testerV03x testerBase
type testerV01xOr02x testerBase
func newTesterByVersion(version string) tester {
switch {
case strings.HasPrefix(version, "1.0."):
return &testerV10x{}
case strings.HasPrefix(version, "0.4."):
return &testerV04x{}
case strings.HasPrefix(version, "0.3."):
return &testerV03x{}
default:
return &testerV01xOr02x{}
}
}
// verifyResult minimally verifies the Result and returns the interface's MAC address
func (t *testerV10x) verifyResult(result types.Result, name string) string {
r, err := types100.GetResult(result)
Expect(err).NotTo(HaveOccurred())
Expect(len(r.Interfaces)).To(Equal(1))
Expect(r.Interfaces[0].Name).To(Equal(name))
Expect(len(r.IPs)).To(Equal(1))
return r.Interfaces[0].Mac
}
func verify0403(result types.Result, name string) string {
r, err := types040.GetResult(result)
Expect(err).NotTo(HaveOccurred())
Expect(len(r.Interfaces)).To(Equal(1))
Expect(r.Interfaces[0].Name).To(Equal(name))
Expect(len(r.IPs)).To(Equal(1))
return r.Interfaces[0].Mac
}
// verifyResult minimally verifies the Result and returns the interface's MAC address
func (t *testerV04x) verifyResult(result types.Result, name string) string {
return verify0403(result, name)
}
// verifyResult minimally verifies the Result and returns the interface's MAC address
func (t *testerV03x) verifyResult(result types.Result, name string) string {
return verify0403(result, name)
}
// verifyResult minimally verifies the Result and returns the interface's MAC address
func (t *testerV01xOr02x) verifyResult(result types.Result, name string) string {
r, err := types020.GetResult(result)
Expect(err).NotTo(HaveOccurred())
Expect(r.IP4.IP.IP).NotTo(BeNil())
Expect(r.IP6).To(BeNil())
// 0.2 and earlier don't return MAC address
return ""
}
var _ = Describe("vlan Operations", func() { var _ = Describe("vlan Operations", func() {
var originalNS ns.NetNS var originalNS, targetNS ns.NetNS
var dataDir string
BeforeEach(func() { BeforeEach(func() {
// Create a new NetNS so we don't modify the host // Create a new NetNS so we don't modify the host
var err error var err error
originalNS, err = testutils.NewNS() originalNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
targetNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
dataDir, err = ioutil.TempDir("", "vlan_test")
Expect(err).NotTo(HaveOccurred())
err = originalNS.Do(func(ns.NetNS) error { err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
@ -120,376 +201,290 @@ var _ = Describe("vlan Operations", func() {
}) })
AfterEach(func() { AfterEach(func() {
Expect(os.RemoveAll(dataDir)).To(Succeed())
Expect(originalNS.Close()).To(Succeed()) Expect(originalNS.Close()).To(Succeed())
Expect(testutils.UnmountNS(originalNS)).To(Succeed()) Expect(testutils.UnmountNS(originalNS)).To(Succeed())
Expect(targetNS.Close()).To(Succeed())
Expect(testutils.UnmountNS(targetNS)).To(Succeed())
}) })
It("creates an vlan link in a non-default namespace with given MTU", func() { for _, ver := range testutils.AllSpecVersions {
conf := &NetConf{ // Redefine ver inside for scope so real value is picked up by each dynamically defined It()
NetConf: types.NetConf{ // See Gingkgo's "Patterns for dynamically generating tests" documentation.
CNIVersion: "0.3.0", ver := ver
Name: "testConfig",
Type: "vlan",
},
Master: MASTER_NAME,
VlanId: 33,
MTU: 1500,
}
// Create vlan in other namespace It(fmt.Sprintf("[%s] creates an vlan link in a non-default namespace with given MTU", ver), func() {
targetNs, err := testutils.NewNS() conf := &NetConf{
Expect(err).NotTo(HaveOccurred()) NetConf: types.NetConf{
defer targetNs.Close() CNIVersion: ver,
Name: "testConfig",
Type: "vlan",
},
Master: MASTER_NAME,
VlanId: 33,
MTU: 1500,
}
err = originalNS.Do(func(ns.NetNS) error { // Create vlan in other namespace
defer GinkgoRecover() err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
_, err := createVlan(conf, "foobar0", targetNs) _, err := createVlan(conf, "foobar0", targetNS)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
return nil return nil
})
Expect(err).NotTo(HaveOccurred())
// Make sure vlan link exists in the target namespace
err = targetNs.Do(func(ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName("foobar0")
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().Name).To(Equal("foobar0"))
Expect(link.Attrs().MTU).To(Equal(1500))
return nil
})
Expect(err).NotTo(HaveOccurred())
})
It("creates an vlan link in a non-default namespace with master's MTU", func() {
conf := &NetConf{
NetConf: types.NetConf{
CNIVersion: "0.3.0",
Name: "testConfig",
Type: "vlan",
},
Master: MASTER_NAME,
VlanId: 33,
}
// Create vlan in other namespace
targetNs, err := testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
defer targetNs.Close()
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
m, err := netlink.LinkByName(MASTER_NAME)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetMTU(m, 1200)
Expect(err).NotTo(HaveOccurred())
_, err = createVlan(conf, "foobar0", targetNs)
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
// Make sure vlan link exists in the target namespace
err = targetNs.Do(func(ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName("foobar0")
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().Name).To(Equal("foobar0"))
Expect(link.Attrs().MTU).To(Equal(1200))
return nil
})
Expect(err).NotTo(HaveOccurred())
})
It("configures and deconfigures an vlan link with ADD/DEL", func() {
const IFNAME = "eth0"
conf := fmt.Sprintf(`{
"cniVersion": "0.3.0",
"name": "mynet",
"type": "vlan",
"master": "%s",
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24"
}
}`, MASTER_NAME)
targetNs, err := testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
defer targetNs.Close()
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNs.Path(),
IfName: IFNAME,
StdinData: []byte(conf),
}
var result *current.Result
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
r, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
result, err = current.GetResult(r) // Make sure vlan link exists in the target namespace
Expect(err).NotTo(HaveOccurred()) err = targetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
Expect(len(result.Interfaces)).To(Equal(1)) link, err := netlink.LinkByName("foobar0")
Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) Expect(err).NotTo(HaveOccurred())
Expect(len(result.IPs)).To(Equal(1)) Expect(link.Attrs().Name).To(Equal("foobar0"))
return nil Expect(link.Attrs().MTU).To(Equal(1500))
}) return nil
Expect(err).NotTo(HaveOccurred())
// Make sure vlan link exists in the target namespace
err = targetNs.Do(func(ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName(IFNAME)
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().Name).To(Equal(IFNAME))
hwaddr, err := net.ParseMAC(result.Interfaces[0].Mac)
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr))
addrs, err := netlink.AddrList(link, syscall.AF_INET)
Expect(err).NotTo(HaveOccurred())
Expect(len(addrs)).To(Equal(1))
return nil
})
Expect(err).NotTo(HaveOccurred())
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
return nil
}) })
Expect(err).NotTo(HaveOccurred())
// Make sure vlan link has been deleted It(fmt.Sprintf("[%s] creates an vlan link in a non-default namespace with master's MTU", ver), func() {
err = targetNs.Do(func(ns.NetNS) error { conf := &NetConf{
defer GinkgoRecover() NetConf: types.NetConf{
CNIVersion: ver,
Name: "testConfig",
Type: "vlan",
},
Master: MASTER_NAME,
VlanId: 33,
}
link, err := netlink.LinkByName(IFNAME) // Create vlan in other namespace
Expect(err).To(HaveOccurred()) err := originalNS.Do(func(ns.NetNS) error {
Expect(link).To(BeNil()) defer GinkgoRecover()
return nil
})
Expect(err).NotTo(HaveOccurred())
// DEL can be called multiple times, make sure no error is returned m, err := netlink.LinkByName(MASTER_NAME)
// if the device is already removed. Expect(err).NotTo(HaveOccurred())
err = originalNS.Do(func(ns.NetNS) error { err = netlink.LinkSetMTU(m, 1200)
defer GinkgoRecover() Expect(err).NotTo(HaveOccurred())
err = testutils.CmdDelWithArgs(args, func() error { _, err = createVlan(conf, "foobar0", targetNS)
return cmdDel(args) Expect(err).NotTo(HaveOccurred())
}) return nil
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
})
It("configures and deconfigures an CNI V4 vlan link with ADD/CHECK/DEL", func() {
const IFNAME = "eth0"
conf := fmt.Sprintf(`{
"cniVersion": "0.4.0",
"name": "vlanTestv4",
"type": "vlan",
"master": "%s",
"vlanId": 1234,
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24"
}
}`, MASTER_NAME)
targetNs, err := testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
defer targetNs.Close()
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNs.Path(),
IfName: IFNAME,
StdinData: []byte(conf),
}
var result *current.Result
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
r, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
result, err = current.GetResult(r) // Make sure vlan link exists in the target namespace
Expect(err).NotTo(HaveOccurred()) err = targetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
Expect(len(result.Interfaces)).To(Equal(1)) link, err := netlink.LinkByName("foobar0")
Expect(result.Interfaces[0].Name).To(Equal(IFNAME)) Expect(err).NotTo(HaveOccurred())
Expect(len(result.IPs)).To(Equal(1)) Expect(link.Attrs().Name).To(Equal("foobar0"))
return nil Expect(link.Attrs().MTU).To(Equal(1200))
}) return nil
Expect(err).NotTo(HaveOccurred())
// Make sure vlan link exists in the target namespace
err = targetNs.Do(func(ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName(IFNAME)
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().Name).To(Equal(IFNAME))
hwaddr, err := net.ParseMAC(result.Interfaces[0].Mac)
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr))
addrs, err := netlink.AddrList(link, syscall.AF_INET)
Expect(err).NotTo(HaveOccurred())
Expect(len(addrs)).To(Equal(1))
return nil
})
Expect(err).NotTo(HaveOccurred())
// call CmdCheck
n := &Net{}
err = json.Unmarshal([]byte(conf), &n)
Expect(err).NotTo(HaveOccurred())
n.IPAM, _, err = allocator.LoadIPAMConfig([]byte(conf), "")
Expect(err).NotTo(HaveOccurred())
cniVersion := "0.4.0"
newConf, err := buildOneConfig("vlanTestv4", cniVersion, n, result)
Expect(err).NotTo(HaveOccurred())
confString, err := json.Marshal(newConf)
Expect(err).NotTo(HaveOccurred())
args.StdinData = confString
// CNI Check host-device in the target namespace
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
var err error
err = testutils.CmdCheckWithArgs(args, func() error { return cmdCheck(args) })
return err
})
Expect(err).NotTo(HaveOccurred())
args.StdinData = []byte(conf)
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
return nil
}) })
Expect(err).NotTo(HaveOccurred())
// Make sure vlan link has been deleted It(fmt.Sprintf("[%s] configures and deconfigures a vlan link with ADD/CHECK/DEL", ver), func() {
err = targetNs.Do(func(ns.NetNS) error { const IFNAME = "eth0"
defer GinkgoRecover()
link, err := netlink.LinkByName(IFNAME) conf := fmt.Sprintf(`{
Expect(err).To(HaveOccurred()) "cniVersion": "%s",
Expect(link).To(BeNil()) "name": "vlanTestv4",
return nil "type": "vlan",
}) "master": "%s",
Expect(err).NotTo(HaveOccurred()) "vlanId": 1234,
}) "ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24",
"dataDir": "%s"
}
}`, ver, MASTER_NAME, dataDir)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: IFNAME,
StdinData: []byte(conf),
}
t := newTesterByVersion(ver)
var result types.Result
var macAddress string
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
var err error
result, _, err = testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
macAddress = t.verifyResult(result, IFNAME)
return nil
})
Expect(err).NotTo(HaveOccurred())
// Make sure vlan link exists in the target namespace
err = targetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName(IFNAME)
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().Name).To(Equal(IFNAME))
if macAddress != "" {
hwaddr, err := net.ParseMAC(macAddress)
Expect(err).NotTo(HaveOccurred())
Expect(link.Attrs().HardwareAddr).To(Equal(hwaddr))
}
addrs, err := netlink.AddrList(link, syscall.AF_INET)
Expect(err).NotTo(HaveOccurred())
Expect(len(addrs)).To(Equal(1))
return nil
})
Expect(err).NotTo(HaveOccurred())
// call CmdCheck
n := &Net{}
err = json.Unmarshal([]byte(conf), &n)
Expect(err).NotTo(HaveOccurred())
n.IPAM, _, err = allocator.LoadIPAMConfig([]byte(conf), "")
Expect(err).NotTo(HaveOccurred())
newConf, err := buildOneConfig("vlanTestv4", ver, n, result)
Expect(err).NotTo(HaveOccurred())
confString, err := json.Marshal(newConf)
Expect(err).NotTo(HaveOccurred())
args.StdinData = confString
// CNI Check host-device in the target namespace
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
return testutils.CmdCheckWithArgs(args, func() error { return cmdCheck(args) })
})
if testutils.SpecVersionHasCHECK(ver) {
Expect(err).NotTo(HaveOccurred())
} else {
Expect(err).To(MatchError("config version does not allow CHECK"))
}
args.StdinData = []byte(conf)
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 { err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover() defer GinkgoRecover()
// set master link's MTU to 1500 err = testutils.CmdDelWithArgs(args, func() error {
link, err := netlink.LinkByName(MASTER_NAME) return cmdDel(args)
Expect(err).NotTo(HaveOccurred()) })
err = netlink.LinkSetMTU(link, 1500)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
// Make sure vlan link has been deleted
err = targetNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
link, err := netlink.LinkByName(IFNAME)
Expect(err).To(HaveOccurred())
Expect(link).To(BeNil())
return nil
})
Expect(err).NotTo(HaveOccurred())
// 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 return nil
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
}) })
It("fails to create vlan link with greater MTU than master interface", func() { Describe("fails to create vlan link with invalid MTU", func() {
var err error const confFmt = `{
"cniVersion": "%s",
"name": "mynet",
"type": "vlan",
"master": "%s",
"mtu": %d,
"ipam": {
"type": "host-local",
"subnet": "10.1.2.0/24",
"dataDir": "%s"
}
}`
args := &skel.CmdArgs{ BeforeEach(func() {
ContainerID: "dummy", var err error
Netns: "/var/run/netns/test", err = originalNS.Do(func(ns.NetNS) error {
IfName: "eth0", defer GinkgoRecover()
StdinData: []byte(fmt.Sprintf(conf, MASTER_NAME, 1600)),
}
_ = originalNS.Do(func(netNS ns.NetNS) error { // set master link's MTU to 1500
defer GinkgoRecover() link, err := netlink.LinkByName(MASTER_NAME)
Expect(err).NotTo(HaveOccurred())
err = netlink.LinkSetMTU(link, 1500)
Expect(err).NotTo(HaveOccurred())
_, _, err = testutils.CmdAddWithArgs(args, func() error { return nil
return cmdAdd(args) })
Expect(err).NotTo(HaveOccurred())
})
It(fmt.Sprintf("[%s] fails to create vlan link with greater MTU than master interface", ver), func() {
var err error
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: "/var/run/netns/test",
IfName: "eth0",
StdinData: []byte(fmt.Sprintf(confFmt, ver, MASTER_NAME, 1600, dataDir)),
}
_ = 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(fmt.Sprintf("[%s] fails to create vlan link with negative MTU", ver), func() {
var err error
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: "/var/run/netns/test",
IfName: "eth0",
StdinData: []byte(fmt.Sprintf(confFmt, ver, MASTER_NAME, -100, dataDir)),
}
_ = 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
}) })
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
})
})
})
}) })

View File

@ -25,7 +25,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/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/errors" "github.com/containernetworking/plugins/pkg/errors"

View File

@ -24,7 +24,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/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/errors" "github.com/containernetworking/plugins/pkg/errors"

File diff suppressed because it is too large Load Diff

View File

@ -106,7 +106,7 @@ func makeTcpClientInNS(netns string, address string, port int, numBytes int) {
Expect(string(out)).To(Equal(message)) Expect(string(out)).To(Equal(message))
} }
func createVeth(hostNamespace string, hostVethIfName string, containerNamespace string, containerVethIfName string, hostIP []byte, containerIP []byte, hostIfaceMTU int) { func createVeth(hostNs ns.NetNS, hostVethIfName string, containerNs ns.NetNS, containerVethIfName string, hostIP []byte, containerIP []byte, hostIfaceMTU int) {
vethDeviceRequest := &netlink.Veth{ vethDeviceRequest := &netlink.Veth{
LinkAttrs: netlink.LinkAttrs{ LinkAttrs: netlink.LinkAttrs{
Name: hostVethIfName, Name: hostVethIfName,
@ -116,10 +116,7 @@ func createVeth(hostNamespace string, hostVethIfName string, containerNamespace
PeerName: containerVethIfName, PeerName: containerVethIfName,
} }
hostNs, err := ns.GetNS(hostNamespace) err := hostNs.Do(func(_ ns.NetNS) error {
Expect(err).NotTo(HaveOccurred())
err = hostNs.Do(func(_ ns.NetNS) error {
if err := netlink.LinkAdd(vethDeviceRequest); err != nil { if err := netlink.LinkAdd(vethDeviceRequest); err != nil {
return fmt.Errorf("creating veth pair: %s", err) return fmt.Errorf("creating veth pair: %s", err)
} }
@ -129,11 +126,6 @@ func createVeth(hostNamespace string, hostVethIfName string, containerNamespace
return fmt.Errorf("failed to find newly-created veth device %q: %v", containerVethIfName, err) return fmt.Errorf("failed to find newly-created veth device %q: %v", containerVethIfName, err)
} }
containerNs, err := ns.GetNS(containerNamespace)
if err != nil {
return err
}
err = netlink.LinkSetNsFd(containerVeth, int(containerNs.Fd())) err = netlink.LinkSetNsFd(containerVeth, int(containerNs.Fd()))
if err != nil { if err != nil {
return fmt.Errorf("failed to move veth to container namespace: %s", err) return fmt.Errorf("failed to move veth to container namespace: %s", err)
@ -169,8 +161,6 @@ func createVeth(hostNamespace string, hostVethIfName string, containerNamespace
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
containerNs, err := ns.GetNS(containerNamespace)
Expect(err).NotTo(HaveOccurred())
err = containerNs.Do(func(_ ns.NetNS) error { err = containerNs.Do(func(_ ns.NetNS) error {
peerAddr := &net.IPNet{ peerAddr := &net.IPNet{
IP: hostIP, IP: hostIP,
@ -203,7 +193,7 @@ func createVeth(hostNamespace string, hostVethIfName string, containerNamespace
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
} }
func createVethInOneNs(namespace, vethName, peerName string) { func createVethInOneNs(netNS ns.NetNS, vethName, peerName string) {
vethDeviceRequest := &netlink.Veth{ vethDeviceRequest := &netlink.Veth{
LinkAttrs: netlink.LinkAttrs{ LinkAttrs: netlink.LinkAttrs{
Name: vethName, Name: vethName,
@ -212,10 +202,7 @@ func createVethInOneNs(namespace, vethName, peerName string) {
PeerName: peerName, PeerName: peerName,
} }
netNS, err := ns.GetNS(namespace) err := netNS.Do(func(_ ns.NetNS) error {
Expect(err).NotTo(HaveOccurred())
err = netNS.Do(func(_ ns.NetNS) error {
if err := netlink.LinkAdd(vethDeviceRequest); err != nil { if err := netlink.LinkAdd(vethDeviceRequest); err != nil {
return fmt.Errorf("failed to create veth pair: %v", err) return fmt.Errorf("failed to create veth pair: %v", err)
} }
@ -229,11 +216,8 @@ func createVethInOneNs(namespace, vethName, peerName string) {
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
} }
func createMacvlan(namespace, master, macvlanName string) { func createMacvlan(netNS ns.NetNS, master, macvlanName string) {
netNS, err := ns.GetNS(namespace) err := netNS.Do(func(_ ns.NetNS) error {
Expect(err).NotTo(HaveOccurred())
err = netNS.Do(func(_ ns.NetNS) error {
m, err := netlink.LinkByName(master) m, err := netlink.LinkByName(master)
if err != nil { if err != nil {
return fmt.Errorf("failed to lookup master %q: %v", master, err) return fmt.Errorf("failed to lookup master %q: %v", master, err)

View File

@ -23,7 +23,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/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/ip" "github.com/containernetworking/plugins/pkg/ip"

View File

@ -24,7 +24,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/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/cni/pkg/version"
bv "github.com/containernetworking/plugins/pkg/utils/buildversion" bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
@ -130,7 +130,9 @@ func cmdAdd(args *skel.CmdArgs) error {
} }
if result == nil { if result == nil {
result = &current.Result{} result = &current.Result{
CNIVersion: current.ImplementedSpecVersion,
}
} }
return types.PrintResult(result, conf.CNIVersion) return types.PrintResult(result, conf.CNIVersion)
} }

View File

@ -24,7 +24,7 @@ import (
"github.com/containernetworking/cni/pkg/invoke" "github.com/containernetworking/cni/pkg/invoke"
"github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils" "github.com/containernetworking/plugins/pkg/testutils"
@ -34,30 +34,7 @@ import (
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
) )
const ( const ifname = "eth0"
confTmpl = `{
"cniVersion": "0.3.1",
"name": "firewalld-test",
"type": "firewall",
"backend": "firewalld",
"zone": "trusted",
"prevResult": {
"cniVersion": "0.3.0",
"interfaces": [
{"name": "%s", "sandbox": "%s"}
],
"ips": [
{
"version": "4",
"address": "10.0.0.2/24",
"gateway": "10.0.0.1",
"interface": 0
}
]
}
}`
ifname = "eth0"
)
type fakeFirewalld struct { type fakeFirewalld struct {
zone string zone string
@ -125,6 +102,30 @@ func spawnSessionDbus(wg *sync.WaitGroup) (string, *exec.Cmd) {
return busAddr, cmd return busAddr, cmd
} }
func makeFirewalldConf(ver, ifname string, ns ns.NetNS) []byte {
return []byte(fmt.Sprintf(`{
"cniVersion": "%s",
"name": "firewalld-test",
"type": "firewall",
"backend": "firewalld",
"zone": "trusted",
"prevResult": {
"cniVersion": "%s",
"interfaces": [
{"name": "%s", "sandbox": "%s"}
],
"ips": [
{
"version": "4",
"address": "10.0.0.2/24",
"gateway": "10.0.0.1",
"interface": 0
}
]
}
}`, ver, ver, ifname, ns.Path()))
}
var _ = Describe("firewalld test", func() { var _ = Describe("firewalld test", func() {
var ( var (
targetNs ns.NetNS targetNs ns.NetNS
@ -177,167 +178,119 @@ var _ = Describe("firewalld test", func() {
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
wg.Wait() wg.Wait()
Expect(targetNs.Close()).To(Succeed())
Expect(testutils.UnmountNS(targetNs)).To(Succeed())
}) })
It("works with a 0.3.1 config", func() { // firewall plugin requires a prevResult and thus only supports 0.3.0
Expect(isFirewalldRunning()).To(BeTrue()) // and later CNI versions
for _, ver := range []string{"0.3.0", "0.3.1", "0.4.0", "1.0.0"} {
// Redefine ver inside for scope so real value is picked up by each dynamically defined It()
// See Gingkgo's "Patterns for dynamically generating tests" documentation.
ver := ver
conf := fmt.Sprintf(confTmpl, ifname, targetNs.Path()) It(fmt.Sprintf("[%s] works with a config", ver), func() {
args := &skel.CmdArgs{ Expect(isFirewalldRunning()).To(BeTrue())
ContainerID: "dummy",
Netns: targetNs.Path(), conf := makeFirewalldConf(ver, ifname, targetNs)
IfName: ifname, args := &skel.CmdArgs{
StdinData: []byte(conf), ContainerID: "dummy",
} Netns: targetNs.Path(),
_, _, err := testutils.CmdAdd(targetNs.Path(), args.ContainerID, ifname, []byte(conf), func() error { IfName: ifname,
return cmdAdd(args) StdinData: []byte(conf),
}
_, _, err := testutils.CmdAdd(targetNs.Path(), args.ContainerID, ifname, []byte(conf), func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
Expect(fwd.zone).To(Equal("trusted"))
Expect(fwd.source).To(Equal("10.0.0.2/32"))
fwd.clear()
err = testutils.CmdDel(targetNs.Path(), args.ContainerID, ifname, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
Expect(fwd.zone).To(Equal("trusted"))
Expect(fwd.source).To(Equal("10.0.0.2/32"))
}) })
Expect(err).NotTo(HaveOccurred())
Expect(fwd.zone).To(Equal("trusted"))
Expect(fwd.source).To(Equal("10.0.0.2/32"))
fwd.clear()
err = testutils.CmdDel(targetNs.Path(), args.ContainerID, ifname, func() error { It(fmt.Sprintf("[%s] defaults to the firewalld backend", ver), func() {
return cmdDel(args) Expect(isFirewalldRunning()).To(BeTrue())
conf := makeFirewalldConf(ver, ifname, targetNs)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNs.Path(),
IfName: ifname,
StdinData: []byte(conf),
}
_, _, err := testutils.CmdAdd(targetNs.Path(), args.ContainerID, ifname, []byte(conf), func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
Expect(fwd.zone).To(Equal("trusted"))
Expect(fwd.source).To(Equal("10.0.0.2/32"))
}) })
Expect(err).NotTo(HaveOccurred())
Expect(fwd.zone).To(Equal("trusted"))
Expect(fwd.source).To(Equal("10.0.0.2/32"))
})
It("defaults to the firewalld backend", func() { It(fmt.Sprintf("[%s] passes through the prevResult", ver), func() {
conf := `{ Expect(isFirewalldRunning()).To(BeTrue())
"cniVersion": "0.3.1",
"name": "firewalld-test",
"type": "firewall",
"zone": "trusted",
"prevResult": {
"cniVersion": "0.3.0",
"interfaces": [
{"name": "eth0", "sandbox": "/foobar"}
],
"ips": [
{
"version": "4",
"address": "10.0.0.2/24",
"gateway": "10.0.0.1",
"interface": 0
}
]
}
}`
Expect(isFirewalldRunning()).To(BeTrue()) conf := makeFirewalldConf(ver, ifname, targetNs)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNs.Path(),
IfName: ifname,
StdinData: []byte(conf),
}
r, _, err := testutils.CmdAdd(targetNs.Path(), args.ContainerID, ifname, []byte(conf), func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
args := &skel.CmdArgs{ result, err := current.GetResult(r)
ContainerID: "dummy", Expect(err).NotTo(HaveOccurred())
Netns: targetNs.Path(),
IfName: ifname, Expect(len(result.Interfaces)).To(Equal(1))
StdinData: []byte(conf), Expect(result.Interfaces[0].Name).To(Equal("eth0"))
} Expect(len(result.IPs)).To(Equal(1))
_, _, err := testutils.CmdAdd(targetNs.Path(), args.ContainerID, ifname, []byte(conf), func() error { Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24"))
return cmdAdd(args)
}) })
Expect(err).NotTo(HaveOccurred())
Expect(fwd.zone).To(Equal("trusted"))
Expect(fwd.source).To(Equal("10.0.0.2/32"))
})
It("passes through the prevResult", func() { It(fmt.Sprintf("[%s] works with Check", ver), func() {
conf := `{ Expect(isFirewalldRunning()).To(BeTrue())
"cniVersion": "0.3.1",
"name": "firewalld-test",
"type": "firewall",
"zone": "trusted",
"prevResult": {
"cniVersion": "0.3.0",
"interfaces": [
{"name": "eth0", "sandbox": "/foobar"}
],
"ips": [
{
"version": "4",
"address": "10.0.0.2/24",
"gateway": "10.0.0.1",
"interface": 0
}
]
}
}`
Expect(isFirewalldRunning()).To(BeTrue()) conf := makeFirewalldConf(ver, ifname, targetNs)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNs.Path(),
IfName: ifname,
StdinData: []byte(conf),
}
r, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
Expect(fwd.zone).To(Equal("trusted"))
Expect(fwd.source).To(Equal("10.0.0.2/32"))
args := &skel.CmdArgs{ if testutils.SpecVersionHasCHECK(ver) {
ContainerID: "dummy", _, err = current.GetResult(r)
Netns: targetNs.Path(), Expect(err).NotTo(HaveOccurred())
IfName: ifname,
StdinData: []byte(conf), err = testutils.CmdCheckWithArgs(args, func() error {
} return cmdCheck(args)
r, _, err := testutils.CmdAdd(targetNs.Path(), args.ContainerID, ifname, []byte(conf), func() error { })
return cmdAdd(args) Expect(err).NotTo(HaveOccurred())
}
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
Expect(fwd.zone).To(Equal("trusted"))
Expect(fwd.source).To(Equal("10.0.0.2/32"))
}) })
Expect(err).NotTo(HaveOccurred()) }
result, err := current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(len(result.Interfaces)).To(Equal(1))
Expect(result.Interfaces[0].Name).To(Equal("eth0"))
Expect(len(result.IPs)).To(Equal(1))
Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24"))
})
It("works with a 0.4.0 config, including Check", func() {
Expect(isFirewalldRunning()).To(BeTrue())
conf := `{
"cniVersion": "0.4.0",
"name": "firewalld-test",
"type": "firewall",
"backend": "firewalld",
"zone": "trusted",
"prevResult": {
"cniVersion": "0.4.0",
"interfaces": [
{"name": "eth0", "sandbox": "/foobar"}
],
"ips": [
{
"version": "4",
"address": "10.0.0.2/24",
"gateway": "10.0.0.1",
"interface": 0
}
]
}
}`
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNs.Path(),
IfName: ifname,
StdinData: []byte(conf),
}
r, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
Expect(fwd.zone).To(Equal("trusted"))
Expect(fwd.source).To(Equal("10.0.0.2/32"))
_, err = current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
err = testutils.CmdCheckWithArgs(args, func() error {
return cmdCheck(args)
})
Expect(err).NotTo(HaveOccurred())
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
Expect(fwd.zone).To(Equal("trusted"))
Expect(fwd.source).To(Equal("10.0.0.2/32"))
})
}) })

View File

@ -21,7 +21,8 @@ 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/current" "github.com/containernetworking/cni/pkg/types/040"
current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils" "github.com/containernetworking/plugins/pkg/testutils"
@ -165,35 +166,38 @@ func validateCleanedUp(bytes []byte) {
} }
} }
func makeIptablesConf(ver string) []byte {
return []byte(fmt.Sprintf(`{
"name": "test",
"type": "firewall",
"backend": "iptables",
"ifName": "dummy0",
"cniVersion": "%s",
"prevResult": {
"cniVersion": "%s",
"interfaces": [
{"name": "dummy0"}
],
"ips": [
{
"version": "4",
"address": "10.0.0.2/24",
"interface": 0
},
{
"version": "6",
"address": "2001:db8:1:2::1/64",
"interface": 0
}
]
}
}`, ver, ver))
}
var _ = Describe("firewall plugin iptables backend", func() { var _ = Describe("firewall plugin iptables backend", func() {
var originalNS, targetNS ns.NetNS var originalNS, targetNS ns.NetNS
const IFNAME string = "dummy0" const IFNAME string = "dummy0"
fullConf := []byte(`{
"name": "test",
"type": "firewall",
"backend": "iptables",
"ifName": "dummy0",
"cniVersion": "0.3.1",
"prevResult": {
"interfaces": [
{"name": "dummy0"}
],
"ips": [
{
"version": "4",
"address": "10.0.0.2/24",
"interface": 0
},
{
"version": "6",
"address": "2001:db8:1:2::1/64",
"interface": 0
}
]
}
}`)
BeforeEach(func() { BeforeEach(func() {
// Create a new NetNS so we don't modify the host // Create a new NetNS so we don't modify the host
var err error var err error
@ -224,296 +228,174 @@ var _ = Describe("firewall plugin iptables backend", func() {
Expect(targetNS.Close()).To(Succeed()) Expect(targetNS.Close()).To(Succeed())
}) })
It("passes prevResult through unchanged", func() { // firewall plugin requires a prevResult and thus only supports 0.3.0
args := &skel.CmdArgs{ // and later CNI versions
ContainerID: "dummy", for _, ver := range []string{"0.3.0", "0.3.1", "0.4.0", "1.0.0"} {
Netns: targetNS.Path(), // Redefine ver inside for scope so real value is picked up by each dynamically defined It()
IfName: IFNAME, // See Gingkgo's "Patterns for dynamically generating tests" documentation.
StdinData: fullConf, ver := ver
}
err := originalNS.Do(func(ns.NetNS) error { It(fmt.Sprintf("[%s] passes prevResult through unchanged", ver), func() {
defer GinkgoRecover() fullConf := makeIptablesConf(ver)
args := &skel.CmdArgs{
r, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error { ContainerID: "dummy",
return cmdAdd(args) Netns: targetNS.Path(),
}) IfName: IFNAME,
Expect(err).NotTo(HaveOccurred()) StdinData: fullConf,
result, err := current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
Expect(len(result.Interfaces)).To(Equal(1))
Expect(result.Interfaces[0].Name).To(Equal(IFNAME))
Expect(len(result.IPs)).To(Equal(2))
Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24"))
Expect(result.IPs[1].Address.String()).To(Equal("2001:db8:1:2::1/64"))
return nil
})
Expect(err).NotTo(HaveOccurred())
})
It("installs the right iptables rules on the host", func() {
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: IFNAME,
StdinData: fullConf,
}
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
_, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
validateFullRuleset(fullConf)
// ensure creation is idempotent
_, _, err = testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
return nil
})
Expect(err).NotTo(HaveOccurred())
})
It("correctly handles a custom IptablesAdminChainName", func() {
conf := []byte(`{
"name": "test",
"type": "firewall",
"backend": "iptables",
"ifName": "dummy0",
"cniVersion": "0.3.1",
"iptablesAdminChainName": "CNI-foobar",
"prevResult": {
"interfaces": [
{"name": "dummy0"}
],
"ips": [
{
"version": "4",
"address": "10.0.0.2/24",
"interface": 0
},
{
"version": "6",
"address": "2001:db8:1:2::1/64",
"interface": 0
} }
]
}
}`)
args := &skel.CmdArgs{ err := originalNS.Do(func(ns.NetNS) error {
ContainerID: "dummy", defer GinkgoRecover()
Netns: targetNS.Path(),
IfName: IFNAME,
StdinData: conf,
}
err := originalNS.Do(func(ns.NetNS) error { r, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error {
defer GinkgoRecover() return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
_, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, conf, func() error { result, err := current.GetResult(r)
return cmdAdd(args) Expect(err).NotTo(HaveOccurred())
Expect(len(result.Interfaces)).To(Equal(1))
Expect(result.Interfaces[0].Name).To(Equal(IFNAME))
Expect(len(result.IPs)).To(Equal(2))
Expect(result.IPs[0].Address.String()).To(Equal("10.0.0.2/24"))
Expect(result.IPs[1].Address.String()).To(Equal("2001:db8:1:2::1/64"))
return nil
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
})
var ipt *iptables.IPTables It(fmt.Sprintf("[%s] installs the right iptables rules on the host", ver), func() {
for _, proto := range []iptables.Protocol{iptables.ProtocolIPv4, iptables.ProtocolIPv6} { fullConf := makeIptablesConf(ver)
ipt, err = iptables.NewWithProtocol(proto) args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: IFNAME,
StdinData: fullConf,
}
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
_, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
// Ensure custom admin chain name validateFullRuleset(fullConf)
chains, err := ipt.ListChains("filter")
// ensure creation is idempotent
_, _, err = testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
var foundAdmin bool
for _, ch := range chains { return nil
if ch == "CNI-foobar" { })
foundAdmin = true Expect(err).NotTo(HaveOccurred())
})
It(fmt.Sprintf("[%s] correctly handles a custom IptablesAdminChainName", ver), func() {
conf := []byte(fmt.Sprintf(`{
"name": "test",
"type": "firewall",
"backend": "iptables",
"ifName": "dummy0",
"cniVersion": "%s",
"iptablesAdminChainName": "CNI-foobar",
"prevResult": {
"cniVersion": "%s",
"interfaces": [
{"name": "dummy0"}
],
"ips": [
{
"version": "4",
"address": "10.0.0.2/24",
"interface": 0
},
{
"version": "6",
"address": "2001:db8:1:2::1/64",
"interface": 0
}
]
}
}`, ver, ver))
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: IFNAME,
StdinData: conf,
}
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
_, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, conf, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
var ipt *iptables.IPTables
for _, proto := range []iptables.Protocol{iptables.ProtocolIPv4, iptables.ProtocolIPv6} {
ipt, err = iptables.NewWithProtocol(proto)
Expect(err).NotTo(HaveOccurred())
// Ensure custom admin chain name
chains, err := ipt.ListChains("filter")
Expect(err).NotTo(HaveOccurred())
var foundAdmin bool
for _, ch := range chains {
if ch == "CNI-foobar" {
foundAdmin = true
}
} }
Expect(foundAdmin).To(Equal(true))
} }
Expect(foundAdmin).To(Equal(true))
return nil
})
Expect(err).NotTo(HaveOccurred())
})
It(fmt.Sprintf("[%s] installs iptables rules, checks rules, then cleans up on delete", ver), func() {
fullConf := makeIptablesConf(ver)
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: IFNAME,
StdinData: fullConf,
} }
return nil err := originalNS.Do(func(ns.NetNS) error {
}) defer GinkgoRecover()
Expect(err).NotTo(HaveOccurred())
})
It("cleans up on delete", func() { r, _, err := testutils.CmdAddWithArgs(args, func() error {
args := &skel.CmdArgs{ return cmdAdd(args)
ContainerID: "dummy", })
Netns: targetNS.Path(), Expect(err).NotTo(HaveOccurred())
IfName: IFNAME,
StdinData: fullConf,
}
err := originalNS.Do(func(ns.NetNS) error { _, err = types040.GetResult(r)
defer GinkgoRecover() Expect(err).NotTo(HaveOccurred())
_, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error { if testutils.SpecVersionHasCHECK(ver) {
return cmdAdd(args) err = testutils.CmdCheckWithArgs(args, func() error {
}) return cmdCheck(args)
Expect(err).NotTo(HaveOccurred()) })
validateFullRuleset(fullConf) Expect(err).NotTo(HaveOccurred())
validateFullRuleset(fullConf)
err = testutils.CmdDel(targetNS.Path(), args.ContainerID, IFNAME, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
validateCleanedUp(fullConf)
return nil
})
Expect(err).NotTo(HaveOccurred())
})
It("installs the right iptables rules on the host v4.0.x and check is successful", func() {
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: IFNAME,
StdinData: fullConf,
}
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
_, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
validateFullRuleset(fullConf)
return nil
})
Expect(err).NotTo(HaveOccurred())
})
It("cleans up on delete v4.0.x", func() {
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: IFNAME,
StdinData: fullConf,
}
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
_, _, err := testutils.CmdAdd(targetNS.Path(), args.ContainerID, IFNAME, fullConf, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
validateFullRuleset(fullConf)
err = testutils.CmdDel(targetNS.Path(), args.ContainerID, IFNAME, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
validateCleanedUp(fullConf)
return nil
})
Expect(err).NotTo(HaveOccurred())
})
})
var _ = Describe("firewall plugin iptables backend v0.4.x", func() {
var originalNS, targetNS ns.NetNS
const IFNAME string = "dummy0"
fullConf := []byte(`{
"name": "test",
"type": "firewall",
"backend": "iptables",
"ifName": "dummy0",
"cniVersion": "0.4.0",
"prevResult": {
"interfaces": [
{"name": "dummy0"}
],
"ips": [
{
"version": "4",
"address": "10.0.0.2/24",
"interface": 0
},
{
"version": "6",
"address": "2001:db8:1:2::1/64",
"interface": 0
} }
]
}
}`)
BeforeEach(func() { err = testutils.CmdDelWithArgs(args, func() error {
// Create a new NetNS so we don't modify the host return cmdDel(args)
var err error })
originalNS, err = testutils.NewNS() Expect(err).NotTo(HaveOccurred())
Expect(err).NotTo(HaveOccurred()) validateCleanedUp(fullConf)
return nil
err = originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
err = netlink.LinkAdd(&netlink.Dummy{
LinkAttrs: netlink.LinkAttrs{
Name: IFNAME,
},
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
_, err = netlink.LinkByName(IFNAME)
Expect(err).NotTo(HaveOccurred())
return nil
}) })
Expect(err).NotTo(HaveOccurred()) }
targetNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
})
AfterEach(func() {
Expect(originalNS.Close()).To(Succeed())
Expect(targetNS.Close()).To(Succeed())
})
It("installs iptables rules, Check rules then cleans up on delete using v4.0.x", func() {
args := &skel.CmdArgs{
ContainerID: "dummy",
Netns: targetNS.Path(),
IfName: IFNAME,
StdinData: fullConf,
}
err := originalNS.Do(func(ns.NetNS) error {
defer GinkgoRecover()
r, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
_, err = current.GetResult(r)
Expect(err).NotTo(HaveOccurred())
err = testutils.CmdCheckWithArgs(args, func() error {
return cmdCheck(args)
})
Expect(err).NotTo(HaveOccurred())
validateFullRuleset(fullConf)
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
validateCleanedUp(fullConf)
return nil
})
Expect(err).NotTo(HaveOccurred())
})
}) })

View File

@ -18,7 +18,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/containernetworking/cni/pkg/types/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/godbus/dbus" "github.com/godbus/dbus"
) )

View File

@ -21,7 +21,7 @@ import (
"fmt" "fmt"
"net" "net"
"github.com/containernetworking/cni/pkg/types/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/utils" "github.com/containernetworking/plugins/pkg/utils"
"github.com/coreos/go-iptables/iptables" "github.com/coreos/go-iptables/iptables"
) )

View File

@ -189,7 +189,7 @@ func consumeScratchNetConf(containerID, dataDir string) (func(error), []byte, er
return cleanup, netConfBytes, err return cleanup, netConfBytes, err
} }
func delegateAdd(cid, dataDir string, netconf map[string]interface{}) error { func delegateAdd(cid, dataDir, cniVersion string, netconf map[string]interface{}) error {
netconfBytes, err := json.Marshal(netconf) netconfBytes, err := json.Marshal(netconf)
if err != nil { if err != nil {
return fmt.Errorf("error serializing delegate netconf: %v", err) return fmt.Errorf("error serializing delegate netconf: %v", err)
@ -205,7 +205,7 @@ func delegateAdd(cid, dataDir string, netconf map[string]interface{}) error {
return err return err
} }
return result.Print() return types.PrintResult(result, cniVersion)
} }
func hasKey(m map[string]interface{}, k string) bool { func hasKey(m map[string]interface{}, k string) bool {
@ -247,7 +247,10 @@ func cmdAdd(args *skel.CmdArgs) error {
n.Delegate["runtimeConfig"] = n.RuntimeConfig n.Delegate["runtimeConfig"] = n.RuntimeConfig
} }
return doCmdAdd(args, n, fenv) // Delegate CNI config version must match flannel plugin config version
n.Delegate["cniVersion"] = n.CNIVersion
return doCmdAdd(args, n.CNIVersion, n, fenv)
} }
func cmdDel(args *skel.CmdArgs) error { func cmdDel(args *skel.CmdArgs) error {

View File

@ -72,7 +72,7 @@ func getDelegateIPAM(n *NetConf, fenv *subnetEnv) (map[string]interface{}, error
return ipam, nil return ipam, nil
} }
func doCmdAdd(args *skel.CmdArgs, n *NetConf, fenv *subnetEnv) error { func doCmdAdd(args *skel.CmdArgs, cniVersion string, n *NetConf, fenv *subnetEnv) error {
n.Delegate["name"] = n.Name n.Delegate["name"] = n.Name
if !hasKey(n.Delegate, "type") { if !hasKey(n.Delegate, "type") {
@ -105,7 +105,7 @@ func doCmdAdd(args *skel.CmdArgs, n *NetConf, fenv *subnetEnv) error {
} }
n.Delegate["ipam"] = ipam n.Delegate["ipam"] = ipam
return delegateAdd(args.ContainerID, n.DataDir, n.Delegate) return delegateAdd(args.ContainerID, n.DataDir, cniVersion, n.Delegate)
} }
func doCmdDel(args *skel.CmdArgs, n *NetConf) (err error) { func doCmdDel(args *skel.CmdArgs, n *NetConf) (err error) {

View File

@ -20,7 +20,7 @@ import (
"os" "os"
"github.com/containernetworking/cni/pkg/skel" "github.com/containernetworking/cni/pkg/skel"
"github.com/containernetworking/cni/pkg/types/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils" "github.com/containernetworking/plugins/pkg/testutils"
@ -31,6 +31,7 @@ import (
var _ = Describe("Flannel", func() { var _ = Describe("Flannel", func() {
var ( var (
originalNS ns.NetNS originalNS ns.NetNS
targetNS ns.NetNS
onlyIpv4Input string onlyIpv4Input string
onlyIpv6Input string onlyIpv6Input string
dualStackInput string dualStackInput string
@ -40,22 +41,12 @@ var _ = Describe("Flannel", func() {
dataDir string dataDir string
) )
BeforeEach(func() { const inputTemplate = `{
var err error "name": "cni-flannel",
originalNS, err = testutils.NewNS() "type": "flannel",
Expect(err).NotTo(HaveOccurred()) "cniVersion": "%s",
}) "subnetFile": "%s",
"dataDir": "%s"%s
AfterEach(func() {
Expect(originalNS.Close()).To(Succeed())
})
const inputTemplate = `
{
"name": "cni-flannel",
"type": "flannel",
"subnetFile": "%s",
"dataDir": "%s"%s
}` }`
const inputIPAMTemplate = ` const inputIPAMTemplate = `
@ -95,6 +86,8 @@ FLANNEL_MTU=1472
FLANNEL_IPMASQ=true FLANNEL_IPMASQ=true
` `
const IFNAME = "eth0"
var writeSubnetEnv = func(contents string) string { var writeSubnetEnv = func(contents string) string {
file, err := ioutil.TempFile("", "subnet.env") file, err := ioutil.TempFile("", "subnet.env")
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
@ -114,17 +107,29 @@ FLANNEL_IPMASQ=true
return c return c
} }
var makeInput = func(inputIPAM string, subnetFile string) string { var makeInput = func(cniVersion, inputIPAM string, subnetFile string) string {
ipamPart := "" ipamPart := ""
if len(inputIPAM) > 0 { if len(inputIPAM) > 0 {
ipamPart = ",\n \"ipam\":\n" + inputIPAM ipamPart = ",\n \"ipam\":\n" + inputIPAM
} }
return fmt.Sprintf(inputTemplate, subnetFile, dataDir, ipamPart) return fmt.Sprintf(inputTemplate, cniVersion, subnetFile, dataDir, ipamPart)
}
var makeHostLocalIPAM = func(dataDir string) string {
return fmt.Sprintf(`{
"type": "host-local",
"dataDir": "%s"
}`, dataDir)
} }
BeforeEach(func() { BeforeEach(func() {
var err error var err error
originalNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
targetNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
// flannel subnet.env // flannel subnet.env
onlyIpv4SubnetFile = writeSubnetEnv(onlyIpv4FlannelSubnetEnv) onlyIpv4SubnetFile = writeSubnetEnv(onlyIpv4FlannelSubnetEnv)
onlyIpv6SubnetFile = writeSubnetEnv(onlyIpv6FlannelSubnetEnv) onlyIpv6SubnetFile = writeSubnetEnv(onlyIpv6FlannelSubnetEnv)
@ -133,264 +138,274 @@ FLANNEL_IPMASQ=true
// flannel state dir // flannel state dir
dataDir, err = ioutil.TempDir("", "dataDir") dataDir, err = ioutil.TempDir("", "dataDir")
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
onlyIpv4Input = makeInput("", onlyIpv4SubnetFile)
onlyIpv6Input = makeInput("", onlyIpv6SubnetFile)
dualStackInput = makeInput("", dualStackSubnetFile)
}) })
AfterEach(func() { AfterEach(func() {
Expect(targetNS.Close()).To(Succeed())
Expect(testutils.UnmountNS(targetNS)).To(Succeed())
Expect(originalNS.Close()).To(Succeed())
Expect(testutils.UnmountNS(originalNS)).To(Succeed())
os.Remove(onlyIpv4SubnetFile) os.Remove(onlyIpv4SubnetFile)
os.Remove(onlyIpv6SubnetFile) os.Remove(onlyIpv6SubnetFile)
os.Remove(dualStackSubnetFile) os.Remove(dualStackSubnetFile)
os.Remove(dataDir) Expect(os.RemoveAll(dataDir)).To(Succeed())
}) })
Describe("CNI lifecycle", func() { Describe("CNI lifecycle", func() {
Context("when using only ipv4 stack", func() { for _, ver := range testutils.AllSpecVersions {
It("uses dataDir for storing network configuration with ipv4 stack", func() { // Redefine ver inside for scope so real value is picked up by each dynamically defined It()
const IFNAME = "eth0" // See Gingkgo's "Patterns for dynamically generating tests" documentation.
ver := ver
targetNs, err := testutils.NewNS() Context("when using only ipv4 stack", func() {
Expect(err).NotTo(HaveOccurred()) It(fmt.Sprintf("[%s] uses dataDir for storing network configuration with ipv4 stack", ver), func() {
defer targetNs.Close() inputIPAM := makeHostLocalIPAM(dataDir)
args := &skel.CmdArgs{
ContainerID: "some-container-id-ipv4",
Netns: targetNS.Path(),
IfName: IFNAME,
StdinData: []byte(makeInput(ver, inputIPAM, onlyIpv4SubnetFile)),
}
args := &skel.CmdArgs{ err := originalNS.Do(func(ns.NetNS) error {
ContainerID: "some-container-id-ipv4", defer GinkgoRecover()
Netns: targetNs.Path(),
IfName: IFNAME,
StdinData: []byte(onlyIpv4Input),
}
err = originalNS.Do(func(ns.NetNS) error { By("calling ADD with ipv4 stack")
defer GinkgoRecover() GinkgoT().Logf("dataDir is %s", dataDir)
GinkgoT().Logf("conf is %s", args.StdinData)
resI, _, err := testutils.CmdAddWithArgs(args, func() error {
return cmdAdd(args)
})
Expect(err).NotTo(HaveOccurred())
By("calling ADD with ipv4 stack") By("check that plugin writes the net config to dataDir with ipv4 stack")
resI, _, err := testutils.CmdAddWithArgs(args, func() error { path := fmt.Sprintf("%s/%s", dataDir, "some-container-id-ipv4")
return cmdAdd(args) Expect(path).Should(BeAnExistingFile())
netConfBytes, err := ioutil.ReadFile(path)
Expect(err).NotTo(HaveOccurred())
expected := fmt.Sprintf(`{
"cniVersion": "%s",
"ipMasq": false,
"ipam": {
"routes": [
{
"dst": "10.1.0.0/16"
}
],
"ranges": [
[{
"subnet": "10.1.17.0/24"
}]
],
"type": "host-local",
"dataDir": "%s"
},
"isGateway": true,
"mtu": 1472,
"name": "cni-flannel",
"type": "bridge"
}`, ver, dataDir)
Expect(netConfBytes).Should(MatchJSON(expected))
result, err := current.NewResultFromResult(resI)
Expect(err).NotTo(HaveOccurred())
Expect(result.IPs).To(HaveLen(1))
By("calling DEL with ipv4 stack")
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
By("check that plugin removes net config from state dir with ipv4 stack")
Expect(path).ShouldNot(BeAnExistingFile())
By("calling DEL again with ipv4 stack")
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
By("check that plugin does not fail due to missing net config with ipv4 stack")
Expect(err).NotTo(HaveOccurred())
return nil
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
By("check that plugin writes the net config to dataDir with ipv4 stack")
path := fmt.Sprintf("%s/%s", dataDir, "some-container-id-ipv4")
Expect(path).Should(BeAnExistingFile())
netConfBytes, err := ioutil.ReadFile(path)
Expect(err).NotTo(HaveOccurred())
expected := `{
"ipMasq": false,
"ipam": {
"routes": [
{
"dst": "10.1.0.0/16"
}
],
"ranges": [
[{
"subnet": "10.1.17.0/24"
}]
],
"type": "host-local"
},
"isGateway": true,
"mtu": 1472,
"name": "cni-flannel",
"type": "bridge"
}
`
Expect(netConfBytes).Should(MatchJSON(expected))
result, err := current.NewResultFromResult(resI)
Expect(err).NotTo(HaveOccurred())
Expect(result.IPs).To(HaveLen(1))
By("calling DEL with ipv4 stack")
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
By("check that plugin removes net config from state dir with ipv4 stack")
Expect(path).ShouldNot(BeAnExistingFile())
By("calling DEL again with ipv4 stack")
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
By("check that plugin does not fail due to missing net config with ipv4 stack")
Expect(err).NotTo(HaveOccurred())
return nil
}) })
Expect(err).NotTo(HaveOccurred())
}) })
})
Context("when using only ipv6 stack", func() { Context("when using only ipv6 stack", func() {
It("uses dataDir for storing network configuration with ipv6 stack", func() { It(fmt.Sprintf("[%s] uses dataDir for storing network configuration with ipv6 stack", ver), func() {
const IFNAME = "eth0" inputIPAM := makeHostLocalIPAM(dataDir)
args := &skel.CmdArgs{
ContainerID: "some-container-id-ipv6",
Netns: targetNS.Path(),
IfName: IFNAME,
StdinData: []byte(makeInput(ver, inputIPAM, onlyIpv6SubnetFile)),
}
targetNs, err := testutils.NewNS() err := originalNS.Do(func(ns.NetNS) error {
Expect(err).NotTo(HaveOccurred()) defer GinkgoRecover()
defer targetNs.Close()
args := &skel.CmdArgs{ By("calling ADD with ipv6 stack")
ContainerID: "some-container-id-ipv6", resI, _, err := testutils.CmdAddWithArgs(args, func() error {
Netns: targetNs.Path(), return cmdAdd(args)
IfName: IFNAME, })
StdinData: []byte(onlyIpv6Input), Expect(err).NotTo(HaveOccurred())
}
err = originalNS.Do(func(ns.NetNS) error { By("check that plugin writes the net config to dataDir with ipv6 stack")
defer GinkgoRecover() path := fmt.Sprintf("%s/%s", dataDir, "some-container-id-ipv6")
Expect(path).Should(BeAnExistingFile())
By("calling ADD with ipv6 stack") netConfBytes, err := ioutil.ReadFile(path)
resI, _, err := testutils.CmdAddWithArgs(args, func() error { Expect(err).NotTo(HaveOccurred())
return cmdAdd(args) expected := fmt.Sprintf(`{
"cniVersion": "%s",
"ipMasq": false,
"ipam": {
"routes": [
{
"dst": "fc00::/48"
}
],
"ranges": [
[{
"subnet": "fc00::/64"
}]
],
"type": "host-local",
"dataDir": "%s"
},
"isGateway": true,
"mtu": 1472,
"name": "cni-flannel",
"type": "bridge"
}`, ver, dataDir)
Expect(netConfBytes).Should(MatchJSON(expected))
result, err := current.NewResultFromResult(resI)
Expect(err).NotTo(HaveOccurred())
Expect(result.IPs).To(HaveLen(1))
By("calling DEL with ipv6 stack")
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
By("check that plugin removes net config from state dir with ipv6 stack")
Expect(path).ShouldNot(BeAnExistingFile())
By("calling DEL again with ipv6 stack")
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
By("check that plugin does not fail due to missing net config with ipv6 stack")
Expect(err).NotTo(HaveOccurred())
return nil
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
By("check that plugin writes the net config to dataDir with ipv6 stack")
path := fmt.Sprintf("%s/%s", dataDir, "some-container-id-ipv6")
Expect(path).Should(BeAnExistingFile())
netConfBytes, err := ioutil.ReadFile(path)
Expect(err).NotTo(HaveOccurred())
expected := `{
"ipMasq": false,
"ipam": {
"routes": [
{
"dst": "fc00::/48"
}
],
"ranges": [
[{
"subnet": "fc00::/64"
}]
],
"type": "host-local"
},
"isGateway": true,
"mtu": 1472,
"name": "cni-flannel",
"type": "bridge"
}
`
Expect(netConfBytes).Should(MatchJSON(expected))
result, err := current.NewResultFromResult(resI)
Expect(err).NotTo(HaveOccurred())
Expect(result.IPs).To(HaveLen(1))
By("calling DEL with ipv6 stack")
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
By("check that plugin removes net config from state dir with ipv6 stack")
Expect(path).ShouldNot(BeAnExistingFile())
By("calling DEL again with ipv6 stack")
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
By("check that plugin does not fail due to missing net config with ipv6 stack")
Expect(err).NotTo(HaveOccurred())
return nil
}) })
Expect(err).NotTo(HaveOccurred())
}) })
})
Context("when using dual stack", func() { Context("when using dual stack", func() {
It("uses dataDir for storing network configuration with dual stack", func() { It(fmt.Sprintf("[%s] uses dataDir for storing network configuration with dual stack", ver), func() {
const IFNAME = "eth0" inputIPAM := makeHostLocalIPAM(dataDir)
args := &skel.CmdArgs{
ContainerID: "some-container-id-dual-stack",
Netns: targetNS.Path(),
IfName: IFNAME,
StdinData: []byte(makeInput(ver, inputIPAM, dualStackSubnetFile)),
}
targetNs, err := testutils.NewNS() err := originalNS.Do(func(ns.NetNS) error {
Expect(err).NotTo(HaveOccurred()) defer GinkgoRecover()
defer targetNs.Close()
args := &skel.CmdArgs{ By("calling ADD with dual stack")
ContainerID: "some-container-id-dual-stack", resI, _, err := testutils.CmdAddWithArgs(args, func() error {
Netns: targetNs.Path(), return cmdAdd(args)
IfName: IFNAME, })
StdinData: []byte(dualStackInput), Expect(err).NotTo(HaveOccurred())
}
err = originalNS.Do(func(ns.NetNS) error { By("check that plugin writes the net config to dataDir with dual stack")
defer GinkgoRecover() path := fmt.Sprintf("%s/%s", dataDir, "some-container-id-dual-stack")
Expect(path).Should(BeAnExistingFile())
By("calling ADD with dual stack") netConfBytes, err := ioutil.ReadFile(path)
resI, _, err := testutils.CmdAddWithArgs(args, func() error { Expect(err).NotTo(HaveOccurred())
return cmdAdd(args) expected := fmt.Sprintf(`{
"cniVersion": "%s",
"ipMasq": false,
"ipam": {
"routes": [
{
"dst": "10.1.0.0/16"
},
{
"dst": "fc00::/48"
}
],
"ranges": [
[{
"subnet": "10.1.17.0/24"
}],
[{
"subnet": "fc00::/64"
}]
],
"type": "host-local",
"dataDir": "%s"
},
"isGateway": true,
"mtu": 1472,
"name": "cni-flannel",
"type": "bridge"
}`, ver, dataDir)
Expect(netConfBytes).Should(MatchJSON(expected))
result, err := current.NewResultFromResult(resI)
Expect(err).NotTo(HaveOccurred())
Expect(result.IPs).To(HaveLen(2))
By("calling DEL with dual stack")
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
By("check that plugin removes net config from state dir with dual stack")
Expect(path).ShouldNot(BeAnExistingFile())
By("calling DEL again with dual stack")
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
By("check that plugin does not fail due to missing net config with dual stack")
Expect(err).NotTo(HaveOccurred())
return nil
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
By("check that plugin writes the net config to dataDir with dual stack")
path := fmt.Sprintf("%s/%s", dataDir, "some-container-id-dual-stack")
Expect(path).Should(BeAnExistingFile())
netConfBytes, err := ioutil.ReadFile(path)
Expect(err).NotTo(HaveOccurred())
expected := `{
"ipMasq": false,
"ipam": {
"routes": [
{
"dst": "10.1.0.0/16"
},
{
"dst": "fc00::/48"
}
],
"ranges": [
[{
"subnet": "10.1.17.0/24"
}],
[{
"subnet": "fc00::/64"
}]
],
"type": "host-local"
},
"isGateway": true,
"mtu": 1472,
"name": "cni-flannel",
"type": "bridge"
}
`
Expect(netConfBytes).Should(MatchJSON(expected))
result, err := current.NewResultFromResult(resI)
Expect(err).NotTo(HaveOccurred())
Expect(result.IPs).To(HaveLen(2))
By("calling DEL with dual stack")
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
Expect(err).NotTo(HaveOccurred())
By("check that plugin removes net config from state dir with dual stack")
Expect(path).ShouldNot(BeAnExistingFile())
By("calling DEL again with dual stack")
err = testutils.CmdDelWithArgs(args, func() error {
return cmdDel(args)
})
By("check that plugin does not fail due to missing net config with dual stack")
Expect(err).NotTo(HaveOccurred())
return nil
}) })
Expect(err).NotTo(HaveOccurred())
}) })
}) }
}) })
Describe("loadFlannelNetConf", func() { Describe("loadFlannelNetConf", func() {
var (
onlyIpv4Input string
onlyIpv6Input string
dualStackInput string
)
BeforeEach(func() {
onlyIpv4Input = makeInput(current.ImplementedSpecVersion, "", onlyIpv4SubnetFile)
onlyIpv6Input = makeInput(current.ImplementedSpecVersion, "", onlyIpv6SubnetFile)
dualStackInput = makeInput(current.ImplementedSpecVersion, "", dualStackSubnetFile)
})
Context("when subnetFile and dataDir are specified with ipv4 stack", func() { Context("when subnetFile and dataDir are specified with ipv4 stack", func() {
It("loads flannel network config with ipv4 stack", func() { It("loads flannel network config with ipv4 stack", func() {
conf, err := loadFlannelNetConf([]byte(onlyIpv4Input)) conf, err := loadFlannelNetConf([]byte(onlyIpv4Input))
@ -553,7 +568,7 @@ FLANNEL_IPMASQ=true
Context("when input IPAM is provided with ipv4 stack", func() { Context("when input IPAM is provided with ipv4 stack", func() {
BeforeEach(func() { BeforeEach(func() {
inputIPAM := makeInputIPAM(inputIPAMType, inputIPAMRoutes, "") inputIPAM := makeInputIPAM(inputIPAMType, inputIPAMRoutes, "")
onlyIpv4Input = makeInput(inputIPAM, onlyIpv4SubnetFile) onlyIpv4Input = makeInput(current.ImplementedSpecVersion, inputIPAM, onlyIpv4SubnetFile)
}) })
It("configures Delegate IPAM accordingly with ipv4 stack", func() { It("configures Delegate IPAM accordingly with ipv4 stack", func() {
conf, err := loadFlannelNetConf([]byte(onlyIpv4Input)) conf, err := loadFlannelNetConf([]byte(onlyIpv4Input))
@ -575,7 +590,7 @@ FLANNEL_IPMASQ=true
Context("when input IPAM is provided with ipv6 stack", func() { Context("when input IPAM is provided with ipv6 stack", func() {
BeforeEach(func() { BeforeEach(func() {
inputIPAM := makeInputIPAM(inputIPAMType, inputIPAMRoutes, "") inputIPAM := makeInputIPAM(inputIPAMType, inputIPAMRoutes, "")
onlyIpv6Input = makeInput(inputIPAM, onlyIpv6SubnetFile) onlyIpv6Input = makeInput(current.ImplementedSpecVersion, inputIPAM, onlyIpv6SubnetFile)
}) })
It("configures Delegate IPAM accordingly with ipv6 stack", func() { It("configures Delegate IPAM accordingly with ipv6 stack", func() {
conf, err := loadFlannelNetConf([]byte(onlyIpv6Input)) conf, err := loadFlannelNetConf([]byte(onlyIpv6Input))
@ -597,7 +612,7 @@ FLANNEL_IPMASQ=true
Context("when input IPAM is provided with dual stack", func() { Context("when input IPAM is provided with dual stack", func() {
BeforeEach(func() { BeforeEach(func() {
inputIPAM := makeInputIPAM(inputIPAMType, inputIPAMRoutes, "") inputIPAM := makeInputIPAM(inputIPAMType, inputIPAMRoutes, "")
dualStackInput = makeInput(inputIPAM, dualStackSubnetFile) dualStackInput = makeInput(current.ImplementedSpecVersion, inputIPAM, dualStackSubnetFile)
}) })
It("configures Delegate IPAM accordingly with dual stack", func() { It("configures Delegate IPAM accordingly with dual stack", func() {
conf, err := loadFlannelNetConf([]byte(dualStackInput)) conf, err := loadFlannelNetConf([]byte(dualStackInput))
@ -619,7 +634,7 @@ FLANNEL_IPMASQ=true
Context("when input IPAM is provided without 'type' with ipv4 stack", func() { Context("when input IPAM is provided without 'type' with ipv4 stack", func() {
BeforeEach(func() { BeforeEach(func() {
inputIPAM := makeInputIPAM("", inputIPAMRoutes, "") inputIPAM := makeInputIPAM("", inputIPAMRoutes, "")
onlyIpv4Input = makeInput(inputIPAM, onlyIpv4SubnetFile) onlyIpv4Input = makeInput(current.ImplementedSpecVersion, inputIPAM, onlyIpv4SubnetFile)
}) })
It("configures Delegate IPAM with 'host-local' ipam with ipv4 stack", func() { It("configures Delegate IPAM with 'host-local' ipam with ipv4 stack", func() {
conf, err := loadFlannelNetConf([]byte(onlyIpv4Input)) conf, err := loadFlannelNetConf([]byte(onlyIpv4Input))
@ -640,7 +655,7 @@ FLANNEL_IPMASQ=true
Context("when input IPAM is provided without 'type' with ipv6 stack", func() { Context("when input IPAM is provided without 'type' with ipv6 stack", func() {
BeforeEach(func() { BeforeEach(func() {
inputIPAM := makeInputIPAM("", inputIPAMRoutes, "") inputIPAM := makeInputIPAM("", inputIPAMRoutes, "")
onlyIpv6Input = makeInput(inputIPAM, onlyIpv6SubnetFile) onlyIpv6Input = makeInput(current.ImplementedSpecVersion, inputIPAM, onlyIpv6SubnetFile)
}) })
It("configures Delegate IPAM with 'host-local' ipam with ipv6 stack", func() { It("configures Delegate IPAM with 'host-local' ipam with ipv6 stack", func() {
conf, err := loadFlannelNetConf([]byte(onlyIpv6Input)) conf, err := loadFlannelNetConf([]byte(onlyIpv6Input))
@ -661,7 +676,7 @@ FLANNEL_IPMASQ=true
Context("when input IPAM is provided without 'type' with dual stack", func() { Context("when input IPAM is provided without 'type' with dual stack", func() {
BeforeEach(func() { BeforeEach(func() {
inputIPAM := makeInputIPAM("", inputIPAMRoutes, "") inputIPAM := makeInputIPAM("", inputIPAMRoutes, "")
dualStackInput = makeInput(inputIPAM, dualStackSubnetFile) dualStackInput = makeInput(current.ImplementedSpecVersion, inputIPAM, dualStackSubnetFile)
}) })
It("configures Delegate IPAM with 'host-local' ipam with dual stack", func() { It("configures Delegate IPAM with 'host-local' ipam with dual stack", func() {
conf, err := loadFlannelNetConf([]byte(dualStackInput)) conf, err := loadFlannelNetConf([]byte(dualStackInput))

View File

@ -30,7 +30,7 @@ import (
"os" "os"
) )
func doCmdAdd(args *skel.CmdArgs, n *NetConf, fenv *subnetEnv) error { func doCmdAdd(args *skel.CmdArgs, cniVersion string, n *NetConf, fenv *subnetEnv) error {
n.Delegate["name"] = n.Name n.Delegate["name"] = n.Name
if !hasKey(n.Delegate, "type") { if !hasKey(n.Delegate, "type") {
@ -52,7 +52,8 @@ func doCmdAdd(args *skel.CmdArgs, n *NetConf, fenv *subnetEnv) error {
"subnet": fenv.sn.String(), "subnet": fenv.sn.String(),
} }
return delegateAdd(hns.GetSandboxContainerID(args.ContainerID, args.Netns), n.DataDir, n.Delegate) sandboxID := hns.GetSandboxContainerID(args.ContainerID, args.Netns)
return delegateAdd(sandboxID, n.DataDir, cniVersion, n.Delegate)
} }
func doCmdDel(args *skel.CmdArgs, n *NetConf) (err error) { func doCmdDel(args *skel.CmdArgs, n *NetConf) (err error) {

View File

@ -33,7 +33,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/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/cni/pkg/version"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
@ -223,9 +223,10 @@ 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.IP != nil { isIPv4 := ip.Address.IP.To4() != nil
if !isIPv4 && conf.ContIPv6.IP != nil {
continue continue
} else if ip.Version == "4" && conf.ContIPv4.IP != nil { } else if isIPv4 && conf.ContIPv4.IP != nil {
continue continue
} }
@ -239,11 +240,10 @@ func parseConfig(stdin []byte, ifName string) (*PortMapConf, *current.Result, er
continue continue
} }
} }
switch ip.Version { if ip.Address.IP.To4() != nil {
case "6":
conf.ContIPv6 = ip.Address
case "4":
conf.ContIPv4 = ip.Address conf.ContIPv4 = ip.Address
} else {
conf.ContIPv6 = ip.Address
} }
} }
} }

View File

@ -25,7 +25,7 @@ import (
"path/filepath" "path/filepath"
"github.com/containernetworking/cni/libcni" "github.com/containernetworking/cni/libcni"
"github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils" "github.com/containernetworking/plugins/pkg/testutils"
"github.com/coreos/go-iptables/iptables" "github.com/coreos/go-iptables/iptables"
@ -37,9 +37,36 @@ import (
const TIMEOUT = 90 const TIMEOUT = 90
func makeConfig(ver string) *libcni.NetworkConfigList {
configList, err := libcni.ConfListFromBytes([]byte(fmt.Sprintf(`{
"cniVersion": "%s",
"name": "cni-portmap-unit-test",
"plugins": [
{
"type": "ptp",
"ipMasq": true,
"ipam": {
"type": "host-local",
"subnet": "172.16.31.0/24",
"routes": [
{"dst": "0.0.0.0/0"}
]
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}`, ver)))
Expect(err).NotTo(HaveOccurred())
return configList
}
var _ = Describe("portmap integration tests", func() { var _ = Describe("portmap integration tests", func() {
var ( var (
configList *libcni.NetworkConfigList
cniConf *libcni.CNIConfig cniConf *libcni.CNIConfig
targetNS ns.NetNS targetNS ns.NetNS
containerPort int containerPort int
@ -47,38 +74,11 @@ var _ = Describe("portmap integration tests", func() {
) )
BeforeEach(func() { BeforeEach(func() {
var err error
rawConfig := `{
"cniVersion": "0.3.0",
"name": "cni-portmap-unit-test",
"plugins": [
{
"type": "ptp",
"ipMasq": true,
"ipam": {
"type": "host-local",
"subnet": "172.16.31.0/24",
"routes": [
{"dst": "0.0.0.0/0"}
]
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}`
configList, err = libcni.ConfListFromBytes([]byte(rawConfig))
Expect(err).NotTo(HaveOccurred())
// turn PATH in to CNI_PATH // turn PATH in to CNI_PATH
dirs := filepath.SplitList(os.Getenv("PATH")) dirs := filepath.SplitList(os.Getenv("PATH"))
cniConf = &libcni.CNIConfig{Path: dirs} cniConf = &libcni.CNIConfig{Path: dirs}
var err error
targetNS, err = testutils.NewNS() targetNS, err = testutils.NewNS()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
fmt.Fprintln(GinkgoWriter, "namespace:", targetNS.Path()) fmt.Fprintln(GinkgoWriter, "namespace:", targetNS.Path())
@ -90,333 +90,340 @@ var _ = Describe("portmap integration tests", func() {
AfterEach(func() { AfterEach(func() {
session.Terminate().Wait() session.Terminate().Wait()
if targetNS != nil { targetNS.Close()
targetNS.Close() testutils.UnmountNS(targetNS)
}
}) })
Describe("Creating an interface in a namespace with the ptp plugin", func() { for _, ver := range []string{"0.3.0", "0.3.1", "0.4.0", "1.0.0"} {
// This needs to be done using Ginkgo's asynchronous testing mode. // Redefine ver inside for scope so real value is picked up by each dynamically defined It()
It("forwards a TCP port on ipv4", func(done Done) { // See Gingkgo's "Patterns for dynamically generating tests" documentation.
var err error ver := ver
hostPort := rand.Intn(10000) + 1025
runtimeConfig := libcni.RuntimeConf{ Describe("Creating an interface in a namespace with the ptp plugin", func() {
ContainerID: fmt.Sprintf("unit-test-%d", hostPort), // This needs to be done using Ginkgo's asynchronous testing mode.
NetNS: targetNS.Path(), It(fmt.Sprintf("[%s] forwards a TCP port on ipv4", ver), func(done Done) {
IfName: "eth0", var err error
CapabilityArgs: map[string]interface{}{ hostPort := rand.Intn(10000) + 1025
"portMappings": []map[string]interface{}{ runtimeConfig := libcni.RuntimeConf{
{ ContainerID: fmt.Sprintf("unit-test-%d", hostPort),
"hostPort": hostPort, NetNS: targetNS.Path(),
"containerPort": containerPort, IfName: "eth0",
"protocol": "tcp", CapabilityArgs: map[string]interface{}{
"portMappings": []map[string]interface{}{
{
"hostPort": hostPort,
"containerPort": containerPort,
"protocol": "tcp",
},
}, },
}, },
},
}
// Make delete idempotent, so we can clean up on failure
netDeleted := false
deleteNetwork := func() error {
if netDeleted {
return nil
} }
netDeleted = true configList := makeConfig(ver)
return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig)
}
// we'll also manually check the iptables chains // Make delete idempotent, so we can clean up on failure
ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4) netDeleted := false
Expect(err).NotTo(HaveOccurred()) deleteNetwork := func() error {
dnatChainName := genDnatChain("cni-portmap-unit-test", runtimeConfig.ContainerID).name if netDeleted {
return nil
// Create the network }
resI, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig) netDeleted = true
Expect(err).NotTo(HaveOccurred()) return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig)
defer deleteNetwork()
// Undo Docker's forwarding policy
cmd := exec.Command("iptables", "-t", "filter",
"-P", "FORWARD", "ACCEPT")
cmd.Stderr = GinkgoWriter
err = cmd.Run()
Expect(err).NotTo(HaveOccurred())
// Check the chain exists
_, err = ipt.List("nat", dnatChainName)
Expect(err).NotTo(HaveOccurred())
result, err := current.GetResult(resI)
Expect(err).NotTo(HaveOccurred())
var contIP net.IP
for _, ip := range result.IPs {
intfIndex := *ip.Interface
if result.Interfaces[intfIndex].Sandbox == "" {
continue
} }
contIP = ip.Address.IP
}
if contIP == nil {
Fail("could not determine container IP")
}
hostIP := getLocalIP() // we'll also manually check the iptables chains
fmt.Fprintf(GinkgoWriter, "hostIP: %s:%d, contIP: %s:%d\n", ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4)
hostIP, hostPort, contIP, containerPort) Expect(err).NotTo(HaveOccurred())
dnatChainName := genDnatChain("cni-portmap-unit-test", runtimeConfig.ContainerID).name
// dump iptables-save output for debugging // Create the network
cmd = exec.Command("iptables-save") resI, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig)
cmd.Stderr = GinkgoWriter Expect(err).NotTo(HaveOccurred())
cmd.Stdout = GinkgoWriter defer deleteNetwork()
Expect(cmd.Run()).To(Succeed())
// dump ip routes output for debugging // Undo Docker's forwarding policy
cmd = exec.Command("ip", "route") cmd := exec.Command("iptables", "-t", "filter",
cmd.Stderr = GinkgoWriter "-P", "FORWARD", "ACCEPT")
cmd.Stdout = GinkgoWriter cmd.Stderr = GinkgoWriter
Expect(cmd.Run()).To(Succeed()) err = cmd.Run()
Expect(err).NotTo(HaveOccurred())
// dump ip addresses output for debugging // Check the chain exists
cmd = exec.Command("ip", "addr") _, err = ipt.List("nat", dnatChainName)
cmd.Stderr = GinkgoWriter Expect(err).NotTo(HaveOccurred())
cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// Sanity check: verify that the container is reachable directly result, err := types100.GetResult(resI)
contOK := testEchoServer(contIP.String(), "tcp", containerPort, "") Expect(err).NotTo(HaveOccurred())
var contIP net.IP
// Verify that a connection to the forwarded port works for _, ip := range result.IPs {
dnatOK := testEchoServer(hostIP, "tcp", hostPort, "") intfIndex := *ip.Interface
if result.Interfaces[intfIndex].Sandbox == "" {
continue
}
contIP = ip.Address.IP
}
if contIP == nil {
Fail("could not determine container IP")
}
// Verify that a connection to localhost works hostIP := getLocalIP()
snatOK := testEchoServer("127.0.0.1", "tcp", hostPort, "") fmt.Fprintf(GinkgoWriter, "hostIP: %s:%d, contIP: %s:%d\n",
hostIP, hostPort, contIP, containerPort)
// verify that hairpin works // dump iptables-save output for debugging
hairpinOK := testEchoServer(hostIP, "tcp", hostPort, targetNS.Path()) cmd = exec.Command("iptables-save")
cmd.Stderr = GinkgoWriter
cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// Cleanup // dump ip routes output for debugging
session.Terminate() cmd = exec.Command("ip", "route")
err = deleteNetwork() cmd.Stderr = GinkgoWriter
Expect(err).NotTo(HaveOccurred()) cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// Verify iptables rules are gone // dump ip addresses output for debugging
_, err = ipt.List("nat", dnatChainName) cmd = exec.Command("ip", "addr")
Expect(err).To(MatchError(ContainSubstring("iptables: No chain/target/match by that name."))) cmd.Stderr = GinkgoWriter
cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// Check that everything succeeded *after* we clean up the network // Sanity check: verify that the container is reachable directly
if !contOK { contOK := testEchoServer(contIP.String(), "tcp", containerPort, "")
Fail("connection direct to " + contIP.String() + " failed")
}
if !dnatOK {
Fail("Connection to " + hostIP + " was not forwarded")
}
if !snatOK {
Fail("connection to 127.0.0.1 was not forwarded")
}
if !hairpinOK {
Fail("Hairpin connection failed")
}
close(done) // Verify that a connection to the forwarded port works
}, TIMEOUT*9) dnatOK := testEchoServer(hostIP, "tcp", hostPort, "")
It("forwards a UDP port on ipv4 and keep working after creating a second container with the same HostPort", func(done Done) { // Verify that a connection to localhost works
var err error snatOK := testEchoServer("127.0.0.1", "tcp", hostPort, "")
hostPort := rand.Intn(10000) + 1025
runtimeConfig := libcni.RuntimeConf{ // verify that hairpin works
ContainerID: fmt.Sprintf("unit-test-%d", hostPort), hairpinOK := testEchoServer(hostIP, "tcp", hostPort, targetNS.Path())
NetNS: targetNS.Path(),
IfName: "eth0", // Cleanup
CapabilityArgs: map[string]interface{}{ session.Terminate()
"portMappings": []map[string]interface{}{ err = deleteNetwork()
{ Expect(err).NotTo(HaveOccurred())
"hostPort": hostPort,
"containerPort": containerPort, // Verify iptables rules are gone
"protocol": "udp", _, err = ipt.List("nat", dnatChainName)
Expect(err).To(MatchError(ContainSubstring("iptables: No chain/target/match by that name.")))
// Check that everything succeeded *after* we clean up the network
if !contOK {
Fail("connection direct to " + contIP.String() + " failed")
}
if !dnatOK {
Fail("Connection to " + hostIP + " was not forwarded")
}
if !snatOK {
Fail("connection to 127.0.0.1 was not forwarded")
}
if !hairpinOK {
Fail("Hairpin connection failed")
}
close(done)
}, TIMEOUT*9)
It(fmt.Sprintf("[%s] forwards a UDP port on ipv4 and keep working after creating a second container with the same HostPort", ver), func(done Done) {
var err error
hostPort := rand.Intn(10000) + 1025
runtimeConfig := libcni.RuntimeConf{
ContainerID: fmt.Sprintf("unit-test-%d", hostPort),
NetNS: targetNS.Path(),
IfName: "eth0",
CapabilityArgs: map[string]interface{}{
"portMappings": []map[string]interface{}{
{
"hostPort": hostPort,
"containerPort": containerPort,
"protocol": "udp",
},
}, },
}, },
},
}
// Make delete idempotent, so we can clean up on failure
netDeleted := false
deleteNetwork := func() error {
if netDeleted {
return nil
} }
netDeleted = true configList := makeConfig(ver)
return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig)
}
// Create the network // Make delete idempotent, so we can clean up on failure
resI, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig) netDeleted := false
Expect(err).NotTo(HaveOccurred()) deleteNetwork := func() error {
defer deleteNetwork() if netDeleted {
return nil
// Undo Docker's forwarding policy }
cmd := exec.Command("iptables", "-t", "filter", netDeleted = true
"-P", "FORWARD", "ACCEPT") return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig)
cmd.Stderr = GinkgoWriter
err = cmd.Run()
Expect(err).NotTo(HaveOccurred())
result, err := current.GetResult(resI)
Expect(err).NotTo(HaveOccurred())
var contIP net.IP
for _, ip := range result.IPs {
intfIndex := *ip.Interface
if result.Interfaces[intfIndex].Sandbox == "" {
continue
} }
contIP = ip.Address.IP
}
if contIP == nil {
Fail("could not determine container IP")
}
hostIP := getLocalIP() // Create the network
fmt.Fprintf(GinkgoWriter, "First container hostIP: %s:%d, contIP: %s:%d\n", resI, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig)
hostIP, hostPort, contIP, containerPort) Expect(err).NotTo(HaveOccurred())
defer deleteNetwork()
// dump iptables-save output for debugging // Undo Docker's forwarding policy
cmd = exec.Command("iptables-save") cmd := exec.Command("iptables", "-t", "filter",
cmd.Stderr = GinkgoWriter "-P", "FORWARD", "ACCEPT")
cmd.Stdout = GinkgoWriter cmd.Stderr = GinkgoWriter
Expect(cmd.Run()).To(Succeed()) err = cmd.Run()
Expect(err).NotTo(HaveOccurred())
// dump ip routes output for debugging result, err := types100.GetResult(resI)
cmd = exec.Command("ip", "route") Expect(err).NotTo(HaveOccurred())
cmd.Stderr = GinkgoWriter var contIP net.IP
cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// dump ip addresses output for debugging for _, ip := range result.IPs {
cmd = exec.Command("ip", "addr") intfIndex := *ip.Interface
cmd.Stderr = GinkgoWriter if result.Interfaces[intfIndex].Sandbox == "" {
cmd.Stdout = GinkgoWriter continue
Expect(cmd.Run()).To(Succeed()) }
contIP = ip.Address.IP
}
if contIP == nil {
Fail("could not determine container IP")
}
// Sanity check: verify that the container is reachable directly hostIP := getLocalIP()
fmt.Fprintln(GinkgoWriter, "Connect to container:", contIP.String(), containerPort) fmt.Fprintf(GinkgoWriter, "First container hostIP: %s:%d, contIP: %s:%d\n",
contOK := testEchoServer(contIP.String(), "udp", containerPort, "") hostIP, hostPort, contIP, containerPort)
// Verify that a connection to the forwarded port works // dump iptables-save output for debugging
fmt.Fprintln(GinkgoWriter, "Connect to host:", hostIP, hostPort) cmd = exec.Command("iptables-save")
dnatOK := testEchoServer(hostIP, "udp", hostPort, "") cmd.Stderr = GinkgoWriter
cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// Cleanup // dump ip routes output for debugging
session.Terminate() cmd = exec.Command("ip", "route")
err = deleteNetwork() cmd.Stderr = GinkgoWriter
Expect(err).NotTo(HaveOccurred()) cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// Check that everything succeeded *after* we clean up the network // dump ip addresses output for debugging
if !contOK { cmd = exec.Command("ip", "addr")
Fail("connection direct to " + contIP.String() + " failed") cmd.Stderr = GinkgoWriter
} cmd.Stdout = GinkgoWriter
if !dnatOK { Expect(cmd.Run()).To(Succeed())
Fail("Connection to " + hostIP + " was not forwarded")
}
// Create a second container
targetNS2, err := testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
fmt.Fprintln(GinkgoWriter, "namespace:", targetNS2.Path())
// Start an echo server and get the port // Sanity check: verify that the container is reachable directly
containerPort, session2, err := StartEchoServerInNamespace(targetNS2) fmt.Fprintln(GinkgoWriter, "Connect to container:", contIP.String(), containerPort)
Expect(err).NotTo(HaveOccurred()) contOK := testEchoServer(contIP.String(), "udp", containerPort, "")
runtimeConfig2 := libcni.RuntimeConf{ // Verify that a connection to the forwarded port works
ContainerID: fmt.Sprintf("unit-test2-%d", hostPort), fmt.Fprintln(GinkgoWriter, "Connect to host:", hostIP, hostPort)
NetNS: targetNS2.Path(), dnatOK := testEchoServer(hostIP, "udp", hostPort, "")
IfName: "eth0",
CapabilityArgs: map[string]interface{}{ // Cleanup
"portMappings": []map[string]interface{}{ session.Terminate()
{ err = deleteNetwork()
"hostPort": hostPort, Expect(err).NotTo(HaveOccurred())
"containerPort": containerPort,
"protocol": "udp", // Check that everything succeeded *after* we clean up the network
if !contOK {
Fail("connection direct to " + contIP.String() + " failed")
}
if !dnatOK {
Fail("Connection to " + hostIP + " was not forwarded")
}
// Create a second container
targetNS2, err := testutils.NewNS()
Expect(err).NotTo(HaveOccurred())
fmt.Fprintln(GinkgoWriter, "namespace:", targetNS2.Path())
// Start an echo server and get the port
containerPort, session2, err := StartEchoServerInNamespace(targetNS2)
Expect(err).NotTo(HaveOccurred())
runtimeConfig2 := libcni.RuntimeConf{
ContainerID: fmt.Sprintf("unit-test2-%d", hostPort),
NetNS: targetNS2.Path(),
IfName: "eth0",
CapabilityArgs: map[string]interface{}{
"portMappings": []map[string]interface{}{
{
"hostPort": hostPort,
"containerPort": containerPort,
"protocol": "udp",
},
}, },
}, },
},
}
// Make delete idempotent, so we can clean up on failure
net2Deleted := false
deleteNetwork2 := func() error {
if net2Deleted {
return nil
} }
net2Deleted = true
return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig2)
}
// Create the network // Make delete idempotent, so we can clean up on failure
resI2, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig2) net2Deleted := false
Expect(err).NotTo(HaveOccurred()) deleteNetwork2 := func() error {
defer deleteNetwork2() if net2Deleted {
return nil
result2, err := current.GetResult(resI2) }
Expect(err).NotTo(HaveOccurred()) net2Deleted = true
var contIP2 net.IP return cniConf.DelNetworkList(context.TODO(), configList, &runtimeConfig2)
for _, ip := range result2.IPs {
intfIndex := *ip.Interface
if result2.Interfaces[intfIndex].Sandbox == "" {
continue
} }
contIP2 = ip.Address.IP
}
if contIP2 == nil {
Fail("could not determine container IP")
}
fmt.Fprintf(GinkgoWriter, "Second container: hostIP: %s:%d, contIP: %s:%d\n", // Create the network
hostIP, hostPort, contIP2, containerPort) resI2, err := cniConf.AddNetworkList(context.TODO(), configList, &runtimeConfig2)
Expect(err).NotTo(HaveOccurred())
defer deleteNetwork2()
// dump iptables-save output for debugging result2, err := types100.GetResult(resI2)
cmd = exec.Command("iptables-save") Expect(err).NotTo(HaveOccurred())
cmd.Stderr = GinkgoWriter var contIP2 net.IP
cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// dump ip routes output for debugging for _, ip := range result2.IPs {
cmd = exec.Command("ip", "route") intfIndex := *ip.Interface
cmd.Stderr = GinkgoWriter if result2.Interfaces[intfIndex].Sandbox == "" {
cmd.Stdout = GinkgoWriter continue
Expect(cmd.Run()).To(Succeed()) }
contIP2 = ip.Address.IP
}
if contIP2 == nil {
Fail("could not determine container IP")
}
// dump ip addresses output for debugging fmt.Fprintf(GinkgoWriter, "Second container: hostIP: %s:%d, contIP: %s:%d\n",
cmd = exec.Command("ip", "addr") hostIP, hostPort, contIP2, containerPort)
cmd.Stderr = GinkgoWriter
cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// Sanity check: verify that the container is reachable directly // dump iptables-save output for debugging
fmt.Fprintln(GinkgoWriter, "Connect to container:", contIP2.String(), containerPort) cmd = exec.Command("iptables-save")
cont2OK := testEchoServer(contIP2.String(), "udp", containerPort, "") cmd.Stderr = GinkgoWriter
cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// Verify that a connection to the forwarded port works // dump ip routes output for debugging
fmt.Fprintln(GinkgoWriter, "Connect to host:", hostIP, hostPort) cmd = exec.Command("ip", "route")
dnat2OK := testEchoServer(hostIP, "udp", hostPort, "") cmd.Stderr = GinkgoWriter
cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// Cleanup // dump ip addresses output for debugging
session2.Terminate() cmd = exec.Command("ip", "addr")
err = deleteNetwork2() cmd.Stderr = GinkgoWriter
Expect(err).NotTo(HaveOccurred()) cmd.Stdout = GinkgoWriter
Expect(cmd.Run()).To(Succeed())
// Check that everything succeeded *after* we clean up the network // Sanity check: verify that the container is reachable directly
if !cont2OK { fmt.Fprintln(GinkgoWriter, "Connect to container:", contIP2.String(), containerPort)
Fail("connection direct to " + contIP2.String() + " failed") cont2OK := testEchoServer(contIP2.String(), "udp", containerPort, "")
}
if !dnat2OK {
Fail("Connection to " + hostIP + " was not forwarded")
}
close(done) // Verify that a connection to the forwarded port works
}, TIMEOUT*9) fmt.Fprintln(GinkgoWriter, "Connect to host:", hostIP, hostPort)
}) dnat2OK := testEchoServer(hostIP, "udp", hostPort, "")
// Cleanup
session2.Terminate()
err = deleteNetwork2()
Expect(err).NotTo(HaveOccurred())
// Check that everything succeeded *after* we clean up the network
if !cont2OK {
Fail("connection direct to " + contIP2.String() + " failed")
}
if !dnat2OK {
Fail("Connection to " + hostIP + " was not forwarded")
}
close(done)
}, 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

View File

@ -27,336 +27,342 @@ var _ = Describe("portmapping configuration", func() {
netName := "testNetName" netName := "testNetName"
containerID := "icee6giejonei6sohng6ahngee7laquohquee9shiGo7fohferakah3Feiyoolu2pei7ciPhoh7shaoX6vai3vuf0ahfaeng8yohb9ceu0daez5hashee8ooYai5wa3y" containerID := "icee6giejonei6sohng6ahngee7laquohquee9shiGo7fohferakah3Feiyoolu2pei7ciPhoh7shaoX6vai3vuf0ahfaeng8yohb9ceu0daez5hashee8ooYai5wa3y"
Context("config parsing", func() { for _, ver := range []string{"0.3.0", "0.3.1", "0.4.0", "1.0.0"} {
It("Correctly parses an ADD config", func() { // Redefine ver inside for scope so real value is picked up by each dynamically defined It()
configBytes := []byte(`{ // See Gingkgo's "Patterns for dynamically generating tests" documentation.
"name": "test", ver := ver
"type": "portmap",
"cniVersion": "0.3.1",
"runtimeConfig": {
"portMappings": [
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"},
{ "hostPort": 8081, "containerPort": 81, "protocol": "udp"}
]
},
"snat": false,
"conditionsV4": ["a", "b"],
"conditionsV6": ["c", "d"],
"prevResult": {
"interfaces": [
{"name": "host"},
{"name": "container", "sandbox":"netns"}
],
"ips": [
{
"version": "4",
"address": "10.0.0.1/24",
"gateway": "10.0.0.1",
"interface": 0
},
{
"version": "6",
"address": "2001:db8:1::2/64",
"gateway": "2001:db8:1::1",
"interface": 1
},
{
"version": "4",
"address": "10.0.0.2/24",
"gateway": "10.0.0.1",
"interface": 1
}
]
}
}`)
c, _, err := parseConfig(configBytes, "container")
Expect(err).NotTo(HaveOccurred())
Expect(c.CNIVersion).To(Equal("0.3.1"))
Expect(c.ConditionsV4).To(Equal(&[]string{"a", "b"}))
Expect(c.ConditionsV6).To(Equal(&[]string{"c", "d"}))
fvar := false
Expect(c.SNAT).To(Equal(&fvar))
Expect(c.Name).To(Equal("test"))
n, err := types.ParseCIDR("10.0.0.2/24") Context("config parsing", func() {
Expect(c.ContIPv4).To(Equal(*n)) It(fmt.Sprintf("[%s] correctly parses an ADD config", ver), func() {
n, err = types.ParseCIDR("2001:db8:1::2/64") configBytes := []byte(fmt.Sprintf(`{
Expect(c.ContIPv6).To(Equal(*n)) "name": "test",
}) "type": "portmap",
"cniVersion": "%s",
It("Correctly parses a DEL config", func() { "runtimeConfig": {
// When called with DEL, neither runtimeConfig nor prevResult may be specified "portMappings": [
configBytes := []byte(`{ { "hostPort": 8080, "containerPort": 80, "protocol": "tcp"},
"name": "test", { "hostPort": 8081, "containerPort": 81, "protocol": "udp"}
"type": "portmap", ]
"cniVersion": "0.3.1", },
"snat": false, "snat": false,
"conditionsV4": ["a", "b"], "conditionsV4": ["a", "b"],
"conditionsV6": ["c", "d"] "conditionsV6": ["c", "d"],
}`) "prevResult": {
c, _, err := parseConfig(configBytes, "container") "interfaces": [
Expect(err).NotTo(HaveOccurred()) {"name": "host"},
Expect(c.CNIVersion).To(Equal("0.3.1")) {"name": "container", "sandbox":"netns"}
Expect(c.ConditionsV4).To(Equal(&[]string{"a", "b"})) ],
Expect(c.ConditionsV6).To(Equal(&[]string{"c", "d"})) "ips": [
fvar := false {
Expect(c.SNAT).To(Equal(&fvar)) "version": "4",
Expect(c.Name).To(Equal("test")) "address": "10.0.0.1/24",
}) "gateway": "10.0.0.1",
"interface": 0
It("fails with invalid mappings", func() { },
configBytes := []byte(`{ {
"name": "test", "version": "6",
"type": "portmap", "address": "2001:db8:1::2/64",
"cniVersion": "0.3.1", "gateway": "2001:db8:1::1",
"snat": false, "interface": 1
"conditionsV4": ["a", "b"], },
"conditionsV6": ["c", "d"], {
"runtimeConfig": { "version": "4",
"portMappings": [ "address": "10.0.0.2/24",
{ "hostPort": 0, "containerPort": 80, "protocol": "tcp"} "gateway": "10.0.0.1",
] "interface": 1
} }
}`) ]
_, _, err := parseConfig(configBytes, "container") }
Expect(err).To(MatchError("Invalid host port number: 0")) }`, ver))
}) c, _, err := parseConfig(configBytes, "container")
It("Does not fail on missing prevResult interface index", func() {
configBytes := []byte(`{
"name": "test",
"type": "portmap",
"cniVersion": "0.3.1",
"runtimeConfig": {
"portMappings": [
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"}
]
},
"conditionsV4": ["a", "b"],
"prevResult": {
"interfaces": [
{"name": "host"}
],
"ips": [
{
"version": "4",
"address": "10.0.0.1/24",
"gateway": "10.0.0.1"
}
]
}
}`)
_, _, err := parseConfig(configBytes, "container")
Expect(err).NotTo(HaveOccurred())
})
})
Describe("Generating chains", func() {
Context("for DNAT", func() {
It("generates a correct standard container chain", func() {
ch := genDnatChain(netName, containerID)
Expect(ch).To(Equal(chain{
table: "nat",
name: "CNI-DN-bfd599665540dd91d5d28",
entryChains: []string{TopLevelDNATChainName},
}))
configBytes := []byte(`{
"name": "test",
"type": "portmap",
"cniVersion": "0.3.1",
"runtimeConfig": {
"portMappings": [
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"},
{ "hostPort": 8081, "containerPort": 80, "protocol": "tcp"},
{ "hostPort": 8080, "containerPort": 81, "protocol": "udp"},
{ "hostPort": 8082, "containerPort": 82, "protocol": "udp"},
{ "hostPort": 8083, "containerPort": 83, "protocol": "tcp", "hostIP": "192.168.0.2"},
{ "hostPort": 8084, "containerPort": 84, "protocol": "tcp", "hostIP": "0.0.0.0"},
{ "hostPort": 8085, "containerPort": 85, "protocol": "tcp", "hostIP": "2001:db8:a::1"},
{ "hostPort": 8086, "containerPort": 86, "protocol": "tcp", "hostIP": "::"}
]
},
"snat": true,
"conditionsV4": ["a", "b"],
"conditionsV6": ["c", "d"]
}`)
conf, _, err := parseConfig(configBytes, "foo")
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
conf.ContainerID = containerID Expect(c.CNIVersion).To(Equal(ver))
Expect(c.ConditionsV4).To(Equal(&[]string{"a", "b"}))
ch = genDnatChain(conf.Name, containerID) Expect(c.ConditionsV6).To(Equal(&[]string{"c", "d"}))
Expect(ch).To(Equal(chain{
table: "nat",
name: "CNI-DN-67e92b96e692a494b6b85",
entryChains: []string{"CNI-HOSTPORT-DNAT"},
}))
n, err := types.ParseCIDR("10.0.0.2/24")
fillDnatRules(&ch, conf, *n)
Expect(ch.entryRules).To(Equal([][]string{
{"-m", "comment", "--comment",
fmt.Sprintf("dnat name: \"test\" id: \"%s\"", containerID),
"-m", "multiport",
"-p", "tcp",
"--destination-ports", "8080,8081,8083,8084,8085,8086",
"a", "b"},
{"-m", "comment", "--comment",
fmt.Sprintf("dnat name: \"test\" id: \"%s\"", containerID),
"-m", "multiport",
"-p", "udp",
"--destination-ports", "8080,8082",
"a", "b"},
}))
Expect(ch.rules).To(Equal([][]string{
// tcp rules and not hostIP
{"-p", "tcp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8080", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
{"-p", "tcp", "--dport", "8081", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8081", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
// udp rules and not hostIP
{"-p", "udp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8080", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:81"},
{"-p", "udp", "--dport", "8082", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8082", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "10.0.0.2:82"},
// tcp rules and hostIP
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-j", "DNAT", "--to-destination", "10.0.0.2:83"},
// tcp rules and hostIP = "0.0.0.0"
{"-p", "tcp", "--dport", "8084", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8084", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8084", "-j", "DNAT", "--to-destination", "10.0.0.2:84"},
}))
ch.rules = nil
ch.entryRules = nil
n, err = types.ParseCIDR("2001:db8::2/64")
fillDnatRules(&ch, conf, *n)
Expect(ch.rules).To(Equal([][]string{
// tcp rules and not hostIP
{"-p", "tcp", "--dport", "8080", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "[2001:db8::2]:80"},
{"-p", "tcp", "--dport", "8081", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "[2001:db8::2]:80"},
// udp rules and not hostIP
{"-p", "udp", "--dport", "8080", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "[2001:db8::2]:81"},
{"-p", "udp", "--dport", "8082", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "[2001:db8::2]:82"},
// tcp rules and hostIP
{"-p", "tcp", "--dport", "8085", "-d", "2001:db8:a::1", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8085", "-d", "2001:db8:a::1", "-j", "DNAT", "--to-destination", "[2001:db8::2]:85"},
// tcp rules and hostIP = "::"
{"-p", "tcp", "--dport", "8086", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8086", "-j", "DNAT", "--to-destination", "[2001:db8::2]:86"},
}))
// Disable snat, generate rules
ch.rules = nil
ch.entryRules = nil
fvar := false fvar := false
conf.SNAT = &fvar Expect(c.SNAT).To(Equal(&fvar))
Expect(c.Name).To(Equal("test"))
n, err = types.ParseCIDR("10.0.0.2/24")
fillDnatRules(&ch, conf, *n)
Expect(ch.rules).To(Equal([][]string{
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
{"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
{"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:81"},
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "10.0.0.2:82"},
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-j", "DNAT", "--to-destination", "10.0.0.2:83"},
{"-p", "tcp", "--dport", "8084", "-j", "DNAT", "--to-destination", "10.0.0.2:84"},
}))
})
It("generates a correct chain with external mark", func() {
ch := genDnatChain(netName, containerID)
Expect(ch).To(Equal(chain{
table: "nat",
name: "CNI-DN-bfd599665540dd91d5d28",
entryChains: []string{TopLevelDNATChainName},
}))
configBytes := []byte(`{
"name": "test",
"type": "portmap",
"cniVersion": "0.3.1",
"runtimeConfig": {
"portMappings": [
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"}
]
},
"externalSetMarkChain": "PLZ-SET-MARK",
"conditionsV4": ["a", "b"],
"conditionsV6": ["c", "d"]
}`)
conf, _, err := parseConfig(configBytes, "foo")
Expect(err).NotTo(HaveOccurred())
conf.ContainerID = containerID
ch = genDnatChain(conf.Name, containerID)
n, err := types.ParseCIDR("10.0.0.2/24") n, err := types.ParseCIDR("10.0.0.2/24")
fillDnatRules(&ch, conf, *n) Expect(c.ContIPv4).To(Equal(*n))
Expect(ch.rules).To(Equal([][]string{ n, err = types.ParseCIDR("2001:db8:1::2/64")
{"-p", "tcp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "PLZ-SET-MARK"}, Expect(c.ContIPv6).To(Equal(*n))
{"-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"},
}))
}) })
It("generates a correct top-level chain", func() { It(fmt.Sprintf("[%s] correctly parses a DEL config", ver), func() {
ch := genToplevelDnatChain() // When called with DEL, neither runtimeConfig nor prevResult may be specified
configBytes := []byte(fmt.Sprintf(`{
Expect(ch).To(Equal(chain{ "name": "test",
table: "nat", "type": "portmap",
name: "CNI-HOSTPORT-DNAT", "cniVersion": "%s",
entryChains: []string{"PREROUTING", "OUTPUT"}, "snat": false,
entryRules: [][]string{{"-m", "addrtype", "--dst-type", "LOCAL"}}, "conditionsV4": ["a", "b"],
})) "conditionsV6": ["c", "d"]
}`, ver))
c, _, err := parseConfig(configBytes, "container")
Expect(err).NotTo(HaveOccurred())
Expect(c.CNIVersion).To(Equal(ver))
Expect(c.ConditionsV4).To(Equal(&[]string{"a", "b"}))
Expect(c.ConditionsV6).To(Equal(&[]string{"c", "d"}))
fvar := false
Expect(c.SNAT).To(Equal(&fvar))
Expect(c.Name).To(Equal("test"))
}) })
It("generates the correct mark chains", func() { It(fmt.Sprintf("[%s] fails with invalid mappings", ver), func() {
masqBit := 5 configBytes := []byte(fmt.Sprintf(`{
ch := genSetMarkChain(masqBit) "name": "test",
Expect(ch).To(Equal(chain{ "type": "portmap",
table: "nat", "cniVersion": "%s",
name: "CNI-HOSTPORT-SETMARK", "snat": false,
rules: [][]string{{ "conditionsV4": ["a", "b"],
"-m", "comment", "conditionsV6": ["c", "d"],
"--comment", "CNI portfwd masquerade mark", "runtimeConfig": {
"-j", "MARK", "portMappings": [
"--set-xmark", "0x20/0x20", { "hostPort": 0, "containerPort": 80, "protocol": "tcp"}
}}, ]
})) }
}`, ver))
_, _, err := parseConfig(configBytes, "container")
Expect(err).To(MatchError("Invalid host port number: 0"))
})
ch = genMarkMasqChain(masqBit) It(fmt.Sprintf("[%s] does not fail on missing prevResult interface index", ver), func() {
Expect(ch).To(Equal(chain{ configBytes := []byte(fmt.Sprintf(`{
table: "nat", "name": "test",
name: "CNI-HOSTPORT-MASQ", "type": "portmap",
entryChains: []string{"POSTROUTING"}, "cniVersion": "%s",
entryRules: [][]string{{ "runtimeConfig": {
"-m", "comment", "portMappings": [
"--comment", "CNI portfwd requiring masquerade", { "hostPort": 8080, "containerPort": 80, "protocol": "tcp"}
}}, ]
rules: [][]string{{ },
"-m", "mark", "conditionsV4": ["a", "b"],
"--mark", "0x20/0x20", "prevResult": {
"-j", "MASQUERADE", "interfaces": [
}}, {"name": "host"}
prependEntry: true, ],
})) "ips": [
{
"version": "4",
"address": "10.0.0.1/24",
"gateway": "10.0.0.1"
}
]
}
}`, ver))
_, _, err := parseConfig(configBytes, "container")
Expect(err).NotTo(HaveOccurred())
}) })
}) })
})
Describe("Generating chains", func() {
Context("for DNAT", func() {
It(fmt.Sprintf("[%s] generates a correct standard container chain", ver), func() {
ch := genDnatChain(netName, containerID)
Expect(ch).To(Equal(chain{
table: "nat",
name: "CNI-DN-bfd599665540dd91d5d28",
entryChains: []string{TopLevelDNATChainName},
}))
configBytes := []byte(fmt.Sprintf(`{
"name": "test",
"type": "portmap",
"cniVersion": "%s",
"runtimeConfig": {
"portMappings": [
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"},
{ "hostPort": 8081, "containerPort": 80, "protocol": "tcp"},
{ "hostPort": 8080, "containerPort": 81, "protocol": "udp"},
{ "hostPort": 8082, "containerPort": 82, "protocol": "udp"},
{ "hostPort": 8083, "containerPort": 83, "protocol": "tcp", "hostIP": "192.168.0.2"},
{ "hostPort": 8084, "containerPort": 84, "protocol": "tcp", "hostIP": "0.0.0.0"},
{ "hostPort": 8085, "containerPort": 85, "protocol": "tcp", "hostIP": "2001:db8:a::1"},
{ "hostPort": 8086, "containerPort": 86, "protocol": "tcp", "hostIP": "::"}
]
},
"snat": true,
"conditionsV4": ["a", "b"],
"conditionsV6": ["c", "d"]
}`, ver))
conf, _, err := parseConfig(configBytes, "foo")
Expect(err).NotTo(HaveOccurred())
conf.ContainerID = containerID
ch = genDnatChain(conf.Name, containerID)
Expect(ch).To(Equal(chain{
table: "nat",
name: "CNI-DN-67e92b96e692a494b6b85",
entryChains: []string{"CNI-HOSTPORT-DNAT"},
}))
n, err := types.ParseCIDR("10.0.0.2/24")
fillDnatRules(&ch, conf, *n)
Expect(ch.entryRules).To(Equal([][]string{
{"-m", "comment", "--comment",
fmt.Sprintf("dnat name: \"test\" id: \"%s\"", containerID),
"-m", "multiport",
"-p", "tcp",
"--destination-ports", "8080,8081,8083,8084,8085,8086",
"a", "b"},
{"-m", "comment", "--comment",
fmt.Sprintf("dnat name: \"test\" id: \"%s\"", containerID),
"-m", "multiport",
"-p", "udp",
"--destination-ports", "8080,8082",
"a", "b"},
}))
Expect(ch.rules).To(Equal([][]string{
// tcp rules and not hostIP
{"-p", "tcp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8080", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
{"-p", "tcp", "--dport", "8081", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8081", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
// udp rules and not hostIP
{"-p", "udp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8080", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:81"},
{"-p", "udp", "--dport", "8082", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8082", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "10.0.0.2:82"},
// tcp rules and hostIP
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-j", "DNAT", "--to-destination", "10.0.0.2:83"},
// tcp rules and hostIP = "0.0.0.0"
{"-p", "tcp", "--dport", "8084", "-s", "10.0.0.2/24", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8084", "-s", "127.0.0.1", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8084", "-j", "DNAT", "--to-destination", "10.0.0.2:84"},
}))
ch.rules = nil
ch.entryRules = nil
n, err = types.ParseCIDR("2001:db8::2/64")
fillDnatRules(&ch, conf, *n)
Expect(ch.rules).To(Equal([][]string{
// tcp rules and not hostIP
{"-p", "tcp", "--dport", "8080", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "[2001:db8::2]:80"},
{"-p", "tcp", "--dport", "8081", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "[2001:db8::2]:80"},
// udp rules and not hostIP
{"-p", "udp", "--dport", "8080", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "[2001:db8::2]:81"},
{"-p", "udp", "--dport", "8082", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "[2001:db8::2]:82"},
// tcp rules and hostIP
{"-p", "tcp", "--dport", "8085", "-d", "2001:db8:a::1", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8085", "-d", "2001:db8:a::1", "-j", "DNAT", "--to-destination", "[2001:db8::2]:85"},
// tcp rules and hostIP = "::"
{"-p", "tcp", "--dport", "8086", "-s", "2001:db8::2/64", "-j", "CNI-HOSTPORT-SETMARK"},
{"-p", "tcp", "--dport", "8086", "-j", "DNAT", "--to-destination", "[2001:db8::2]:86"},
}))
// Disable snat, generate rules
ch.rules = nil
ch.entryRules = nil
fvar := false
conf.SNAT = &fvar
n, err = types.ParseCIDR("10.0.0.2/24")
fillDnatRules(&ch, conf, *n)
Expect(ch.rules).To(Equal([][]string{
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
{"-p", "tcp", "--dport", "8081", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
{"-p", "udp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:81"},
{"-p", "udp", "--dport", "8082", "-j", "DNAT", "--to-destination", "10.0.0.2:82"},
{"-p", "tcp", "--dport", "8083", "-d", "192.168.0.2", "-j", "DNAT", "--to-destination", "10.0.0.2:83"},
{"-p", "tcp", "--dport", "8084", "-j", "DNAT", "--to-destination", "10.0.0.2:84"},
}))
})
It(fmt.Sprintf("[%s] generates a correct chain with external mark", ver), func() {
ch := genDnatChain(netName, containerID)
Expect(ch).To(Equal(chain{
table: "nat",
name: "CNI-DN-bfd599665540dd91d5d28",
entryChains: []string{TopLevelDNATChainName},
}))
configBytes := []byte(fmt.Sprintf(`{
"name": "test",
"type": "portmap",
"cniVersion": "%s",
"runtimeConfig": {
"portMappings": [
{ "hostPort": 8080, "containerPort": 80, "protocol": "tcp"}
]
},
"externalSetMarkChain": "PLZ-SET-MARK",
"conditionsV4": ["a", "b"],
"conditionsV6": ["c", "d"]
}`, ver))
conf, _, err := parseConfig(configBytes, "foo")
Expect(err).NotTo(HaveOccurred())
conf.ContainerID = containerID
ch = genDnatChain(conf.Name, containerID)
n, err := types.ParseCIDR("10.0.0.2/24")
fillDnatRules(&ch, conf, *n)
Expect(ch.rules).To(Equal([][]string{
{"-p", "tcp", "--dport", "8080", "-s", "10.0.0.2/24", "-j", "PLZ-SET-MARK"},
{"-p", "tcp", "--dport", "8080", "-s", "127.0.0.1", "-j", "PLZ-SET-MARK"},
{"-p", "tcp", "--dport", "8080", "-j", "DNAT", "--to-destination", "10.0.0.2:80"},
}))
})
It(fmt.Sprintf("[%s] generates a correct top-level chain", ver), func() {
ch := genToplevelDnatChain()
Expect(ch).To(Equal(chain{
table: "nat",
name: "CNI-HOSTPORT-DNAT",
entryChains: []string{"PREROUTING", "OUTPUT"},
entryRules: [][]string{{"-m", "addrtype", "--dst-type", "LOCAL"}},
}))
})
It(fmt.Sprintf("[%s] generates the correct mark chains", ver), func() {
masqBit := 5
ch := genSetMarkChain(masqBit)
Expect(ch).To(Equal(chain{
table: "nat",
name: "CNI-HOSTPORT-SETMARK",
rules: [][]string{{
"-m", "comment",
"--comment", "CNI portfwd masquerade mark",
"-j", "MARK",
"--set-xmark", "0x20/0x20",
}},
}))
ch = genMarkMasqChain(masqBit)
Expect(ch).To(Equal(chain{
table: "nat",
name: "CNI-HOSTPORT-MASQ",
entryChains: []string{"POSTROUTING"},
entryRules: [][]string{{
"-m", "comment",
"--comment", "CNI portfwd requiring masquerade",
}},
rules: [][]string{{
"-m", "mark",
"--mark", "0x20/0x20",
"-j", "MASQUERADE",
}},
prependEntry: true,
}))
})
})
})
}
}) })

View File

@ -26,7 +26,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/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"
@ -234,7 +234,7 @@ func doRoutes(ipCfgs []*current.IPConfig, origRoutes []*types.Route, iface strin
// Source must be restricted to a single IP, not a full subnet // Source must be restricted to a single IP, not a full subnet
var src net.IPNet var src net.IPNet
src.IP = ipCfg.Address.IP src.IP = ipCfg.Address.IP
if ipCfg.Version == "4" { if src.IP.To4() != nil {
src.Mask = net.CIDRMask(32, 32) src.Mask = net.CIDRMask(32, 32)
} else { } else {
src.Mask = net.CIDRMask(128, 128) src.Mask = net.CIDRMask(128, 128)
@ -253,7 +253,7 @@ func doRoutes(ipCfgs []*current.IPConfig, origRoutes []*types.Route, iface strin
log.Printf("Adding default route to gateway %s", ipCfg.Gateway.String()) log.Printf("Adding default route to gateway %s", ipCfg.Gateway.String())
var dest net.IPNet var dest net.IPNet
if ipCfg.Version == "4" { if ipCfg.Address.IP.To4() != nil {
dest.IP = net.IPv4zero dest.IP = net.IPv4zero
dest.Mask = net.CIDRMask(0, 32) dest.Mask = net.CIDRMask(0, 32)
} else { } else {

View File

@ -241,6 +241,7 @@ var _ = Describe("sbr test", func() {
"name": "cni-plugin-sbr-test", "name": "cni-plugin-sbr-test",
"type": "sbr", "type": "sbr",
"prevResult": { "prevResult": {
"cniVersion": "0.3.0",
"interfaces": [ "interfaces": [
{ {
"name": "%s", "name": "%s",
@ -332,6 +333,7 @@ var _ = Describe("sbr test", func() {
"name": "cni-plugin-sbr-test", "name": "cni-plugin-sbr-test",
"type": "sbr", "type": "sbr",
"prevResult": { "prevResult": {
"cniVersion": "0.3.0",
"interfaces": [ "interfaces": [
{ {
"name": "%s", "name": "%s",
@ -399,19 +401,12 @@ var _ = Describe("sbr test", func() {
Expect(equalRoutes(expNet1.Routes, devNet1.Routes)).To(BeTrue()) Expect(equalRoutes(expNet1.Routes, devNet1.Routes)).To(BeTrue())
}) })
It("works with a 0.2.0 config", func() { It("fails with CNI spec versions that don't support plugin chaining", func() {
conf := `{ conf := `{
"cniVersion": "0.2.0", "cniVersion": "0.2.0",
"name": "cni-plugin-sbr-test", "name": "cni-plugin-sbr-test",
"type": "sbr", "type": "sbr",
"anotherAwesomeArg": "foo", "anotherAwesomeArg": "foo"
"prevResult": {
"ip4": {
"ip": "192.168.1.209/24",
"gateway": "192.168.1.1",
"routes": []
}
}
}` }`
args := &skel.CmdArgs{ args := &skel.CmdArgs{
@ -424,7 +419,7 @@ var _ = Describe("sbr test", func() {
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
_, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) _, _, err = testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) })
Expect(err).NotTo(HaveOccurred()) Expect(err).To(MatchError("This plugin must be called as chained plugin"))
}) })
}) })

View File

@ -32,7 +32,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/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"
@ -322,7 +322,7 @@ func cmdAdd(args *skel.CmdArgs) error {
} }
for _, ipc := range result.IPs { for _, ipc := range result.IPs {
if ipc.Version == "4" { if ipc.Address.IP.To4() != nil {
_ = arping.GratuitousArpOverIfaceByName(ipc.Address.IP, args.IfName) _ = arping.GratuitousArpOverIfaceByName(ipc.Address.IP, args.IfName)
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -22,7 +22,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/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/cni/pkg/version"
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"

View File

@ -20,7 +20,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/current" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/plugins/pkg/ns" "github.com/containernetworking/plugins/pkg/ns"
"github.com/containernetworking/plugins/pkg/testutils" "github.com/containernetworking/plugins/pkg/testutils"

View File

@ -19,11 +19,10 @@ package main
import ( import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"net"
"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" current "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/version" "github.com/containernetworking/cni/pkg/version"
bv "github.com/containernetworking/plugins/pkg/utils/buildversion" bv "github.com/containernetworking/plugins/pkg/utils/buildversion"
@ -33,21 +32,15 @@ import (
// is passed in on stdin. Your plugin may wish to expose its functionality via // is passed in on stdin. Your plugin may wish to expose its functionality via
// runtime args, see CONVENTIONS.md in the CNI spec. // runtime args, see CONVENTIONS.md in the CNI spec.
type PluginConf struct { type PluginConf struct {
types.NetConf // You may wish to not nest this type // This embeds the standard NetConf structure which allows your plugin
// to more easily parse standard fields like Name, Type, CNIVersion,
// and PrevResult.
types.NetConf
RuntimeConfig *struct { RuntimeConfig *struct {
SampleConfig map[string]interface{} `json:"sample"` SampleConfig map[string]interface{} `json:"sample"`
} `json:"runtimeConfig"` } `json:"runtimeConfig"`
// This is the previous result, when called in the context of a chained
// plugin. Because this plugin supports multiple versions, we'll have to
// parse this in two passes. If your plugin is not chained, this can be
// removed (though you may wish to error if a non-chainable plugin is
// chained.
// If you need to modify the result before returning it, you will need
// to actually convert it to a concrete versioned struct.
RawPrevResult *map[string]interface{} `json:"prevResult"`
PrevResult *current.Result `json:"-"`
// Add plugin-specifc flags here // Add plugin-specifc flags here
MyAwesomeFlag bool `json:"myAwesomeFlag"` MyAwesomeFlag bool `json:"myAwesomeFlag"`
AnotherAwesomeArg string `json:"anotherAwesomeArg"` AnotherAwesomeArg string `json:"anotherAwesomeArg"`
@ -61,21 +54,12 @@ func parseConfig(stdin []byte) (*PluginConf, error) {
return nil, fmt.Errorf("failed to parse network configuration: %v", err) return nil, fmt.Errorf("failed to parse network configuration: %v", err)
} }
// Parse previous result. Remove this if your plugin is not chained. // Parse previous result. This will parse, validate, and place the
if conf.RawPrevResult != nil { // previous result object into conf.PrevResult. If you need to modify
resultBytes, err := json.Marshal(conf.RawPrevResult) // or inspect the PrevResult you will need to convert it to a concrete
if err != nil { // versioned Result struct.
return nil, fmt.Errorf("could not serialize prevResult: %v", err) if err := version.ParsePrevResult(&conf.NetConf); err != nil {
} return nil, fmt.Errorf("could not parse prevResult: %v", err)
res, err := version.NewResult(conf.CNIVersion, resultBytes)
if err != nil {
return nil, fmt.Errorf("could not parse prevResult: %v", err)
}
conf.RawPrevResult = nil
conf.PrevResult, err = current.NewResultFromResult(res)
if err != nil {
return nil, fmt.Errorf("could not convert result to current version: %v", err)
}
} }
// End previous result parsing // End previous result parsing
@ -94,50 +78,62 @@ func cmdAdd(args *skel.CmdArgs) error {
return err return err
} }
// Remove this if this is an "originating" plugin // A plugin can be either an "originating" plugin or a "chained" plugin.
// Originating plugins perform initial sandbox setup and do not require
// any result from a previous plugin in the chain. A chained plugin
// modifies sandbox configuration that was previously set up by an
// originating plugin and may optionally require a PrevResult from
// earlier plugins in the chain.
// START chained plugin code
if conf.PrevResult == nil { if conf.PrevResult == nil {
return fmt.Errorf("must be called as chained plugin") return fmt.Errorf("must be called as chained plugin")
} }
// Uncomment if this is an "originating" plugin // Convert the PrevResult to a concrete Result type that can be modified.
prevResult, err := current.GetResult(conf.PrevResult)
//if conf.PrevResult != nil { if err != nil {
// return fmt.Errorf("must be called as the first plugin") return fmt.Errorf("failed to convert prevResult: %v", err)
// }
// This is some sample code to generate the list of container-side IPs.
// We're casting the prevResult to a 0.3.0 response, which can also include
// host-side IPs (but doesn't when converted from a 0.2.0 response).
//
// You don't need this if you are writing an "originating" plugin.
containerIPs := make([]net.IP, 0, len(conf.PrevResult.IPs))
if conf.CNIVersion != "0.3.0" {
for _, ip := range conf.PrevResult.IPs {
containerIPs = append(containerIPs, ip.Address.IP)
}
} else {
for _, ip := range conf.PrevResult.IPs {
if ip.Interface == nil {
continue
}
intIdx := *ip.Interface
// Every IP is indexed in to the interfaces array, with "-1" standing
// for an unknown interface (which we'll assume to be Container-side
// Skip all IPs we know belong to an interface with the wrong name.
if intIdx >= 0 && intIdx < len(conf.PrevResult.Interfaces) && conf.PrevResult.Interfaces[intIdx].Name != args.IfName {
continue
}
containerIPs = append(containerIPs, ip.Address.IP)
}
} }
if len(containerIPs) == 0 {
if len(prevResult.IPs) == 0 {
return fmt.Errorf("got no container IPs") return fmt.Errorf("got no container IPs")
} }
// Pass the prevResult through this plugin to the next one
result := prevResult
// END chained plugin code
// START originating plugin code
// if conf.PrevResult != nil {
// return fmt.Errorf("must be called as the first plugin")
// }
// Generate some fake container IPs and add to the result
// result := &current.Result{CNIVersion: current.ImplementedSpecVersion}
// result.Interfaces = []*current.Interface{
// {
// Name: "intf0",
// Sandbox: args.Netns,
// Mac: "00:11:22:33:44:55",
// },
// }
// result.IPs = []*current.IPConfig{
// {
// Address: "1.2.3.4/24",
// Gateway: "1.2.3.1",
// // Interface is an index into the Interfaces array
// // of the Interface element this IP applies to
// Interface: current.Int(0),
// }
// }
// END originating plugin code
// Implement your plugin here // Implement your plugin here
// Pass through the result for the next plugin // Pass through the result for the next plugin
return types.PrintResult(conf.PrevResult, conf.CNIVersion) return types.PrintResult(result, conf.CNIVersion)
} }
// cmdDel is called for DELETE requests // cmdDel is called for DELETE requests

View File

@ -45,6 +45,7 @@ var _ = Describe("sample test", func() {
"type": "sample", "type": "sample",
"anotherAwesomeArg": "awesome", "anotherAwesomeArg": "awesome",
"prevResult": { "prevResult": {
"cniVersion": "0.3.0",
"interfaces": [ "interfaces": [
{ {
"name": "%s", "name": "%s",
@ -71,7 +72,6 @@ var _ = Describe("sample test", func() {
} }
_, _, err := testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) _, _, err := testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
}) })
It("fails an invalid config", func() { It("fails an invalid config", func() {
@ -106,22 +106,14 @@ var _ = Describe("sample test", func() {
} }
_, _, err := testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) _, _, err := testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) })
Expect(err).To(MatchError("anotherAwesomeArg must be specified")) Expect(err).To(MatchError("anotherAwesomeArg must be specified"))
}) })
It("works with a 0.2.0 config", func() { It("fails with CNI spec versions that don't support plugin chaining", func() {
conf := `{ conf := `{
"cniVersion": "0.2.0", "cniVersion": "0.2.0",
"name": "cni-plugin-sample-test", "name": "cni-plugin-sample-test",
"type": "sample", "type": "sample",
"anotherAwesomeArg": "foo", "anotherAwesomeArg": "foo"
"prevResult": {
"ip4": {
"ip": "10.0.0.2/24",
"gateway": "10.0.0.1",
"routes": []
}
}
}` }`
args := &skel.CmdArgs{ args := &skel.CmdArgs{
@ -131,8 +123,7 @@ var _ = Describe("sample test", func() {
StdinData: []byte(conf), StdinData: []byte(conf),
} }
_, _, err := testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) }) _, _, err := testutils.CmdAddWithArgs(args, func() error { return cmdAdd(args) })
Expect(err).NotTo(HaveOccurred()) Expect(err).To(MatchError("must be called as chained plugin"))
}) })
}) })

View File

@ -22,25 +22,44 @@ import (
"os" "os"
"github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types"
convert "github.com/containernetworking/cni/pkg/types/internal"
) )
const ImplementedSpecVersion string = "0.2.0" const ImplementedSpecVersion string = "0.2.0"
var SupportedVersions = []string{"", "0.1.0", ImplementedSpecVersion} var supportedVersions = []string{"", "0.1.0", ImplementedSpecVersion}
// Register converters for all versions less than the implemented spec version
func init() {
convert.RegisterConverter("0.1.0", []string{ImplementedSpecVersion}, convertFrom010)
convert.RegisterConverter(ImplementedSpecVersion, []string{"0.1.0"}, convertTo010)
// Creator
convert.RegisterCreator(supportedVersions, NewResult)
}
// Compatibility types for CNI version 0.1.0 and 0.2.0 // Compatibility types for CNI version 0.1.0 and 0.2.0
// NewResult creates a new Result object from JSON data. The JSON data
// must be compatible with the CNI versions implemented by this type.
func NewResult(data []byte) (types.Result, error) { func NewResult(data []byte) (types.Result, error) {
result := &Result{} result := &Result{}
if err := json.Unmarshal(data, result); err != nil { if err := json.Unmarshal(data, result); err != nil {
return nil, err return nil, err
} }
return result, nil for _, v := range supportedVersions {
if result.CNIVersion == v {
return result, nil
}
}
return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q",
supportedVersions, result.CNIVersion)
} }
// GetResult converts the given Result object to the ImplementedSpecVersion
// and returns the concrete type or an error
func GetResult(r types.Result) (*Result, error) { func GetResult(r types.Result) (*Result, error) {
// We expect version 0.1.0/0.2.0 results result020, err := convert.Convert(r, ImplementedSpecVersion)
result020, err := r.GetAsVersion(ImplementedSpecVersion)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -51,6 +70,32 @@ func GetResult(r types.Result) (*Result, error) {
return result, nil return result, nil
} }
func convertFrom010(from types.Result, toVersion string) (types.Result, error) {
if toVersion != "0.2.0" {
panic("only converts to version 0.2.0")
}
fromResult := from.(*Result)
return &Result{
CNIVersion: ImplementedSpecVersion,
IP4: fromResult.IP4.Copy(),
IP6: fromResult.IP6.Copy(),
DNS: *fromResult.DNS.Copy(),
}, nil
}
func convertTo010(from types.Result, toVersion string) (types.Result, error) {
if toVersion != "0.1.0" {
panic("only converts to version 0.1.0")
}
fromResult := from.(*Result)
return &Result{
CNIVersion: "0.1.0",
IP4: fromResult.IP4.Copy(),
IP6: fromResult.IP6.Copy(),
DNS: *fromResult.DNS.Copy(),
}, nil
}
// Result is what gets returned from the plugin (via stdout) to the caller // Result is what gets returned from the plugin (via stdout) to the caller
type Result struct { type Result struct {
CNIVersion string `json:"cniVersion,omitempty"` CNIVersion string `json:"cniVersion,omitempty"`
@ -60,17 +105,16 @@ type Result struct {
} }
func (r *Result) Version() string { func (r *Result) Version() string {
return ImplementedSpecVersion return r.CNIVersion
} }
func (r *Result) GetAsVersion(version string) (types.Result, error) { func (r *Result) GetAsVersion(version string) (types.Result, error) {
for _, supportedVersion := range SupportedVersions { // If the creator of the result did not set the CNIVersion, assume it
if version == supportedVersion { // should be the highest spec version implemented by this Result
r.CNIVersion = version if r.CNIVersion == "" {
return r, nil r.CNIVersion = ImplementedSpecVersion
}
} }
return nil, fmt.Errorf("cannot convert version %q to %s", SupportedVersions, version) return convert.Convert(r, version)
} }
func (r *Result) Print() error { func (r *Result) Print() error {
@ -93,6 +137,22 @@ type IPConfig struct {
Routes []types.Route Routes []types.Route
} }
func (i *IPConfig) Copy() *IPConfig {
if i == nil {
return nil
}
var routes []types.Route
for _, fromRoute := range i.Routes {
routes = append(routes, *fromRoute.Copy())
}
return &IPConfig{
IP: i.IP,
Gateway: i.Gateway,
Routes: routes,
}
}
// net.IPNet is not JSON (un)marshallable so this duality is needed // net.IPNet is not JSON (un)marshallable so this duality is needed
// for our custom IPNet type // for our custom IPNet type

View File

@ -0,0 +1,305 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types040
import (
"encoding/json"
"fmt"
"io"
"net"
"os"
"github.com/containernetworking/cni/pkg/types"
types020 "github.com/containernetworking/cni/pkg/types/020"
convert "github.com/containernetworking/cni/pkg/types/internal"
)
const ImplementedSpecVersion string = "0.4.0"
var supportedVersions = []string{"0.3.0", "0.3.1", ImplementedSpecVersion}
// Register converters for all versions less than the implemented spec version
func init() {
// Up-converters
convert.RegisterConverter("0.1.0", supportedVersions, convertFrom02x)
convert.RegisterConverter("0.2.0", supportedVersions, convertFrom02x)
convert.RegisterConverter("0.3.0", supportedVersions, convertInternal)
convert.RegisterConverter("0.3.1", supportedVersions, convertInternal)
// Down-converters
convert.RegisterConverter("0.4.0", []string{"0.3.0", "0.3.1"}, convertInternal)
convert.RegisterConverter("0.4.0", []string{"0.1.0", "0.2.0"}, convertTo02x)
convert.RegisterConverter("0.3.1", []string{"0.1.0", "0.2.0"}, convertTo02x)
convert.RegisterConverter("0.3.0", []string{"0.1.0", "0.2.0"}, convertTo02x)
// Creator
convert.RegisterCreator(supportedVersions, NewResult)
}
func NewResult(data []byte) (types.Result, error) {
result := &Result{}
if err := json.Unmarshal(data, result); err != nil {
return nil, err
}
for _, v := range supportedVersions {
if result.CNIVersion == v {
return result, nil
}
}
return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q",
supportedVersions, result.CNIVersion)
}
func GetResult(r types.Result) (*Result, error) {
resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion)
if err != nil {
return nil, err
}
result, ok := resultCurrent.(*Result)
if !ok {
return nil, fmt.Errorf("failed to convert result")
}
return result, nil
}
func NewResultFromResult(result types.Result) (*Result, error) {
newResult, err := convert.Convert(result, ImplementedSpecVersion)
if err != nil {
return nil, err
}
return newResult.(*Result), nil
}
// Result is what gets returned from the plugin (via stdout) to the caller
type Result struct {
CNIVersion string `json:"cniVersion,omitempty"`
Interfaces []*Interface `json:"interfaces,omitempty"`
IPs []*IPConfig `json:"ips,omitempty"`
Routes []*types.Route `json:"routes,omitempty"`
DNS types.DNS `json:"dns,omitempty"`
}
func convert020IPConfig(from *types020.IPConfig, ipVersion string) *IPConfig {
return &IPConfig{
Version: ipVersion,
Address: from.IP,
Gateway: from.Gateway,
}
}
func convertFrom02x(from types.Result, toVersion string) (types.Result, error) {
fromResult := from.(*types020.Result)
toResult := &Result{
CNIVersion: toVersion,
DNS: *fromResult.DNS.Copy(),
Routes: []*types.Route{},
}
if fromResult.IP4 != nil {
toResult.IPs = append(toResult.IPs, convert020IPConfig(fromResult.IP4, "4"))
for _, fromRoute := range fromResult.IP4.Routes {
toResult.Routes = append(toResult.Routes, fromRoute.Copy())
}
}
if fromResult.IP6 != nil {
toResult.IPs = append(toResult.IPs, convert020IPConfig(fromResult.IP6, "6"))
for _, fromRoute := range fromResult.IP6.Routes {
toResult.Routes = append(toResult.Routes, fromRoute.Copy())
}
}
return toResult, nil
}
func convertInternal(from types.Result, toVersion string) (types.Result, error) {
fromResult := from.(*Result)
toResult := &Result{
CNIVersion: toVersion,
DNS: *fromResult.DNS.Copy(),
Routes: []*types.Route{},
}
for _, fromIntf := range fromResult.Interfaces {
toResult.Interfaces = append(toResult.Interfaces, fromIntf.Copy())
}
for _, fromIPC := range fromResult.IPs {
toResult.IPs = append(toResult.IPs, fromIPC.Copy())
}
for _, fromRoute := range fromResult.Routes {
toResult.Routes = append(toResult.Routes, fromRoute.Copy())
}
return toResult, nil
}
func convertTo02x(from types.Result, toVersion string) (types.Result, error) {
fromResult := from.(*Result)
toResult := &types020.Result{
CNIVersion: toVersion,
DNS: *fromResult.DNS.Copy(),
}
for _, fromIP := range fromResult.IPs {
// Only convert the first IP address of each version as 0.2.0
// and earlier cannot handle multiple IP addresses
if fromIP.Version == "4" && toResult.IP4 == nil {
toResult.IP4 = &types020.IPConfig{
IP: fromIP.Address,
Gateway: fromIP.Gateway,
}
} else if fromIP.Version == "6" && toResult.IP6 == nil {
toResult.IP6 = &types020.IPConfig{
IP: fromIP.Address,
Gateway: fromIP.Gateway,
}
}
if toResult.IP4 != nil && toResult.IP6 != nil {
break
}
}
for _, fromRoute := range fromResult.Routes {
is4 := fromRoute.Dst.IP.To4() != nil
if is4 && toResult.IP4 != nil {
toResult.IP4.Routes = append(toResult.IP4.Routes, types.Route{
Dst: fromRoute.Dst,
GW: fromRoute.GW,
})
} else if !is4 && toResult.IP6 != nil {
toResult.IP6.Routes = append(toResult.IP6.Routes, types.Route{
Dst: fromRoute.Dst,
GW: fromRoute.GW,
})
}
}
if toResult.IP4 == nil && toResult.IP6 == nil {
return nil, fmt.Errorf("cannot convert: no valid IP addresses")
}
return toResult, nil
}
func (r *Result) Version() string {
return r.CNIVersion
}
func (r *Result) GetAsVersion(version string) (types.Result, error) {
// If the creator of the result did not set the CNIVersion, assume it
// should be the highest spec version implemented by this Result
if r.CNIVersion == "" {
r.CNIVersion = ImplementedSpecVersion
}
return convert.Convert(r, version)
}
func (r *Result) Print() error {
return r.PrintTo(os.Stdout)
}
func (r *Result) PrintTo(writer io.Writer) error {
data, err := json.MarshalIndent(r, "", " ")
if err != nil {
return err
}
_, err = writer.Write(data)
return err
}
// Interface contains values about the created interfaces
type Interface struct {
Name string `json:"name"`
Mac string `json:"mac,omitempty"`
Sandbox string `json:"sandbox,omitempty"`
}
func (i *Interface) String() string {
return fmt.Sprintf("%+v", *i)
}
func (i *Interface) Copy() *Interface {
if i == nil {
return nil
}
newIntf := *i
return &newIntf
}
// Int returns a pointer to the int value passed in. Used to
// set the IPConfig.Interface field.
func Int(v int) *int {
return &v
}
// IPConfig contains values necessary to configure an IP address on an interface
type IPConfig struct {
// IP version, either "4" or "6"
Version string
// Index into Result structs Interfaces list
Interface *int
Address net.IPNet
Gateway net.IP
}
func (i *IPConfig) String() string {
return fmt.Sprintf("%+v", *i)
}
func (i *IPConfig) Copy() *IPConfig {
if i == nil {
return nil
}
ipc := &IPConfig{
Version: i.Version,
Address: i.Address,
Gateway: i.Gateway,
}
if i.Interface != nil {
intf := *i.Interface
ipc.Interface = &intf
}
return ipc
}
// JSON (un)marshallable types
type ipConfig struct {
Version string `json:"version"`
Interface *int `json:"interface,omitempty"`
Address types.IPNet `json:"address"`
Gateway net.IP `json:"gateway,omitempty"`
}
func (c *IPConfig) MarshalJSON() ([]byte, error) {
ipc := ipConfig{
Version: c.Version,
Interface: c.Interface,
Address: types.IPNet(c.Address),
Gateway: c.Gateway,
}
return json.Marshal(ipc)
}
func (c *IPConfig) UnmarshalJSON(data []byte) error {
ipc := ipConfig{}
if err := json.Unmarshal(data, &ipc); err != nil {
return err
}
c.Version = ipc.Version
c.Interface = ipc.Interface
c.Address = net.IPNet(ipc.Address)
c.Gateway = ipc.Gateway
return nil
}

View File

@ -0,0 +1,307 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package types100
import (
"encoding/json"
"fmt"
"io"
"net"
"os"
"github.com/containernetworking/cni/pkg/types"
types040 "github.com/containernetworking/cni/pkg/types/040"
convert "github.com/containernetworking/cni/pkg/types/internal"
)
const ImplementedSpecVersion string = "1.0.0"
var supportedVersions = []string{ImplementedSpecVersion}
// Register converters for all versions less than the implemented spec version
func init() {
// Up-converters
convert.RegisterConverter("0.1.0", supportedVersions, convertFrom02x)
convert.RegisterConverter("0.2.0", supportedVersions, convertFrom02x)
convert.RegisterConverter("0.3.0", supportedVersions, convertFrom04x)
convert.RegisterConverter("0.3.1", supportedVersions, convertFrom04x)
convert.RegisterConverter("0.4.0", supportedVersions, convertFrom04x)
// Down-converters
convert.RegisterConverter("1.0.0", []string{"0.3.0", "0.3.1", "0.4.0"}, convertTo04x)
convert.RegisterConverter("1.0.0", []string{"0.1.0", "0.2.0"}, convertTo02x)
// Creator
convert.RegisterCreator(supportedVersions, NewResult)
}
func NewResult(data []byte) (types.Result, error) {
result := &Result{}
if err := json.Unmarshal(data, result); err != nil {
return nil, err
}
for _, v := range supportedVersions {
if result.CNIVersion == v {
return result, nil
}
}
return nil, fmt.Errorf("result type supports %v but unmarshalled CNIVersion is %q",
supportedVersions, result.CNIVersion)
}
func GetResult(r types.Result) (*Result, error) {
resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion)
if err != nil {
return nil, err
}
result, ok := resultCurrent.(*Result)
if !ok {
return nil, fmt.Errorf("failed to convert result")
}
return result, nil
}
func NewResultFromResult(result types.Result) (*Result, error) {
newResult, err := convert.Convert(result, ImplementedSpecVersion)
if err != nil {
return nil, err
}
return newResult.(*Result), nil
}
// Result is what gets returned from the plugin (via stdout) to the caller
type Result struct {
CNIVersion string `json:"cniVersion,omitempty"`
Interfaces []*Interface `json:"interfaces,omitempty"`
IPs []*IPConfig `json:"ips,omitempty"`
Routes []*types.Route `json:"routes,omitempty"`
DNS types.DNS `json:"dns,omitempty"`
}
func convertFrom02x(from types.Result, toVersion string) (types.Result, error) {
result040, err := convert.Convert(from, "0.4.0")
if err != nil {
return nil, err
}
result100, err := convertFrom04x(result040, ImplementedSpecVersion)
if err != nil {
return nil, err
}
return result100, nil
}
func convertIPConfigFrom040(from *types040.IPConfig) *IPConfig {
to := &IPConfig{
Address: from.Address,
Gateway: from.Gateway,
}
if from.Interface != nil {
intf := *from.Interface
to.Interface = &intf
}
return to
}
func convertInterfaceFrom040(from *types040.Interface) *Interface {
return &Interface{
Name: from.Name,
Mac: from.Mac,
Sandbox: from.Sandbox,
}
}
func convertFrom04x(from types.Result, toVersion string) (types.Result, error) {
fromResult := from.(*types040.Result)
toResult := &Result{
CNIVersion: toVersion,
DNS: *fromResult.DNS.Copy(),
Routes: []*types.Route{},
}
for _, fromIntf := range fromResult.Interfaces {
toResult.Interfaces = append(toResult.Interfaces, convertInterfaceFrom040(fromIntf))
}
for _, fromIPC := range fromResult.IPs {
toResult.IPs = append(toResult.IPs, convertIPConfigFrom040(fromIPC))
}
for _, fromRoute := range fromResult.Routes {
toResult.Routes = append(toResult.Routes, fromRoute.Copy())
}
return toResult, nil
}
func convertIPConfigTo040(from *IPConfig) *types040.IPConfig {
version := "6"
if from.Address.IP.To4() != nil {
version = "4"
}
to := &types040.IPConfig{
Version: version,
Address: from.Address,
Gateway: from.Gateway,
}
if from.Interface != nil {
intf := *from.Interface
to.Interface = &intf
}
return to
}
func convertInterfaceTo040(from *Interface) *types040.Interface {
return &types040.Interface{
Name: from.Name,
Mac: from.Mac,
Sandbox: from.Sandbox,
}
}
func convertTo04x(from types.Result, toVersion string) (types.Result, error) {
fromResult := from.(*Result)
toResult := &types040.Result{
CNIVersion: toVersion,
DNS: *fromResult.DNS.Copy(),
Routes: []*types.Route{},
}
for _, fromIntf := range fromResult.Interfaces {
toResult.Interfaces = append(toResult.Interfaces, convertInterfaceTo040(fromIntf))
}
for _, fromIPC := range fromResult.IPs {
toResult.IPs = append(toResult.IPs, convertIPConfigTo040(fromIPC))
}
for _, fromRoute := range fromResult.Routes {
toResult.Routes = append(toResult.Routes, fromRoute.Copy())
}
return toResult, nil
}
func convertTo02x(from types.Result, toVersion string) (types.Result, error) {
// First convert to 0.4.0
result040, err := convertTo04x(from, "0.4.0")
if err != nil {
return nil, err
}
result02x, err := convert.Convert(result040, toVersion)
if err != nil {
return nil, err
}
return result02x, nil
}
func (r *Result) Version() string {
return r.CNIVersion
}
func (r *Result) GetAsVersion(version string) (types.Result, error) {
// If the creator of the result did not set the CNIVersion, assume it
// should be the highest spec version implemented by this Result
if r.CNIVersion == "" {
r.CNIVersion = ImplementedSpecVersion
}
return convert.Convert(r, version)
}
func (r *Result) Print() error {
return r.PrintTo(os.Stdout)
}
func (r *Result) PrintTo(writer io.Writer) error {
data, err := json.MarshalIndent(r, "", " ")
if err != nil {
return err
}
_, err = writer.Write(data)
return err
}
// Interface contains values about the created interfaces
type Interface struct {
Name string `json:"name"`
Mac string `json:"mac,omitempty"`
Sandbox string `json:"sandbox,omitempty"`
}
func (i *Interface) String() string {
return fmt.Sprintf("%+v", *i)
}
func (i *Interface) Copy() *Interface {
if i == nil {
return nil
}
newIntf := *i
return &newIntf
}
// Int returns a pointer to the int value passed in. Used to
// set the IPConfig.Interface field.
func Int(v int) *int {
return &v
}
// IPConfig contains values necessary to configure an IP address on an interface
type IPConfig struct {
// Index into Result structs Interfaces list
Interface *int
Address net.IPNet
Gateway net.IP
}
func (i *IPConfig) String() string {
return fmt.Sprintf("%+v", *i)
}
func (i *IPConfig) Copy() *IPConfig {
if i == nil {
return nil
}
ipc := &IPConfig{
Address: i.Address,
Gateway: i.Gateway,
}
if i.Interface != nil {
intf := *i.Interface
ipc.Interface = &intf
}
return ipc
}
// JSON (un)marshallable types
type ipConfig struct {
Interface *int `json:"interface,omitempty"`
Address types.IPNet `json:"address"`
Gateway net.IP `json:"gateway,omitempty"`
}
func (c *IPConfig) MarshalJSON() ([]byte, error) {
ipc := ipConfig{
Interface: c.Interface,
Address: types.IPNet(c.Address),
Gateway: c.Gateway,
}
return json.Marshal(ipc)
}
func (c *IPConfig) UnmarshalJSON(data []byte) error {
ipc := ipConfig{}
if err := json.Unmarshal(data, &ipc); err != nil {
return err
}
c.Interface = ipc.Interface
c.Address = net.IPNet(ipc.Address)
c.Gateway = ipc.Gateway
return nil
}

View File

@ -0,0 +1,26 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package create
import (
"github.com/containernetworking/cni/pkg/types"
convert "github.com/containernetworking/cni/pkg/types/internal"
)
// Create creates a CNI Result using the given JSON, or an error if the creation
// could not be performed
func Create(version string, bytes []byte) (types.Result, error) {
return convert.Create(version, bytes)
}

View File

@ -1,276 +0,0 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package current
import (
"encoding/json"
"fmt"
"io"
"net"
"os"
"github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/020"
)
const ImplementedSpecVersion string = "0.4.0"
var SupportedVersions = []string{"0.3.0", "0.3.1", ImplementedSpecVersion}
func NewResult(data []byte) (types.Result, error) {
result := &Result{}
if err := json.Unmarshal(data, result); err != nil {
return nil, err
}
return result, nil
}
func GetResult(r types.Result) (*Result, error) {
resultCurrent, err := r.GetAsVersion(ImplementedSpecVersion)
if err != nil {
return nil, err
}
result, ok := resultCurrent.(*Result)
if !ok {
return nil, fmt.Errorf("failed to convert result")
}
return result, nil
}
var resultConverters = []struct {
versions []string
convert func(types.Result) (*Result, error)
}{
{types020.SupportedVersions, convertFrom020},
{SupportedVersions, convertFrom030},
}
func convertFrom020(result types.Result) (*Result, error) {
oldResult, err := types020.GetResult(result)
if err != nil {
return nil, err
}
newResult := &Result{
CNIVersion: ImplementedSpecVersion,
DNS: oldResult.DNS,
Routes: []*types.Route{},
}
if oldResult.IP4 != nil {
newResult.IPs = append(newResult.IPs, &IPConfig{
Version: "4",
Address: oldResult.IP4.IP,
Gateway: oldResult.IP4.Gateway,
})
for _, route := range oldResult.IP4.Routes {
newResult.Routes = append(newResult.Routes, &types.Route{
Dst: route.Dst,
GW: route.GW,
})
}
}
if oldResult.IP6 != nil {
newResult.IPs = append(newResult.IPs, &IPConfig{
Version: "6",
Address: oldResult.IP6.IP,
Gateway: oldResult.IP6.Gateway,
})
for _, route := range oldResult.IP6.Routes {
newResult.Routes = append(newResult.Routes, &types.Route{
Dst: route.Dst,
GW: route.GW,
})
}
}
return newResult, nil
}
func convertFrom030(result types.Result) (*Result, error) {
newResult, ok := result.(*Result)
if !ok {
return nil, fmt.Errorf("failed to convert result")
}
newResult.CNIVersion = ImplementedSpecVersion
return newResult, nil
}
func NewResultFromResult(result types.Result) (*Result, error) {
version := result.Version()
for _, converter := range resultConverters {
for _, supportedVersion := range converter.versions {
if version == supportedVersion {
return converter.convert(result)
}
}
}
return nil, fmt.Errorf("unsupported CNI result22 version %q", version)
}
// Result is what gets returned from the plugin (via stdout) to the caller
type Result struct {
CNIVersion string `json:"cniVersion,omitempty"`
Interfaces []*Interface `json:"interfaces,omitempty"`
IPs []*IPConfig `json:"ips,omitempty"`
Routes []*types.Route `json:"routes,omitempty"`
DNS types.DNS `json:"dns,omitempty"`
}
// Convert to the older 0.2.0 CNI spec Result type
func (r *Result) convertTo020() (*types020.Result, error) {
oldResult := &types020.Result{
CNIVersion: types020.ImplementedSpecVersion,
DNS: r.DNS,
}
for _, ip := range r.IPs {
// Only convert the first IP address of each version as 0.2.0
// and earlier cannot handle multiple IP addresses
if ip.Version == "4" && oldResult.IP4 == nil {
oldResult.IP4 = &types020.IPConfig{
IP: ip.Address,
Gateway: ip.Gateway,
}
} else if ip.Version == "6" && oldResult.IP6 == nil {
oldResult.IP6 = &types020.IPConfig{
IP: ip.Address,
Gateway: ip.Gateway,
}
}
if oldResult.IP4 != nil && oldResult.IP6 != nil {
break
}
}
for _, route := range r.Routes {
is4 := route.Dst.IP.To4() != nil
if is4 && oldResult.IP4 != nil {
oldResult.IP4.Routes = append(oldResult.IP4.Routes, types.Route{
Dst: route.Dst,
GW: route.GW,
})
} else if !is4 && oldResult.IP6 != nil {
oldResult.IP6.Routes = append(oldResult.IP6.Routes, types.Route{
Dst: route.Dst,
GW: route.GW,
})
}
}
if oldResult.IP4 == nil && oldResult.IP6 == nil {
return nil, fmt.Errorf("cannot convert: no valid IP addresses")
}
return oldResult, nil
}
func (r *Result) Version() string {
return ImplementedSpecVersion
}
func (r *Result) GetAsVersion(version string) (types.Result, error) {
switch version {
case "0.3.0", "0.3.1", ImplementedSpecVersion:
r.CNIVersion = version
return r, nil
case types020.SupportedVersions[0], types020.SupportedVersions[1], types020.SupportedVersions[2]:
return r.convertTo020()
}
return nil, fmt.Errorf("cannot convert version 0.3.x to %q", version)
}
func (r *Result) Print() error {
return r.PrintTo(os.Stdout)
}
func (r *Result) PrintTo(writer io.Writer) error {
data, err := json.MarshalIndent(r, "", " ")
if err != nil {
return err
}
_, err = writer.Write(data)
return err
}
// Convert this old version result to the current CNI version result
func (r *Result) Convert() (*Result, error) {
return r, nil
}
// Interface contains values about the created interfaces
type Interface struct {
Name string `json:"name"`
Mac string `json:"mac,omitempty"`
Sandbox string `json:"sandbox,omitempty"`
}
func (i *Interface) String() string {
return fmt.Sprintf("%+v", *i)
}
// Int returns a pointer to the int value passed in. Used to
// set the IPConfig.Interface field.
func Int(v int) *int {
return &v
}
// IPConfig contains values necessary to configure an IP address on an interface
type IPConfig struct {
// IP version, either "4" or "6"
Version string
// Index into Result structs Interfaces list
Interface *int
Address net.IPNet
Gateway net.IP
}
func (i *IPConfig) String() string {
return fmt.Sprintf("%+v", *i)
}
// JSON (un)marshallable types
type ipConfig struct {
Version string `json:"version"`
Interface *int `json:"interface,omitempty"`
Address types.IPNet `json:"address"`
Gateway net.IP `json:"gateway,omitempty"`
}
func (c *IPConfig) MarshalJSON() ([]byte, error) {
ipc := ipConfig{
Version: c.Version,
Interface: c.Interface,
Address: types.IPNet(c.Address),
Gateway: c.Gateway,
}
return json.Marshal(ipc)
}
func (c *IPConfig) UnmarshalJSON(data []byte) error {
ipc := ipConfig{}
if err := json.Unmarshal(data, &ipc); err != nil {
return err
}
c.Version = ipc.Version
c.Interface = ipc.Interface
c.Address = net.IPNet(ipc.Address)
c.Gateway = ipc.Gateway
return nil
}

View File

@ -0,0 +1,88 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package convert
import (
"fmt"
"github.com/containernetworking/cni/pkg/types"
)
// ConvertFn should convert from the given arbitrary Result type into a
// Result implementing CNI specification version passed in toVersion.
// The function is guaranteed to be passed a Result type matching the
// fromVersion it was registered with, and is guaranteed to be
// passed a toVersion matching one of the toVersions it was registered with.
type ConvertFn func(from types.Result, toVersion string) (types.Result, error)
type converter struct {
// fromVersion is the CNI Result spec version that convertFn accepts
fromVersion string
// toVersions is a list of versions that convertFn can convert to
toVersions []string
convertFn ConvertFn
}
var converters []*converter
func findConverter(fromVersion, toVersion string) *converter {
for _, c := range converters {
if c.fromVersion == fromVersion {
for _, v := range c.toVersions {
if v == toVersion {
return c
}
}
}
}
return nil
}
// Convert converts a CNI Result to the requested CNI specification version,
// or returns an error if the converstion could not be performed or failed
func Convert(from types.Result, toVersion string) (types.Result, error) {
fromVersion := from.Version()
// Shortcut for same version
if fromVersion == toVersion {
return from, nil
}
// Otherwise find the right converter
c := findConverter(fromVersion, toVersion)
if c == nil {
return nil, fmt.Errorf("no converter for CNI result version %s to %s",
fromVersion, toVersion)
}
return c.convertFn(from, toVersion)
}
// RegisterConverter registers a CNI Result converter. SHOULD NOT BE CALLED
// EXCEPT FROM CNI ITSELF.
func RegisterConverter(fromVersion string, toVersions []string, convertFn ConvertFn) {
// Make sure there is no converter already registered for these
// from and to versions
for _, v := range toVersions {
if findConverter(fromVersion, v) != nil {
panic(fmt.Sprintf("converter already registered for %s to %s",
fromVersion, v))
}
}
converters = append(converters, &converter{
fromVersion: fromVersion,
toVersions: toVersions,
convertFn: convertFn,
})
}

View File

@ -0,0 +1,66 @@
// Copyright 2016 CNI authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package convert
import (
"fmt"
"github.com/containernetworking/cni/pkg/types"
)
type ResultFactoryFunc func([]byte) (types.Result, error)
type creator struct {
// CNI Result spec versions that createFn can create a Result for
versions []string
createFn ResultFactoryFunc
}
var creators []*creator
func findCreator(version string) *creator {
for _, c := range creators {
for _, v := range c.versions {
if v == version {
return c
}
}
}
return nil
}
// Create creates a CNI Result using the given JSON, or an error if the creation
// could not be performed
func Create(version string, bytes []byte) (types.Result, error) {
if c := findCreator(version); c != nil {
return c.createFn(bytes)
}
return nil, fmt.Errorf("unsupported CNI result version %q", version)
}
// RegisterCreator registers a CNI Result creator. SHOULD NOT BE CALLED
// EXCEPT FROM CNI ITSELF.
func RegisterCreator(versions []string, createFn ResultFactoryFunc) {
// Make sure there is no creator already registered for these versions
for _, v := range versions {
if findCreator(v) != nil {
panic(fmt.Sprintf("creator already registered for %s", v))
}
}
creators = append(creators, &creator{
versions: versions,
createFn: createFn,
})
}

View File

@ -83,8 +83,6 @@ type NetConfList struct {
Plugins []*NetConf `json:"plugins,omitempty"` Plugins []*NetConf `json:"plugins,omitempty"`
} }
type ResultFactoryFunc func([]byte) (Result, error)
// Result is an interface that provides the result of plugin execution // Result is an interface that provides the result of plugin execution
type Result interface { type Result interface {
// The highest CNI specification result version the result supports // The highest CNI specification result version the result supports
@ -118,6 +116,24 @@ type DNS struct {
Options []string `json:"options,omitempty"` Options []string `json:"options,omitempty"`
} }
func (d *DNS) Copy() *DNS {
if d == nil {
return nil
}
to := &DNS{Domain: d.Domain}
for _, ns := range d.Nameservers {
to.Nameservers = append(to.Nameservers, ns)
}
for _, s := range d.Search {
to.Search = append(to.Search, s)
}
for _, o := range d.Options {
to.Options = append(to.Options, o)
}
return to
}
type Route struct { type Route struct {
Dst net.IPNet Dst net.IPNet
GW net.IP GW net.IP
@ -127,6 +143,17 @@ func (r *Route) String() string {
return fmt.Sprintf("%+v", *r) return fmt.Sprintf("%+v", *r)
} }
func (r *Route) Copy() *Route {
if r == nil {
return nil
}
return &Route{
Dst: r.Dst,
GW: r.GW,
}
}
// Well known error codes // Well known error codes
// see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes // see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes
const ( const (

View File

@ -19,13 +19,13 @@ import (
"fmt" "fmt"
"github.com/containernetworking/cni/pkg/types" "github.com/containernetworking/cni/pkg/types"
"github.com/containernetworking/cni/pkg/types/020" "github.com/containernetworking/cni/pkg/types/100"
"github.com/containernetworking/cni/pkg/types/current" "github.com/containernetworking/cni/pkg/types/create"
) )
// Current reports the version of the CNI spec implemented by this library // Current reports the version of the CNI spec implemented by this library
func Current() string { func Current() string {
return "0.4.0" return types100.ImplementedSpecVersion
} }
// Legacy PluginInfo describes a plugin that is backwards compatible with the // Legacy PluginInfo describes a plugin that is backwards compatible with the
@ -36,29 +36,12 @@ func Current() string {
// Any future CNI spec versions which meet this definition should be added to // Any future CNI spec versions which meet this definition should be added to
// this list. // this list.
var Legacy = PluginSupports("0.1.0", "0.2.0") var Legacy = PluginSupports("0.1.0", "0.2.0")
var All = PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0") var All = PluginSupports("0.1.0", "0.2.0", "0.3.0", "0.3.1", "0.4.0", "1.0.0")
var resultFactories = []struct {
supportedVersions []string
newResult types.ResultFactoryFunc
}{
{current.SupportedVersions, current.NewResult},
{types020.SupportedVersions, types020.NewResult},
}
// Finds a Result object matching the requested version (if any) and asks // Finds a Result object matching the requested version (if any) and asks
// that object to parse the plugin result, returning an error if parsing failed. // that object to parse the plugin result, returning an error if parsing failed.
func NewResult(version string, resultBytes []byte) (types.Result, error) { func NewResult(version string, resultBytes []byte) (types.Result, error) {
reconciler := &Reconciler{} return create.Create(version, resultBytes)
for _, resultFactory := range resultFactories {
err := reconciler.CheckRaw(version, resultFactory.supportedVersions)
if err == nil {
// Result supports this version
return resultFactory.newResult(resultBytes)
}
}
return nil, fmt.Errorf("unsupported CNI result version %q", version)
} }
// ParsePrevResult parses a prevResult in a NetConf structure and sets // ParsePrevResult parses a prevResult in a NetConf structure and sets
@ -68,6 +51,13 @@ func ParsePrevResult(conf *types.NetConf) error {
return nil return nil
} }
// Prior to 1.0.0, Result types may not marshal a CNIVersion. Since the
// result version must match the config version, if the Result's version
// is empty, inject the config version.
if ver, ok := conf.RawPrevResult["CNIVersion"]; !ok || ver == "" {
conf.RawPrevResult["CNIVersion"] = conf.CNIVersion
}
resultBytes, err := json.Marshal(conf.RawPrevResult) resultBytes, err := json.Marshal(conf.RawPrevResult)
if err != nil { if err != nil {
return fmt.Errorf("could not serialize prevResult: %v", err) return fmt.Errorf("could not serialize prevResult: %v", err)

View File

@ -1,5 +1,12 @@
root = true root = true
[*] [*.go]
indent_style = tab indent_style = tab
indent_size = 4 indent_size = 4
insert_final_newline = true
[*.{yml,yaml}]
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

1
vendor/github.com/fsnotify/fsnotify/.gitattributes generated vendored Normal file
View File

@ -0,0 +1 @@
go.sum linguist-generated

View File

@ -2,29 +2,35 @@ sudo: false
language: go language: go
go: go:
- 1.8.x - "stable"
- 1.9.x - "1.11.x"
- tip - "1.10.x"
- "1.9.x"
matrix: matrix:
include:
- go: "stable"
env: GOLINT=true
allow_failures: allow_failures:
- go: tip - go: tip
fast_finish: true fast_finish: true
before_script:
- go get -u github.com/golang/lint/golint before_install:
- if [ ! -z "${GOLINT}" ]; then go get -u golang.org/x/lint/golint; fi
script: script:
- go test -v --race ./... - go test --race ./...
after_script: after_script:
- test -z "$(gofmt -s -l -w . | tee /dev/stderr)" - test -z "$(gofmt -s -l -w . | tee /dev/stderr)"
- test -z "$(golint ./... | tee /dev/stderr)" - if [ ! -z "${GOLINT}" ]; then echo running golint; golint --set_exit_status ./...; else echo skipping golint; fi
- go vet ./... - go vet ./...
os: os:
- linux - linux
- osx - osx
- windows
notifications: notifications:
email: false email: false

View File

@ -1,5 +1,5 @@
Copyright (c) 2012 The Go Authors. All rights reserved. Copyright (c) 2012 The Go Authors. All rights reserved.
Copyright (c) 2012 fsnotify Authors. All rights reserved. Copyright (c) 2012-2019 fsnotify Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are modification, are permitted provided that the following conditions are

View File

@ -10,16 +10,16 @@ go get -u golang.org/x/sys/...
Cross platform: Windows, Linux, BSD and macOS. Cross platform: Windows, Linux, BSD and macOS.
|Adapter |OS |Status | | Adapter | OS | Status |
|----------|----------|----------| | --------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|inotify |Linux 2.6.27 or later, Android\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)| | inotify | Linux 2.6.27 or later, Android\* | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) |
|kqueue |BSD, macOS, iOS\*|Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify)| | kqueue | BSD, macOS, iOS\* | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) |
|ReadDirectoryChangesW|Windows|Supported [![Build status](https://ci.appveyor.com/api/projects/status/ivwjubaih4r0udeh/branch/master?svg=true)](https://ci.appveyor.com/project/NathanYoungman/fsnotify/branch/master)| | ReadDirectoryChangesW | Windows | Supported [![Build Status](https://travis-ci.org/fsnotify/fsnotify.svg?branch=master)](https://travis-ci.org/fsnotify/fsnotify) |
|FSEvents |macOS |[Planned](https://github.com/fsnotify/fsnotify/issues/11)| | FSEvents | macOS | [Planned](https://github.com/fsnotify/fsnotify/issues/11) |
|FEN |Solaris 11 |[In Progress](https://github.com/fsnotify/fsnotify/issues/12)| | FEN | Solaris 11 | [In Progress](https://github.com/fsnotify/fsnotify/issues/12) |
|fanotify |Linux 2.6.37+ | | | fanotify | Linux 2.6.37+ | [Planned](https://github.com/fsnotify/fsnotify/issues/114) |
|USN Journals |Windows |[Maybe](https://github.com/fsnotify/fsnotify/issues/53)| | USN Journals | Windows | [Maybe](https://github.com/fsnotify/fsnotify/issues/53) |
|Polling |*All* |[Maybe](https://github.com/fsnotify/fsnotify/issues/9)| | Polling | *All* | [Maybe](https://github.com/fsnotify/fsnotify/issues/9) |
\* Android and iOS are untested. \* Android and iOS are untested.
@ -33,6 +33,53 @@ All [releases](https://github.com/fsnotify/fsnotify/releases) are tagged based o
Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`. Go 1.6 supports dependencies located in the `vendor/` folder. Unless you are creating a library, it is recommended that you copy fsnotify into `vendor/github.com/fsnotify/fsnotify` within your project, and likewise for `golang.org/x/sys`.
## Usage
```go
package main
import (
"log"
"github.com/fsnotify/fsnotify"
)
func main() {
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
done := make(chan bool)
go func() {
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
log.Println("event:", event)
if event.Op&fsnotify.Write == fsnotify.Write {
log.Println("modified file:", event.Name)
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Println("error:", err)
}
}
}()
err = watcher.Add("/tmp/foo")
if err != nil {
log.Fatal(err)
}
<-done
}
```
## Contributing ## Contributing
Please refer to [CONTRIBUTING][] before opening an issue or pull request. Please refer to [CONTRIBUTING][] before opening an issue or pull request.
@ -65,6 +112,10 @@ There are OS-specific limits as to how many watches can be created:
* Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error. * Linux: /proc/sys/fs/inotify/max_user_watches contains the limit, reaching this limit results in a "no space left on device" error.
* BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error. * BSD / OSX: sysctl variables "kern.maxfiles" and "kern.maxfilesperproc", reaching these limits results in a "too many open files" error.
**Why don't notifications work with NFS filesystems or filesystem in userspace (FUSE)?**
fsnotify requires support from underlying OS to work. The current NFS protocol does not provide network level support for file notifications.
[#62]: https://github.com/howeyc/fsnotify/issues/62 [#62]: https://github.com/howeyc/fsnotify/issues/62
[#18]: https://github.com/fsnotify/fsnotify/issues/18 [#18]: https://github.com/fsnotify/fsnotify/issues/18
[#11]: https://github.com/fsnotify/fsnotify/issues/11 [#11]: https://github.com/fsnotify/fsnotify/issues/11

View File

@ -63,4 +63,6 @@ func (e Event) String() string {
} }
// Common errors that can be reported by a watcher // Common errors that can be reported by a watcher
var ErrEventOverflow = errors.New("fsnotify queue overflow") var (
ErrEventOverflow = errors.New("fsnotify queue overflow")
)

5
vendor/github.com/fsnotify/fsnotify/go.mod generated vendored Normal file
View File

@ -0,0 +1,5 @@
module github.com/fsnotify/fsnotify
go 1.13
require golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9

2
vendor/github.com/fsnotify/fsnotify/go.sum generated vendored Normal file
View File

@ -0,0 +1,2 @@
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9 h1:L2auWcuQIvxz9xSEqzESnV/QN/gNRXNApHi3fYwl2w0=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@ -40,12 +40,12 @@ func newFdPoller(fd int) (*fdPoller, error) {
poller.fd = fd poller.fd = fd
// Create epoll fd // Create epoll fd
poller.epfd, errno = unix.EpollCreate1(0) poller.epfd, errno = unix.EpollCreate1(unix.EPOLL_CLOEXEC)
if poller.epfd == -1 { if poller.epfd == -1 {
return nil, errno return nil, errno
} }
// Create pipe; pipe[0] is the read end, pipe[1] the write end. // Create pipe; pipe[0] is the read end, pipe[1] the write end.
errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK) errno = unix.Pipe2(poller.pipe[:], unix.O_NONBLOCK|unix.O_CLOEXEC)
if errno != nil { if errno != nil {
return nil, errno return nil, errno
} }

View File

@ -8,4 +8,4 @@ package fsnotify
import "golang.org/x/sys/unix" import "golang.org/x/sys/unix"
const openMode = unix.O_NONBLOCK | unix.O_RDONLY const openMode = unix.O_NONBLOCK | unix.O_RDONLY | unix.O_CLOEXEC

View File

@ -9,4 +9,4 @@ package fsnotify
import "golang.org/x/sys/unix" import "golang.org/x/sys/unix"
// note: this constant is not defined on BSD // note: this constant is not defined on BSD
const openMode = unix.O_EVTONLY const openMode = unix.O_EVTONLY | unix.O_CLOEXEC

View File

@ -1,3 +1,26 @@
## 1.13.0
### Features
- Add a version of table.Entry that allows dumping the entry parameters. (#689) [21eaef2]
### Fixes
- Ensure integration tests pass in an environment sans GOPATH [606fba2]
- Add books package (#568) [fc0e44e]
- doc(readme): installation via "tools package" (#677) [83bb20e]
- Solve the undefined: unix.Dup2 compile error on mips64le (#680) [0624f75]
- Import package without dot (#687) [6321024]
- Fix integration tests to stop require GOPATH (#686) [a912ec5]
## 1.12.3
### Fixes
- Print correct code location of failing table test (#666) [c6d7afb]
## 1.12.2
### Fixes
- Update dependencies [ea4a036]
## 1.12.1 ## 1.12.1
### Fixes ### Fixes

View File

@ -4,7 +4,23 @@
Jump to the [docs](https://onsi.github.io/ginkgo/) to learn more. To start rolling your Ginkgo tests *now* [keep reading](#set-me-up)! Jump to the [docs](https://onsi.github.io/ginkgo/) to learn more. To start rolling your Ginkgo tests *now* [keep reading](#set-me-up)!
If you have a question, comment, bug report, feature request, etc. please open a GitHub issue. If you have a question, comment, bug report, feature request, etc. please open a GitHub issue, or visit the [Ginkgo Slack channel](https://app.slack.com/client/T029RQSE6/CQQ50BBNW).
## TLDR
Ginkgo builds on Go's `testing` package, allowing expressive [Behavior-Driven Development](https://en.wikipedia.org/wiki/Behavior-driven_development) ("BDD") style tests.
It is typically (and optionally) paired with the [Gomega](https://github.com/onsi/gomega) matcher library.
```go
Describe("the strings package", func() {
Context("strings.Contains()", func() {
When("the string contains the substring in the middle", func() {
It("returns `true`", func() {
Expect(strings.Contains("Ginkgo is awesome", "is")).To(BeTrue())
})
})
})
})
```
## Feature List ## Feature List
@ -59,15 +75,36 @@ Ginkgo is best paired with Gomega. Learn more about Gomega [here](https://onsi.
Agouti allows you run WebDriver integration tests. Learn more about Agouti [here](https://agouti.org) Agouti allows you run WebDriver integration tests. Learn more about Agouti [here](https://agouti.org)
## Set Me Up! ## Getting Started
You'll need the Go command-line tools. Ginkgo is tested with Go 1.6+, but preferably you should get the latest. Follow the [installation instructions](https://golang.org/doc/install) if you don't have it installed. You'll need the Go command-line tools. Follow the [installation instructions](https://golang.org/doc/install) if you don't have it installed.
### Global installation
To install the Ginkgo command line interface into the `$PATH` (actually to `$GOBIN`):
```bash ```bash
go get -u github.com/onsi/ginkgo/ginkgo
```
go get -u github.com/onsi/ginkgo/ginkgo # installs the ginkgo CLI ### Go module ["tools package"](https://github.com/golang/go/issues/25922):
go get -u github.com/onsi/gomega/... # fetches the matcher library Create (or update) a file called `tools/tools.go` with the following contents:
```go
// +build tools
package tools
import (
_ "github.com/onsi/ginkgo"
)
// This file imports packages that are used when running go generate, or used
// during the development process but not otherwise depended on by built code.
```
The Ginkgo command can then be run via `go run github.com/onsi/ginkgo/ginkgo`.
This approach allows the version of Ginkgo to be maintained under source control for reproducible results,
and is well suited to automated test pipelines.
### Bootstrapping
```bash
cd path/to/package/you/want/to/test cd path/to/package/you/want/to/test
ginkgo bootstrap # set up a new ginkgo suite ginkgo bootstrap # set up a new ginkgo suite

View File

@ -20,7 +20,7 @@ import (
"fmt" "fmt"
) )
const VERSION = "1.12.1" const VERSION = "1.13.0"
type GinkgoConfigType struct { type GinkgoConfigType struct {
RandomSeed int64 RandomSeed int64

View File

@ -12,7 +12,9 @@ import (
"fmt" "fmt"
"reflect" "reflect"
"github.com/onsi/ginkgo" "github.com/onsi/ginkgo/internal/codelocation"
"github.com/onsi/ginkgo/internal/global"
"github.com/onsi/ginkgo/types"
) )
/* /*
@ -40,9 +42,28 @@ Under the hood, `DescribeTable` simply generates a new Ginkgo `Describe`. Each
It's important to understand that the `Describe`s and `It`s are generated at evaluation time (i.e. when Ginkgo constructs the tree of tests and before the tests run). It's important to understand that the `Describe`s and `It`s are generated at evaluation time (i.e. when Ginkgo constructs the tree of tests and before the tests run).
Individual Entries can be focused (with FEntry) or marked pending (with PEntry or XEntry). In addition, the entire table can be focused or marked pending with FDescribeTable and PDescribeTable/XDescribeTable. Individual Entries can be focused (with FEntry) or marked pending (with PEntry or XEntry). In addition, the entire table can be focused or marked pending with FDescribeTable and PDescribeTable/XDescribeTable.
A description function can be passed to Entry in place of the description. The function is then fed with the entry parameters to generate the description of the It corresponding to that particular Entry.
For example:
describe := func(desc string) func(int, int, bool) string {
return func(x, y int, expected bool) string {
return fmt.Sprintf("%s x=%d y=%d expected:%t", desc, x, y, expected)
}
}
DescribeTable("a simple table",
func(x int, y int, expected bool) {
Ω(x > y).Should(Equal(expected))
},
Entry(describe("x > y"), 1, 0, true),
Entry(describe("x == y"), 0, 0, false),
Entry(describe("x < y"), 0, 1, false),
)
*/ */
func DescribeTable(description string, itBody interface{}, entries ...TableEntry) bool { func DescribeTable(description string, itBody interface{}, entries ...TableEntry) bool {
describeTable(description, itBody, entries, false, false) describeTable(description, itBody, entries, types.FlagTypeNone)
return true return true
} }
@ -50,7 +71,7 @@ func DescribeTable(description string, itBody interface{}, entries ...TableEntry
You can focus a table with `FDescribeTable`. This is equivalent to `FDescribe`. You can focus a table with `FDescribeTable`. This is equivalent to `FDescribe`.
*/ */
func FDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool { func FDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool {
describeTable(description, itBody, entries, false, true) describeTable(description, itBody, entries, types.FlagTypeFocused)
return true return true
} }
@ -58,7 +79,7 @@ func FDescribeTable(description string, itBody interface{}, entries ...TableEntr
You can mark a table as pending with `PDescribeTable`. This is equivalent to `PDescribe`. You can mark a table as pending with `PDescribeTable`. This is equivalent to `PDescribe`.
*/ */
func PDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool { func PDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool {
describeTable(description, itBody, entries, true, false) describeTable(description, itBody, entries, types.FlagTypePending)
return true return true
} }
@ -66,33 +87,24 @@ func PDescribeTable(description string, itBody interface{}, entries ...TableEntr
You can mark a table as pending with `XDescribeTable`. This is equivalent to `XDescribe`. You can mark a table as pending with `XDescribeTable`. This is equivalent to `XDescribe`.
*/ */
func XDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool { func XDescribeTable(description string, itBody interface{}, entries ...TableEntry) bool {
describeTable(description, itBody, entries, true, false) describeTable(description, itBody, entries, types.FlagTypePending)
return true return true
} }
func describeTable(description string, itBody interface{}, entries []TableEntry, pending bool, focused bool) { func describeTable(description string, itBody interface{}, entries []TableEntry, flag types.FlagType) {
itBodyValue := reflect.ValueOf(itBody) itBodyValue := reflect.ValueOf(itBody)
if itBodyValue.Kind() != reflect.Func { if itBodyValue.Kind() != reflect.Func {
panic(fmt.Sprintf("DescribeTable expects a function, got %#v", itBody)) panic(fmt.Sprintf("DescribeTable expects a function, got %#v", itBody))
} }
if pending { global.Suite.PushContainerNode(
ginkgo.PDescribe(description, func() { description,
func() {
for _, entry := range entries { for _, entry := range entries {
entry.generateIt(itBodyValue) entry.generateIt(itBodyValue)
} }
}) },
} else if focused { flag,
ginkgo.FDescribe(description, func() { codelocation.New(2),
for _, entry := range entries { )
entry.generateIt(itBodyValue)
}
})
} else {
ginkgo.Describe(description, func() {
for _, entry := range entries {
entry.generateIt(itBodyValue)
}
})
}
} }

View File

@ -1,49 +1,82 @@
package table package table
import ( import (
"fmt"
"reflect" "reflect"
"github.com/onsi/ginkgo" "github.com/onsi/ginkgo/internal/codelocation"
"github.com/onsi/ginkgo/internal/global"
"github.com/onsi/ginkgo/types"
) )
/* /*
TableEntry represents an entry in a table test. You generally use the `Entry` constructor. TableEntry represents an entry in a table test. You generally use the `Entry` constructor.
*/ */
type TableEntry struct { type TableEntry struct {
Description string Description interface{}
Parameters []interface{} Parameters []interface{}
Pending bool Pending bool
Focused bool Focused bool
codeLocation types.CodeLocation
} }
func (t TableEntry) generateIt(itBody reflect.Value) { func (t TableEntry) generateIt(itBody reflect.Value) {
if t.codeLocation == (types.CodeLocation{}) {
// The user created the TableEntry struct directly instead of having used the (F/P/X)Entry constructors.
// Therefore default to the code location of the surrounding DescribeTable.
t.codeLocation = codelocation.New(5)
}
var description string
descriptionValue := reflect.ValueOf(t.Description)
switch descriptionValue.Kind() {
case reflect.String:
description = descriptionValue.String()
case reflect.Func:
values := castParameters(descriptionValue, t.Parameters)
res := descriptionValue.Call(values)
if len(res) != 1 {
panic(fmt.Sprintf("The describe function should return only a value, returned %d", len(res)))
}
if res[0].Kind() != reflect.String {
panic(fmt.Sprintf("The describe function should return a string, returned %#v", res[0]))
}
description = res[0].String()
default:
panic(fmt.Sprintf("Description can either be a string or a function, got %#v", descriptionValue))
}
if t.Pending { if t.Pending {
ginkgo.PIt(t.Description) global.Suite.PushItNode(description, func() {}, types.FlagTypePending, t.codeLocation, 0)
return return
} }
values := make([]reflect.Value, len(t.Parameters)) values := castParameters(itBody, t.Parameters)
iBodyType := itBody.Type()
for i, param := range t.Parameters {
if param == nil {
inType := iBodyType.In(i)
values[i] = reflect.Zero(inType)
} else {
values[i] = reflect.ValueOf(param)
}
}
body := func() { body := func() {
itBody.Call(values) itBody.Call(values)
} }
if t.Focused { if t.Focused {
ginkgo.FIt(t.Description, body) global.Suite.PushItNode(description, body, types.FlagTypeFocused, t.codeLocation, global.DefaultTimeout)
} else { } else {
ginkgo.It(t.Description, body) global.Suite.PushItNode(description, body, types.FlagTypeNone, t.codeLocation, global.DefaultTimeout)
} }
} }
func castParameters(function reflect.Value, parameters []interface{}) []reflect.Value {
res := make([]reflect.Value, len(parameters))
funcType := function.Type()
for i, param := range parameters {
if param == nil {
inType := funcType.In(i)
res[i] = reflect.Zero(inType)
} else {
res[i] = reflect.ValueOf(param)
}
}
return res
}
/* /*
Entry constructs a TableEntry. Entry constructs a TableEntry.
@ -52,27 +85,51 @@ Subsequent parameters are saved off and sent to the callback passed in to `Descr
Each Entry ends up generating an individual Ginkgo It. Each Entry ends up generating an individual Ginkgo It.
*/ */
func Entry(description string, parameters ...interface{}) TableEntry { func Entry(description interface{}, parameters ...interface{}) TableEntry {
return TableEntry{description, parameters, false, false} return TableEntry{
Description: description,
Parameters: parameters,
Pending: false,
Focused: false,
codeLocation: codelocation.New(1),
}
} }
/* /*
You can focus a particular entry with FEntry. This is equivalent to FIt. You can focus a particular entry with FEntry. This is equivalent to FIt.
*/ */
func FEntry(description string, parameters ...interface{}) TableEntry { func FEntry(description interface{}, parameters ...interface{}) TableEntry {
return TableEntry{description, parameters, false, true} return TableEntry{
Description: description,
Parameters: parameters,
Pending: false,
Focused: true,
codeLocation: codelocation.New(1),
}
} }
/* /*
You can mark a particular entry as pending with PEntry. This is equivalent to PIt. You can mark a particular entry as pending with PEntry. This is equivalent to PIt.
*/ */
func PEntry(description string, parameters ...interface{}) TableEntry { func PEntry(description interface{}, parameters ...interface{}) TableEntry {
return TableEntry{description, parameters, true, false} return TableEntry{
Description: description,
Parameters: parameters,
Pending: true,
Focused: false,
codeLocation: codelocation.New(1),
}
} }
/* /*
You can mark a particular entry as pending with XEntry. This is equivalent to XIt. You can mark a particular entry as pending with XEntry. This is equivalent to XIt.
*/ */
func XEntry(description string, parameters ...interface{}) TableEntry { func XEntry(description interface{}, parameters ...interface{}) TableEntry {
return TableEntry{description, parameters, true, false} return TableEntry{
Description: description,
Parameters: parameters,
Pending: true,
Focused: false,
codeLocation: codelocation.New(1),
}
} }

View File

@ -22,9 +22,8 @@ import (
"github.com/onsi/ginkgo/config" "github.com/onsi/ginkgo/config"
"github.com/onsi/ginkgo/internal/codelocation" "github.com/onsi/ginkgo/internal/codelocation"
"github.com/onsi/ginkgo/internal/failer" "github.com/onsi/ginkgo/internal/global"
"github.com/onsi/ginkgo/internal/remote" "github.com/onsi/ginkgo/internal/remote"
"github.com/onsi/ginkgo/internal/suite"
"github.com/onsi/ginkgo/internal/testingtproxy" "github.com/onsi/ginkgo/internal/testingtproxy"
"github.com/onsi/ginkgo/internal/writer" "github.com/onsi/ginkgo/internal/writer"
"github.com/onsi/ginkgo/reporters" "github.com/onsi/ginkgo/reporters"
@ -46,16 +45,10 @@ To circumvent this, you should call
at the top of the goroutine that caused this panic. at the top of the goroutine that caused this panic.
` `
const defaultTimeout = 1
var globalSuite *suite.Suite
var globalFailer *failer.Failer
func init() { func init() {
config.Flags(flag.CommandLine, "ginkgo", true) config.Flags(flag.CommandLine, "ginkgo", true)
GinkgoWriter = writer.New(os.Stdout) GinkgoWriter = writer.New(os.Stdout)
globalFailer = failer.New()
globalSuite = suite.New(globalFailer)
} }
//GinkgoWriter implements an io.Writer //GinkgoWriter implements an io.Writer
@ -156,7 +149,7 @@ type GinkgoTestDescription struct {
//CurrentGinkgoTestDescripton returns information about the current running test. //CurrentGinkgoTestDescripton returns information about the current running test.
func CurrentGinkgoTestDescription() GinkgoTestDescription { func CurrentGinkgoTestDescription() GinkgoTestDescription {
summary, ok := globalSuite.CurrentRunningSpecSummary() summary, ok := global.Suite.CurrentRunningSpecSummary()
if !ok { if !ok {
return GinkgoTestDescription{} return GinkgoTestDescription{}
} }
@ -223,7 +216,7 @@ func RunSpecsWithCustomReporters(t GinkgoTestingT, description string, specRepor
for i, reporter := range specReporters { for i, reporter := range specReporters {
reporters[i] = reporter reporters[i] = reporter
} }
passed, hasFocusedTests := globalSuite.Run(t, description, reporters, writer, config.GinkgoConfig) passed, hasFocusedTests := global.Suite.Run(t, description, reporters, writer, config.GinkgoConfig)
if passed && hasFocusedTests && strings.TrimSpace(os.Getenv("GINKGO_EDITOR_INTEGRATION")) == "" { if passed && hasFocusedTests && strings.TrimSpace(os.Getenv("GINKGO_EDITOR_INTEGRATION")) == "" {
fmt.Println("PASS | FOCUSED") fmt.Println("PASS | FOCUSED")
os.Exit(types.GINKGO_FOCUS_EXIT_CODE) os.Exit(types.GINKGO_FOCUS_EXIT_CODE)
@ -252,7 +245,7 @@ func Skip(message string, callerSkip ...int) {
skip = callerSkip[0] skip = callerSkip[0]
} }
globalFailer.Skip(message, codelocation.New(skip+1)) global.Failer.Skip(message, codelocation.New(skip+1))
panic(GINKGO_PANIC) panic(GINKGO_PANIC)
} }
@ -263,7 +256,7 @@ func Fail(message string, callerSkip ...int) {
skip = callerSkip[0] skip = callerSkip[0]
} }
globalFailer.Fail(message, codelocation.New(skip+1)) global.Failer.Fail(message, codelocation.New(skip+1))
panic(GINKGO_PANIC) panic(GINKGO_PANIC)
} }
@ -280,7 +273,7 @@ func Fail(message string, callerSkip ...int) {
func GinkgoRecover() { func GinkgoRecover() {
e := recover() e := recover()
if e != nil { if e != nil {
globalFailer.Panic(codelocation.New(1), e) global.Failer.Panic(codelocation.New(1), e)
} }
} }
@ -291,25 +284,25 @@ func GinkgoRecover() {
//equivalent. The difference is purely semantic -- you typically Describe the behavior of an object //equivalent. The difference is purely semantic -- you typically Describe the behavior of an object
//or method and, within that Describe, outline a number of Contexts and Whens. //or method and, within that Describe, outline a number of Contexts and Whens.
func Describe(text string, body func()) bool { func Describe(text string, body func()) bool {
globalSuite.PushContainerNode(text, body, types.FlagTypeNone, codelocation.New(1)) global.Suite.PushContainerNode(text, body, types.FlagTypeNone, codelocation.New(1))
return true return true
} }
//You can focus the tests within a describe block using FDescribe //You can focus the tests within a describe block using FDescribe
func FDescribe(text string, body func()) bool { func FDescribe(text string, body func()) bool {
globalSuite.PushContainerNode(text, body, types.FlagTypeFocused, codelocation.New(1)) global.Suite.PushContainerNode(text, body, types.FlagTypeFocused, codelocation.New(1))
return true return true
} }
//You can mark the tests within a describe block as pending using PDescribe //You can mark the tests within a describe block as pending using PDescribe
func PDescribe(text string, body func()) bool { func PDescribe(text string, body func()) bool {
globalSuite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1)) global.Suite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1))
return true return true
} }
//You can mark the tests within a describe block as pending using XDescribe //You can mark the tests within a describe block as pending using XDescribe
func XDescribe(text string, body func()) bool { func XDescribe(text string, body func()) bool {
globalSuite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1)) global.Suite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1))
return true return true
} }
@ -320,25 +313,25 @@ func XDescribe(text string, body func()) bool {
//equivalent. The difference is purely semantic -- you typical Describe the behavior of an object //equivalent. The difference is purely semantic -- you typical Describe the behavior of an object
//or method and, within that Describe, outline a number of Contexts and Whens. //or method and, within that Describe, outline a number of Contexts and Whens.
func Context(text string, body func()) bool { func Context(text string, body func()) bool {
globalSuite.PushContainerNode(text, body, types.FlagTypeNone, codelocation.New(1)) global.Suite.PushContainerNode(text, body, types.FlagTypeNone, codelocation.New(1))
return true return true
} }
//You can focus the tests within a describe block using FContext //You can focus the tests within a describe block using FContext
func FContext(text string, body func()) bool { func FContext(text string, body func()) bool {
globalSuite.PushContainerNode(text, body, types.FlagTypeFocused, codelocation.New(1)) global.Suite.PushContainerNode(text, body, types.FlagTypeFocused, codelocation.New(1))
return true return true
} }
//You can mark the tests within a describe block as pending using PContext //You can mark the tests within a describe block as pending using PContext
func PContext(text string, body func()) bool { func PContext(text string, body func()) bool {
globalSuite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1)) global.Suite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1))
return true return true
} }
//You can mark the tests within a describe block as pending using XContext //You can mark the tests within a describe block as pending using XContext
func XContext(text string, body func()) bool { func XContext(text string, body func()) bool {
globalSuite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1)) global.Suite.PushContainerNode(text, body, types.FlagTypePending, codelocation.New(1))
return true return true
} }
@ -349,25 +342,25 @@ func XContext(text string, body func()) bool {
//equivalent. The difference is purely semantic -- you typical Describe the behavior of an object //equivalent. The difference is purely semantic -- you typical Describe the behavior of an object
//or method and, within that Describe, outline a number of Contexts and Whens. //or method and, within that Describe, outline a number of Contexts and Whens.
func When(text string, body func()) bool { func When(text string, body func()) bool {
globalSuite.PushContainerNode("when "+text, body, types.FlagTypeNone, codelocation.New(1)) global.Suite.PushContainerNode("when "+text, body, types.FlagTypeNone, codelocation.New(1))
return true return true
} }
//You can focus the tests within a describe block using FWhen //You can focus the tests within a describe block using FWhen
func FWhen(text string, body func()) bool { func FWhen(text string, body func()) bool {
globalSuite.PushContainerNode("when "+text, body, types.FlagTypeFocused, codelocation.New(1)) global.Suite.PushContainerNode("when "+text, body, types.FlagTypeFocused, codelocation.New(1))
return true return true
} }
//You can mark the tests within a describe block as pending using PWhen //You can mark the tests within a describe block as pending using PWhen
func PWhen(text string, body func()) bool { func PWhen(text string, body func()) bool {
globalSuite.PushContainerNode("when "+text, body, types.FlagTypePending, codelocation.New(1)) global.Suite.PushContainerNode("when "+text, body, types.FlagTypePending, codelocation.New(1))
return true return true
} }
//You can mark the tests within a describe block as pending using XWhen //You can mark the tests within a describe block as pending using XWhen
func XWhen(text string, body func()) bool { func XWhen(text string, body func()) bool {
globalSuite.PushContainerNode("when "+text, body, types.FlagTypePending, codelocation.New(1)) global.Suite.PushContainerNode("when "+text, body, types.FlagTypePending, codelocation.New(1))
return true return true
} }
@ -377,25 +370,25 @@ func XWhen(text string, body func()) bool {
//Ginkgo will normally run It blocks synchronously. To perform asynchronous tests, pass a //Ginkgo will normally run It blocks synchronously. To perform asynchronous tests, pass a
//function that accepts a Done channel. When you do this, you can also provide an optional timeout. //function that accepts a Done channel. When you do this, you can also provide an optional timeout.
func It(text string, body interface{}, timeout ...float64) bool { func It(text string, body interface{}, timeout ...float64) bool {
globalSuite.PushItNode(text, body, types.FlagTypeNone, codelocation.New(1), parseTimeout(timeout...)) global.Suite.PushItNode(text, body, types.FlagTypeNone, codelocation.New(1), parseTimeout(timeout...))
return true return true
} }
//You can focus individual Its using FIt //You can focus individual Its using FIt
func FIt(text string, body interface{}, timeout ...float64) bool { func FIt(text string, body interface{}, timeout ...float64) bool {
globalSuite.PushItNode(text, body, types.FlagTypeFocused, codelocation.New(1), parseTimeout(timeout...)) global.Suite.PushItNode(text, body, types.FlagTypeFocused, codelocation.New(1), parseTimeout(timeout...))
return true return true
} }
//You can mark Its as pending using PIt //You can mark Its as pending using PIt
func PIt(text string, _ ...interface{}) bool { func PIt(text string, _ ...interface{}) bool {
globalSuite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0) global.Suite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0)
return true return true
} }
//You can mark Its as pending using XIt //You can mark Its as pending using XIt
func XIt(text string, _ ...interface{}) bool { func XIt(text string, _ ...interface{}) bool {
globalSuite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0) global.Suite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0)
return true return true
} }
@ -403,25 +396,25 @@ func XIt(text string, _ ...interface{}) bool {
//which "It" does not fit into a natural sentence flow. All the same protocols apply for Specify blocks //which "It" does not fit into a natural sentence flow. All the same protocols apply for Specify blocks
//which apply to It blocks. //which apply to It blocks.
func Specify(text string, body interface{}, timeout ...float64) bool { func Specify(text string, body interface{}, timeout ...float64) bool {
globalSuite.PushItNode(text, body, types.FlagTypeNone, codelocation.New(1), parseTimeout(timeout...)) global.Suite.PushItNode(text, body, types.FlagTypeNone, codelocation.New(1), parseTimeout(timeout...))
return true return true
} }
//You can focus individual Specifys using FSpecify //You can focus individual Specifys using FSpecify
func FSpecify(text string, body interface{}, timeout ...float64) bool { func FSpecify(text string, body interface{}, timeout ...float64) bool {
globalSuite.PushItNode(text, body, types.FlagTypeFocused, codelocation.New(1), parseTimeout(timeout...)) global.Suite.PushItNode(text, body, types.FlagTypeFocused, codelocation.New(1), parseTimeout(timeout...))
return true return true
} }
//You can mark Specifys as pending using PSpecify //You can mark Specifys as pending using PSpecify
func PSpecify(text string, is ...interface{}) bool { func PSpecify(text string, is ...interface{}) bool {
globalSuite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0) global.Suite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0)
return true return true
} }
//You can mark Specifys as pending using XSpecify //You can mark Specifys as pending using XSpecify
func XSpecify(text string, is ...interface{}) bool { func XSpecify(text string, is ...interface{}) bool {
globalSuite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0) global.Suite.PushItNode(text, func() {}, types.FlagTypePending, codelocation.New(1), 0)
return true return true
} }
@ -452,25 +445,25 @@ func By(text string, callbacks ...func()) {
//The body function must have the signature: //The body function must have the signature:
// func(b Benchmarker) // func(b Benchmarker)
func Measure(text string, body interface{}, samples int) bool { func Measure(text string, body interface{}, samples int) bool {
globalSuite.PushMeasureNode(text, body, types.FlagTypeNone, codelocation.New(1), samples) global.Suite.PushMeasureNode(text, body, types.FlagTypeNone, codelocation.New(1), samples)
return true return true
} }
//You can focus individual Measures using FMeasure //You can focus individual Measures using FMeasure
func FMeasure(text string, body interface{}, samples int) bool { func FMeasure(text string, body interface{}, samples int) bool {
globalSuite.PushMeasureNode(text, body, types.FlagTypeFocused, codelocation.New(1), samples) global.Suite.PushMeasureNode(text, body, types.FlagTypeFocused, codelocation.New(1), samples)
return true return true
} }
//You can mark Measurements as pending using PMeasure //You can mark Measurements as pending using PMeasure
func PMeasure(text string, _ ...interface{}) bool { func PMeasure(text string, _ ...interface{}) bool {
globalSuite.PushMeasureNode(text, func(b Benchmarker) {}, types.FlagTypePending, codelocation.New(1), 0) global.Suite.PushMeasureNode(text, func(b Benchmarker) {}, types.FlagTypePending, codelocation.New(1), 0)
return true return true
} }
//You can mark Measurements as pending using XMeasure //You can mark Measurements as pending using XMeasure
func XMeasure(text string, _ ...interface{}) bool { func XMeasure(text string, _ ...interface{}) bool {
globalSuite.PushMeasureNode(text, func(b Benchmarker) {}, types.FlagTypePending, codelocation.New(1), 0) global.Suite.PushMeasureNode(text, func(b Benchmarker) {}, types.FlagTypePending, codelocation.New(1), 0)
return true return true
} }
@ -481,7 +474,7 @@ func XMeasure(text string, _ ...interface{}) bool {
// //
//You may only register *one* BeforeSuite handler per test suite. You typically do so in your bootstrap file at the top level. //You may only register *one* BeforeSuite handler per test suite. You typically do so in your bootstrap file at the top level.
func BeforeSuite(body interface{}, timeout ...float64) bool { func BeforeSuite(body interface{}, timeout ...float64) bool {
globalSuite.SetBeforeSuiteNode(body, codelocation.New(1), parseTimeout(timeout...)) global.Suite.SetBeforeSuiteNode(body, codelocation.New(1), parseTimeout(timeout...))
return true return true
} }
@ -494,7 +487,7 @@ func BeforeSuite(body interface{}, timeout ...float64) bool {
// //
//You may only register *one* AfterSuite handler per test suite. You typically do so in your bootstrap file at the top level. //You may only register *one* AfterSuite handler per test suite. You typically do so in your bootstrap file at the top level.
func AfterSuite(body interface{}, timeout ...float64) bool { func AfterSuite(body interface{}, timeout ...float64) bool {
globalSuite.SetAfterSuiteNode(body, codelocation.New(1), parseTimeout(timeout...)) global.Suite.SetAfterSuiteNode(body, codelocation.New(1), parseTimeout(timeout...))
return true return true
} }
@ -539,7 +532,7 @@ func AfterSuite(body interface{}, timeout ...float64) bool {
// Ω(err).ShouldNot(HaveOccurred()) // Ω(err).ShouldNot(HaveOccurred())
// }) // })
func SynchronizedBeforeSuite(node1Body interface{}, allNodesBody interface{}, timeout ...float64) bool { func SynchronizedBeforeSuite(node1Body interface{}, allNodesBody interface{}, timeout ...float64) bool {
globalSuite.SetSynchronizedBeforeSuiteNode( global.Suite.SetSynchronizedBeforeSuiteNode(
node1Body, node1Body,
allNodesBody, allNodesBody,
codelocation.New(1), codelocation.New(1),
@ -566,7 +559,7 @@ func SynchronizedBeforeSuite(node1Body interface{}, allNodesBody interface{}, ti
// dbRunner.Stop() // dbRunner.Stop()
// }) // })
func SynchronizedAfterSuite(allNodesBody interface{}, node1Body interface{}, timeout ...float64) bool { func SynchronizedAfterSuite(allNodesBody interface{}, node1Body interface{}, timeout ...float64) bool {
globalSuite.SetSynchronizedAfterSuiteNode( global.Suite.SetSynchronizedAfterSuiteNode(
allNodesBody, allNodesBody,
node1Body, node1Body,
codelocation.New(1), codelocation.New(1),
@ -581,7 +574,7 @@ func SynchronizedAfterSuite(allNodesBody interface{}, node1Body interface{}, tim
//Like It blocks, BeforeEach blocks can be made asynchronous by providing a body function that accepts //Like It blocks, BeforeEach blocks can be made asynchronous by providing a body function that accepts
//a Done channel //a Done channel
func BeforeEach(body interface{}, timeout ...float64) bool { func BeforeEach(body interface{}, timeout ...float64) bool {
globalSuite.PushBeforeEachNode(body, codelocation.New(1), parseTimeout(timeout...)) global.Suite.PushBeforeEachNode(body, codelocation.New(1), parseTimeout(timeout...))
return true return true
} }
@ -591,7 +584,7 @@ func BeforeEach(body interface{}, timeout ...float64) bool {
//Like It blocks, BeforeEach blocks can be made asynchronous by providing a body function that accepts //Like It blocks, BeforeEach blocks can be made asynchronous by providing a body function that accepts
//a Done channel //a Done channel
func JustBeforeEach(body interface{}, timeout ...float64) bool { func JustBeforeEach(body interface{}, timeout ...float64) bool {
globalSuite.PushJustBeforeEachNode(body, codelocation.New(1), parseTimeout(timeout...)) global.Suite.PushJustBeforeEachNode(body, codelocation.New(1), parseTimeout(timeout...))
return true return true
} }
@ -601,7 +594,7 @@ func JustBeforeEach(body interface{}, timeout ...float64) bool {
//Like It blocks, JustAfterEach blocks can be made asynchronous by providing a body function that accepts //Like It blocks, JustAfterEach blocks can be made asynchronous by providing a body function that accepts
//a Done channel //a Done channel
func JustAfterEach(body interface{}, timeout ...float64) bool { func JustAfterEach(body interface{}, timeout ...float64) bool {
globalSuite.PushJustAfterEachNode(body, codelocation.New(1), parseTimeout(timeout...)) global.Suite.PushJustAfterEachNode(body, codelocation.New(1), parseTimeout(timeout...))
return true return true
} }
@ -611,13 +604,13 @@ func JustAfterEach(body interface{}, timeout ...float64) bool {
//Like It blocks, AfterEach blocks can be made asynchronous by providing a body function that accepts //Like It blocks, AfterEach blocks can be made asynchronous by providing a body function that accepts
//a Done channel //a Done channel
func AfterEach(body interface{}, timeout ...float64) bool { func AfterEach(body interface{}, timeout ...float64) bool {
globalSuite.PushAfterEachNode(body, codelocation.New(1), parseTimeout(timeout...)) global.Suite.PushAfterEachNode(body, codelocation.New(1), parseTimeout(timeout...))
return true return true
} }
func parseTimeout(timeout ...float64) time.Duration { func parseTimeout(timeout ...float64) time.Duration {
if len(timeout) == 0 { if len(timeout) == 0 {
return time.Duration(defaultTimeout * int64(time.Second)) return global.DefaultTimeout
} else { } else {
return time.Duration(timeout[0] * float64(time.Second)) return time.Duration(timeout[0] * float64(time.Second))
} }

View File

@ -1,9 +1,12 @@
module github.com/onsi/ginkgo module github.com/onsi/ginkgo
require ( require (
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/nxadm/tail v1.4.4 github.com/nxadm/tail v1.4.4
github.com/onsi/gomega v1.7.1 github.com/onsi/gomega v1.10.1
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e github.com/sclevine/agouti v3.0.0+incompatible // indirect
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299
golang.org/x/text v0.3.2 // indirect
) )
go 1.12 go 1.13

40
vendor/github.com/onsi/ginkgo/go.sum generated vendored
View File

@ -1,24 +1,62 @@
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ= github.com/onsi/gomega v1.7.1 h1:K0jcRCwNQM3vFGh1ppMtDh/+7ApJrjldlX8fA0jDTLQ=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/sclevine/agouti v3.0.0+incompatible h1:8IBJS6PWz3uTlMP3YBIR5f+KAldcGuOeFkFbUWfBgK4=
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7 h1:AeiKBIuRw3UomYXSbLy0Mc2dDLfdtbT/IVn4keq83P0=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e h1:N7DeIrjYszNmSW409R3frPPwglRwMkXSBzwVbkOjLLA=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299 h1:DYfZAGf2WMFjMxbgTjaC+2HC7NkNAQs+6Q8b9WEB/F4=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyzM=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
@ -27,3 +65,5 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

18
vendor/github.com/onsi/ginkgo/internal/global/init.go generated vendored Normal file
View File

@ -0,0 +1,18 @@
package global
import (
"time"
"github.com/onsi/ginkgo/internal/failer"
"github.com/onsi/ginkgo/internal/suite"
)
const DefaultTimeout = time.Duration(1 * time.Second)
var Suite *suite.Suite
var Failer *failer.Failer
func init() {
Failer = failer.New()
Suite = suite.New(Failer)
}

View File

@ -0,0 +1,11 @@
// +build darwin
package remote
import (
"golang.org/x/sys/unix"
)
func interceptorDupx(oldfd int, newfd int) {
unix.Dup2(oldfd, newfd)
}

View File

@ -0,0 +1,11 @@
// +build dragonfly
package remote
import (
"golang.org/x/sys/unix"
)
func interceptorDupx(oldfd int, newfd int) {
unix.Dup2(oldfd, newfd)
}

View File

@ -0,0 +1,11 @@
// +build freebsd
package remote
import (
"golang.org/x/sys/unix"
)
func interceptorDupx(oldfd int, newfd int) {
unix.Dup2(oldfd, newfd)
}

View File

@ -0,0 +1,12 @@
// +build linux
// +build !mips64le
package remote
import (
"golang.org/x/sys/unix"
)
func interceptorDupx(oldfd int, newfd int) {
unix.Dup2(oldfd, newfd)
}

View File

@ -0,0 +1,12 @@
// +build linux
// +build mips64le
package remote
import (
"golang.org/x/sys/unix"
)
func interceptorDupx(oldfd int, newfd int) {
unix.Dup3(oldfd, newfd, 0)
}

View File

@ -0,0 +1,11 @@
// +build netbsd
package remote
import (
"golang.org/x/sys/unix"
)
func interceptorDupx(oldfd int, newfd int) {
unix.Dup2(oldfd, newfd)
}

View File

@ -0,0 +1,11 @@
// +build openbsd
package remote
import (
"golang.org/x/sys/unix"
)
func interceptorDupx(oldfd int, newfd int) {
unix.Dup2(oldfd, newfd)
}

View File

@ -0,0 +1,11 @@
// +build solaris
package remote
import (
"golang.org/x/sys/unix"
)
func interceptorDupx(oldfd int, newfd int) {
unix.Dup2(oldfd, newfd)
}

View File

@ -8,7 +8,6 @@ import (
"os" "os"
"github.com/nxadm/tail" "github.com/nxadm/tail"
"golang.org/x/sys/unix"
) )
func NewOutputInterceptor() OutputInterceptor { func NewOutputInterceptor() OutputInterceptor {
@ -36,10 +35,8 @@ func (interceptor *outputInterceptor) StartInterceptingOutput() error {
return err return err
} }
// This might call Dup3 if the dup2 syscall is not available, e.g. on interceptorDupx(int(interceptor.redirectFile.Fd()), 1)
// linux/arm64 or linux/riscv64 interceptorDupx(int(interceptor.redirectFile.Fd()), 2)
unix.Dup2(int(interceptor.redirectFile.Fd()), 1)
unix.Dup2(int(interceptor.redirectFile.Fd()), 2)
if interceptor.streamTarget != nil { if interceptor.streamTarget != nil {
interceptor.tailer, _ = tail.TailFile(interceptor.redirectFile.Name(), tail.Config{Follow: true}) interceptor.tailer, _ = tail.TailFile(interceptor.redirectFile.Name(), tail.Config{Follow: true})

12
vendor/modules.txt vendored
View File

@ -28,14 +28,17 @@ github.com/alexflint/go-filemutex
# github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44 # github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44
## explicit ## explicit
github.com/buger/jsonparser github.com/buger/jsonparser
# github.com/containernetworking/cni v0.8.1 # github.com/containernetworking/cni v0.8.1-0.20201216164644-62e54113f44a
## explicit ## explicit
github.com/containernetworking/cni/libcni github.com/containernetworking/cni/libcni
github.com/containernetworking/cni/pkg/invoke github.com/containernetworking/cni/pkg/invoke
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 github.com/containernetworking/cni/pkg/types/020
github.com/containernetworking/cni/pkg/types/current github.com/containernetworking/cni/pkg/types/040
github.com/containernetworking/cni/pkg/types/100
github.com/containernetworking/cni/pkg/types/create
github.com/containernetworking/cni/pkg/types/internal
github.com/containernetworking/cni/pkg/utils github.com/containernetworking/cni/pkg/utils
github.com/containernetworking/cni/pkg/version github.com/containernetworking/cni/pkg/version
# github.com/coreos/go-iptables v0.5.0 # github.com/coreos/go-iptables v0.5.0
@ -57,7 +60,7 @@ github.com/d2g/dhcp4server/leasepool
github.com/d2g/dhcp4server/leasepool/memorypool github.com/d2g/dhcp4server/leasepool/memorypool
# github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4 # github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4
## explicit ## explicit
# github.com/fsnotify/fsnotify v1.4.7 # github.com/fsnotify/fsnotify v1.4.9
github.com/fsnotify/fsnotify github.com/fsnotify/fsnotify
# github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c # github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c
## explicit ## explicit
@ -74,7 +77,7 @@ github.com/nxadm/tail/ratelimiter
github.com/nxadm/tail/util github.com/nxadm/tail/util
github.com/nxadm/tail/watch github.com/nxadm/tail/watch
github.com/nxadm/tail/winfile github.com/nxadm/tail/winfile
# github.com/onsi/ginkgo v1.12.1 # github.com/onsi/ginkgo v1.13.0
## explicit ## explicit
github.com/onsi/ginkgo github.com/onsi/ginkgo
github.com/onsi/ginkgo/config github.com/onsi/ginkgo/config
@ -82,6 +85,7 @@ github.com/onsi/ginkgo/extensions/table
github.com/onsi/ginkgo/internal/codelocation github.com/onsi/ginkgo/internal/codelocation
github.com/onsi/ginkgo/internal/containernode github.com/onsi/ginkgo/internal/containernode
github.com/onsi/ginkgo/internal/failer github.com/onsi/ginkgo/internal/failer
github.com/onsi/ginkgo/internal/global
github.com/onsi/ginkgo/internal/leafnodes github.com/onsi/ginkgo/internal/leafnodes
github.com/onsi/ginkgo/internal/remote github.com/onsi/ginkgo/internal/remote
github.com/onsi/ginkgo/internal/spec github.com/onsi/ginkgo/internal/spec