github.com/mccv1r0/cni@v0.7.0-alpha1/pkg/invoke/exec_test.go (about)

     1  // Copyright 2016 CNI authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package invoke_test
    16  
    17  import (
    18  	"encoding/json"
    19  	"errors"
    20  
    21  	"github.com/containernetworking/cni/pkg/invoke"
    22  	"github.com/containernetworking/cni/pkg/invoke/fakes"
    23  	"github.com/containernetworking/cni/pkg/types/current"
    24  	"github.com/containernetworking/cni/pkg/version"
    25  
    26  	. "github.com/onsi/ginkgo"
    27  	. "github.com/onsi/gomega"
    28  )
    29  
    30  var _ = Describe("Executing a plugin, unit tests", func() {
    31  	var (
    32  		pluginExec     invoke.Exec
    33  		rawExec        *fakes.RawExec
    34  		versionDecoder *fakes.VersionDecoder
    35  
    36  		pluginPath string
    37  		netconf    []byte
    38  		cniargs    *fakes.CNIArgs
    39  	)
    40  
    41  	BeforeEach(func() {
    42  		rawExec = &fakes.RawExec{}
    43  		rawExec.ExecPluginCall.Returns.ResultBytes = []byte(`{ "ips": [ { "version": "4", "address": "1.2.3.4/24" } ] }`)
    44  
    45  		versionDecoder = &fakes.VersionDecoder{}
    46  		versionDecoder.DecodeCall.Returns.PluginInfo = version.PluginSupports("0.42.0")
    47  
    48  		pluginExec = &struct {
    49  			*fakes.RawExec
    50  			*fakes.VersionDecoder
    51  		}{
    52  			RawExec:        rawExec,
    53  			VersionDecoder: versionDecoder,
    54  		}
    55  		pluginPath = "/some/plugin/path"
    56  		netconf = []byte(`{ "some": "stdin", "cniVersion": "0.3.1" }`)
    57  		cniargs = &fakes.CNIArgs{}
    58  		cniargs.AsEnvCall.Returns.Env = []string{"SOME=ENV"}
    59  	})
    60  
    61  	Describe("returning a result", func() {
    62  		It("unmarshals the result bytes into the Result type", func() {
    63  			r, err := invoke.ExecPluginWithResult(pluginPath, netconf, cniargs, pluginExec)
    64  			Expect(err).NotTo(HaveOccurred())
    65  
    66  			result, err := current.GetResult(r)
    67  			Expect(err).NotTo(HaveOccurred())
    68  			Expect(len(result.IPs)).To(Equal(1))
    69  			Expect(result.IPs[0].Address.IP.String()).To(Equal("1.2.3.4"))
    70  		})
    71  
    72  		It("passes its arguments through to the rawExec", func() {
    73  			invoke.ExecPluginWithResult(pluginPath, netconf, cniargs, pluginExec)
    74  			Expect(rawExec.ExecPluginCall.Received.PluginPath).To(Equal(pluginPath))
    75  			Expect(rawExec.ExecPluginCall.Received.StdinData).To(Equal(netconf))
    76  			Expect(rawExec.ExecPluginCall.Received.Environ).To(Equal([]string{"SOME=ENV"}))
    77  		})
    78  
    79  		Context("when the rawExec fails", func() {
    80  			BeforeEach(func() {
    81  				rawExec.ExecPluginCall.Returns.Error = errors.New("banana")
    82  			})
    83  			It("returns the error", func() {
    84  				_, err := invoke.ExecPluginWithResult(pluginPath, netconf, cniargs, pluginExec)
    85  				Expect(err).To(MatchError("banana"))
    86  			})
    87  		})
    88  
    89  		It("returns an error using the default exec interface", func() {
    90  			// pluginPath should not exist on-disk so we expect an error.
    91  			// This test simply tests that the default exec handler
    92  			// is run when the exec interface is nil.
    93  			_, err := invoke.ExecPluginWithResult(pluginPath, netconf, cniargs, nil)
    94  			Expect(err).To(HaveOccurred())
    95  		})
    96  	})
    97  
    98  	Describe("without returning a result", func() {
    99  		It("passes its arguments through to the rawExec", func() {
   100  			invoke.ExecPluginWithoutResult(pluginPath, netconf, cniargs, pluginExec)
   101  			Expect(rawExec.ExecPluginCall.Received.PluginPath).To(Equal(pluginPath))
   102  			Expect(rawExec.ExecPluginCall.Received.StdinData).To(Equal(netconf))
   103  			Expect(rawExec.ExecPluginCall.Received.Environ).To(Equal([]string{"SOME=ENV"}))
   104  		})
   105  
   106  		Context("when the rawExec fails", func() {
   107  			BeforeEach(func() {
   108  				rawExec.ExecPluginCall.Returns.Error = errors.New("banana")
   109  			})
   110  			It("returns the error", func() {
   111  				err := invoke.ExecPluginWithoutResult(pluginPath, netconf, cniargs, pluginExec)
   112  				Expect(err).To(MatchError("banana"))
   113  			})
   114  		})
   115  
   116  		It("returns an error using the default exec interface", func() {
   117  			// pluginPath should not exist on-disk so we expect an error.
   118  			// This test simply tests that the default exec handler
   119  			// is run when the exec interface is nil.
   120  			err := invoke.ExecPluginWithoutResult(pluginPath, netconf, cniargs, nil)
   121  			Expect(err).To(HaveOccurred())
   122  		})
   123  	})
   124  
   125  	Describe("discovering the plugin version", func() {
   126  		BeforeEach(func() {
   127  			rawExec.ExecPluginCall.Returns.ResultBytes = []byte(`{ "some": "version-info" }`)
   128  		})
   129  
   130  		It("execs the plugin with the command VERSION", func() {
   131  			invoke.GetVersionInfo(pluginPath, pluginExec)
   132  			Expect(rawExec.ExecPluginCall.Received.PluginPath).To(Equal(pluginPath))
   133  			Expect(rawExec.ExecPluginCall.Received.Environ).To(ContainElement("CNI_COMMAND=VERSION"))
   134  			expectedStdin, _ := json.Marshal(map[string]string{"cniVersion": version.Current()})
   135  			Expect(rawExec.ExecPluginCall.Received.StdinData).To(MatchJSON(expectedStdin))
   136  		})
   137  
   138  		It("decodes and returns the version info", func() {
   139  			versionInfo, err := invoke.GetVersionInfo(pluginPath, pluginExec)
   140  			Expect(err).NotTo(HaveOccurred())
   141  			Expect(versionInfo.SupportedVersions()).To(Equal([]string{"0.42.0"}))
   142  			Expect(versionDecoder.DecodeCall.Received.JSONBytes).To(MatchJSON(`{ "some": "version-info" }`))
   143  		})
   144  
   145  		Context("when the rawExec fails", func() {
   146  			BeforeEach(func() {
   147  				rawExec.ExecPluginCall.Returns.Error = errors.New("banana")
   148  			})
   149  			It("returns the error", func() {
   150  				_, err := invoke.GetVersionInfo(pluginPath, pluginExec)
   151  				Expect(err).To(MatchError("banana"))
   152  			})
   153  		})
   154  
   155  		Context("when the plugin is too old to recognize the VERSION command", func() {
   156  			BeforeEach(func() {
   157  				rawExec.ExecPluginCall.Returns.Error = errors.New("unknown CNI_COMMAND: VERSION")
   158  			})
   159  
   160  			It("interprets the error as a 0.1.0 version", func() {
   161  				versionInfo, err := invoke.GetVersionInfo(pluginPath, pluginExec)
   162  				Expect(err).NotTo(HaveOccurred())
   163  				Expect(versionInfo.SupportedVersions()).To(ConsistOf("0.1.0"))
   164  			})
   165  
   166  			It("sets dummy values for env vars required by very old plugins", func() {
   167  				invoke.GetVersionInfo(pluginPath, pluginExec)
   168  
   169  				env := rawExec.ExecPluginCall.Received.Environ
   170  				Expect(env).To(ContainElement("CNI_NETNS=dummy"))
   171  				Expect(env).To(ContainElement("CNI_IFNAME=dummy"))
   172  				Expect(env).To(ContainElement("CNI_PATH=dummy"))
   173  			})
   174  		})
   175  	})
   176  })