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