github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/environs/config/config_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package config_test 5 6 import ( 7 "fmt" 8 "os" 9 "regexp" 10 "strings" 11 stdtesting "testing" 12 "time" 13 14 "github.com/juju/loggo" 15 "github.com/juju/schema" 16 gitjujutesting "github.com/juju/testing" 17 jc "github.com/juju/testing/checkers" 18 "github.com/juju/utils/proxy" 19 "github.com/juju/utils/series" 20 "github.com/juju/version" 21 gc "gopkg.in/check.v1" 22 "gopkg.in/juju/charmrepo.v2-unstable" 23 "gopkg.in/juju/environschema.v1" 24 "gopkg.in/macaroon-bakery.v1/bakery" 25 26 "github.com/juju/juju/cert" 27 "github.com/juju/juju/environs/config" 28 "github.com/juju/juju/juju/osenv" 29 "github.com/juju/juju/testing" 30 ) 31 32 func Test(t *stdtesting.T) { 33 gc.TestingT(t) 34 } 35 36 type ConfigSuite struct { 37 testing.FakeJujuXDGDataHomeSuite 38 home string 39 } 40 41 var _ = gc.Suite(&ConfigSuite{}) 42 43 func (s *ConfigSuite) SetUpTest(c *gc.C) { 44 s.FakeJujuXDGDataHomeSuite.SetUpTest(c) 45 // Make sure that the defaults are used, which 46 // is <root>=WARNING 47 loggo.ResetLoggers() 48 } 49 50 // sampleConfig holds a configuration with all required 51 // attributes set. 52 var sampleConfig = testing.Attrs{ 53 "type": "my-type", 54 "name": "my-name", 55 "uuid": testing.ModelTag.Id(), 56 "controller-uuid": testing.ModelTag.Id(), 57 "authorized-keys": testing.FakeAuthKeys, 58 "firewall-mode": config.FwInstance, 59 "admin-secret": "foo", 60 "unknown": "my-unknown", 61 "ca-cert": caCert, 62 "ssl-hostname-verification": true, 63 "development": false, 64 "state-port": 1234, 65 "api-port": 4321, 66 "default-series": series.LatestLts(), 67 } 68 69 type configTest struct { 70 about string 71 useDefaults config.Defaulting 72 attrs testing.Attrs 73 expected testing.Attrs 74 err string 75 } 76 77 var testResourceTags = []string{"a=b", "c=", "d=e"} 78 var testResourceTagsMap = map[string]string{ 79 "a": "b", "c": "", "d": "e", 80 } 81 82 var quotedPathSeparator = regexp.QuoteMeta(string(os.PathSeparator)) 83 84 var minimalConfigAttrs = testing.Attrs{ 85 "type": "my-type", 86 "name": "my-name", 87 "uuid": testing.ModelTag.Id(), 88 "controller-uuid": testing.ModelTag.Id(), 89 } 90 91 var configTests = []configTest{ 92 { 93 about: "The minimum good configuration", 94 useDefaults: config.UseDefaults, 95 attrs: minimalConfigAttrs, 96 }, { 97 about: "Agent Stream", 98 useDefaults: config.UseDefaults, 99 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 100 "image-metadata-url": "image-url", 101 "agent-stream": "released", 102 }), 103 }, { 104 about: "Deprecated tools-stream used", 105 useDefaults: config.UseDefaults, 106 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 107 "tools-stream": "tools-stream-value", 108 }), 109 }, { 110 about: "Deprecated tools-stream ignored", 111 useDefaults: config.UseDefaults, 112 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 113 "agent-stream": "released", 114 "tools-stream": "ignore-me", 115 }), 116 }, { 117 about: "Metadata URLs", 118 useDefaults: config.UseDefaults, 119 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 120 "image-metadata-url": "image-url", 121 "agent-metadata-url": "agent-metadata-url-value", 122 }), 123 }, { 124 about: "Deprecated tools metadata URL used", 125 useDefaults: config.UseDefaults, 126 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 127 "tools-metadata-url": "tools-metadata-url-value", 128 }), 129 }, { 130 about: "Deprecated tools metadata URL ignored", 131 useDefaults: config.UseDefaults, 132 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 133 "agent-metadata-url": "agent-metadata-url-value", 134 "tools-metadata-url": "ignore-me", 135 }), 136 }, { 137 about: "Explicit series", 138 useDefaults: config.UseDefaults, 139 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 140 "default-series": "my-series", 141 }), 142 }, { 143 about: "Implicit series with empty value", 144 useDefaults: config.UseDefaults, 145 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 146 "default-series": "", 147 }), 148 }, { 149 about: "Explicit logging", 150 useDefaults: config.UseDefaults, 151 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 152 "logging-config": "juju=INFO", 153 }), 154 }, { 155 about: "Explicit authorized-keys", 156 useDefaults: config.UseDefaults, 157 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 158 "authorized-keys": testing.FakeAuthKeys, 159 }), 160 }, { 161 about: "Load authorized-keys from path", 162 useDefaults: config.UseDefaults, 163 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 164 "authorized-keys-path": "~/.ssh/authorized_keys2", 165 }), 166 }, { 167 about: "LXC clone values", 168 useDefaults: config.UseDefaults, 169 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 170 "default-series": "precise", 171 "lxc-clone": true, 172 "lxc-clone-aufs": true, 173 }), 174 }, { 175 about: "Deprecated lxc-use-clone used", 176 useDefaults: config.UseDefaults, 177 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 178 "lxc-use-clone": true, 179 }), 180 }, { 181 about: "Deprecated lxc-use-clone ignored", 182 useDefaults: config.UseDefaults, 183 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 184 "lxc-use-clone": false, 185 "lxc-clone": true, 186 }), 187 }, { 188 about: "Allow LXC loop mounts true", 189 useDefaults: config.UseDefaults, 190 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 191 "allow-lxc-loop-mounts": "true", 192 }), 193 }, { 194 about: "Allow LXC loop mounts default", 195 useDefaults: config.UseDefaults, 196 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 197 "allow-lxc-loop-mounts": "false", 198 }), 199 expected: minimalConfigAttrs.Merge(testing.Attrs{ 200 "allow-lxc-loop-mounts": false, 201 }), 202 }, { 203 about: "LXC default MTU not set", 204 useDefaults: config.UseDefaults, 205 attrs: minimalConfigAttrs, 206 }, { 207 about: "LXC default MTU set explicitly", 208 useDefaults: config.UseDefaults, 209 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 210 "lxc-default-mtu": 9000, 211 }), 212 }, { 213 about: "LXC default MTU invalid (not a number)", 214 useDefaults: config.UseDefaults, 215 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 216 "lxc-default-mtu": "foo", 217 }), 218 err: `lxc-default-mtu: expected number, got string\("foo"\)`, 219 }, { 220 about: "LXC default MTU invalid (negative)", 221 useDefaults: config.UseDefaults, 222 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 223 "lxc-default-mtu": -42, 224 }), 225 err: `lxc-default-mtu: expected positive integer, got -42`, 226 }, { 227 about: "CA cert & key from path", 228 useDefaults: config.UseDefaults, 229 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 230 "ca-cert-path": "cacert2.pem", 231 "ca-private-key-path": "cakey2.pem", 232 }), 233 }, { 234 about: "CA cert & key from path; cert attribute set too", 235 useDefaults: config.UseDefaults, 236 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 237 "ca-cert-path": "cacert2.pem", 238 "ca-cert": "ignored", 239 "ca-private-key-path": "cakey2.pem", 240 }), 241 }, { 242 about: "CA cert & key from ~ path", 243 useDefaults: config.UseDefaults, 244 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 245 "ca-cert-path": "~/othercert.pem", 246 "ca-private-key-path": "~/otherkey.pem", 247 }), 248 }, { 249 about: "CA cert and key as attributes", 250 useDefaults: config.UseDefaults, 251 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 252 "ca-cert": caCert, 253 "ca-private-key": caKey, 254 }), 255 }, { 256 about: "Mismatched CA cert and key", 257 useDefaults: config.UseDefaults, 258 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 259 "ca-cert": caCert, 260 "ca-private-key": caKey2, 261 }), 262 err: "bad CA certificate/key in configuration: crypto/tls: private key does not match public key", 263 }, { 264 about: "Invalid CA cert", 265 useDefaults: config.UseDefaults, 266 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 267 "ca-cert": invalidCACert, 268 }), 269 err: `bad CA certificate/key in configuration: (asn1:|ASN\.1) syntax error:.*`, 270 }, { 271 about: "Invalid CA key", 272 useDefaults: config.UseDefaults, 273 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 274 "ca-cert": caCert, 275 "ca-private-key": invalidCAKey, 276 }), 277 err: "bad CA certificate/key in configuration: crypto/tls:.*", 278 }, { 279 about: "CA cert specified as non-existent file", 280 useDefaults: config.UseDefaults, 281 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 282 "ca-cert-path": "no-such-file", 283 }), 284 err: fmt.Sprintf(`open .*\.local%sshare%sjuju%sno-such-file: .*`, quotedPathSeparator, quotedPathSeparator, quotedPathSeparator), 285 }, { 286 about: "CA key specified as non-existent file", 287 useDefaults: config.UseDefaults, 288 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 289 "ca-private-key-path": "no-such-file", 290 }), 291 err: fmt.Sprintf(`open .*\.local%sshare%sjuju%sno-such-file: .*`, quotedPathSeparator, quotedPathSeparator, quotedPathSeparator), 292 }, { 293 about: "Specified agent version", 294 useDefaults: config.UseDefaults, 295 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 296 "authorized-keys": testing.FakeAuthKeys, 297 "agent-version": "1.2.3", 298 }), 299 }, { 300 about: "Specified development flag", 301 useDefaults: config.UseDefaults, 302 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 303 "authorized-keys": testing.FakeAuthKeys, 304 "development": true, 305 }), 306 }, { 307 about: "Specified admin secret", 308 useDefaults: config.UseDefaults, 309 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 310 "authorized-keys": testing.FakeAuthKeys, 311 "development": false, 312 "admin-secret": "pork", 313 }), 314 }, { 315 about: "Invalid development flag", 316 useDefaults: config.UseDefaults, 317 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 318 "authorized-keys": testing.FakeAuthKeys, 319 "development": "invalid", 320 }), 321 err: `development: expected bool, got string\("invalid"\)`, 322 }, { 323 about: "Invalid disable-network-management flag", 324 useDefaults: config.UseDefaults, 325 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 326 "authorized-keys": testing.FakeAuthKeys, 327 "disable-network-management": "invalid", 328 }), 329 err: `disable-network-management: expected bool, got string\("invalid"\)`, 330 }, { 331 about: "disable-network-management off", 332 useDefaults: config.UseDefaults, 333 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 334 "disable-network-management": false, 335 }), 336 }, { 337 about: "disable-network-management on", 338 useDefaults: config.UseDefaults, 339 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 340 "disable-network-management": true, 341 }), 342 }, { 343 about: "Invalid ignore-machine-addresses flag", 344 useDefaults: config.UseDefaults, 345 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 346 "ignore-machine-addresses": "invalid", 347 }), 348 err: `ignore-machine-addresses: expected bool, got string\("invalid"\)`, 349 }, { 350 about: "ignore-machine-addresses off", 351 useDefaults: config.UseDefaults, 352 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 353 "ignore-machine-addresses": false, 354 }), 355 }, { 356 about: "ignore-machine-addresses on", 357 useDefaults: config.UseDefaults, 358 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 359 "ignore-machine-addresses": true, 360 }), 361 }, { 362 about: "set-numa-control-policy on", 363 useDefaults: config.UseDefaults, 364 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 365 "set-numa-control-policy": true, 366 }), 367 }, { 368 about: "set-numa-control-policy off", 369 useDefaults: config.UseDefaults, 370 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 371 "set-numa-control-policy": false, 372 }), 373 }, { 374 about: "block-destroy-model on", 375 useDefaults: config.UseDefaults, 376 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 377 "block-destroy-model": true, 378 }), 379 }, { 380 about: "block-destroy-model off", 381 useDefaults: config.UseDefaults, 382 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 383 "block-destroy-model": false, 384 }), 385 }, { 386 about: "block-remove-object on", 387 useDefaults: config.UseDefaults, 388 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 389 "block-remove-object": true, 390 }), 391 }, { 392 about: "block-remove-object off", 393 useDefaults: config.UseDefaults, 394 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 395 "block-remove-object": false, 396 }), 397 }, { 398 about: "block-all-changes on", 399 useDefaults: config.UseDefaults, 400 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 401 "block-all-changes": true, 402 }), 403 }, { 404 about: "block-all-changes off", 405 useDefaults: config.UseDefaults, 406 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 407 "block-all-changest": false, 408 }), 409 }, { 410 about: "Invalid prefer-ipv6 flag", 411 useDefaults: config.UseDefaults, 412 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 413 "authorized-keys": testing.FakeAuthKeys, 414 "prefer-ipv6": "invalid", 415 }), 416 err: `prefer-ipv6: expected bool, got string\("invalid"\)`, 417 }, { 418 about: "prefer-ipv6 off", 419 useDefaults: config.UseDefaults, 420 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 421 "prefer-ipv6": false, 422 }), 423 }, { 424 about: "prefer-ipv6 on", 425 useDefaults: config.UseDefaults, 426 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 427 "prefer-ipv6": true, 428 }), 429 }, { 430 about: "Invalid agent version", 431 useDefaults: config.UseDefaults, 432 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 433 "authorized-keys": testing.FakeAuthKeys, 434 "agent-version": "2", 435 }), 436 err: `invalid agent version in model configuration: "2"`, 437 }, { 438 about: "Missing type", 439 useDefaults: config.UseDefaults, 440 attrs: minimalConfigAttrs.Delete("type"), 441 err: "type: expected string, got nothing", 442 }, { 443 about: "Empty type", 444 useDefaults: config.UseDefaults, 445 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 446 "type": "", 447 }), 448 err: "empty type in model configuration", 449 }, { 450 about: "Missing name", 451 useDefaults: config.UseDefaults, 452 attrs: minimalConfigAttrs.Delete("name"), 453 err: "name: expected string, got nothing", 454 }, { 455 about: "Bad name, no slash", 456 useDefaults: config.UseDefaults, 457 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 458 "name": "foo/bar", 459 }), 460 err: "model name contains unsafe characters", 461 }, { 462 about: "Bad name, no backslash", 463 useDefaults: config.UseDefaults, 464 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 465 "name": "foo\\bar", 466 }), 467 err: "model name contains unsafe characters", 468 }, { 469 about: "Empty name", 470 useDefaults: config.UseDefaults, 471 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 472 "name": "", 473 }), 474 err: "empty name in model configuration", 475 }, { 476 about: "Default firewall mode", 477 useDefaults: config.UseDefaults, 478 attrs: minimalConfigAttrs, 479 }, { 480 about: "Empty firewall mode", 481 useDefaults: config.UseDefaults, 482 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 483 "firewall-mode": "", 484 }), 485 }, { 486 about: "Instance firewall mode", 487 useDefaults: config.UseDefaults, 488 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 489 "firewall-mode": config.FwInstance, 490 }), 491 }, { 492 about: "Global firewall mode", 493 useDefaults: config.UseDefaults, 494 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 495 "firewall-mode": config.FwGlobal, 496 }), 497 }, { 498 about: "None firewall mode", 499 useDefaults: config.UseDefaults, 500 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 501 "firewall-mode": config.FwNone, 502 }), 503 }, { 504 about: "Illegal firewall mode", 505 useDefaults: config.UseDefaults, 506 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 507 "firewall-mode": "illegal", 508 }), 509 err: `firewall-mode: expected one of \[instance global none ], got "illegal"`, 510 }, { 511 about: "ssl-hostname-verification off", 512 useDefaults: config.UseDefaults, 513 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 514 "ssl-hostname-verification": false, 515 }), 516 }, { 517 about: "ssl-hostname-verification incorrect", 518 useDefaults: config.UseDefaults, 519 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 520 "ssl-hostname-verification": "yes please", 521 }), 522 err: `ssl-hostname-verification: expected bool, got string\("yes please"\)`, 523 }, { 524 about: fmt.Sprintf( 525 "%s: %s", 526 "provisioner-harvest-mode", 527 config.HarvestAll.String(), 528 ), 529 useDefaults: config.UseDefaults, 530 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 531 "provisioner-harvest-mode": config.HarvestAll.String(), 532 }), 533 }, { 534 about: fmt.Sprintf( 535 "%s: %s", 536 "provisioner-harvest-mode", 537 config.HarvestDestroyed.String(), 538 ), 539 useDefaults: config.UseDefaults, 540 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 541 "provisioner-harvest-mode": config.HarvestDestroyed.String(), 542 }), 543 }, { 544 about: fmt.Sprintf( 545 "%s: %s", 546 "provisioner-harvest-mode", 547 config.HarvestUnknown.String(), 548 ), 549 useDefaults: config.UseDefaults, 550 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 551 "provisioner-harvest-mode": config.HarvestUnknown.String(), 552 }), 553 }, { 554 about: fmt.Sprintf( 555 "%s: %s", 556 "provisioner-harvest-mode", 557 config.HarvestNone.String(), 558 ), 559 useDefaults: config.UseDefaults, 560 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 561 "provisioner-harvest-mode": config.HarvestNone.String(), 562 }), 563 }, { 564 about: "provisioner-harvest-mode: incorrect", 565 useDefaults: config.UseDefaults, 566 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 567 "provisioner-harvest-mode": "yes please", 568 }), 569 err: `provisioner-harvest-mode: expected one of \[all none unknown destroyed], got "yes please"`, 570 }, { 571 about: "default image stream", 572 useDefaults: config.UseDefaults, 573 attrs: minimalConfigAttrs, 574 }, { 575 about: "explicit image stream", 576 useDefaults: config.UseDefaults, 577 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 578 "image-stream": "daily", 579 }), 580 }, { 581 about: "explicit tools stream", 582 useDefaults: config.UseDefaults, 583 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 584 "agent-stream": "proposed", 585 }), 586 }, { 587 about: "Explicit state port", 588 useDefaults: config.UseDefaults, 589 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 590 "state-port": 37042, 591 }), 592 }, { 593 about: "Invalid state port", 594 useDefaults: config.UseDefaults, 595 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 596 "state-port": "illegal", 597 }), 598 err: `state-port: expected number, got string\("illegal"\)`, 599 }, { 600 about: "Explicit API port", 601 useDefaults: config.UseDefaults, 602 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 603 "api-port": 77042, 604 }), 605 }, { 606 about: "Invalid API port", 607 useDefaults: config.UseDefaults, 608 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 609 "api-port": "illegal", 610 }), 611 err: `api-port: expected number, got string\("illegal"\)`, 612 }, { 613 about: "Explicit bootstrap timeout", 614 useDefaults: config.UseDefaults, 615 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 616 "bootstrap-timeout": 300, 617 }), 618 }, { 619 about: "Invalid bootstrap timeout", 620 useDefaults: config.UseDefaults, 621 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 622 "bootstrap-timeout": "illegal", 623 }), 624 err: `bootstrap-timeout: expected number, got string\("illegal"\)`, 625 }, { 626 about: "Explicit bootstrap retry delay", 627 useDefaults: config.UseDefaults, 628 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 629 "bootstrap-retry-delay": 5, 630 }), 631 }, { 632 about: "Invalid bootstrap retry delay", 633 useDefaults: config.UseDefaults, 634 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 635 "bootstrap-retry-delay": "illegal", 636 }), 637 err: `bootstrap-retry-delay: expected number, got string\("illegal"\)`, 638 }, { 639 about: "Explicit bootstrap addresses delay", 640 useDefaults: config.UseDefaults, 641 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 642 "bootstrap-addresses-delay": 15, 643 }), 644 }, { 645 about: "Invalid bootstrap addresses delay", 646 useDefaults: config.UseDefaults, 647 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 648 "bootstrap-addresses-delay": "illegal", 649 }), 650 err: `bootstrap-addresses-delay: expected number, got string\("illegal"\)`, 651 }, { 652 about: "Invalid logging configuration", 653 useDefaults: config.UseDefaults, 654 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 655 "logging-config": "foo=bar", 656 }), 657 err: `unknown severity level "bar"`, 658 }, { 659 about: "Sample configuration", 660 useDefaults: config.UseDefaults, 661 attrs: sampleConfig, 662 }, { 663 about: "No defaults: sample configuration", 664 useDefaults: config.NoDefaults, 665 attrs: sampleConfig, 666 }, { 667 about: "No defaults: with ca-cert-path", 668 useDefaults: config.NoDefaults, 669 attrs: sampleConfig.Merge(testing.Attrs{"ca-cert-path": "arble"}), 670 err: `attribute "ca-cert-path" is not allowed in configuration`, 671 }, { 672 about: "No defaults: with ca-private-key-path", 673 useDefaults: config.NoDefaults, 674 attrs: sampleConfig.Merge(testing.Attrs{"ca-private-key-path": "arble"}), 675 err: `attribute "ca-private-key-path" is not allowed in configuration`, 676 }, { 677 about: "No defaults: with authorized-keys-path", 678 useDefaults: config.NoDefaults, 679 attrs: sampleConfig.Merge(testing.Attrs{"authorized-keys-path": "arble"}), 680 err: `attribute "authorized-keys-path" is not allowed in configuration`, 681 }, { 682 about: "No defaults: missing authorized-keys", 683 useDefaults: config.NoDefaults, 684 attrs: sampleConfig.Delete("authorized-keys"), 685 err: `authorized-keys missing from model configuration`, 686 }, { 687 about: "Config settings from juju 1.13.3 actual installation", 688 useDefaults: config.NoDefaults, 689 attrs: map[string]interface{}{ 690 "name": "sample", 691 "development": false, 692 "admin-secret": "", 693 "ssl-hostname-verification": true, 694 "authorized-keys": "ssh-rsa mykeys rog@rog-x220\n", 695 "region": "us-east-1", 696 "image-metadata-url": "", 697 "ca-private-key": "", 698 "default-series": "precise", 699 "agent-metadata-url": "", 700 "secret-key": "a-secret-key", 701 "access-key": "an-access-key", 702 "agent-version": "1.13.2", 703 "ca-cert": caCert, 704 "firewall-mode": "instance", 705 "type": "ec2", 706 // These ones weren't actual values, but we require 707 // them now, and have no backwards-compatibility 708 // with the old config. 709 "uuid": testing.ModelTag.Id(), 710 "controller-uuid": testing.ModelTag.Id(), 711 }, 712 }, { 713 about: "Provider type null is replaced with manual", 714 useDefaults: config.UseDefaults, 715 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 716 "type": "null", 717 }), 718 }, { 719 about: "TestMode flag specified", 720 useDefaults: config.UseDefaults, 721 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 722 "test-mode": true, 723 }), 724 }, { 725 about: "valid uuid", 726 useDefaults: config.UseDefaults, 727 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 728 "uuid": "dcfbdb4a-bca2-49ad-aa7c-f011424e0fe4", 729 }), 730 }, { 731 about: "valid controller-uuid", 732 useDefaults: config.UseDefaults, 733 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 734 "controller-uuid": "dcfbdb4a-bca2-49ad-aa7c-f011424e0fe4", 735 }), 736 }, { 737 about: "invalid uuid 1", 738 useDefaults: config.UseDefaults, 739 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 740 "uuid": "dcfbdb4abca249adaa7cf011424e0fe4", 741 }), 742 err: `uuid: expected UUID, got string\("dcfbdb4abca249adaa7cf011424e0fe4"\)`, 743 }, { 744 about: "invalid uuid 2", 745 useDefaults: config.UseDefaults, 746 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 747 "uuid": "uuid", 748 }), 749 err: `uuid: expected UUID, got string\("uuid"\)`, 750 }, { 751 about: "blank uuid", 752 useDefaults: config.UseDefaults, 753 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 754 "uuid": "", 755 }), 756 err: `empty uuid in model configuration`, 757 }, 758 missingAttributeNoDefault("firewall-mode"), 759 missingAttributeNoDefault("development"), 760 missingAttributeNoDefault("ssl-hostname-verification"), 761 // TODO(rog) reinstate these tests when we can lose 762 // backward compatibility with pre-1.13 config. 763 // missingAttributeNoDefault("state-port"), 764 // missingAttributeNoDefault("api-port"), 765 { 766 about: "Deprecated safe-mode failover", 767 useDefaults: config.UseDefaults, 768 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 769 "provisioner-safe-mode": true, 770 "provisioner-harvest-mode": config.HarvestNone.String(), 771 }), 772 }, 773 { 774 about: "Explicit apt-mirror", 775 useDefaults: config.UseDefaults, 776 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 777 "apt-mirror": "http://my.archive.ubuntu.com", 778 }), 779 }, 780 { 781 about: "Resource tags as space-separated string", 782 useDefaults: config.UseDefaults, 783 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 784 "resource-tags": strings.Join(testResourceTags, " "), 785 }), 786 }, 787 { 788 about: "Resource tags as list of strings", 789 useDefaults: config.UseDefaults, 790 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 791 "resource-tags": testResourceTags, 792 }), 793 }, 794 { 795 about: "Resource tags contains non-keyvalues", 796 useDefaults: config.UseDefaults, 797 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 798 "resource-tags": []string{"a"}, 799 }), 800 err: `resource-tags: expected "key=value", got "a"`, 801 }, 802 { 803 about: "Invalid identity URL value", 804 useDefaults: config.UseDefaults, 805 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 806 "identity-url": "%", 807 }), 808 err: `invalid identity URL: parse %: invalid URL escape "%"`, 809 }, { 810 about: "Not using https in identity URL", 811 useDefaults: config.UseDefaults, 812 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 813 "identity-url": "http://test-identity", 814 }), 815 err: `URL needs to be https`, 816 }, 817 { 818 about: "Invalid identity public key", 819 useDefaults: config.UseDefaults, 820 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 821 "identity-public-key": "_", 822 }), 823 err: `invalid identity public key: cannot decode base64 key: illegal base64 data at input byte 0`, 824 }, 825 { 826 about: "Valid identity URL and public key values", 827 useDefaults: config.UseDefaults, 828 attrs: minimalConfigAttrs.Merge(testing.Attrs{ 829 "identity-url": "https://test-identity", 830 "identity-public-key": "o/yOqSNWncMo1GURWuez/dGR30TscmmuIxgjztpoHEY=", 831 }), 832 }, 833 } 834 835 func missingAttributeNoDefault(attrName string) configTest { 836 return configTest{ 837 about: fmt.Sprintf("No default: missing %s", attrName), 838 useDefaults: config.NoDefaults, 839 attrs: sampleConfig.Delete(attrName), 840 err: fmt.Sprintf("%s: expected [a-z]+, got nothing", attrName), 841 } 842 } 843 844 type testFile struct { 845 name, data string 846 } 847 848 func (s *ConfigSuite) TestConfig(c *gc.C) { 849 files := []gitjujutesting.TestFile{ 850 {".ssh/id_dsa.pub", "dsa"}, 851 {".ssh/id_rsa.pub", "rsa\n"}, 852 {".ssh/identity.pub", "identity"}, 853 {".ssh/authorized_keys", "auth0\n# first\nauth1\n\n"}, 854 {".ssh/authorized_keys2", "auth2\nauth3\n"}, 855 856 {".local/share/juju/my-name-cert.pem", caCert}, 857 {".local/share/juju/my-name-private-key.pem", caKey}, 858 {".local/share/juju/cacert2.pem", caCert2}, 859 {".local/share/juju/cakey2.pem", caKey2}, 860 {"othercert.pem", caCert3}, 861 {"otherkey.pem", caKey3}, 862 } 863 s.FakeHomeSuite.Home.AddFiles(c, files...) 864 for i, test := range configTests { 865 c.Logf("test %d. %s", i, test.about) 866 test.check(c, s.FakeHomeSuite.Home) 867 } 868 } 869 870 var noCertFilesTests = []configTest{ 871 { 872 about: "Unspecified certificate and key", 873 useDefaults: config.UseDefaults, 874 attrs: testing.Attrs{ 875 "type": "my-type", 876 "name": "my-name", 877 "uuid": testing.ModelTag.Id(), 878 "controller-uuid": testing.ModelTag.Id(), 879 "authorized-keys": testing.FakeAuthKeys, 880 }, 881 }, { 882 about: "Unspecified certificate, specified key", 883 useDefaults: config.UseDefaults, 884 attrs: testing.Attrs{ 885 "type": "my-type", 886 "name": "my-name", 887 "uuid": testing.ModelTag.Id(), 888 "controller-uuid": testing.ModelTag.Id(), 889 "authorized-keys": testing.FakeAuthKeys, 890 "ca-private-key": caKey, 891 }, 892 err: "bad CA certificate/key in configuration: crypto/tls:.*", 893 }, 894 } 895 896 func (s *ConfigSuite) TestConfigNoCertFiles(c *gc.C) { 897 for i, test := range noCertFilesTests { 898 c.Logf("test %d. %s", i, test.about) 899 test.check(c, s.FakeHomeSuite.Home) 900 } 901 } 902 903 var emptyCertFilesTests = []configTest{ 904 { 905 about: "Cert unspecified; key specified", 906 useDefaults: config.UseDefaults, 907 attrs: testing.Attrs{ 908 "type": "my-type", 909 "name": "my-name", 910 "uuid": testing.ModelTag.Id(), 911 "controller-uuid": testing.ModelTag.Id(), 912 "authorized-keys": testing.FakeAuthKeys, 913 "ca-private-key": caKey, 914 }, 915 err: fmt.Sprintf(`file ".*%smy-name-cert.pem" is empty`, regexp.QuoteMeta(string(os.PathSeparator))), 916 }, { 917 about: "Cert and key unspecified", 918 useDefaults: config.UseDefaults, 919 attrs: testing.Attrs{ 920 "type": "my-type", 921 "name": "my-name", 922 "uuid": testing.ModelTag.Id(), 923 "controller-uuid": testing.ModelTag.Id(), 924 "authorized-keys": testing.FakeAuthKeys, 925 }, 926 err: fmt.Sprintf(`file ".*%smy-name-cert.pem" is empty`, regexp.QuoteMeta(string(os.PathSeparator))), 927 }, { 928 about: "Cert specified, key unspecified", 929 useDefaults: config.UseDefaults, 930 attrs: testing.Attrs{ 931 "type": "my-type", 932 "name": "my-name", 933 "uuid": testing.ModelTag.Id(), 934 "controller-uuid": testing.ModelTag.Id(), 935 "authorized-keys": testing.FakeAuthKeys, 936 "ca-cert": caCert, 937 }, 938 err: fmt.Sprintf(`file ".*%smy-name-private-key.pem" is empty`, regexp.QuoteMeta(string(os.PathSeparator))), 939 }, /* { 940 about: "Cert and key specified as absent", 941 useDefaults: config.UseDefaults, 942 attrs: testing.Attrs{ 943 "type": "my-type", 944 "name": "my-name", 945 "authorized-keys": testing.FakeAuthKeys, 946 "ca-cert": "", 947 "ca-private-key": "", 948 }, 949 }, { 950 about: "Cert specified as absent", 951 useDefaults: config.UseDefaults, 952 attrs: testing.Attrs{ 953 "type": "my-type", 954 "name": "my-name", 955 "authorized-keys": testing.FakeAuthKeys, 956 "ca-cert": "", 957 }, 958 err: "bad CA certificate/key in configuration: crypto/tls: .*", 959 }, */ 960 } 961 962 func (s *ConfigSuite) TestConfigEmptyCertFiles(c *gc.C) { 963 files := []gitjujutesting.TestFile{ 964 {".local/share/juju/my-name-cert.pem", ""}, 965 {".local/share/juju/my-name-private-key.pem", ""}, 966 } 967 s.FakeHomeSuite.Home.AddFiles(c, files...) 968 969 for i, test := range emptyCertFilesTests { 970 c.Logf("test %d. %s", i, test.about) 971 test.check(c, s.FakeHomeSuite.Home) 972 } 973 } 974 975 func (s *ConfigSuite) TestNoDefinedPrivateCert(c *gc.C) { 976 // Server-side there is no juju home. 977 osenv.SetJujuXDGDataHome("") 978 attrs := testing.Attrs{ 979 "type": "my-type", 980 "name": "my-name", 981 "uuid": testing.ModelTag.Id(), 982 "controller-uuid": testing.ModelTag.Id(), 983 "authorized-keys": testing.FakeAuthKeys, 984 "ca-cert": testing.CACert, 985 "ca-private-key": "", 986 } 987 988 _, err := config.New(config.UseDefaults, attrs) 989 c.Assert(err, jc.ErrorIsNil) 990 } 991 992 func (s *ConfigSuite) TestSafeModeDeprecatesGracefully(c *gc.C) { 993 994 cfg, err := config.New(config.UseDefaults, testing.Attrs{ 995 "name": "name", 996 "type": "type", 997 "uuid": testing.ModelTag.Id(), 998 "controller-uuid": testing.ModelTag.Id(), 999 "provisioner-safe-mode": false, 1000 }) 1001 c.Assert(err, jc.ErrorIsNil) 1002 1003 c.Check( 1004 cfg.ProvisionerHarvestMode().String(), 1005 gc.Equals, 1006 config.HarvestAll.String(), 1007 ) 1008 1009 cfg, err = config.New(config.UseDefaults, testing.Attrs{ 1010 "name": "name", 1011 "type": "type", 1012 "uuid": testing.ModelTag.Id(), 1013 "controller-uuid": testing.ModelTag.Id(), 1014 "provisioner-safe-mode": true, 1015 }) 1016 c.Assert(err, jc.ErrorIsNil) 1017 1018 c.Check( 1019 cfg.ProvisionerHarvestMode().String(), 1020 gc.Equals, 1021 config.HarvestDestroyed.String(), 1022 ) 1023 } 1024 1025 func (test configTest) check(c *gc.C, home *gitjujutesting.FakeHome) { 1026 cfg, err := config.New(test.useDefaults, test.attrs) 1027 if test.err != "" { 1028 c.Check(cfg, gc.IsNil) 1029 c.Assert(err, gc.ErrorMatches, test.err) 1030 return 1031 } 1032 c.Assert(err, jc.ErrorIsNil) 1033 1034 typ, _ := test.attrs["type"].(string) 1035 // "null" has been deprecated in favour of "manual", 1036 // and is automatically switched. 1037 if typ == "null" { 1038 typ = "manual" 1039 } 1040 name, _ := test.attrs["name"].(string) 1041 c.Assert(cfg.Type(), gc.Equals, typ) 1042 c.Assert(cfg.Name(), gc.Equals, name) 1043 agentVersion, ok := cfg.AgentVersion() 1044 if s := test.attrs["agent-version"]; s != nil { 1045 c.Assert(ok, jc.IsTrue) 1046 c.Assert(agentVersion, gc.Equals, version.MustParse(s.(string))) 1047 } else { 1048 c.Assert(ok, jc.IsFalse) 1049 c.Assert(agentVersion, gc.Equals, version.Zero) 1050 } 1051 1052 if statePort, ok := test.attrs["state-port"]; ok { 1053 c.Assert(cfg.StatePort(), gc.Equals, statePort) 1054 } 1055 if apiPort, ok := test.attrs["api-port"]; ok { 1056 c.Assert(cfg.APIPort(), gc.Equals, apiPort) 1057 } 1058 if expected, ok := test.attrs["uuid"]; ok { 1059 c.Assert(cfg.UUID(), gc.Equals, expected) 1060 } 1061 if expected, ok := test.attrs["controller-uuid"]; ok { 1062 c.Assert(cfg.ControllerUUID(), gc.Equals, expected) 1063 } 1064 if identityURL, ok := test.attrs["identity-url"]; ok { 1065 c.Assert(cfg.IdentityURL(), gc.Equals, identityURL) 1066 } 1067 if identityPublicKey, ok := test.attrs["identity-public-key"]; ok { 1068 var pk bakery.PublicKey 1069 err := pk.UnmarshalText([]byte(identityPublicKey.(string))) 1070 c.Assert(err, jc.ErrorIsNil) 1071 c.Assert(cfg.IdentityPublicKey(), gc.DeepEquals, &pk) 1072 } 1073 1074 dev, _ := test.attrs["development"].(bool) 1075 c.Assert(cfg.Development(), gc.Equals, dev) 1076 1077 testmode, _ := test.attrs["test-mode"].(bool) 1078 c.Assert(cfg.TestMode(), gc.Equals, testmode) 1079 1080 series, _ := test.attrs["default-series"].(string) 1081 if defaultSeries, ok := cfg.DefaultSeries(); ok { 1082 c.Assert(defaultSeries, gc.Equals, series) 1083 } else { 1084 c.Assert(series, gc.Equals, "") 1085 c.Assert(defaultSeries, gc.Equals, "") 1086 } 1087 1088 if m, _ := test.attrs["firewall-mode"].(string); m != "" { 1089 c.Assert(cfg.FirewallMode(), gc.Equals, m) 1090 } 1091 if secret, _ := test.attrs["admin-secret"].(string); secret != "" { 1092 c.Assert(cfg.AdminSecret(), gc.Equals, secret) 1093 } 1094 1095 if path, _ := test.attrs["authorized-keys-path"].(string); path != "" { 1096 c.Assert(cfg.AuthorizedKeys(), gc.Equals, home.FileContents(c, path)) 1097 c.Assert(cfg.AllAttrs()["authorized-keys-path"], gc.IsNil) 1098 } else if keys, _ := test.attrs["authorized-keys"].(string); keys != "" { 1099 c.Assert(cfg.AuthorizedKeys(), gc.Equals, keys) 1100 } else { 1101 // Content of all the files that are read by default. 1102 c.Assert(cfg.AuthorizedKeys(), gc.Equals, "dsa\nrsa\nidentity\n") 1103 } 1104 1105 cert, certPresent := cfg.CACert() 1106 if path, _ := test.attrs["ca-cert-path"].(string); path != "" { 1107 c.Assert(certPresent, jc.IsTrue) 1108 c.Assert(string(cert), gc.Equals, home.FileContents(c, path)) 1109 } else if v, ok := test.attrs["ca-cert"].(string); v != "" { 1110 c.Assert(certPresent, jc.IsTrue) 1111 c.Assert(string(cert), gc.Equals, v) 1112 } else if ok { 1113 c.Check(cert, gc.HasLen, 0) 1114 c.Assert(certPresent, jc.IsFalse) 1115 } else if bool(test.useDefaults) && home.FileExists(".local/share/juju/my-name-cert.pem") { 1116 c.Assert(certPresent, jc.IsTrue) 1117 c.Assert(string(cert), gc.Equals, home.FileContents(c, "my-name-cert.pem")) 1118 } else { 1119 c.Check(cert, gc.HasLen, 0) 1120 c.Assert(certPresent, jc.IsFalse) 1121 } 1122 1123 key, keyPresent := cfg.CAPrivateKey() 1124 if path, _ := test.attrs["ca-private-key-path"].(string); path != "" { 1125 c.Assert(keyPresent, jc.IsTrue) 1126 c.Assert(string(key), gc.Equals, home.FileContents(c, path)) 1127 } else if v, ok := test.attrs["ca-private-key"].(string); v != "" { 1128 c.Assert(keyPresent, jc.IsTrue) 1129 c.Assert(string(key), gc.Equals, v) 1130 } else if ok { 1131 c.Check(key, gc.HasLen, 0) 1132 c.Assert(keyPresent, jc.IsFalse) 1133 } else if bool(test.useDefaults) && home.FileExists(".local/share/juju/my-name-private-key.pem") { 1134 c.Assert(keyPresent, jc.IsTrue) 1135 c.Assert(string(key), gc.Equals, home.FileContents(c, "my-name-private-key.pem")) 1136 } else { 1137 c.Check(key, gc.HasLen, 0) 1138 c.Assert(keyPresent, jc.IsFalse) 1139 } 1140 1141 if v, ok := test.attrs["ssl-hostname-verification"]; ok { 1142 c.Assert(cfg.SSLHostnameVerification(), gc.Equals, v) 1143 } 1144 1145 if v, ok := test.attrs["provisioner-harvest-mode"]; ok { 1146 hvstMeth, err := config.ParseHarvestMode(v.(string)) 1147 c.Assert(err, jc.ErrorIsNil) 1148 c.Assert(cfg.ProvisionerHarvestMode(), gc.Equals, hvstMeth) 1149 } else { 1150 c.Assert(cfg.ProvisionerHarvestMode(), gc.Equals, config.HarvestDestroyed) 1151 } 1152 sshOpts := cfg.BootstrapSSHOpts() 1153 test.assertDuration( 1154 c, 1155 "bootstrap-timeout", 1156 sshOpts.Timeout, 1157 config.DefaultBootstrapSSHTimeout, 1158 ) 1159 test.assertDuration( 1160 c, 1161 "bootstrap-retry-delay", 1162 sshOpts.RetryDelay, 1163 config.DefaultBootstrapSSHRetryDelay, 1164 ) 1165 test.assertDuration( 1166 c, 1167 "bootstrap-addresses-delay", 1168 sshOpts.AddressesDelay, 1169 config.DefaultBootstrapSSHAddressesDelay, 1170 ) 1171 1172 if v, ok := test.attrs["image-stream"]; ok { 1173 c.Assert(cfg.ImageStream(), gc.Equals, v) 1174 } else { 1175 c.Assert(cfg.ImageStream(), gc.Equals, "released") 1176 } 1177 1178 url, urlPresent := cfg.ImageMetadataURL() 1179 if v, _ := test.attrs["image-metadata-url"].(string); v != "" { 1180 c.Assert(url, gc.Equals, v) 1181 c.Assert(urlPresent, jc.IsTrue) 1182 } else { 1183 c.Assert(urlPresent, jc.IsFalse) 1184 } 1185 1186 toolsURL, urlPresent := cfg.AgentMetadataURL() 1187 oldToolsURL := cfg.AllAttrs()["tools-metadata-url"] 1188 oldToolsURLAttrValue, oldTSTPresent := test.attrs["tools-metadata-url"] 1189 expectedToolsURLValue := test.attrs["agent-metadata-url"] 1190 expectedToolsURLPresent := true 1191 if expectedToolsURLValue == nil || expectedToolsURLValue == "" { 1192 if oldTSTPresent { 1193 expectedToolsURLValue = oldToolsURLAttrValue 1194 } else { 1195 expectedToolsURLValue = oldToolsURL 1196 expectedToolsURLPresent = false 1197 } 1198 } 1199 c.Assert(toolsURL, gc.Equals, expectedToolsURLValue) 1200 c.Assert(urlPresent, gc.Equals, expectedToolsURLPresent) 1201 1202 // assertions for deprecated tools-stream attribute used with new agent-stream 1203 agentStreamValue := cfg.AgentStream() 1204 oldTstToolsStreamAttr, oldTstOk := test.attrs["tools-stream"] 1205 expectedAgentStreamAttr := test.attrs["agent-stream"] 1206 1207 // When no agent-stream provided, look for tools-stream 1208 if expectedAgentStreamAttr == nil { 1209 if oldTstOk { 1210 expectedAgentStreamAttr = oldTstToolsStreamAttr 1211 } else { 1212 // If it's still nil, then hard-coded default is used 1213 expectedAgentStreamAttr = "released" 1214 } 1215 } 1216 c.Assert(agentStreamValue, gc.Equals, expectedAgentStreamAttr) 1217 1218 useLxcClone, useLxcClonePresent := cfg.LXCUseClone() 1219 oldUseClone, oldUseClonePresent := cfg.AllAttrs()["lxc-use-clone"] 1220 if v, ok := test.attrs["lxc-clone"]; ok { 1221 c.Assert(useLxcClone, gc.Equals, v) 1222 c.Assert(useLxcClonePresent, jc.IsTrue) 1223 } else { 1224 if oldUseClonePresent { 1225 c.Assert(useLxcClonePresent, jc.IsTrue) 1226 c.Assert(useLxcClone, gc.Equals, oldUseClone) 1227 } else { 1228 c.Assert(useLxcClonePresent, jc.IsFalse) 1229 c.Assert(useLxcClone, jc.IsFalse) 1230 } 1231 } 1232 useLxcCloneAufs, ok := cfg.LXCUseCloneAUFS() 1233 if v, ok := test.attrs["lxc-clone-aufs"]; ok { 1234 c.Assert(useLxcCloneAufs, gc.Equals, v) 1235 } else { 1236 c.Assert(useLxcCloneAufs, jc.IsFalse) 1237 } 1238 1239 resourceTags, cfgHasResourceTags := cfg.ResourceTags() 1240 if _, ok := test.attrs["resource-tags"]; ok { 1241 c.Assert(cfgHasResourceTags, jc.IsTrue) 1242 c.Assert(resourceTags, jc.DeepEquals, testResourceTagsMap) 1243 } else { 1244 c.Assert(cfgHasResourceTags, jc.IsFalse) 1245 } 1246 } 1247 1248 func (test configTest) assertDuration(c *gc.C, name string, actual time.Duration, defaultInSeconds int) { 1249 value, ok := test.attrs[name].(int) 1250 if !ok || value == 0 { 1251 c.Assert(actual, gc.Equals, time.Duration(defaultInSeconds)*time.Second) 1252 } else { 1253 c.Assert(actual, gc.Equals, time.Duration(value)*time.Second) 1254 } 1255 } 1256 1257 func (s *ConfigSuite) TestConfigAttrs(c *gc.C) { 1258 // Normally this is handled by gitjujutesting.FakeHome 1259 s.PatchEnvironment(osenv.JujuLoggingConfigEnvKey, "") 1260 attrs := map[string]interface{}{ 1261 "type": "my-type", 1262 "name": "my-name", 1263 "uuid": "90168e4c-2f10-4e9c-83c2-1fb55a58e5a9", 1264 "controller-uuid": "90168e4c-2f10-4e9c-83c2-1fb55a58e5a9", 1265 "authorized-keys": testing.FakeAuthKeys, 1266 "firewall-mode": config.FwInstance, 1267 "admin-secret": "foo", 1268 "unknown": "my-unknown", 1269 "ca-cert": caCert, 1270 "ssl-hostname-verification": true, 1271 "development": false, 1272 "state-port": 1234, 1273 "api-port": 4321, 1274 "bootstrap-timeout": 3600, 1275 "bootstrap-retry-delay": 30, 1276 "bootstrap-addresses-delay": 10, 1277 "default-series": series.LatestLts(), 1278 "test-mode": false, 1279 } 1280 cfg, err := config.New(config.NoDefaults, attrs) 1281 c.Assert(err, jc.ErrorIsNil) 1282 1283 // These attributes are added if not set. 1284 attrs["development"] = false 1285 attrs["logging-config"] = "<root>=WARNING;unit=DEBUG" 1286 attrs["ca-private-key"] = "" 1287 attrs["image-metadata-url"] = "" 1288 attrs["agent-metadata-url"] = "" 1289 attrs["tools-metadata-url"] = "" 1290 attrs["image-stream"] = "" 1291 attrs["proxy-ssh"] = false 1292 attrs["lxc-clone-aufs"] = false 1293 attrs["prefer-ipv6"] = false 1294 attrs["set-numa-control-policy"] = false 1295 attrs["allow-lxc-loop-mounts"] = false 1296 1297 // Default firewall mode is instance 1298 attrs["firewall-mode"] = string(config.FwInstance) 1299 c.Assert(cfg.AllAttrs(), jc.DeepEquals, attrs) 1300 c.Assert(cfg.UnknownAttrs(), jc.DeepEquals, map[string]interface{}{"unknown": "my-unknown"}) 1301 1302 // Verify that default provisioner-harvest-mode is good. 1303 c.Assert(cfg.ProvisionerHarvestMode(), gc.Equals, config.HarvestDestroyed) 1304 1305 newcfg, err := cfg.Apply(map[string]interface{}{ 1306 "name": "new-name", 1307 "uuid": "6216dfc3-6e82-408f-9f74-8565e63e6158", 1308 "controller-uuid": "6216dfc3-6e82-408f-9f74-8565e63e6158", 1309 "new-unknown": "my-new-unknown", 1310 }) 1311 c.Assert(err, jc.ErrorIsNil) 1312 1313 attrs["name"] = "new-name" 1314 attrs["uuid"] = "6216dfc3-6e82-408f-9f74-8565e63e6158" 1315 attrs["controller-uuid"] = "6216dfc3-6e82-408f-9f74-8565e63e6158" 1316 attrs["new-unknown"] = "my-new-unknown" 1317 c.Assert(newcfg.AllAttrs(), jc.DeepEquals, attrs) 1318 } 1319 1320 type validationTest struct { 1321 about string 1322 new testing.Attrs 1323 old testing.Attrs 1324 err string 1325 } 1326 1327 var validationTests = []validationTest{{ 1328 about: "Can't change the type", 1329 new: testing.Attrs{"type": "new-type"}, 1330 err: `cannot change type from "my-type" to "new-type"`, 1331 }, { 1332 about: "Can't change the name", 1333 new: testing.Attrs{"name": "new-name"}, 1334 err: `cannot change name from "my-name" to "new-name"`, 1335 }, { 1336 about: "Can set agent version", 1337 new: testing.Attrs{"agent-version": "1.9.13"}, 1338 }, { 1339 about: "Can change agent version", 1340 old: testing.Attrs{"agent-version": "1.9.13"}, 1341 new: testing.Attrs{"agent-version": "1.9.27"}, 1342 }, { 1343 about: "Can't clear agent version", 1344 old: testing.Attrs{"agent-version": "1.9.27"}, 1345 err: `cannot clear agent-version`, 1346 }, { 1347 about: "Can't change the firewall-mode (global->instance)", 1348 old: testing.Attrs{"firewall-mode": config.FwGlobal}, 1349 new: testing.Attrs{"firewall-mode": config.FwInstance}, 1350 err: `cannot change firewall-mode from "global" to "instance"`, 1351 }, { 1352 about: "Can't change the firewall-mode (global->none)", 1353 old: testing.Attrs{"firewall-mode": config.FwGlobal}, 1354 new: testing.Attrs{"firewall-mode": config.FwNone}, 1355 err: `cannot change firewall-mode from "global" to "none"`, 1356 }, { 1357 about: "Cannot change the state-port", 1358 old: testing.Attrs{"state-port": config.DefaultStatePort}, 1359 new: testing.Attrs{"state-port": 42}, 1360 err: `cannot change state-port from 37017 to 42`, 1361 }, { 1362 about: "Cannot change the api-port", 1363 old: testing.Attrs{"api-port": config.DefaultAPIPort}, 1364 new: testing.Attrs{"api-port": 42}, 1365 err: `cannot change api-port from 17070 to 42`, 1366 }, { 1367 about: "Can change the state-port from explicit-default to implicit-default", 1368 old: testing.Attrs{"state-port": config.DefaultStatePort}, 1369 }, { 1370 about: "Can change the api-port from explicit-default to implicit-default", 1371 old: testing.Attrs{"api-port": config.DefaultAPIPort}, 1372 }, { 1373 about: "Can change the state-port from implicit-default to explicit-default", 1374 new: testing.Attrs{"state-port": config.DefaultStatePort}, 1375 }, { 1376 about: "Can change the api-port from implicit-default to explicit-default", 1377 new: testing.Attrs{"api-port": config.DefaultAPIPort}, 1378 }, { 1379 about: "Cannot change the state-port from implicit-default to different value", 1380 new: testing.Attrs{"state-port": 42}, 1381 err: `cannot change state-port from 37017 to 42`, 1382 }, { 1383 about: "Cannot change the api-port from implicit-default to different value", 1384 new: testing.Attrs{"api-port": 42}, 1385 err: `cannot change api-port from 17070 to 42`, 1386 }, { 1387 about: "Cannot change the bootstrap-timeout from implicit-default to different value", 1388 new: testing.Attrs{"bootstrap-timeout": 5}, 1389 err: `cannot change bootstrap-timeout from 600 to 5`, 1390 }, { 1391 about: "Cannot change lxc-clone", 1392 old: testing.Attrs{"lxc-clone": false}, 1393 new: testing.Attrs{"lxc-clone": true}, 1394 err: `cannot change lxc-clone from false to true`, 1395 }, { 1396 about: "Cannot change lxc-clone-aufs", 1397 old: testing.Attrs{"lxc-clone-aufs": false}, 1398 new: testing.Attrs{"lxc-clone-aufs": true}, 1399 err: `cannot change lxc-clone-aufs from false to true`, 1400 }, { 1401 about: "Cannot change lxc-default-mtu", 1402 old: testing.Attrs{"lxc-default-mtu": 9000}, 1403 new: testing.Attrs{"lxc-default-mtu": 42}, 1404 err: `cannot change lxc-default-mtu from 9000 to 42`, 1405 }, { 1406 about: "Cannot change prefer-ipv6", 1407 old: testing.Attrs{"prefer-ipv6": false}, 1408 new: testing.Attrs{"prefer-ipv6": true}, 1409 err: `cannot change prefer-ipv6 from false to true`, 1410 }, { 1411 about: "Cannot change uuid", 1412 old: testing.Attrs{"uuid": "90168e4c-2f10-4e9c-83c2-1fb55a58e5a9"}, 1413 new: testing.Attrs{"uuid": "dcfbdb4a-bca2-49ad-aa7c-f011424e0fe4"}, 1414 err: "cannot change uuid from \"90168e4c-2f10-4e9c-83c2-1fb55a58e5a9\" to \"dcfbdb4a-bca2-49ad-aa7c-f011424e0fe4\"", 1415 }, { 1416 about: "Cannot change controller-uuid", 1417 old: testing.Attrs{"controller-uuid": "90168e4c-2f10-4e9c-83c2-1fb55a58e5a9"}, 1418 new: testing.Attrs{"controller-uuid": "dcfbdb4a-bca2-49ad-aa7c-f011424e0fe4"}, 1419 err: "cannot change controller-uuid from \"90168e4c-2f10-4e9c-83c2-1fb55a58e5a9\" to \"dcfbdb4a-bca2-49ad-aa7c-f011424e0fe4\"", 1420 }} 1421 1422 func (s *ConfigSuite) TestValidateChange(c *gc.C) { 1423 files := []gitjujutesting.TestFile{ 1424 {".ssh/identity.pub", "identity"}, 1425 } 1426 s.FakeHomeSuite.Home.AddFiles(c, files...) 1427 1428 for i, test := range validationTests { 1429 c.Logf("test %d: %s", i, test.about) 1430 newConfig := newTestConfig(c, test.new) 1431 oldConfig := newTestConfig(c, test.old) 1432 err := config.Validate(newConfig, oldConfig) 1433 if test.err == "" { 1434 c.Check(err, jc.ErrorIsNil) 1435 } else { 1436 c.Check(err, gc.ErrorMatches, test.err) 1437 } 1438 } 1439 } 1440 1441 func (s *ConfigSuite) addJujuFiles(c *gc.C) { 1442 s.FakeHomeSuite.Home.AddFiles(c, []gitjujutesting.TestFile{ 1443 {".ssh/id_rsa.pub", "rsa\n"}, 1444 {".local/share/juju/myenv-cert.pem", caCert}, 1445 {".local/share/juju/myenv-private-key.pem", caKey}, 1446 }...) 1447 } 1448 1449 func (s *ConfigSuite) TestValidateUnknownAttrs(c *gc.C) { 1450 s.addJujuFiles(c) 1451 cfg, err := config.New(config.UseDefaults, map[string]interface{}{ 1452 "name": "myenv", 1453 "type": "other", 1454 "uuid": testing.ModelTag.Id(), 1455 "controller-uuid": testing.ModelTag.Id(), 1456 "known": "this", 1457 "unknown": "that", 1458 }) 1459 c.Assert(err, jc.ErrorIsNil) 1460 1461 // No fields: all attrs passed through. 1462 attrs, err := cfg.ValidateUnknownAttrs(nil, nil) 1463 c.Assert(err, jc.ErrorIsNil) 1464 c.Assert(attrs, gc.DeepEquals, map[string]interface{}{ 1465 "known": "this", 1466 "unknown": "that", 1467 }) 1468 1469 // Valid field: that and other attrs passed through. 1470 fields := schema.Fields{"known": schema.String()} 1471 attrs, err = cfg.ValidateUnknownAttrs(fields, nil) 1472 c.Assert(err, jc.ErrorIsNil) 1473 c.Assert(attrs, gc.DeepEquals, map[string]interface{}{ 1474 "known": "this", 1475 "unknown": "that", 1476 }) 1477 1478 // Default field: inserted. 1479 fields["default"] = schema.String() 1480 defaults := schema.Defaults{"default": "the other"} 1481 attrs, err = cfg.ValidateUnknownAttrs(fields, defaults) 1482 c.Assert(err, jc.ErrorIsNil) 1483 c.Assert(attrs, gc.DeepEquals, map[string]interface{}{ 1484 "known": "this", 1485 "unknown": "that", 1486 "default": "the other", 1487 }) 1488 1489 // Invalid field: failure. 1490 fields["known"] = schema.Int() 1491 _, err = cfg.ValidateUnknownAttrs(fields, defaults) 1492 c.Assert(err, gc.ErrorMatches, `known: expected int, got string\("this"\)`) 1493 } 1494 1495 type testAttr struct { 1496 message string 1497 aKey string 1498 aValue string 1499 checker gc.Checker 1500 } 1501 1502 var emptyAttributeTests = []testAttr{ 1503 { 1504 message: "Warning message about unknown attribute (%v) is expected because attribute value exists", 1505 aKey: "unknown", 1506 aValue: "unknown value", 1507 checker: gc.Matches, 1508 }, { 1509 message: "Warning message about unknown attribute (%v) is unexpected because attribute value is empty", 1510 aKey: "unknown-empty", 1511 aValue: "", 1512 checker: gc.Not(gc.Matches), 1513 }, 1514 } 1515 1516 func (s *ConfigSuite) TestValidateUnknownEmptyAttr(c *gc.C) { 1517 s.addJujuFiles(c) 1518 cfg, err := config.New(config.UseDefaults, map[string]interface{}{ 1519 "name": "myenv", 1520 "type": "other", 1521 "uuid": testing.ModelTag.Id(), 1522 "controller-uuid": testing.ModelTag.Id(), 1523 }) 1524 c.Assert(err, jc.ErrorIsNil) 1525 warningTxt := `.* unknown config field %q.*` 1526 1527 for i, test := range emptyAttributeTests { 1528 c.Logf("test %d: %v\n", i, fmt.Sprintf(test.message, test.aKey)) 1529 testCfg, err := cfg.Apply(map[string]interface{}{test.aKey: test.aValue}) 1530 c.Assert(err, jc.ErrorIsNil) 1531 attrs, err := testCfg.ValidateUnknownAttrs(nil, nil) 1532 c.Assert(err, jc.ErrorIsNil) 1533 // all attrs passed through 1534 c.Assert(attrs, gc.DeepEquals, map[string]interface{}{test.aKey: test.aValue}) 1535 expectedWarning := fmt.Sprintf(warningTxt, test.aKey) 1536 logOutputText := strings.Replace(c.GetTestLog(), "\n", "", -1) 1537 // warning displayed or not based on test expectation 1538 c.Assert(logOutputText, test.checker, expectedWarning, gc.Commentf(test.message, test.aKey)) 1539 } 1540 } 1541 1542 func newTestConfig(c *gc.C, explicit testing.Attrs) *config.Config { 1543 final := testing.Attrs{ 1544 "type": "my-type", "name": "my-name", 1545 "uuid": testing.ModelTag.Id(), 1546 "controller-uuid": testing.ModelTag.Id(), 1547 } 1548 for key, value := range explicit { 1549 final[key] = value 1550 } 1551 result, err := config.New(config.UseDefaults, final) 1552 c.Assert(err, jc.ErrorIsNil) 1553 return result 1554 } 1555 1556 func (s *ConfigSuite) TestLoggingConfig(c *gc.C) { 1557 s.addJujuFiles(c) 1558 config := newTestConfig(c, testing.Attrs{ 1559 "logging-config": "<root>=WARNING;juju=DEBUG"}) 1560 c.Assert(config.LoggingConfig(), gc.Equals, "<root>=WARNING;juju=DEBUG;unit=DEBUG") 1561 } 1562 1563 func (s *ConfigSuite) TestLoggingConfigWithUnit(c *gc.C) { 1564 s.addJujuFiles(c) 1565 config := newTestConfig(c, testing.Attrs{ 1566 "logging-config": "<root>=WARNING;unit=INFO"}) 1567 c.Assert(config.LoggingConfig(), gc.Equals, "<root>=WARNING;unit=INFO") 1568 } 1569 1570 func (s *ConfigSuite) TestLoggingConfigFromEnvironment(c *gc.C) { 1571 s.addJujuFiles(c) 1572 s.PatchEnvironment(osenv.JujuLoggingConfigEnvKey, "<root>=INFO") 1573 1574 config := newTestConfig(c, nil) 1575 c.Assert(config.LoggingConfig(), gc.Equals, "<root>=INFO;unit=DEBUG") 1576 } 1577 1578 func (s *ConfigSuite) TestAutoHookRetryDefault(c *gc.C) { 1579 config := newTestConfig(c, testing.Attrs{}) 1580 c.Assert(config.AutomaticallyRetryHooks(), gc.Equals, true) 1581 } 1582 1583 func (s *ConfigSuite) TestAutoHookRetryFalseEnv(c *gc.C) { 1584 config := newTestConfig(c, testing.Attrs{ 1585 "automatically-retry-hooks": "false"}) 1586 c.Assert(config.AutomaticallyRetryHooks(), gc.Equals, false) 1587 } 1588 1589 func (s *ConfigSuite) TestAutoHookRetryTrueEnv(c *gc.C) { 1590 config := newTestConfig(c, testing.Attrs{ 1591 "automatically-retry-hooks": "true"}) 1592 c.Assert(config.AutomaticallyRetryHooks(), gc.Equals, true) 1593 } 1594 1595 func (s *ConfigSuite) TestCloudImageBaseURL(c *gc.C) { 1596 s.addJujuFiles(c) 1597 config := newTestConfig(c, testing.Attrs{}) 1598 c.Assert(config.CloudImageBaseURL(), gc.Equals, "") 1599 } 1600 1601 func (s *ConfigSuite) TestCloudImageBaseURLSet(c *gc.C) { 1602 s.addJujuFiles(c) 1603 config := newTestConfig(c, testing.Attrs{ 1604 "cloudimg-base-url": "http://local.foo/query"}) 1605 c.Assert(config.CloudImageBaseURL(), gc.Equals, "http://local.foo/query") 1606 } 1607 1608 func (s *ConfigSuite) TestProxyValuesWithFallback(c *gc.C) { 1609 s.addJujuFiles(c) 1610 1611 config := newTestConfig(c, testing.Attrs{ 1612 "http-proxy": "http://user@10.0.0.1", 1613 "https-proxy": "https://user@10.0.0.1", 1614 "ftp-proxy": "ftp://user@10.0.0.1", 1615 "no-proxy": "localhost,10.0.3.1", 1616 }) 1617 c.Assert(config.HttpProxy(), gc.Equals, "http://user@10.0.0.1") 1618 c.Assert(config.AptHttpProxy(), gc.Equals, "http://user@10.0.0.1") 1619 c.Assert(config.HttpsProxy(), gc.Equals, "https://user@10.0.0.1") 1620 c.Assert(config.AptHttpsProxy(), gc.Equals, "https://user@10.0.0.1") 1621 c.Assert(config.FtpProxy(), gc.Equals, "ftp://user@10.0.0.1") 1622 c.Assert(config.AptFtpProxy(), gc.Equals, "ftp://user@10.0.0.1") 1623 c.Assert(config.NoProxy(), gc.Equals, "localhost,10.0.3.1") 1624 } 1625 1626 func (s *ConfigSuite) TestProxyValuesWithFallbackNoScheme(c *gc.C) { 1627 s.addJujuFiles(c) 1628 1629 config := newTestConfig(c, testing.Attrs{ 1630 "http-proxy": "user@10.0.0.1", 1631 "https-proxy": "user@10.0.0.1", 1632 "ftp-proxy": "user@10.0.0.1", 1633 "no-proxy": "localhost,10.0.3.1", 1634 }) 1635 c.Assert(config.HttpProxy(), gc.Equals, "user@10.0.0.1") 1636 c.Assert(config.AptHttpProxy(), gc.Equals, "http://user@10.0.0.1") 1637 c.Assert(config.HttpsProxy(), gc.Equals, "user@10.0.0.1") 1638 c.Assert(config.AptHttpsProxy(), gc.Equals, "https://user@10.0.0.1") 1639 c.Assert(config.FtpProxy(), gc.Equals, "user@10.0.0.1") 1640 c.Assert(config.AptFtpProxy(), gc.Equals, "ftp://user@10.0.0.1") 1641 c.Assert(config.NoProxy(), gc.Equals, "localhost,10.0.3.1") 1642 } 1643 1644 func (s *ConfigSuite) TestProxyValues(c *gc.C) { 1645 s.addJujuFiles(c) 1646 config := newTestConfig(c, testing.Attrs{ 1647 "http-proxy": "http://user@10.0.0.1", 1648 "https-proxy": "https://user@10.0.0.1", 1649 "ftp-proxy": "ftp://user@10.0.0.1", 1650 "apt-http-proxy": "http://user@10.0.0.2", 1651 "apt-https-proxy": "https://user@10.0.0.2", 1652 "apt-ftp-proxy": "ftp://user@10.0.0.2", 1653 }) 1654 c.Assert(config.HttpProxy(), gc.Equals, "http://user@10.0.0.1") 1655 c.Assert(config.AptHttpProxy(), gc.Equals, "http://user@10.0.0.2") 1656 c.Assert(config.HttpsProxy(), gc.Equals, "https://user@10.0.0.1") 1657 c.Assert(config.AptHttpsProxy(), gc.Equals, "https://user@10.0.0.2") 1658 c.Assert(config.FtpProxy(), gc.Equals, "ftp://user@10.0.0.1") 1659 c.Assert(config.AptFtpProxy(), gc.Equals, "ftp://user@10.0.0.2") 1660 } 1661 1662 func (s *ConfigSuite) TestProxyValuesNotSet(c *gc.C) { 1663 s.addJujuFiles(c) 1664 config := newTestConfig(c, testing.Attrs{}) 1665 c.Assert(config.HttpProxy(), gc.Equals, "") 1666 c.Assert(config.AptHttpProxy(), gc.Equals, "") 1667 c.Assert(config.HttpsProxy(), gc.Equals, "") 1668 c.Assert(config.AptHttpsProxy(), gc.Equals, "") 1669 c.Assert(config.FtpProxy(), gc.Equals, "") 1670 c.Assert(config.AptFtpProxy(), gc.Equals, "") 1671 c.Assert(config.NoProxy(), gc.Equals, "") 1672 } 1673 1674 func (s *ConfigSuite) TestProxyConfigMap(c *gc.C) { 1675 s.addJujuFiles(c) 1676 cfg := newTestConfig(c, testing.Attrs{}) 1677 proxySettings := proxy.Settings{ 1678 Http: "http proxy", 1679 Https: "https proxy", 1680 Ftp: "ftp proxy", 1681 NoProxy: "no proxy", 1682 } 1683 expectedProxySettings := proxy.Settings{ 1684 Http: "http://http proxy", 1685 Https: "https://https proxy", 1686 Ftp: "ftp://ftp proxy", 1687 NoProxy: "", 1688 } 1689 cfg, err := cfg.Apply(config.ProxyConfigMap(proxySettings)) 1690 c.Assert(err, jc.ErrorIsNil) 1691 c.Assert(cfg.ProxySettings(), gc.DeepEquals, proxySettings) 1692 // Apt proxy settings always include the scheme. NoProxy is empty. 1693 c.Assert(cfg.AptProxySettings(), gc.DeepEquals, expectedProxySettings) 1694 } 1695 1696 func (s *ConfigSuite) TestAptProxyConfigMap(c *gc.C) { 1697 s.addJujuFiles(c) 1698 cfg := newTestConfig(c, testing.Attrs{}) 1699 proxySettings := proxy.Settings{ 1700 Http: "http://httpproxy", 1701 Https: "https://httpsproxy", 1702 Ftp: "ftp://ftpproxy", 1703 } 1704 cfg, err := cfg.Apply(config.AptProxyConfigMap(proxySettings)) 1705 c.Assert(err, jc.ErrorIsNil) 1706 // The default proxy settings should still be empty. 1707 c.Assert(cfg.ProxySettings(), gc.DeepEquals, proxy.Settings{}) 1708 c.Assert(cfg.AptProxySettings(), gc.DeepEquals, proxySettings) 1709 } 1710 1711 func (s *ConfigSuite) TestSchemaNoExtra(c *gc.C) { 1712 schema, err := config.Schema(nil) 1713 c.Assert(err, gc.IsNil) 1714 orig := config.ConfigSchema 1715 c.Assert(schema, jc.DeepEquals, orig) 1716 // Check that we actually returned a copy, not the original. 1717 schema["foo"] = environschema.Attr{} 1718 _, ok := orig["foo"] 1719 c.Assert(ok, jc.IsFalse) 1720 } 1721 1722 func (s *ConfigSuite) TestSchemaWithExtraFields(c *gc.C) { 1723 extraField := environschema.Attr{ 1724 Description: "fooish", 1725 Type: environschema.Tstring, 1726 } 1727 schema, err := config.Schema(environschema.Fields{ 1728 "foo": extraField, 1729 }) 1730 c.Assert(err, gc.IsNil) 1731 c.Assert(schema["foo"], gc.DeepEquals, extraField) 1732 delete(schema, "foo") 1733 orig := config.ConfigSchema 1734 c.Assert(schema, jc.DeepEquals, orig) 1735 } 1736 1737 func (s *ConfigSuite) TestSchemaWithExtraOverlap(c *gc.C) { 1738 schema, err := config.Schema(environschema.Fields{ 1739 "type": environschema.Attr{ 1740 Description: "duplicate", 1741 Type: environschema.Tstring, 1742 }, 1743 }) 1744 c.Assert(err, gc.ErrorMatches, `config field "type" clashes with global config`) 1745 c.Assert(schema, gc.IsNil) 1746 } 1747 1748 func (s *ConfigSuite) TestGenerateControllerCertAndKey(c *gc.C) { 1749 // Add a cert. 1750 s.FakeHomeSuite.Home.AddFiles(c, gitjujutesting.TestFile{".ssh/id_rsa.pub", "rsa\n"}) 1751 1752 for _, test := range []struct { 1753 configValues map[string]interface{} 1754 sanValues []string 1755 errMatch string 1756 }{{ 1757 configValues: map[string]interface{}{ 1758 "name": "test-no-certs", 1759 "type": "dummy", 1760 "uuid": testing.ModelTag.Id(), 1761 "controller-uuid": testing.ModelTag.Id(), 1762 }, 1763 errMatch: "model configuration has no ca-cert", 1764 }, { 1765 configValues: map[string]interface{}{ 1766 "name": "test-no-certs", 1767 "type": "dummy", 1768 "uuid": testing.ModelTag.Id(), 1769 "controller-uuid": testing.ModelTag.Id(), 1770 "ca-cert": testing.CACert, 1771 }, 1772 errMatch: "model configuration has no ca-private-key", 1773 }, { 1774 configValues: map[string]interface{}{ 1775 "name": "test-no-certs", 1776 "type": "dummy", 1777 "uuid": testing.ModelTag.Id(), 1778 "controller-uuid": testing.ModelTag.Id(), 1779 "ca-cert": testing.CACert, 1780 "ca-private-key": testing.CAKey, 1781 }, 1782 }, { 1783 configValues: map[string]interface{}{ 1784 "name": "test-no-certs", 1785 "type": "dummy", 1786 "uuid": testing.ModelTag.Id(), 1787 "controller-uuid": testing.ModelTag.Id(), 1788 "ca-cert": testing.CACert, 1789 "ca-private-key": testing.CAKey, 1790 }, 1791 sanValues: []string{"10.0.0.1", "192.168.1.1"}, 1792 }} { 1793 cfg, err := config.New(config.UseDefaults, test.configValues) 1794 c.Assert(err, jc.ErrorIsNil) 1795 certPEM, keyPEM, err := cfg.GenerateControllerCertAndKey(test.sanValues) 1796 if test.errMatch == "" { 1797 c.Assert(err, jc.ErrorIsNil) 1798 1799 _, _, err = cert.ParseCertAndKey(certPEM, keyPEM) 1800 c.Check(err, jc.ErrorIsNil) 1801 1802 err = cert.Verify(certPEM, testing.CACert, time.Now()) 1803 c.Assert(err, jc.ErrorIsNil) 1804 err = cert.Verify(certPEM, testing.CACert, time.Now().AddDate(9, 0, 0)) 1805 c.Assert(err, jc.ErrorIsNil) 1806 err = cert.Verify(certPEM, testing.CACert, time.Now().AddDate(10, 0, 1)) 1807 c.Assert(err, gc.NotNil) 1808 srvCert, err := cert.ParseCert(certPEM) 1809 c.Assert(err, jc.ErrorIsNil) 1810 sanIPs := make([]string, len(srvCert.IPAddresses)) 1811 for i, ip := range srvCert.IPAddresses { 1812 sanIPs[i] = ip.String() 1813 } 1814 c.Assert(sanIPs, jc.SameContents, test.sanValues) 1815 } else { 1816 c.Assert(err, gc.ErrorMatches, test.errMatch) 1817 c.Assert(certPEM, gc.Equals, "") 1818 c.Assert(keyPEM, gc.Equals, "") 1819 } 1820 } 1821 } 1822 1823 var specializeCharmRepoTests = []struct { 1824 about string 1825 testMode bool 1826 repo charmrepo.Interface 1827 }{{ 1828 about: "test mode disabled, charm store", 1829 repo: &specializedCharmRepo{}, 1830 }, { 1831 about: "test mode enabled, charm store", 1832 testMode: true, 1833 repo: &specializedCharmRepo{}, 1834 }} 1835 1836 func (s *ConfigSuite) TestSpecializeCharmRepo(c *gc.C) { 1837 for i, test := range specializeCharmRepoTests { 1838 c.Logf("test %d: %s", i, test.about) 1839 cfg := newTestConfig(c, testing.Attrs{"test-mode": test.testMode}) 1840 repo := config.SpecializeCharmRepo(test.repo, cfg) 1841 store := repo.(*specializedCharmRepo) 1842 c.Assert(store.testMode, gc.Equals, test.testMode) 1843 } 1844 } 1845 1846 type specializedCharmRepo struct { 1847 *charmrepo.CharmStore 1848 testMode bool 1849 } 1850 1851 func (s *specializedCharmRepo) WithTestMode() charmrepo.Interface { 1852 s.testMode = true 1853 return s 1854 } 1855 1856 var caCert = ` 1857 -----BEGIN CERTIFICATE----- 1858 MIIBjDCCATigAwIBAgIBADALBgkqhkiG9w0BAQUwHjENMAsGA1UEChMEanVqdTEN 1859 MAsGA1UEAxMEcm9vdDAeFw0xMjExMDkxNjQwMjhaFw0yMjExMDkxNjQ1MjhaMB4x 1860 DTALBgNVBAoTBGp1anUxDTALBgNVBAMTBHJvb3QwWTALBgkqhkiG9w0BAQEDSgAw 1861 RwJAduA1Gnb2VJLxNGfG4St0Qy48Y3q5Z5HheGtTGmti/FjlvQvScCFGCnJG7fKA 1862 Knd7ia3vWg7lxYkIvMPVP88LAQIDAQABo2YwZDAOBgNVHQ8BAf8EBAMCAKQwEgYD 1863 VR0TAQH/BAgwBgEB/wIBATAdBgNVHQ4EFgQUlvKX8vwp0o+VdhdhoA9O6KlOm00w 1864 HwYDVR0jBBgwFoAUlvKX8vwp0o+VdhdhoA9O6KlOm00wCwYJKoZIhvcNAQEFA0EA 1865 LlNpevtFr8gngjAFFAO/FXc7KiZcCrA5rBfb/rEy297lIqmKt5++aVbLEPyxCIFC 1866 r71Sj63TUTFWtRZAxvn9qQ== 1867 -----END CERTIFICATE----- 1868 `[1:] 1869 1870 var caKey = ` 1871 -----BEGIN RSA PRIVATE KEY----- 1872 MIIBOQIBAAJAduA1Gnb2VJLxNGfG4St0Qy48Y3q5Z5HheGtTGmti/FjlvQvScCFG 1873 CnJG7fKAKnd7ia3vWg7lxYkIvMPVP88LAQIDAQABAkEAsFOdMSYn+AcF1M/iBfjo 1874 uQWJ+Zz+CgwuvumjGNsUtmwxjA+hh0fCn0Ah2nAt4Ma81vKOKOdQ8W6bapvsVDH0 1875 6QIhAJOkLmEKm4H5POQV7qunRbRsLbft/n/SHlOBz165WFvPAiEAzh9fMf70std1 1876 sVCHJRQWKK+vw3oaEvPKvkPiV5ui0C8CIGNsvybuo8ald5IKCw5huRlFeIxSo36k 1877 m3OVCXc6zfwVAiBnTUe7WcivPNZqOC6TAZ8dYvdWo4Ifz3jjpEfymjid1wIgBIJv 1878 ERPyv2NQqIFQZIyzUP7LVRIWfpFFOo9/Ww/7s5Y= 1879 -----END RSA PRIVATE KEY----- 1880 `[1:] 1881 1882 var caCert2 = ` 1883 -----BEGIN CERTIFICATE----- 1884 MIIBjTCCATmgAwIBAgIBADALBgkqhkiG9w0BAQUwHjENMAsGA1UEChMEanVqdTEN 1885 MAsGA1UEAxMEcm9vdDAeFw0xMjExMDkxNjQxMDhaFw0yMjExMDkxNjQ2MDhaMB4x 1886 DTALBgNVBAoTBGp1anUxDTALBgNVBAMTBHJvb3QwWjALBgkqhkiG9w0BAQEDSwAw 1887 SAJBAJkSWRrr81y8pY4dbNgt+8miSKg4z6glp2KO2NnxxAhyyNtQHKvC+fJALJj+ 1888 C2NhuvOv9xImxOl3Hg8fFPCXCtcCAwEAAaNmMGQwDgYDVR0PAQH/BAQDAgCkMBIG 1889 A1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFOsX/ZCqKzWCAaTTVcWsWKT5Msow 1890 MB8GA1UdIwQYMBaAFOsX/ZCqKzWCAaTTVcWsWKT5MsowMAsGCSqGSIb3DQEBBQNB 1891 AAVV57jetEzJQnjgBzhvx/UwauFn78jGhXfV5BrQmxIb4SF4DgSCFstPwUQOAr8h 1892 XXzJqBQH92KYmp+y3YXDoMQ= 1893 -----END CERTIFICATE----- 1894 `[1:] 1895 1896 var caKey2 = ` 1897 -----BEGIN RSA PRIVATE KEY----- 1898 MIIBOQIBAAJBAJkSWRrr81y8pY4dbNgt+8miSKg4z6glp2KO2NnxxAhyyNtQHKvC 1899 +fJALJj+C2NhuvOv9xImxOl3Hg8fFPCXCtcCAwEAAQJATQNzO11NQvJS5U6eraFt 1900 FgSFQ8XZjILtVWQDbJv8AjdbEgKMHEy33icsAKIUAx8jL9kjq6K9kTdAKXZi9grF 1901 UQIhAPD7jccIDUVm785E5eR9eisq0+xpgUIa24Jkn8cAlst5AiEAopxVFl1auer3 1902 GP2In3pjdL4ydzU/gcRcYisoJqwHpM8CIHtqmaXBPeq5WT9ukb5/dL3+5SJCtmxA 1903 jQMuvZWRe6khAiBvMztYtPSDKXRbCZ4xeQ+kWSDHtok8Y5zNoTeu4nvDrwIgb3Al 1904 fikzPveC5g6S6OvEQmyDz59tYBubm2XHgvxqww0= 1905 -----END RSA PRIVATE KEY----- 1906 `[1:] 1907 1908 var caCert3 = ` 1909 -----BEGIN CERTIFICATE----- 1910 MIIBjTCCATmgAwIBAgIBADALBgkqhkiG9w0BAQUwHjENMAsGA1UEChMEanVqdTEN 1911 MAsGA1UEAxMEcm9vdDAeFw0xMjExMDkxNjQxMjlaFw0yMjExMDkxNjQ2MjlaMB4x 1912 DTALBgNVBAoTBGp1anUxDTALBgNVBAMTBHJvb3QwWjALBgkqhkiG9w0BAQEDSwAw 1913 SAJBAIW7CbHFJivvV9V6mO8AGzJS9lqjUf6MdEPsdF6wx2Cpzr/lSFIggCwRA138 1914 9MuFxflxb/3U8Nq+rd8rVtTgFMECAwEAAaNmMGQwDgYDVR0PAQH/BAQDAgCkMBIG 1915 A1UdEwEB/wQIMAYBAf8CAQEwHQYDVR0OBBYEFJafrxqByMN9BwGfcmuF0Lw/1QII 1916 MB8GA1UdIwQYMBaAFJafrxqByMN9BwGfcmuF0Lw/1QIIMAsGCSqGSIb3DQEBBQNB 1917 AHq3vqNhxya3s33DlQfSj9whsnqM0Nm+u8mBX/T76TF5rV7+B33XmYzSyfA3yBi/ 1918 zHaUR/dbHuiNTO+KXs3/+Y4= 1919 -----END CERTIFICATE----- 1920 `[1:] 1921 1922 var caKey3 = ` 1923 -----BEGIN RSA PRIVATE KEY----- 1924 MIIBOgIBAAJBAIW7CbHFJivvV9V6mO8AGzJS9lqjUf6MdEPsdF6wx2Cpzr/lSFIg 1925 gCwRA1389MuFxflxb/3U8Nq+rd8rVtTgFMECAwEAAQJAaivPi4qJPrJb2onl50H/ 1926 VZnWKqmljGF4YQDWduMEt7GTPk+76x9SpO7W4gfY490Ivd9DEXfbr/KZqhwWikNw 1927 LQIhALlLfRXLF2ZfToMfB1v1v+jith5onAu24O68mkdRc5PLAiEAuMJ/6U07hggr 1928 Ckf9OT93wh84DK66h780HJ/FUHKcoCMCIDsPZaJBpoa50BOZG0ZjcTTwti3BGCPf 1929 uZg+w0oCGz27AiEAsUCYKqEXy/ymHhT2kSecozYENdajyXvcaOG3EPkD3nUCICOP 1930 zatzs7c/4mx4a0JBG6Za0oEPUcm2I34is50KSohz 1931 -----END RSA PRIVATE KEY----- 1932 `[1:] 1933 1934 var invalidCAKey = ` 1935 -----BEGIN RSA PRIVATE KEY----- 1936 MIIBOgIBAAJAZabKgKInuOxj5vDWLwHHQtK3/45KB+32D15w94Nt83BmuGxo90lw 1937 -----END RSA PRIVATE KEY----- 1938 `[1:] 1939 1940 var invalidCACert = ` 1941 -----BEGIN CERTIFICATE----- 1942 MIIBOgIBAAJAZabKgKInuOxj5vDWLwHHQtK3/45KB+32D15w94Nt83BmuGxo90lw 1943 -----END CERTIFICATE----- 1944 `[1:]