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 })