spec/plugins: return interface details and multiple IP addresses to runtime
Updates the spec and plugins to return an array of interfaces and IP details to the runtime including: - interface names and MAC addresses configured by the plugin - whether the interfaces are sandboxed (container/VM) or host (bridge, veth, etc) - multiple IP addresses configured by IPAM and which interface they have been assigned to Returning interface details is useful for runtimes, as well as allowing more flexible chaining of CNI plugins themselves. For example, some meta plugins may need to know the host-side interface to be able to apply firewall or traffic shaping rules to the container.
This commit is contained in:
133
types/020/types.go
Normal file
133
types/020/types.go
Normal file
@ -0,0 +1,133 @@
|
||||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package types020
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
)
|
||||
|
||||
const implementedSpecVersion string = "0.2.0"
|
||||
|
||||
var SupportedVersions = []string{"", "0.1.0", implementedSpecVersion}
|
||||
|
||||
// Compatibility types for CNI version 0.1.0 and 0.2.0
|
||||
|
||||
func NewResult(data []byte) (types.Result, error) {
|
||||
result := &Result{}
|
||||
if err := json.Unmarshal(data, result); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func GetResult(r types.Result) (*Result, error) {
|
||||
// We expect version 0.1.0/0.2.0 results
|
||||
result020, err := r.GetAsVersion(implementedSpecVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, ok := result020.(*Result)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to convert result")
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Result is what gets returned from the plugin (via stdout) to the caller
|
||||
type Result struct {
|
||||
IP4 *IPConfig `json:"ip4,omitempty"`
|
||||
IP6 *IPConfig `json:"ip6,omitempty"`
|
||||
DNS types.DNS `json:"dns,omitempty"`
|
||||
}
|
||||
|
||||
func (r *Result) Version() string {
|
||||
return implementedSpecVersion
|
||||
}
|
||||
|
||||
func (r *Result) GetAsVersion(version string) (types.Result, error) {
|
||||
for _, supportedVersion := range SupportedVersions {
|
||||
if version == supportedVersion {
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("cannot convert version %q to %s", SupportedVersions, version)
|
||||
}
|
||||
|
||||
func (r *Result) Print() error {
|
||||
data, err := json.MarshalIndent(r, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = os.Stdout.Write(data)
|
||||
return err
|
||||
}
|
||||
|
||||
// String returns a formatted string in the form of "[IP4: $1,][ IP6: $2,] DNS: $3" where
|
||||
// $1 represents the receiver's IPv4, $2 represents the receiver's IPv6 and $3 the
|
||||
// receiver's DNS. If $1 or $2 are nil, they won't be present in the returned string.
|
||||
func (r *Result) String() string {
|
||||
var str string
|
||||
if r.IP4 != nil {
|
||||
str = fmt.Sprintf("IP4:%+v, ", *r.IP4)
|
||||
}
|
||||
if r.IP6 != nil {
|
||||
str += fmt.Sprintf("IP6:%+v, ", *r.IP6)
|
||||
}
|
||||
return fmt.Sprintf("%sDNS:%+v", str, r.DNS)
|
||||
}
|
||||
|
||||
// IPConfig contains values necessary to configure an interface
|
||||
type IPConfig struct {
|
||||
IP net.IPNet
|
||||
Gateway net.IP
|
||||
Routes []types.Route
|
||||
}
|
||||
|
||||
// net.IPNet is not JSON (un)marshallable so this duality is needed
|
||||
// for our custom IPNet type
|
||||
|
||||
// JSON (un)marshallable types
|
||||
type ipConfig struct {
|
||||
IP types.IPNet `json:"ip"`
|
||||
Gateway net.IP `json:"gateway,omitempty"`
|
||||
Routes []types.Route `json:"routes,omitempty"`
|
||||
}
|
||||
|
||||
func (c *IPConfig) MarshalJSON() ([]byte, error) {
|
||||
ipc := ipConfig{
|
||||
IP: types.IPNet(c.IP),
|
||||
Gateway: c.Gateway,
|
||||
Routes: c.Routes,
|
||||
}
|
||||
|
||||
return json.Marshal(ipc)
|
||||
}
|
||||
|
||||
func (c *IPConfig) UnmarshalJSON(data []byte) error {
|
||||
ipc := ipConfig{}
|
||||
if err := json.Unmarshal(data, &ipc); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.IP = net.IPNet(ipc.IP)
|
||||
c.Gateway = ipc.Gateway
|
||||
c.Routes = ipc.Routes
|
||||
return nil
|
||||
}
|
27
types/020/types_suite_test.go
Normal file
27
types/020/types_suite_test.go
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package types020_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTypes010(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "0.1.0/0.2.0 Types Suite")
|
||||
}
|
128
types/020/types_test.go
Normal file
128
types/020/types_test.go
Normal file
@ -0,0 +1,128 @@
|
||||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package types020_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/types/020"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Ensures compatibility with the 0.1.0/0.2.0 spec", func() {
|
||||
It("correctly encodes a 0.1.0/0.2.0 Result", func() {
|
||||
ipv4, err := types.ParseCIDR("1.2.3.30/24")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(ipv4).NotTo(BeNil())
|
||||
|
||||
routegwv4, routev4, err := net.ParseCIDR("15.5.6.8/24")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(routev4).NotTo(BeNil())
|
||||
Expect(routegwv4).NotTo(BeNil())
|
||||
|
||||
ipv6, err := types.ParseCIDR("abcd:1234:ffff::cdde/64")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(ipv6).NotTo(BeNil())
|
||||
|
||||
routegwv6, routev6, err := net.ParseCIDR("1111:dddd::aaaa/80")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(routev6).NotTo(BeNil())
|
||||
Expect(routegwv6).NotTo(BeNil())
|
||||
|
||||
// Set every field of the struct to ensure source compatibility
|
||||
res := types020.Result{
|
||||
IP4: &types020.IPConfig{
|
||||
IP: *ipv4,
|
||||
Gateway: net.ParseIP("1.2.3.1"),
|
||||
Routes: []types.Route{
|
||||
{Dst: *routev4, GW: routegwv4},
|
||||
},
|
||||
},
|
||||
IP6: &types020.IPConfig{
|
||||
IP: *ipv6,
|
||||
Gateway: net.ParseIP("abcd:1234:ffff::1"),
|
||||
Routes: []types.Route{
|
||||
{Dst: *routev6, GW: routegwv6},
|
||||
},
|
||||
},
|
||||
DNS: types.DNS{
|
||||
Nameservers: []string{"1.2.3.4", "1::cafe"},
|
||||
Domain: "acompany.com",
|
||||
Search: []string{"somedomain.com", "otherdomain.net"},
|
||||
Options: []string{"foo", "bar"},
|
||||
},
|
||||
}
|
||||
|
||||
Expect(res.String()).To(Equal("IP4:{IP:{IP:1.2.3.30 Mask:ffffff00} Gateway:1.2.3.1 Routes:[{Dst:{IP:15.5.6.0 Mask:ffffff00} GW:15.5.6.8}]}, IP6:{IP:{IP:abcd:1234:ffff::cdde Mask:ffffffffffffffff0000000000000000} Gateway:abcd:1234:ffff::1 Routes:[{Dst:{IP:1111:dddd:: Mask:ffffffffffffffffffff000000000000} GW:1111:dddd::aaaa}]}, DNS:{Nameservers:[1.2.3.4 1::cafe] Domain:acompany.com Search:[somedomain.com otherdomain.net] Options:[foo bar]}"))
|
||||
|
||||
// Redirect stdout to capture JSON result
|
||||
oldStdout := os.Stdout
|
||||
r, w, err := os.Pipe()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
os.Stdout = w
|
||||
err = res.Print()
|
||||
w.Close()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// parse the result
|
||||
out, err := ioutil.ReadAll(r)
|
||||
os.Stdout = oldStdout
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(string(out)).To(Equal(`{
|
||||
"ip4": {
|
||||
"ip": "1.2.3.30/24",
|
||||
"gateway": "1.2.3.1",
|
||||
"routes": [
|
||||
{
|
||||
"dst": "15.5.6.0/24",
|
||||
"gw": "15.5.6.8"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ip6": {
|
||||
"ip": "abcd:1234:ffff::cdde/64",
|
||||
"gateway": "abcd:1234:ffff::1",
|
||||
"routes": [
|
||||
{
|
||||
"dst": "1111:dddd::/80",
|
||||
"gw": "1111:dddd::aaaa"
|
||||
}
|
||||
]
|
||||
},
|
||||
"dns": {
|
||||
"nameservers": [
|
||||
"1.2.3.4",
|
||||
"1::cafe"
|
||||
],
|
||||
"domain": "acompany.com",
|
||||
"search": [
|
||||
"somedomain.com",
|
||||
"otherdomain.net"
|
||||
],
|
||||
"options": [
|
||||
"foo",
|
||||
"bar"
|
||||
]
|
||||
}
|
||||
}`))
|
||||
})
|
||||
})
|
@ -21,11 +21,12 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/types/020"
|
||||
)
|
||||
|
||||
const implementedSpecVersion string = "0.2.0"
|
||||
const implementedSpecVersion string = "0.3.0"
|
||||
|
||||
var SupportedVersions = []string{"", "0.1.0", implementedSpecVersion}
|
||||
var SupportedVersions = []string{implementedSpecVersion}
|
||||
|
||||
func NewResult(data []byte) (types.Result, error) {
|
||||
result := &Result{}
|
||||
@ -36,11 +37,11 @@ func NewResult(data []byte) (types.Result, error) {
|
||||
}
|
||||
|
||||
func GetResult(r types.Result) (*Result, error) {
|
||||
newResult, err := r.GetAsVersion(implementedSpecVersion)
|
||||
resultCurrent, err := r.GetAsVersion(implementedSpecVersion)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
result, ok := newResult.(*Result)
|
||||
result, ok := resultCurrent.(*Result)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to convert result")
|
||||
}
|
||||
@ -51,10 +52,67 @@ var resultConverters = []struct {
|
||||
versions []string
|
||||
convert func(types.Result) (*Result, error)
|
||||
}{
|
||||
{SupportedVersions, convertFrom020},
|
||||
{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{
|
||||
DNS: oldResult.DNS,
|
||||
Routes: []*types.Route{},
|
||||
}
|
||||
|
||||
if oldResult.IP4 != nil {
|
||||
newResult.IPs = append(newResult.IPs, &IPConfig{
|
||||
Version: "4",
|
||||
Interface: -1,
|
||||
Address: oldResult.IP4.IP,
|
||||
Gateway: oldResult.IP4.Gateway,
|
||||
})
|
||||
for _, route := range oldResult.IP4.Routes {
|
||||
gw := route.GW
|
||||
if gw == nil {
|
||||
gw = oldResult.IP4.Gateway
|
||||
}
|
||||
newResult.Routes = append(newResult.Routes, &types.Route{
|
||||
Dst: route.Dst,
|
||||
GW: gw,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if oldResult.IP6 != nil {
|
||||
newResult.IPs = append(newResult.IPs, &IPConfig{
|
||||
Version: "6",
|
||||
Interface: -1,
|
||||
Address: oldResult.IP6.IP,
|
||||
Gateway: oldResult.IP6.Gateway,
|
||||
})
|
||||
for _, route := range oldResult.IP6.Routes {
|
||||
gw := route.GW
|
||||
if gw == nil {
|
||||
gw = oldResult.IP6.Gateway
|
||||
}
|
||||
newResult.Routes = append(newResult.Routes, &types.Route{
|
||||
Dst: route.Dst,
|
||||
GW: gw,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if len(newResult.IPs) == 0 {
|
||||
return nil, fmt.Errorf("cannot convert: no valid IP addresses")
|
||||
}
|
||||
|
||||
return newResult, nil
|
||||
}
|
||||
|
||||
func convertFrom030(result types.Result) (*Result, error) {
|
||||
newResult, ok := result.(*Result)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to convert result")
|
||||
@ -76,9 +134,58 @@ func NewResultFromResult(result types.Result) (*Result, error) {
|
||||
|
||||
// Result is what gets returned from the plugin (via stdout) to the caller
|
||||
type Result struct {
|
||||
IP4 *IPConfig `json:"ip4,omitempty"`
|
||||
IP6 *IPConfig `json:"ip6,omitempty"`
|
||||
DNS types.DNS `json:"dns,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{
|
||||
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 {
|
||||
@ -86,12 +193,13 @@ func (r *Result) Version() string {
|
||||
}
|
||||
|
||||
func (r *Result) GetAsVersion(version string) (types.Result, error) {
|
||||
for _, supportedVersion := range SupportedVersions {
|
||||
if version == supportedVersion {
|
||||
return r, nil
|
||||
}
|
||||
switch version {
|
||||
case implementedSpecVersion:
|
||||
return r, nil
|
||||
case types020.SupportedVersions[0], types020.SupportedVersions[1], types020.SupportedVersions[2]:
|
||||
return r.convertTo020()
|
||||
}
|
||||
return nil, fmt.Errorf("cannot convert version %q to %s", SupportedVersions, version)
|
||||
return nil, fmt.Errorf("cannot convert version 0.3.0 to %q", version)
|
||||
}
|
||||
|
||||
func (r *Result) Print() error {
|
||||
@ -103,42 +211,67 @@ func (r *Result) Print() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// String returns a formatted string in the form of "[IP4: $1,][ IP6: $2,] DNS: $3" where
|
||||
// $1 represents the receiver's IPv4, $2 represents the receiver's IPv6 and $3 the
|
||||
// String returns a formatted string in the form of "[Interfaces: $1,][ IP: $2,] DNS: $3" where
|
||||
// $1 represents the receiver's Interfaces, $2 represents the receiver's IP addresses and $3 the
|
||||
// receiver's DNS. If $1 or $2 are nil, they won't be present in the returned string.
|
||||
func (r *Result) String() string {
|
||||
var str string
|
||||
if r.IP4 != nil {
|
||||
str = fmt.Sprintf("IP4:%+v, ", *r.IP4)
|
||||
if len(r.Interfaces) > 0 {
|
||||
str += fmt.Sprintf("Interfaces:%+v, ", r.Interfaces)
|
||||
}
|
||||
if r.IP6 != nil {
|
||||
str += fmt.Sprintf("IP6:%+v, ", *r.IP6)
|
||||
if len(r.IPs) > 0 {
|
||||
str += fmt.Sprintf("IP:%+v, ", r.IPs)
|
||||
}
|
||||
if len(r.Routes) > 0 {
|
||||
str += fmt.Sprintf("Routes:%+v, ", r.Routes)
|
||||
}
|
||||
return fmt.Sprintf("%sDNS:%+v", str, r.DNS)
|
||||
}
|
||||
|
||||
// IPConfig contains values necessary to configure an interface
|
||||
type IPConfig struct {
|
||||
IP net.IPNet
|
||||
Gateway net.IP
|
||||
Routes []types.Route
|
||||
// Convert this old version result to the current CNI version result
|
||||
func (r *Result) Convert() (*Result, error) {
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// net.IPNet is not JSON (un)marshallable so this duality is needed
|
||||
// for our custom IPNet type
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
IP types.IPNet `json:"ip"`
|
||||
Gateway net.IP `json:"gateway,omitempty"`
|
||||
Routes []types.Route `json:"routes,omitempty"`
|
||||
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{
|
||||
IP: types.IPNet(c.IP),
|
||||
Gateway: c.Gateway,
|
||||
Routes: c.Routes,
|
||||
Version: c.Version,
|
||||
Interface: c.Interface,
|
||||
Address: types.IPNet(c.Address),
|
||||
Gateway: c.Gateway,
|
||||
}
|
||||
|
||||
return json.Marshal(ipc)
|
||||
@ -150,8 +283,9 @@ func (c *IPConfig) UnmarshalJSON(data []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
c.IP = net.IPNet(ipc.IP)
|
||||
c.Version = ipc.Version
|
||||
c.Interface = ipc.Interface
|
||||
c.Address = net.IPNet(ipc.Address)
|
||||
c.Gateway = ipc.Gateway
|
||||
c.Routes = ipc.Routes
|
||||
return nil
|
||||
}
|
||||
|
@ -23,5 +23,5 @@ import (
|
||||
|
||||
func TestTypes010(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "0.1.0 Types Suite")
|
||||
RunSpecs(t, "0.3.0 Types Suite")
|
||||
}
|
||||
|
@ -26,51 +26,66 @@ import (
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Ensures compatibility with the 0.1.0 spec", func() {
|
||||
It("correctly encodes a 0.1.0 Result", func() {
|
||||
ipv4, err := types.ParseCIDR("1.2.3.30/24")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(ipv4).NotTo(BeNil())
|
||||
func testResult() *current.Result {
|
||||
ipv4, err := types.ParseCIDR("1.2.3.30/24")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(ipv4).NotTo(BeNil())
|
||||
|
||||
routegwv4, routev4, err := net.ParseCIDR("15.5.6.8/24")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(routev4).NotTo(BeNil())
|
||||
Expect(routegwv4).NotTo(BeNil())
|
||||
routegwv4, routev4, err := net.ParseCIDR("15.5.6.8/24")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(routev4).NotTo(BeNil())
|
||||
Expect(routegwv4).NotTo(BeNil())
|
||||
|
||||
ipv6, err := types.ParseCIDR("abcd:1234:ffff::cdde/64")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(ipv6).NotTo(BeNil())
|
||||
ipv6, err := types.ParseCIDR("abcd:1234:ffff::cdde/64")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(ipv6).NotTo(BeNil())
|
||||
|
||||
routegwv6, routev6, err := net.ParseCIDR("1111:dddd::aaaa/80")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(routev6).NotTo(BeNil())
|
||||
Expect(routegwv6).NotTo(BeNil())
|
||||
routegwv6, routev6, err := net.ParseCIDR("1111:dddd::aaaa/80")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(routev6).NotTo(BeNil())
|
||||
Expect(routegwv6).NotTo(BeNil())
|
||||
|
||||
// Set every field of the struct to ensure source compatibility
|
||||
res := current.Result{
|
||||
IP4: ¤t.IPConfig{
|
||||
IP: *ipv4,
|
||||
Gateway: net.ParseIP("1.2.3.1"),
|
||||
Routes: []types.Route{
|
||||
{Dst: *routev4, GW: routegwv4},
|
||||
},
|
||||
// Set every field of the struct to ensure source compatibility
|
||||
return ¤t.Result{
|
||||
Interfaces: []*current.Interface{
|
||||
{
|
||||
Name: "eth0",
|
||||
Mac: "00:11:22:33:44:55",
|
||||
Sandbox: "/proc/3553/ns/net",
|
||||
},
|
||||
IP6: ¤t.IPConfig{
|
||||
IP: *ipv6,
|
||||
Gateway: net.ParseIP("abcd:1234:ffff::1"),
|
||||
Routes: []types.Route{
|
||||
{Dst: *routev6, GW: routegwv6},
|
||||
},
|
||||
},
|
||||
IPs: []*current.IPConfig{
|
||||
{
|
||||
Version: "4",
|
||||
Interface: 0,
|
||||
Address: *ipv4,
|
||||
Gateway: net.ParseIP("1.2.3.1"),
|
||||
},
|
||||
DNS: types.DNS{
|
||||
Nameservers: []string{"1.2.3.4", "1::cafe"},
|
||||
Domain: "acompany.com",
|
||||
Search: []string{"somedomain.com", "otherdomain.net"},
|
||||
Options: []string{"foo", "bar"},
|
||||
{
|
||||
Version: "6",
|
||||
Interface: 0,
|
||||
Address: *ipv6,
|
||||
Gateway: net.ParseIP("abcd:1234:ffff::1"),
|
||||
},
|
||||
}
|
||||
},
|
||||
Routes: []*types.Route{
|
||||
{Dst: *routev4, GW: routegwv4},
|
||||
{Dst: *routev6, GW: routegwv6},
|
||||
},
|
||||
DNS: types.DNS{
|
||||
Nameservers: []string{"1.2.3.4", "1::cafe"},
|
||||
Domain: "acompany.com",
|
||||
Search: []string{"somedomain.com", "otherdomain.net"},
|
||||
Options: []string{"foo", "bar"},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
Expect(res.String()).To(Equal("IP4:{IP:{IP:1.2.3.30 Mask:ffffff00} Gateway:1.2.3.1 Routes:[{Dst:{IP:15.5.6.0 Mask:ffffff00} GW:15.5.6.8}]}, IP6:{IP:{IP:abcd:1234:ffff::cdde Mask:ffffffffffffffff0000000000000000} Gateway:abcd:1234:ffff::1 Routes:[{Dst:{IP:1111:dddd:: Mask:ffffffffffffffffffff000000000000} GW:1111:dddd::aaaa}]}, DNS:{Nameservers:[1.2.3.4 1::cafe] Domain:acompany.com Search:[somedomain.com otherdomain.net] Options:[foo bar]}"))
|
||||
var _ = Describe("Ensures compatibility with the 0.3.0 spec", func() {
|
||||
It("correctly encodes a 0.3.0 Result", func() {
|
||||
res := testResult()
|
||||
|
||||
Expect(res.String()).To(Equal("Interfaces:[{Name:eth0 Mac:00:11:22:33:44:55 Sandbox:/proc/3553/ns/net}], IP:[{Version:4 Interface:0 Address:{IP:1.2.3.30 Mask:ffffff00} Gateway:1.2.3.1} {Version:6 Interface:0 Address:{IP:abcd:1234:ffff::cdde Mask:ffffffffffffffff0000000000000000} Gateway:abcd:1234:ffff::1}], Routes:[{Dst:{IP:15.5.6.0 Mask:ffffff00} GW:15.5.6.8} {Dst:{IP:1111:dddd:: Mask:ffffffffffffffffffff000000000000} GW:1111:dddd::aaaa}], DNS:{Nameservers:[1.2.3.4 1::cafe] Domain:acompany.com Search:[somedomain.com otherdomain.net] Options:[foo bar]}"))
|
||||
|
||||
// Redirect stdout to capture JSON result
|
||||
oldStdout := os.Stdout
|
||||
@ -88,6 +103,76 @@ var _ = Describe("Ensures compatibility with the 0.1.0 spec", func() {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(string(out)).To(Equal(`{
|
||||
"interfaces": [
|
||||
{
|
||||
"name": "eth0",
|
||||
"mac": "00:11:22:33:44:55",
|
||||
"sandbox": "/proc/3553/ns/net"
|
||||
}
|
||||
],
|
||||
"ips": [
|
||||
{
|
||||
"version": "4",
|
||||
"address": "1.2.3.30/24",
|
||||
"gateway": "1.2.3.1"
|
||||
},
|
||||
{
|
||||
"version": "6",
|
||||
"address": "abcd:1234:ffff::cdde/64",
|
||||
"gateway": "abcd:1234:ffff::1"
|
||||
}
|
||||
],
|
||||
"routes": [
|
||||
{
|
||||
"dst": "15.5.6.0/24",
|
||||
"gw": "15.5.6.8"
|
||||
},
|
||||
{
|
||||
"dst": "1111:dddd::/80",
|
||||
"gw": "1111:dddd::aaaa"
|
||||
}
|
||||
],
|
||||
"dns": {
|
||||
"nameservers": [
|
||||
"1.2.3.4",
|
||||
"1::cafe"
|
||||
],
|
||||
"domain": "acompany.com",
|
||||
"search": [
|
||||
"somedomain.com",
|
||||
"otherdomain.net"
|
||||
],
|
||||
"options": [
|
||||
"foo",
|
||||
"bar"
|
||||
]
|
||||
}
|
||||
}`))
|
||||
})
|
||||
|
||||
var _ = Describe("Ensures compatibility with the 0.1.0 spec", func() {
|
||||
It("correctly encodes a 0.1.0 Result", func() {
|
||||
res, err := testResult().GetAsVersion("0.1.0")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(res.String()).To(Equal("IP4:{IP:{IP:1.2.3.30 Mask:ffffff00} Gateway:1.2.3.1 Routes:[{Dst:{IP:15.5.6.0 Mask:ffffff00} GW:15.5.6.8}]}, IP6:{IP:{IP:abcd:1234:ffff::cdde Mask:ffffffffffffffff0000000000000000} Gateway:abcd:1234:ffff::1 Routes:[{Dst:{IP:1111:dddd:: Mask:ffffffffffffffffffff000000000000} GW:1111:dddd::aaaa}]}, DNS:{Nameservers:[1.2.3.4 1::cafe] Domain:acompany.com Search:[somedomain.com otherdomain.net] Options:[foo bar]}"))
|
||||
|
||||
// Redirect stdout to capture JSON result
|
||||
oldStdout := os.Stdout
|
||||
r, w, err := os.Pipe()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
os.Stdout = w
|
||||
err = res.Print()
|
||||
w.Close()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// parse the result
|
||||
out, err := ioutil.ReadAll(r)
|
||||
os.Stdout = oldStdout
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(string(out)).To(Equal(`{
|
||||
"ip4": {
|
||||
"ip": "1.2.3.30/24",
|
||||
"gateway": "1.2.3.1",
|
||||
@ -124,5 +209,6 @@ var _ = Describe("Ensures compatibility with the 0.1.0 spec", func() {
|
||||
]
|
||||
}
|
||||
}`))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -16,6 +16,7 @@ package types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
@ -114,6 +115,10 @@ type Route struct {
|
||||
GW net.IP
|
||||
}
|
||||
|
||||
func (r *Route) String() string {
|
||||
return fmt.Sprintf("%+v", *r)
|
||||
}
|
||||
|
||||
// Well known error codes
|
||||
// see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes
|
||||
const (
|
||||
|
Reference in New Issue
Block a user