github.com/jk-he/cni@v0.8.1/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  	"context"
    19  	"encoding/json"
    20  	"errors"
    21  
    22  	"github.com/containernetworking/cni/pkg/invoke"
    23  	"github.com/containernetworking/cni/pkg/invoke/fakes"
    24  	"github.com/containernetworking/cni/pkg/types/current"
    25  	"github.com/containernetworking/cni/pkg/version"
    26  
    27  	. "github.com/onsi/ginkgo"
    28  	. "github.com/onsi/gomega"
    29  )
    30  
    31  var _ = Describe("Executing a plugin, unit tests", func() {
    32  	var (
    33  		pluginExec     invoke.Exec
    34  		rawExec        *fakes.RawExec
    35  		versionDecoder *fakes.VersionDecoder
    36  
    37  		pluginPath string
    38  		netconf    []byte
    39  		cniargs    *fakes.CNIArgs
    40  		ctx        context.Context
    41  	)
    42  
    43  	BeforeEach(func() {
    44  		rawExec = &fakes.RawExec{}
    45  		rawExec.ExecPluginCall.Returns.ResultBytes = []byte(`{ "ips": [ { "version": "4", "address": "1.2.3.4/24" } ] }`)
    46  
    47  		versionDecoder = &fakes.VersionDecoder{}
    48  		versionDecoder.DecodeCall.Returns.PluginInfo = version.PluginSupports("0.42.0")
    49  
    50  		pluginExec = &struct {
    51  			*fakes.RawExec
    52  			*fakes.VersionDecoder
    53  		}{
    54  			RawExec:        rawExec,
    55  			VersionDecoder: versionDecoder,
    56  		}
    57  		pluginPath = "/some/plugin/path"
    58  		netconf = []byte(`{ "some": "stdin", "cniVersion": "0.3.1" }`)
    59  		cniargs = &fakes.CNIArgs{}
    60  		cniargs.AsEnvCall.Returns.Env = []string{"SOME=ENV"}
    61  		ctx = context.TODO()
    62  	})
    63  
    64  	Describe("returning a result", func() {
    65  		It("unmarshals the result bytes into the Result type", func() {
    66  			r, err := invoke.ExecPluginWithResult(ctx, pluginPath, netconf, cniargs, pluginExec)
    67  			Expect(err).NotTo(HaveOccurred())
    68  
    69  			result, err := current.GetResult(r)
    70  			Expect(err).NotTo(HaveOccurred())
    71  			Expect(len(result.IPs)).To(Equal(1))
    72  			Expect(result.IPs[0].Address.IP.String()).To(Equal("1.2.3.4"))
    73  		})
    74  
    75  		It("passes its arguments through to the rawExec", func() {
    76  			invoke.ExecPluginWithResult(ctx, pluginPath, netconf, cniargs, pluginExec)
    77  			Expect(rawExec.ExecPluginCall.Received.PluginPath).To(Equal(pluginPath))
    78  			Expect(rawExec.ExecPluginCall.Received.StdinData).To(Equal(netconf))
    79  			Expect(rawExec.ExecPluginCall.Received.Environ).To(Equal([]string{"SOME=ENV"}))
    80  		})
    81  
    82  		Context("when the rawExec fails", func() {
    83  			BeforeEach(func() {
    84  				rawExec.ExecPluginCall.Returns.Error = errors.New("banana")
    85  			})
    86  			It("returns the error", func() {
    87  				_, err := invoke.ExecPluginWithResult(ctx, pluginPath, netconf, cniargs, pluginExec)
    88  				Expect(err).To(MatchError("banana"))
    89  			})
    90  		})
    91  
    92  		It("returns an error using the default exec interface", func() {
    93  			// pluginPath should not exist on-disk so we expect an error.
    94  			// This test simply tests that the default exec handler
    95  			// is run when the exec interface is nil.
    96  			_, err := invoke.ExecPluginWithResult(ctx, pluginPath, netconf, cniargs, nil)
    97  			Expect(err).To(HaveOccurred())
    98  		})
    99  	})
   100  
   101  	Describe("without returning a result", func() {
   102  		It("passes its arguments through to the rawExec", func() {
   103  			invoke.ExecPluginWithoutResult(ctx, pluginPath, netconf, cniargs, pluginExec)
   104  			Expect(rawExec.ExecPluginCall.Received.PluginPath).To(Equal(pluginPath))
   105  			Expect(rawExec.ExecPluginCall.Received.StdinData).To(Equal(netconf))
   106  			Expect(rawExec.ExecPluginCall.Received.Environ).To(Equal([]string{"SOME=ENV"}))
   107  		})
   108  
   109  		Context("when the rawExec fails", func() {
   110  			BeforeEach(func() {
   111  				rawExec.ExecPluginCall.Returns.Error = errors.New("banana")
   112  			})
   113  			It("returns the error", func() {
   114  				err := invoke.ExecPluginWithoutResult(ctx, pluginPath, netconf, cniargs, pluginExec)
   115  				Expect(err).To(MatchError("banana"))
   116  			})
   117  		})
   118  
   119  		It("returns an error using the default exec interface", func() {
   120  			// pluginPath should not exist on-disk so we expect an error.
   121  			// This test simply tests that the default exec handler
   122  			// is run when the exec interface is nil.
   123  			err := invoke.ExecPluginWithoutResult(ctx, pluginPath, netconf, cniargs, nil)
   124  			Expect(err).To(HaveOccurred())
   125  		})
   126  	})
   127  
   128  	Describe("discovering the plugin version", func() {
   129  		BeforeEach(func() {
   130  			rawExec.ExecPluginCall.Returns.ResultBytes = []byte(`{ "some": "version-info" }`)
   131  		})
   132  
   133  		It("execs the plugin with the command VERSION", func() {
   134  			invoke.GetVersionInfo(ctx, pluginPath, pluginExec)
   135  			Expect(rawExec.ExecPluginCall.Received.PluginPath).To(Equal(pluginPath))
   136  			Expect(rawExec.ExecPluginCall.Received.Environ).To(ContainElement("CNI_COMMAND=VERSION"))
   137  			expectedStdin, _ := json.Marshal(map[string]string{"cniVersion": version.Current()})
   138  			Expect(rawExec.ExecPluginCall.Received.StdinData).To(MatchJSON(expectedStdin))
   139  		})
   140  
   141  		It("decodes and returns the version info", func() {
   142  			versionInfo, err := invoke.GetVersionInfo(ctx, pluginPath, pluginExec)
   143  			Expect(err).NotTo(HaveOccurred())
   144  			Expect(versionInfo.SupportedVersions()).To(Equal([]string{"0.42.0"}))
   145  			Expect(versionDecoder.DecodeCall.Received.JSONBytes).To(MatchJSON(`{ "some": "version-info" }`))
   146  		})
   147  
   148  		Context("when the rawExec fails", func() {
   149  			BeforeEach(func() {
   150  				rawExec.ExecPluginCall.Returns.Error = errors.New("banana")
   151  			})
   152  			It("returns the error", func() {
   153  				_, err := invoke.GetVersionInfo(ctx, pluginPath, pluginExec)
   154  				Expect(err).To(MatchError("banana"))
   155  			})
   156  		})
   157  
   158  		Context("when the plugin is too old to recognize the VERSION command", func() {
   159  			BeforeEach(func() {
   160  				rawExec.ExecPluginCall.Returns.Error = errors.New("unknown CNI_COMMAND: VERSION")
   161  			})
   162  
   163  			It("interprets the error as a 0.1.0 version", func() {
   164  				versionInfo, err := invoke.GetVersionInfo(ctx, pluginPath, pluginExec)
   165  				Expect(err).NotTo(HaveOccurred())
   166  				Expect(versionInfo.SupportedVersions()).To(ConsistOf("0.1.0"))
   167  			})
   168  
   169  			It("sets dummy values for env vars required by very old plugins", func() {
   170  				invoke.GetVersionInfo(ctx, pluginPath, pluginExec)
   171  
   172  				env := rawExec.ExecPluginCall.Received.Environ
   173  				Expect(env).To(ContainElement("CNI_NETNS=dummy"))
   174  				Expect(env).To(ContainElement("CNI_IFNAME=dummy"))
   175  				Expect(env).To(ContainElement("CNI_PATH=dummy"))
   176  			})
   177  		})
   178  	})
   179  })