github.com/john-lin/cni@v0.6.0-rc1.0.20170712150331-b69e640cc0e2/libcni/conf_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 libcni_test
    16  
    17  import (
    18  	"io/ioutil"
    19  	"os"
    20  	"path/filepath"
    21  
    22  	"github.com/containernetworking/cni/libcni"
    23  	"github.com/containernetworking/cni/pkg/types"
    24  	. "github.com/onsi/ginkgo"
    25  	. "github.com/onsi/gomega"
    26  )
    27  
    28  var _ = Describe("Loading configuration from disk", func() {
    29  	Describe("LoadConf", func() {
    30  		var (
    31  			configDir    string
    32  			pluginConfig []byte
    33  		)
    34  
    35  		BeforeEach(func() {
    36  			var err error
    37  			configDir, err = ioutil.TempDir("", "plugin-conf")
    38  			Expect(err).NotTo(HaveOccurred())
    39  
    40  			pluginConfig = []byte(`{ "name": "some-plugin", "some-key": "some-value" }`)
    41  			Expect(ioutil.WriteFile(filepath.Join(configDir, "50-whatever.conf"), pluginConfig, 0600)).To(Succeed())
    42  		})
    43  
    44  		AfterEach(func() {
    45  			Expect(os.RemoveAll(configDir)).To(Succeed())
    46  		})
    47  
    48  		It("finds the network config file for the plugin of the given type", func() {
    49  			netConfig, err := libcni.LoadConf(configDir, "some-plugin")
    50  			Expect(err).NotTo(HaveOccurred())
    51  			Expect(netConfig).To(Equal(&libcni.NetworkConfig{
    52  				Network: &types.NetConf{Name: "some-plugin"},
    53  				Bytes:   pluginConfig,
    54  			}))
    55  		})
    56  
    57  		Context("when the config directory does not exist", func() {
    58  			BeforeEach(func() {
    59  				Expect(os.RemoveAll(configDir)).To(Succeed())
    60  			})
    61  
    62  			It("returns a useful error", func() {
    63  				_, err := libcni.LoadConf(configDir, "some-plugin")
    64  				Expect(err).To(MatchError(libcni.NoConfigsFoundError{Dir: configDir}))
    65  			})
    66  		})
    67  
    68  		Context("when the config file is .json extension instead of .conf", func() {
    69  			BeforeEach(func() {
    70  				Expect(os.Remove(configDir + "/50-whatever.conf")).To(Succeed())
    71  				pluginConfig = []byte(`{ "name": "some-plugin", "some-key": "some-value" }`)
    72  				Expect(ioutil.WriteFile(filepath.Join(configDir, "50-whatever.json"), pluginConfig, 0600)).To(Succeed())
    73  			})
    74  			It("finds the network config file for the plugin of the given type", func() {
    75  				netConfig, err := libcni.LoadConf(configDir, "some-plugin")
    76  				Expect(err).NotTo(HaveOccurred())
    77  				Expect(netConfig).To(Equal(&libcni.NetworkConfig{
    78  					Network: &types.NetConf{Name: "some-plugin"},
    79  					Bytes:   pluginConfig,
    80  				}))
    81  			})
    82  		})
    83  
    84  		Context("when there is no config for the desired plugin", func() {
    85  			It("returns a useful error", func() {
    86  				_, err := libcni.LoadConf(configDir, "some-other-plugin")
    87  				Expect(err).To(MatchError(ContainSubstring(`no net configuration with name "some-other-plugin" in`)))
    88  			})
    89  		})
    90  
    91  		Context("when a config file is malformed", func() {
    92  			BeforeEach(func() {
    93  				Expect(ioutil.WriteFile(filepath.Join(configDir, "00-bad.conf"), []byte(`{`), 0600)).To(Succeed())
    94  			})
    95  
    96  			It("returns a useful error", func() {
    97  				_, err := libcni.LoadConf(configDir, "some-plugin")
    98  				Expect(err).To(MatchError(`error parsing configuration: unexpected end of JSON input`))
    99  			})
   100  		})
   101  
   102  		Context("when the config is in a nested subdir", func() {
   103  			BeforeEach(func() {
   104  				subdir := filepath.Join(configDir, "subdir1", "subdir2")
   105  				Expect(os.MkdirAll(subdir, 0700)).To(Succeed())
   106  
   107  				pluginConfig = []byte(`{ "name": "deep", "some-key": "some-value" }`)
   108  				Expect(ioutil.WriteFile(filepath.Join(subdir, "90-deep.conf"), pluginConfig, 0600)).To(Succeed())
   109  			})
   110  
   111  			It("will not find the config", func() {
   112  				_, err := libcni.LoadConf(configDir, "deep")
   113  				Expect(err).To(MatchError(HavePrefix("no net configuration with name")))
   114  			})
   115  		})
   116  	})
   117  
   118  	Describe("Capabilities", func() {
   119  		var configDir string
   120  
   121  		BeforeEach(func() {
   122  			var err error
   123  			configDir, err = ioutil.TempDir("", "plugin-conf")
   124  			Expect(err).NotTo(HaveOccurred())
   125  
   126  			pluginConfig := []byte(`{ "name": "some-plugin", "type": "noop", "cniVersion": "0.3.1", "capabilities": { "portMappings": true, "somethingElse": true, "noCapability": false } }`)
   127  			Expect(ioutil.WriteFile(filepath.Join(configDir, "50-whatever.conf"), pluginConfig, 0600)).To(Succeed())
   128  		})
   129  
   130  		AfterEach(func() {
   131  			Expect(os.RemoveAll(configDir)).To(Succeed())
   132  		})
   133  
   134  		It("reads plugin capabilities from network config", func() {
   135  			netConfig, err := libcni.LoadConf(configDir, "some-plugin")
   136  			Expect(err).NotTo(HaveOccurred())
   137  			Expect(netConfig.Network.Capabilities).To(Equal(map[string]bool{
   138  				"portMappings":  true,
   139  				"somethingElse": true,
   140  				"noCapability":  false,
   141  			}))
   142  		})
   143  	})
   144  
   145  	Describe("ConfFromFile", func() {
   146  		Context("when the file cannot be opened", func() {
   147  			It("returns a useful error", func() {
   148  				_, err := libcni.ConfFromFile("/tmp/nope/not-here")
   149  				Expect(err).To(MatchError(HavePrefix(`error reading /tmp/nope/not-here: open /tmp/nope/not-here`)))
   150  			})
   151  		})
   152  	})
   153  
   154  	Describe("LoadConfList", func() {
   155  		var (
   156  			configDir  string
   157  			configList []byte
   158  		)
   159  
   160  		BeforeEach(func() {
   161  			var err error
   162  			configDir, err = ioutil.TempDir("", "plugin-conf")
   163  			Expect(err).NotTo(HaveOccurred())
   164  
   165  			configList = []byte(`{
   166    "name": "some-list",
   167    "cniVersion": "0.2.0",
   168    "plugins": [
   169      {
   170        "type": "host-local",
   171        "subnet": "10.0.0.1/24"
   172      },
   173      {
   174        "type": "bridge",
   175        "mtu": 1400
   176      },
   177      {
   178        "type": "port-forwarding",
   179        "ports": {"20.0.0.1:8080": "80"}
   180      }
   181    ]
   182  }`)
   183  			Expect(ioutil.WriteFile(filepath.Join(configDir, "50-whatever.conflist"), configList, 0600)).To(Succeed())
   184  		})
   185  
   186  		AfterEach(func() {
   187  			Expect(os.RemoveAll(configDir)).To(Succeed())
   188  		})
   189  
   190  		It("finds the network config file for the plugin of the given type", func() {
   191  			netConfigList, err := libcni.LoadConfList(configDir, "some-list")
   192  			Expect(err).NotTo(HaveOccurred())
   193  			Expect(netConfigList).To(Equal(&libcni.NetworkConfigList{
   194  				Name:       "some-list",
   195  				CNIVersion: "0.2.0",
   196  				Plugins: []*libcni.NetworkConfig{
   197  					{
   198  						Network: &types.NetConf{Type: "host-local"},
   199  						Bytes:   []byte(`{"subnet":"10.0.0.1/24","type":"host-local"}`),
   200  					},
   201  					{
   202  						Network: &types.NetConf{Type: "bridge"},
   203  						Bytes:   []byte(`{"mtu":1400,"type":"bridge"}`),
   204  					},
   205  					{
   206  						Network: &types.NetConf{Type: "port-forwarding"},
   207  						Bytes:   []byte(`{"ports":{"20.0.0.1:8080":"80"},"type":"port-forwarding"}`),
   208  					},
   209  				},
   210  				Bytes: configList,
   211  			}))
   212  		})
   213  
   214  		Context("when there is a config file with the same name as the list", func() {
   215  			BeforeEach(func() {
   216  				configFile := []byte(`{
   217  					"name": "some-list",
   218  					"cniVersion": "0.2.0",
   219  					"type": "bridge"
   220  				}`)
   221  				Expect(ioutil.WriteFile(filepath.Join(configDir, "49-whatever.conf"), configFile, 0600)).To(Succeed())
   222  			})
   223  
   224  			It("Loads the config list first", func() {
   225  				netConfigList, err := libcni.LoadConfList(configDir, "some-list")
   226  				Expect(err).NotTo(HaveOccurred())
   227  				Expect(len(netConfigList.Plugins)).To(Equal(3))
   228  			})
   229  
   230  			It("falls back to the config file", func() {
   231  				Expect(os.Remove(filepath.Join(configDir, "50-whatever.conflist"))).To(Succeed())
   232  
   233  				netConfigList, err := libcni.LoadConfList(configDir, "some-list")
   234  				Expect(err).NotTo(HaveOccurred())
   235  				Expect(len(netConfigList.Plugins)).To(Equal(1))
   236  				Expect(netConfigList.Plugins[0].Network.Type).To(Equal("bridge"))
   237  			})
   238  		})
   239  
   240  		Context("when the config directory does not exist", func() {
   241  			BeforeEach(func() {
   242  				Expect(os.RemoveAll(configDir)).To(Succeed())
   243  			})
   244  
   245  			It("returns a useful error", func() {
   246  				_, err := libcni.LoadConfList(configDir, "some-plugin")
   247  				Expect(err).To(MatchError(libcni.NoConfigsFoundError{Dir: configDir}))
   248  			})
   249  		})
   250  
   251  		Context("when there is no config for the desired plugin list", func() {
   252  			It("returns a useful error", func() {
   253  				_, err := libcni.LoadConfList(configDir, "some-other-plugin")
   254  				Expect(err).To(MatchError(libcni.NotFoundError{configDir, "some-other-plugin"}))
   255  			})
   256  		})
   257  
   258  		Context("when a config file is malformed", func() {
   259  			BeforeEach(func() {
   260  				Expect(ioutil.WriteFile(filepath.Join(configDir, "00-bad.conflist"), []byte(`{`), 0600)).To(Succeed())
   261  			})
   262  
   263  			It("returns a useful error", func() {
   264  				_, err := libcni.LoadConfList(configDir, "some-plugin")
   265  				Expect(err).To(MatchError(`error parsing configuration list: unexpected end of JSON input`))
   266  			})
   267  		})
   268  
   269  		Context("when the config is in a nested subdir", func() {
   270  			BeforeEach(func() {
   271  				subdir := filepath.Join(configDir, "subdir1", "subdir2")
   272  				Expect(os.MkdirAll(subdir, 0700)).To(Succeed())
   273  
   274  				configList = []byte(`{
   275    "name": "deep",
   276    "cniVersion": "0.2.0",
   277    "plugins": [
   278      {
   279        "type": "host-local",
   280        "subnet": "10.0.0.1/24"
   281      },
   282    ]
   283  }`)
   284  				Expect(ioutil.WriteFile(filepath.Join(subdir, "90-deep.conflist"), configList, 0600)).To(Succeed())
   285  			})
   286  
   287  			It("will not find the config", func() {
   288  				_, err := libcni.LoadConfList(configDir, "deep")
   289  				Expect(err).To(MatchError(HavePrefix("no net configuration with name")))
   290  			})
   291  		})
   292  	})
   293  
   294  	Describe("ConfListFromFile", func() {
   295  		Context("when the file cannot be opened", func() {
   296  			It("returns a useful error", func() {
   297  				_, err := libcni.ConfListFromFile("/tmp/nope/not-here")
   298  				Expect(err).To(MatchError(HavePrefix(`error reading /tmp/nope/not-here: open /tmp/nope/not-here`)))
   299  			})
   300  		})
   301  	})
   302  
   303  	Describe("InjectConf", func() {
   304  		var testNetConfig *libcni.NetworkConfig
   305  
   306  		BeforeEach(func() {
   307  			testNetConfig = &libcni.NetworkConfig{Network: &types.NetConf{Name: "some-plugin"},
   308  				Bytes: []byte(`{ "name": "some-plugin" }`)}
   309  		})
   310  
   311  		Context("when function parameters are incorrect", func() {
   312  			It("returns unmarshal error", func() {
   313  				conf := &libcni.NetworkConfig{Network: &types.NetConf{Name: "some-plugin"},
   314  					Bytes: []byte(`{ cc cc cc}`)}
   315  
   316  				_, err := libcni.InjectConf(conf, map[string]interface{}{"": nil})
   317  				Expect(err).To(MatchError(HavePrefix(`unmarshal existing network bytes`)))
   318  			})
   319  
   320  			It("returns key  error", func() {
   321  				_, err := libcni.InjectConf(testNetConfig, map[string]interface{}{"": nil})
   322  				Expect(err).To(MatchError(HavePrefix(`keys cannot be empty`)))
   323  			})
   324  
   325  			It("returns newValue  error", func() {
   326  				_, err := libcni.InjectConf(testNetConfig, map[string]interface{}{"test": nil})
   327  				Expect(err).To(MatchError(HavePrefix(`key 'test' value must not be nil`)))
   328  			})
   329  		})
   330  
   331  		Context("when new string value added", func() {
   332  			It("adds the new key & value to the config", func() {
   333  				newPluginConfig := []byte(`{"name":"some-plugin","test":"test"}`)
   334  
   335  				resultConfig, err := libcni.InjectConf(testNetConfig, map[string]interface{}{"test": "test"})
   336  				Expect(err).NotTo(HaveOccurred())
   337  				Expect(resultConfig).To(Equal(&libcni.NetworkConfig{
   338  					Network: &types.NetConf{Name: "some-plugin"},
   339  					Bytes:   newPluginConfig,
   340  				}))
   341  			})
   342  
   343  			It("adds the new value for exiting key", func() {
   344  				newPluginConfig := []byte(`{"name":"some-plugin","test":"changedValue"}`)
   345  
   346  				resultConfig, err := libcni.InjectConf(testNetConfig, map[string]interface{}{"test": "test"})
   347  				Expect(err).NotTo(HaveOccurred())
   348  
   349  				resultConfig, err = libcni.InjectConf(resultConfig, map[string]interface{}{"test": "changedValue"})
   350  				Expect(err).NotTo(HaveOccurred())
   351  
   352  				Expect(resultConfig).To(Equal(&libcni.NetworkConfig{
   353  					Network: &types.NetConf{Name: "some-plugin"},
   354  					Bytes:   newPluginConfig,
   355  				}))
   356  			})
   357  
   358  			It("adds existing key & value", func() {
   359  				newPluginConfig := []byte(`{"name":"some-plugin","test":"test"}`)
   360  
   361  				resultConfig, err := libcni.InjectConf(testNetConfig, map[string]interface{}{"test": "test"})
   362  				Expect(err).NotTo(HaveOccurred())
   363  
   364  				resultConfig, err = libcni.InjectConf(resultConfig, map[string]interface{}{"test": "test"})
   365  				Expect(err).NotTo(HaveOccurred())
   366  
   367  				Expect(resultConfig).To(Equal(&libcni.NetworkConfig{
   368  					Network: &types.NetConf{Name: "some-plugin"},
   369  					Bytes:   newPluginConfig,
   370  				}))
   371  			})
   372  
   373  			It("adds sub-fields of NetworkConfig.Network to the config", func() {
   374  
   375  				expectedPluginConfig := []byte(`{"dns":{"domain":"local","nameservers":["server1","server2"]},"name":"some-plugin","type":"bridge"}`)
   376  				servers := []string{"server1", "server2"}
   377  				newDNS := &types.DNS{Nameservers: servers, Domain: "local"}
   378  
   379  				// inject DNS
   380  				resultConfig, err := libcni.InjectConf(testNetConfig, map[string]interface{}{"dns": newDNS})
   381  				Expect(err).NotTo(HaveOccurred())
   382  
   383  				// inject type
   384  				resultConfig, err = libcni.InjectConf(resultConfig, map[string]interface{}{"type": "bridge"})
   385  				Expect(err).NotTo(HaveOccurred())
   386  
   387  				Expect(resultConfig).To(Equal(&libcni.NetworkConfig{
   388  					Network: &types.NetConf{Name: "some-plugin", Type: "bridge", DNS: types.DNS{Nameservers: servers, Domain: "local"}},
   389  					Bytes:   expectedPluginConfig,
   390  				}))
   391  			})
   392  		})
   393  	})
   394  })
   395  
   396  var _ = Describe("ConfListFromConf", func() {
   397  	var testNetConfig *libcni.NetworkConfig
   398  
   399  	BeforeEach(func() {
   400  		pb := []byte(`{"name":"some-plugin","cniVersion":"0.3.1" }`)
   401  		tc, err := libcni.ConfFromBytes(pb)
   402  		Expect(err).NotTo(HaveOccurred())
   403  		testNetConfig = tc
   404  	})
   405  
   406  	It("correctly upconverts a NetworkConfig to a NetworkConfigList", func() {
   407  		ncl, err := libcni.ConfListFromConf(testNetConfig)
   408  		Expect(err).NotTo(HaveOccurred())
   409  		bytes := ncl.Bytes
   410  
   411  		// null out the json - we don't care about the exact marshalling
   412  		ncl.Bytes = nil
   413  		ncl.Plugins[0].Bytes = nil
   414  		testNetConfig.Bytes = nil
   415  
   416  		Expect(ncl).To(Equal(&libcni.NetworkConfigList{
   417  			Name:       "some-plugin",
   418  			CNIVersion: "0.3.1",
   419  			Plugins:    []*libcni.NetworkConfig{testNetConfig},
   420  		}))
   421  
   422  		//Test that the json unmarshals to the same data
   423  		ncl2, err := libcni.ConfListFromBytes(bytes)
   424  		Expect(err).NotTo(HaveOccurred())
   425  		ncl2.Bytes = nil
   426  		ncl2.Plugins[0].Bytes = nil
   427  
   428  		Expect(ncl2).To(Equal(ncl))
   429  	})
   430  
   431  })