github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/tools/lxdclient/remote_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // +build go1.3 5 6 package lxdclient_test 7 8 import ( 9 "net" 10 11 "github.com/juju/errors" 12 jc "github.com/juju/testing/checkers" 13 gc "gopkg.in/check.v1" 14 15 "github.com/juju/juju/tools/lxdclient" 16 ) 17 18 var ( 19 _ = gc.Suite(&remoteSuite{}) 20 _ = gc.Suite(&remoteFunctionalSuite{}) 21 ) 22 23 type remoteSuite struct { 24 lxdclient.BaseSuite 25 } 26 27 const ( 28 testingCert = ` 29 -----BEGIN CERTIFICATE----- 30 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 61 EiFwoj8EbmwqzbNpOTNAOdo= 62 -----END CERTIFICATE----- 63 ` 64 65 testingKey = ` 66 -----BEGIN RSA PRIVATE KEY----- 67 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 116 -----END RSA PRIVATE KEY----- 117 ` 118 ) 119 120 func (s *remoteSuite) TestWithDefaultsNoop(c *gc.C) { 121 remote := lxdclient.Remote{ 122 Name: "my-remote", 123 Host: "some-host", 124 Protocol: lxdclient.LXDProtocol, 125 Cert: s.Cert, 126 } 127 updated, err := remote.WithDefaults() 128 c.Assert(err, jc.ErrorIsNil) 129 err = updated.Validate() 130 131 c.Check(err, jc.ErrorIsNil) 132 c.Check(updated, jc.DeepEquals, remote) 133 } 134 135 func (s *remoteSuite) TestWithDefaultsMissingName(c *gc.C) { 136 remote := lxdclient.Remote{ 137 Name: "", 138 Host: "some-host", 139 Protocol: lxdclient.LXDProtocol, 140 Cert: s.Cert, 141 } 142 updated, err := remote.WithDefaults() 143 c.Assert(err, jc.ErrorIsNil) 144 145 c.Check(updated, jc.DeepEquals, remote) // Name is not updated. 146 } 147 148 // TODO(ericsnow) Move this test to a functional suite. 149 func (s *remoteSuite) TestWithDefaultsMissingCert(c *gc.C) { 150 lxdclient.PatchGenerateCertificate(&s.CleanupSuite, testingCert, testingKey) 151 remote := lxdclient.Remote{ 152 Name: "my-remote", 153 Host: "some-host", 154 Protocol: lxdclient.LXDProtocol, 155 Cert: nil, 156 } 157 updated, err := remote.WithDefaults() 158 c.Assert(err, jc.ErrorIsNil) 159 err = updated.Validate() 160 161 c.Check(err, jc.ErrorIsNil) 162 c.Assert(updated.Cert, gc.NotNil) 163 c.Check(updated.Cert.Validate(), jc.ErrorIsNil) 164 updated.Cert = nil // Validate ensured that the cert was okay. 165 c.Check(updated, jc.DeepEquals, lxdclient.Remote{ 166 Name: "my-remote", 167 Host: "some-host", 168 Protocol: lxdclient.LXDProtocol, 169 Cert: nil, 170 }) 171 } 172 173 func (s *remoteSuite) TestWithDefaultsMissingProtocol(c *gc.C) { 174 remote := lxdclient.Remote{ 175 Name: "my-remote", 176 Host: "some-host", 177 Cert: s.Cert, 178 } 179 updated, err := remote.WithDefaults() 180 c.Assert(err, jc.ErrorIsNil) 181 err = updated.Validate() 182 183 c.Check(err, jc.ErrorIsNil) 184 c.Assert(updated.Cert, gc.NotNil) 185 c.Check(updated.Cert.Validate(), jc.ErrorIsNil) 186 updated.Cert = nil // Validate ensured that the cert was okay. 187 c.Check(updated, jc.DeepEquals, lxdclient.Remote{ 188 Name: "my-remote", 189 Host: "some-host", 190 Protocol: lxdclient.LXDProtocol, 191 Cert: nil, 192 }) 193 } 194 195 func (s *remoteSuite) TestWithDefaultsZeroValue(c *gc.C) { 196 var remote lxdclient.Remote 197 updated, err := remote.WithDefaults() 198 c.Assert(err, jc.ErrorIsNil) 199 err = updated.Validate() 200 201 c.Check(err, jc.ErrorIsNil) 202 c.Check(updated, jc.DeepEquals, lxdclient.Remote{ 203 Name: "local", 204 Host: "", 205 Protocol: lxdclient.LXDProtocol, 206 Cert: nil, 207 }) 208 } 209 210 func (s *remoteSuite) TestWithDefaultsLocalNoop(c *gc.C) { 211 remote := lxdclient.Remote{ 212 Name: "my-local", 213 Host: "", 214 Cert: nil, 215 } 216 updated, err := remote.WithDefaults() 217 c.Assert(err, jc.ErrorIsNil) 218 err = updated.Validate() 219 220 c.Check(err, jc.ErrorIsNil) 221 c.Check(updated, jc.DeepEquals, lxdclient.Remote{ 222 Name: "my-local", 223 Host: "", 224 Protocol: lxdclient.LXDProtocol, 225 Cert: nil, 226 }) 227 } 228 229 func (s *remoteSuite) TestWithDefaultsLocalMissingName(c *gc.C) { 230 remote := lxdclient.Remote{ 231 Name: "", 232 Host: "", 233 Cert: nil, 234 } 235 updated, err := remote.WithDefaults() 236 c.Assert(err, jc.ErrorIsNil) 237 err = updated.Validate() 238 239 c.Check(err, jc.ErrorIsNil) 240 c.Check(updated, jc.DeepEquals, lxdclient.Remote{ 241 Name: "local", 242 Host: "", 243 Cert: nil, 244 Protocol: lxdclient.LXDProtocol, 245 }) 246 } 247 248 func (s *remoteSuite) TestValidateOkay(c *gc.C) { 249 remote := lxdclient.Remote{ 250 Name: "my-remote", 251 Host: "some-host", 252 Protocol: lxdclient.LXDProtocol, 253 Cert: s.Cert, 254 } 255 err := remote.Validate() 256 257 c.Check(err, jc.ErrorIsNil) 258 } 259 260 func (s *remoteSuite) TestValidateZeroValue(c *gc.C) { 261 var remote lxdclient.Remote 262 err := remote.Validate() 263 264 c.Check(err, jc.Satisfies, errors.IsNotValid) 265 } 266 267 func (s *remoteSuite) TestValidateMissingName(c *gc.C) { 268 remote := lxdclient.Remote{ 269 Name: "", 270 Host: "some-host", 271 Protocol: lxdclient.LXDProtocol, 272 Cert: s.Cert, 273 } 274 err := remote.Validate() 275 276 c.Check(err, jc.Satisfies, errors.IsNotValid) 277 } 278 279 func (s *remoteSuite) TestValidateMissingCert(c *gc.C) { 280 // We can have "public" remotes that don't require a client certificate 281 // to connect to and get images from. 282 remote := lxdclient.Remote{ 283 Name: "my-remote", 284 Host: "some-host", 285 Protocol: lxdclient.LXDProtocol, 286 Cert: nil, 287 } 288 err := remote.Validate() 289 290 c.Check(err, jc.ErrorIsNil) 291 } 292 293 func (s *remoteSuite) TestValidateBadCert(c *gc.C) { 294 remote := lxdclient.Remote{ 295 Name: "my-remote", 296 Host: "some-host", 297 Protocol: lxdclient.LXDProtocol, 298 Cert: &lxdclient.Cert{}, 299 } 300 err := remote.Validate() 301 302 c.Check(err, jc.Satisfies, errors.IsNotValid) 303 } 304 305 func (s *remoteSuite) TestValidateLocalOkay(c *gc.C) { 306 remote := lxdclient.Remote{ 307 Name: "my-local", 308 Host: "", 309 Protocol: lxdclient.LXDProtocol, 310 Cert: nil, 311 } 312 err := remote.Validate() 313 314 c.Check(err, jc.ErrorIsNil) 315 } 316 317 func (s *remoteSuite) TestValidateLocalMissingName(c *gc.C) { 318 remote := lxdclient.Remote{ 319 Name: "", 320 Host: "", 321 Protocol: lxdclient.LXDProtocol, 322 Cert: nil, 323 } 324 err := remote.Validate() 325 326 c.Check(err, jc.Satisfies, errors.IsNotValid) 327 } 328 329 func (s *remoteSuite) TestValidateLocalSimplestreamsInvalid(c *gc.C) { 330 remote := lxdclient.Remote{ 331 Name: "", 332 Host: "", 333 Protocol: lxdclient.SimplestreamsProtocol, 334 Cert: nil, 335 } 336 err := remote.Validate() 337 338 c.Check(err, jc.Satisfies, errors.IsNotValid) 339 } 340 341 func (s *remoteSuite) TestValidateLocalWithCert(c *gc.C) { 342 remote := lxdclient.Remote{ 343 Name: "my-local", 344 Host: "", 345 Protocol: lxdclient.LXDProtocol, 346 Cert: &lxdclient.Cert{}, 347 } 348 err := remote.Validate() 349 350 c.Check(err, jc.Satisfies, errors.IsNotValid) 351 } 352 353 func (s *remoteSuite) TestValidateSimplestreamsOkay(c *gc.C) { 354 remote := lxdclient.Remote{ 355 Name: "remote", 356 Host: "http://somewhere/else", 357 Protocol: lxdclient.SimplestreamsProtocol, 358 Cert: nil, 359 } 360 err := remote.Validate() 361 362 c.Check(err, jc.ErrorIsNil) 363 } 364 365 func (s *remoteSuite) TestValidateUnknownProtocol(c *gc.C) { 366 remote := lxdclient.Remote{ 367 Name: "remote", 368 Host: "http://somewhere/else", 369 Protocol: "bogus-protocol", 370 Cert: nil, 371 } 372 err := remote.Validate() 373 374 c.Check(err, jc.Satisfies, errors.IsNotValid) 375 } 376 377 func (s *remoteSuite) TestLocal(c *gc.C) { 378 expected := lxdclient.Remote{ 379 Name: "local", 380 Host: "", 381 Protocol: lxdclient.LXDProtocol, 382 Cert: nil, 383 } 384 c.Check(lxdclient.Local, jc.DeepEquals, expected) 385 } 386 387 func (s *remoteSuite) TestIDOkay(c *gc.C) { 388 remote := lxdclient.Remote{ 389 Name: "my-remote", 390 Host: "some-host", 391 Cert: s.Cert, 392 } 393 id := remote.ID() 394 395 c.Check(id, gc.Equals, "my-remote") 396 } 397 398 func (s *remoteSuite) TestIDLocal(c *gc.C) { 399 remote := lxdclient.Remote{ 400 Name: "my-remote", 401 Host: "", 402 Cert: s.Cert, 403 } 404 id := remote.ID() 405 406 c.Check(id, gc.Equals, "local") 407 } 408 409 func (s *remoteSuite) TestUsingTCPOkay(c *gc.C) { 410 c.Skip("not implemented yet") 411 // TODO(ericsnow) Finish this! 412 } 413 414 func (s *remoteSuite) TestUsingTCPNoop(c *gc.C) { 415 remote := lxdclient.Remote{ 416 Name: "my-remote", 417 Host: "some-host", 418 Protocol: lxdclient.LXDProtocol, 419 Cert: s.Cert, 420 } 421 nonlocal, err := remote.UsingTCP() 422 c.Assert(err, jc.ErrorIsNil) 423 424 c.Check(nonlocal, jc.DeepEquals, remote) 425 } 426 427 type remoteFunctionalSuite struct { 428 lxdclient.BaseSuite 429 } 430 431 func (s *remoteFunctionalSuite) TestUsingTCP(c *gc.C) { 432 if _, err := net.InterfaceByName(lxdclient.DefaultLXDBridge); err != nil { 433 c.Skip("network bridge interface not found") 434 } 435 lxdclient.PatchGenerateCertificate(&s.CleanupSuite, testingCert, testingKey) 436 437 remote := lxdclient.Remote{ 438 Name: "my-remote", 439 Host: "", 440 Cert: nil, 441 } 442 nonlocal, err := remote.UsingTCP() 443 c.Assert(err, jc.ErrorIsNil) 444 445 checkValidRemote(c, &nonlocal) 446 c.Check(nonlocal, jc.DeepEquals, lxdclient.Remote{ 447 Name: "my-remote", 448 Host: nonlocal.Host, 449 Protocol: lxdclient.LXDProtocol, 450 Cert: nonlocal.Cert, 451 }) 452 } 453 454 func checkValidRemote(c *gc.C, remote *lxdclient.Remote) { 455 c.Check(remote.Host, jc.Satisfies, isValidAddr) 456 checkValidCert(c, remote.Cert) 457 } 458 459 func isValidAddr(value interface{}) bool { 460 addr, ok := value.(string) 461 if !ok { 462 return false 463 } 464 return net.ParseIP(addr) != nil 465 }