2016-04-01 13:35:21 +00:00
// 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.
2016-03-18 21:28:14 +00:00
package skel
import (
2016-07-14 02:24:34 +00:00
"bytes"
2016-07-14 02:12:06 +00:00
"errors"
"strings"
2016-03-18 21:28:14 +00:00
2016-07-14 02:12:06 +00:00
"github.com/containernetworking/cni/pkg/types"
2016-07-14 03:50:54 +00:00
"github.com/containernetworking/cni/pkg/version"
2016-07-14 02:12:06 +00:00
"github.com/containernetworking/cni/pkg/testutils"
2016-03-18 21:28:14 +00:00
. "github.com/onsi/ginkgo"
2016-07-14 02:12:06 +00:00
. "github.com/onsi/ginkgo/extensions/table"
2016-03-18 21:28:14 +00:00
. "github.com/onsi/gomega"
)
2016-07-14 02:12:06 +00:00
type fakeCmd struct {
CallCount int
Returns struct {
Error error
}
Received struct {
CmdArgs * CmdArgs
}
}
func ( c * fakeCmd ) Func ( args * CmdArgs ) error {
c . CallCount ++
c . Received . CmdArgs = args
return c . Returns . Error
}
var _ = Describe ( "dispatching to the correct callback" , func ( ) {
2016-03-18 21:28:14 +00:00
var (
2016-07-14 02:12:06 +00:00
environment map [ string ] string
2016-09-07 00:19:26 +00:00
stdinData string
2016-07-14 03:50:54 +00:00
stdout , stderr * bytes . Buffer
2016-07-14 02:12:06 +00:00
cmdAdd , cmdDel * fakeCmd
dispatch * dispatcher
expectedCmdArgs * CmdArgs
2016-08-22 06:48:04 +00:00
versionInfo version . PluginInfo
2016-03-18 21:28:14 +00:00
)
2016-07-14 02:12:06 +00:00
BeforeEach ( func ( ) {
environment = map [ string ] string {
"CNI_COMMAND" : "ADD" ,
"CNI_CONTAINERID" : "some-container-id" ,
"CNI_NETNS" : "/some/netns/path" ,
"CNI_IFNAME" : "eth0" ,
"CNI_ARGS" : "some;extra;args" ,
"CNI_PATH" : "/some/cni/path" ,
}
2016-09-07 00:19:26 +00:00
stdinData = ` { "some": "config", "cniVersion": "9.8.7" } `
2016-07-14 03:50:54 +00:00
stdout = & bytes . Buffer { }
2016-07-14 02:24:34 +00:00
stderr = & bytes . Buffer { }
2016-08-22 06:48:04 +00:00
versionInfo = version . PluginSupports ( "9.8.7" )
2016-07-14 02:12:06 +00:00
dispatch = & dispatcher {
2016-08-22 06:48:04 +00:00
Getenv : func ( key string ) string { return environment [ key ] } ,
2016-09-07 00:19:26 +00:00
Stdin : strings . NewReader ( stdinData ) ,
2016-08-22 06:48:04 +00:00
Stdout : stdout ,
Stderr : stderr ,
2016-07-14 02:12:06 +00:00
}
cmdAdd = & fakeCmd { }
cmdDel = & fakeCmd { }
expectedCmdArgs = & CmdArgs {
ContainerID : "some-container-id" ,
Netns : "/some/netns/path" ,
IfName : "eth0" ,
Args : "some;extra;args" ,
Path : "/some/cni/path" ,
2016-09-07 00:19:26 +00:00
StdinData : [ ] byte ( stdinData ) ,
2016-03-18 21:28:14 +00:00
}
} )
2016-07-14 02:12:06 +00:00
var envVarChecker = func ( envVar string , isRequired bool ) {
delete ( environment , envVar )
2016-08-22 06:48:04 +00:00
err := dispatch . pluginMain ( cmdAdd . Func , cmdDel . Func , versionInfo )
2016-07-14 02:12:06 +00:00
if isRequired {
Expect ( err ) . To ( Equal ( & types . Error {
Code : 100 ,
Msg : "required env variables missing" ,
} ) )
2016-07-14 02:24:34 +00:00
Expect ( stderr . String ( ) ) . To ( ContainSubstring ( envVar + " env variable missing\n" ) )
2016-07-14 02:12:06 +00:00
} else {
Expect ( err ) . NotTo ( HaveOccurred ( ) )
}
}
Context ( "when the CNI_COMMAND is ADD" , func ( ) {
It ( "extracts env vars and stdin data and calls cmdAdd" , func ( ) {
2016-08-22 06:48:04 +00:00
err := dispatch . pluginMain ( cmdAdd . Func , cmdDel . Func , versionInfo )
2016-03-18 21:28:14 +00:00
Expect ( err ) . NotTo ( HaveOccurred ( ) )
2016-07-14 02:12:06 +00:00
Expect ( cmdAdd . CallCount ) . To ( Equal ( 1 ) )
Expect ( cmdDel . CallCount ) . To ( Equal ( 0 ) )
Expect ( cmdAdd . Received . CmdArgs ) . To ( Equal ( expectedCmdArgs ) )
2016-03-18 21:28:14 +00:00
} )
2016-07-14 02:12:06 +00:00
It ( "does not call cmdDel" , func ( ) {
2016-08-22 06:48:04 +00:00
err := dispatch . pluginMain ( cmdAdd . Func , cmdDel . Func , versionInfo )
2016-03-18 21:28:14 +00:00
Expect ( err ) . NotTo ( HaveOccurred ( ) )
2016-07-14 02:12:06 +00:00
Expect ( cmdDel . CallCount ) . To ( Equal ( 0 ) )
2016-03-18 21:28:14 +00:00
} )
2016-07-14 02:12:06 +00:00
DescribeTable ( "required / optional env vars" , envVarChecker ,
2016-07-14 02:54:22 +00:00
Entry ( "command" , "CNI_COMMAND" , true ) ,
2016-07-14 02:12:06 +00:00
Entry ( "container id" , "CNI_CONTAINER_ID" , false ) ,
Entry ( "net ns" , "CNI_NETNS" , true ) ,
Entry ( "if name" , "CNI_IFNAME" , true ) ,
Entry ( "args" , "CNI_ARGS" , false ) ,
Entry ( "path" , "CNI_PATH" , true ) ,
)
2016-07-14 02:24:34 +00:00
Context ( "when multiple required env vars are missing" , func ( ) {
BeforeEach ( func ( ) {
delete ( environment , "CNI_NETNS" )
delete ( environment , "CNI_IFNAME" )
delete ( environment , "CNI_PATH" )
} )
It ( "reports that all of them are missing, not just the first" , func ( ) {
2016-08-22 06:48:04 +00:00
Expect ( dispatch . pluginMain ( cmdAdd . Func , cmdDel . Func , versionInfo ) ) . NotTo ( Succeed ( ) )
2016-07-14 02:24:34 +00:00
log := stderr . String ( )
Expect ( log ) . To ( ContainSubstring ( "CNI_NETNS env variable missing\n" ) )
Expect ( log ) . To ( ContainSubstring ( "CNI_IFNAME env variable missing\n" ) )
Expect ( log ) . To ( ContainSubstring ( "CNI_PATH env variable missing\n" ) )
} )
} )
2016-09-07 00:19:26 +00:00
Context ( "when the stdin data is missing the required cniVersion config" , func ( ) {
BeforeEach ( func ( ) {
dispatch . Stdin = strings . NewReader ( ` { "some": "config" } ` )
} )
2016-09-19 20:00:49 +00:00
Context ( "when the plugin supports version 0.1.0" , func ( ) {
BeforeEach ( func ( ) {
versionInfo = version . PluginSupports ( "0.1.0" )
expectedCmdArgs . StdinData = [ ] byte ( ` { "some": "config" } ` )
} )
It ( "infers the config is 0.1.0 and calls the cmdAdd callback" , func ( ) {
err := dispatch . pluginMain ( cmdAdd . Func , cmdDel . Func , versionInfo )
Expect ( err ) . NotTo ( HaveOccurred ( ) )
Expect ( cmdAdd . CallCount ) . To ( Equal ( 1 ) )
Expect ( cmdAdd . Received . CmdArgs ) . To ( Equal ( expectedCmdArgs ) )
} )
2016-09-07 00:19:26 +00:00
} )
2016-09-19 20:00:49 +00:00
Context ( "when the plugin does not support 0.1.0" , func ( ) {
BeforeEach ( func ( ) {
versionInfo = version . PluginSupports ( "4.3.2" )
} )
It ( "immediately returns a useful error" , func ( ) {
err := dispatch . pluginMain ( cmdAdd . Func , cmdDel . Func , versionInfo )
2016-09-19 20:14:02 +00:00
Expect ( err . Code ) . To ( Equal ( types . ErrIncompatibleCNIVersion ) ) // see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes
2016-09-19 20:00:49 +00:00
Expect ( err . Msg ) . To ( Equal ( "incompatible CNI versions" ) )
Expect ( err . Details ) . To ( Equal ( ` config is "0.1.0", plugin supports ["4.3.2"] ` ) )
} )
It ( "does not call either callback" , func ( ) {
dispatch . pluginMain ( cmdAdd . Func , cmdDel . Func , versionInfo )
Expect ( cmdAdd . CallCount ) . To ( Equal ( 0 ) )
Expect ( cmdDel . CallCount ) . To ( Equal ( 0 ) )
} )
2016-09-07 00:19:26 +00:00
} )
} )
2016-07-14 02:12:06 +00:00
} )
Context ( "when the CNI_COMMAND is DEL" , func ( ) {
BeforeEach ( func ( ) {
environment [ "CNI_COMMAND" ] = "DEL"
} )
It ( "calls cmdDel with the env vars and stdin data" , func ( ) {
2016-08-22 06:48:04 +00:00
err := dispatch . pluginMain ( cmdAdd . Func , cmdDel . Func , versionInfo )
2016-05-27 10:26:42 +00:00
Expect ( err ) . NotTo ( HaveOccurred ( ) )
2016-07-14 02:12:06 +00:00
Expect ( cmdDel . CallCount ) . To ( Equal ( 1 ) )
Expect ( cmdDel . Received . CmdArgs ) . To ( Equal ( expectedCmdArgs ) )
} )
It ( "does not call cmdAdd" , func ( ) {
2016-08-22 06:48:04 +00:00
err := dispatch . pluginMain ( cmdAdd . Func , cmdDel . Func , versionInfo )
2016-07-14 02:12:06 +00:00
2016-05-27 10:26:42 +00:00
Expect ( err ) . NotTo ( HaveOccurred ( ) )
2016-07-14 02:12:06 +00:00
Expect ( cmdAdd . CallCount ) . To ( Equal ( 0 ) )
2016-05-27 10:26:42 +00:00
} )
2016-07-14 02:12:06 +00:00
DescribeTable ( "required / optional env vars" , envVarChecker ,
2016-07-14 02:54:22 +00:00
Entry ( "command" , "CNI_COMMAND" , true ) ,
2016-07-14 02:12:06 +00:00
Entry ( "container id" , "CNI_CONTAINER_ID" , false ) ,
Entry ( "net ns" , "CNI_NETNS" , false ) ,
Entry ( "if name" , "CNI_IFNAME" , true ) ,
Entry ( "args" , "CNI_ARGS" , false ) ,
Entry ( "path" , "CNI_PATH" , true ) ,
)
} )
2016-07-14 03:50:54 +00:00
Context ( "when the CNI_COMMAND is VERSION" , func ( ) {
BeforeEach ( func ( ) {
environment [ "CNI_COMMAND" ] = "VERSION"
} )
It ( "prints the version to stdout" , func ( ) {
2016-08-22 06:48:04 +00:00
err := dispatch . pluginMain ( cmdAdd . Func , cmdDel . Func , versionInfo )
2016-07-14 03:50:54 +00:00
Expect ( err ) . NotTo ( HaveOccurred ( ) )
2016-08-22 06:48:04 +00:00
Expect ( stdout ) . To ( MatchJSON ( ` {
2016-09-06 19:24:12 +00:00
"cniVersion" : "0.2.0" ,
2016-08-22 06:48:04 +00:00
"supportedVersions" : [ "9.8.7" ]
} ` ) )
2016-07-14 03:50:54 +00:00
} )
It ( "does not call cmdAdd or cmdDel" , func ( ) {
2016-08-22 06:48:04 +00:00
err := dispatch . pluginMain ( cmdAdd . Func , cmdDel . Func , versionInfo )
2016-07-14 03:50:54 +00:00
Expect ( err ) . NotTo ( HaveOccurred ( ) )
Expect ( cmdAdd . CallCount ) . To ( Equal ( 0 ) )
Expect ( cmdDel . CallCount ) . To ( Equal ( 0 ) )
} )
DescribeTable ( "VERSION does not need the usual env vars" , envVarChecker ,
Entry ( "command" , "CNI_COMMAND" , true ) ,
Entry ( "container id" , "CNI_CONTAINER_ID" , false ) ,
Entry ( "net ns" , "CNI_NETNS" , false ) ,
Entry ( "if name" , "CNI_IFNAME" , false ) ,
Entry ( "args" , "CNI_ARGS" , false ) ,
Entry ( "path" , "CNI_PATH" , false ) ,
)
2016-09-19 20:00:49 +00:00
Context ( "when the stdin is empty" , func ( ) {
BeforeEach ( func ( ) {
dispatch . Stdin = strings . NewReader ( "" )
} )
It ( "succeeds without error" , func ( ) {
err := dispatch . pluginMain ( cmdAdd . Func , cmdDel . Func , versionInfo )
Expect ( err ) . NotTo ( HaveOccurred ( ) )
Expect ( stdout ) . To ( MatchJSON ( ` {
"cniVersion" : "0.2.0" ,
"supportedVersions" : [ "9.8.7" ]
} ` ) )
} )
} )
2016-07-14 03:50:54 +00:00
} )
2016-07-14 02:12:06 +00:00
Context ( "when the CNI_COMMAND is unrecognized" , func ( ) {
BeforeEach ( func ( ) {
environment [ "CNI_COMMAND" ] = "NOPE"
} )
It ( "does not call any cmd callback" , func ( ) {
2016-08-22 06:48:04 +00:00
dispatch . pluginMain ( cmdAdd . Func , cmdDel . Func , versionInfo )
2016-07-14 02:12:06 +00:00
Expect ( cmdAdd . CallCount ) . To ( Equal ( 0 ) )
Expect ( cmdDel . CallCount ) . To ( Equal ( 0 ) )
} )
It ( "returns an error" , func ( ) {
2016-08-22 06:48:04 +00:00
err := dispatch . pluginMain ( cmdAdd . Func , cmdDel . Func , versionInfo )
2016-07-14 02:12:06 +00:00
Expect ( err ) . To ( Equal ( & types . Error {
Code : 100 ,
Msg : "unknown CNI_COMMAND: NOPE" ,
} ) )
} )
} )
Context ( "when stdin cannot be read" , func ( ) {
BeforeEach ( func ( ) {
dispatch . Stdin = & testutils . BadReader { }
} )
It ( "does not call any cmd callback" , func ( ) {
2016-08-22 06:48:04 +00:00
dispatch . pluginMain ( cmdAdd . Func , cmdDel . Func , versionInfo )
2016-07-14 02:12:06 +00:00
Expect ( cmdAdd . CallCount ) . To ( Equal ( 0 ) )
Expect ( cmdDel . CallCount ) . To ( Equal ( 0 ) )
} )
It ( "wraps and returns the error" , func ( ) {
2016-08-22 06:48:04 +00:00
err := dispatch . pluginMain ( cmdAdd . Func , cmdDel . Func , versionInfo )
2016-07-14 02:12:06 +00:00
Expect ( err ) . To ( Equal ( & types . Error {
Code : 100 ,
Msg : "error reading from stdin: banana" ,
} ) )
} )
} )
Context ( "when the callback returns an error" , func ( ) {
Context ( "when it is a typed Error" , func ( ) {
BeforeEach ( func ( ) {
cmdAdd . Returns . Error = & types . Error {
Code : 1234 ,
Msg : "insufficient something" ,
}
} )
It ( "returns the error as-is" , func ( ) {
2016-08-22 06:48:04 +00:00
err := dispatch . pluginMain ( cmdAdd . Func , cmdDel . Func , versionInfo )
2016-07-14 02:12:06 +00:00
Expect ( err ) . To ( Equal ( & types . Error {
Code : 1234 ,
Msg : "insufficient something" ,
} ) )
} )
} )
Context ( "when it is an unknown error" , func ( ) {
BeforeEach ( func ( ) {
cmdAdd . Returns . Error = errors . New ( "potato" )
} )
It ( "wraps and returns the error" , func ( ) {
2016-08-22 06:48:04 +00:00
err := dispatch . pluginMain ( cmdAdd . Func , cmdDel . Func , versionInfo )
2016-07-14 02:12:06 +00:00
Expect ( err ) . To ( Equal ( & types . Error {
Code : 100 ,
Msg : "potato" ,
} ) )
} )
} )
2016-03-18 21:28:14 +00:00
} )
} )