github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/controller/config_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package controller_test 5 6 import ( 7 stdtesting "testing" 8 "time" 9 10 "github.com/juju/collections/set" 11 "github.com/juju/loggo" 12 "github.com/juju/romulus" 13 gitjujutesting "github.com/juju/testing" 14 jc "github.com/juju/testing/checkers" 15 utilscert "github.com/juju/utils/cert" 16 gc "gopkg.in/check.v1" 17 "gopkg.in/juju/charmrepo.v3/csclient" 18 19 "github.com/juju/juju/cert" 20 "github.com/juju/juju/controller" 21 "github.com/juju/juju/testing" 22 ) 23 24 func Test(t *stdtesting.T) { 25 gc.TestingT(t) 26 } 27 28 type ConfigSuite struct { 29 testing.FakeJujuXDGDataHomeSuite 30 home string 31 } 32 33 var _ = gc.Suite(&ConfigSuite{}) 34 35 func (s *ConfigSuite) SetUpTest(c *gc.C) { 36 s.FakeJujuXDGDataHomeSuite.SetUpTest(c) 37 // Make sure that the defaults are used, which 38 // is <root>=WARNING 39 loggo.DefaultContext().ResetLoggerLevels() 40 } 41 42 func (s *ConfigSuite) TestGenerateControllerCertAndKey(c *gc.C) { 43 // Add a cert. 44 s.FakeHomeSuite.Home.AddFiles(c, gitjujutesting.TestFile{Name: ".ssh/id_rsa.pub", Data: "rsa\n"}) 45 46 for _, test := range []struct { 47 caCert string 48 caKey string 49 sanValues []string 50 }{{ 51 caCert: testing.CACert, 52 caKey: testing.CAKey, 53 }, { 54 caCert: testing.CACert, 55 caKey: testing.CAKey, 56 sanValues: []string{"10.0.0.1", "192.168.1.1"}, 57 }} { 58 certPEM, keyPEM, err := controller.GenerateControllerCertAndKey(test.caCert, test.caKey, test.sanValues) 59 c.Assert(err, jc.ErrorIsNil) 60 61 _, _, err = utilscert.ParseCertAndKey(certPEM, keyPEM) 62 c.Check(err, jc.ErrorIsNil) 63 64 err = cert.Verify(certPEM, testing.CACert, time.Now()) 65 c.Assert(err, jc.ErrorIsNil) 66 err = cert.Verify(certPEM, testing.CACert, time.Now().AddDate(9, 0, 0)) 67 c.Assert(err, jc.ErrorIsNil) 68 err = cert.Verify(certPEM, testing.CACert, time.Now().AddDate(10, 0, 1)) 69 c.Assert(err, gc.NotNil) 70 srvCert, err := utilscert.ParseCert(certPEM) 71 c.Assert(err, jc.ErrorIsNil) 72 sanIPs := make([]string, len(srvCert.IPAddresses)) 73 for i, ip := range srvCert.IPAddresses { 74 sanIPs[i] = ip.String() 75 } 76 c.Assert(sanIPs, jc.SameContents, test.sanValues) 77 } 78 } 79 80 var validateTests = []struct { 81 about string 82 config controller.Config 83 expectError string 84 }{{ 85 about: "missing CA cert", 86 expectError: `missing CA certificate`, 87 }, { 88 about: "bad CA cert", 89 config: controller.Config{ 90 controller.CACertKey: "xxx", 91 }, 92 expectError: `bad CA certificate in configuration: no certificates found`, 93 }, { 94 about: "bad controller UUID", 95 config: controller.Config{ 96 controller.ControllerUUIDKey: "xxx", 97 controller.CACertKey: testing.CACert, 98 }, 99 expectError: `controller-uuid: expected UUID, got string\("xxx"\)`, 100 }, { 101 about: "HTTPS identity URL OK", 102 config: controller.Config{ 103 controller.IdentityURL: "https://0.1.2.3/foo", 104 controller.CACertKey: testing.CACert, 105 }, 106 }, { 107 about: "HTTP identity URL requires public key", 108 config: controller.Config{ 109 controller.IdentityURL: "http://0.1.2.3/foo", 110 controller.CACertKey: testing.CACert, 111 }, 112 expectError: `URL needs to be https when identity-public-key not provided`, 113 }, { 114 about: "HTTP identity URL OK if public key is provided", 115 config: controller.Config{ 116 controller.IdentityPublicKey: `o/yOqSNWncMo1GURWuez/dGR30TscmmuIxgjztpoHEY=`, 117 controller.IdentityURL: "http://0.1.2.3/foo", 118 controller.CACertKey: testing.CACert, 119 }, 120 }, { 121 about: "invalid identity public key", 122 config: controller.Config{ 123 controller.IdentityPublicKey: `xxxx`, 124 controller.CACertKey: testing.CACert, 125 }, 126 expectError: `invalid identity public key: wrong length for base64 key, got 3 want 32`, 127 }, { 128 about: "invalid management space name - whitespace", 129 config: controller.Config{ 130 controller.CACertKey: testing.CACert, 131 controller.JujuManagementSpace: " ", 132 }, 133 expectError: `juju mgmt space name " " not valid`, 134 }, { 135 about: "invalid management space name - caps", 136 config: controller.Config{ 137 controller.CACertKey: testing.CACert, 138 controller.JujuManagementSpace: "CAPS", 139 }, 140 expectError: `juju mgmt space name "CAPS" not valid`, 141 }, { 142 about: "invalid management space name - carriage return", 143 config: controller.Config{ 144 controller.CACertKey: testing.CACert, 145 controller.JujuManagementSpace: "\n", 146 }, 147 expectError: `juju mgmt space name "\\n" not valid`, 148 }, { 149 about: "invalid HA space name - number", 150 config: controller.Config{ 151 controller.CACertKey: testing.CACert, 152 controller.JujuHASpace: 666, 153 }, 154 expectError: `type for juju HA space name 666 not valid`, 155 }, { 156 about: "invalid HA space name - bool", 157 config: controller.Config{ 158 controller.CACertKey: testing.CACert, 159 controller.JujuHASpace: true, 160 }, 161 expectError: `type for juju HA space name true not valid`, 162 }, { 163 about: "invalid audit log max size", 164 config: controller.Config{ 165 controller.CACertKey: testing.CACert, 166 controller.AuditLogMaxSize: "abcd", 167 }, 168 expectError: `invalid audit log max size in configuration: expected a non-negative number, got "abcd"`, 169 }, { 170 about: "zero audit log max size", 171 config: controller.Config{ 172 controller.CACertKey: testing.CACert, 173 controller.AuditingEnabled: true, 174 controller.AuditLogMaxSize: "0M", 175 }, 176 expectError: `invalid audit log max size: can't be 0 if auditing is enabled`, 177 }, { 178 about: "invalid audit log max backups", 179 config: controller.Config{ 180 controller.CACertKey: testing.CACert, 181 controller.AuditLogMaxBackups: -10, 182 }, 183 expectError: `invalid audit log max backups: should be a number of files \(or 0 to keep all\), got -10`, 184 }, { 185 about: "invalid audit log exclude", 186 config: controller.Config{ 187 controller.CACertKey: testing.CACert, 188 controller.AuditLogExcludeMethods: []interface{}{"Dap.Kings", "ReadOnlyMethods", "Sharon Jones"}, 189 }, 190 expectError: `invalid audit log exclude methods: should be a list of "Facade.Method" names \(or "ReadOnlyMethods"\), got "Sharon Jones" at position 3`, 191 }, { 192 about: "invalid CAAS operator docker image path", 193 config: controller.Config{ 194 controller.CACertKey: testing.CACert, 195 controller.CAASOperatorImagePath: "foo?bar", 196 }, 197 expectError: `docker image path "foo\?bar" not valid`, 198 }, { 199 about: "invalid CAAS operator docker image path - leading colon", 200 config: controller.Config{ 201 controller.CACertKey: testing.CACert, 202 controller.CAASOperatorImagePath: ":foo", 203 }, 204 expectError: `docker image path ":foo" not valid`, 205 }, { 206 about: "invalid CAAS operator docker image path - trailing colon", 207 config: controller.Config{ 208 controller.CACertKey: testing.CACert, 209 controller.CAASOperatorImagePath: "foo:", 210 }, 211 expectError: `docker image path "foo:" not valid`, 212 }, { 213 about: "invalid CAAS operator docker image path - extra colon", 214 config: controller.Config{ 215 controller.CACertKey: testing.CACert, 216 controller.CAASOperatorImagePath: "foo::bar", 217 }, 218 expectError: `docker image path "foo::bar" not valid`, 219 }, { 220 about: "invalid CAAS operator docker image path - leading /", 221 config: controller.Config{ 222 controller.CACertKey: testing.CACert, 223 controller.CAASOperatorImagePath: "/foo", 224 }, 225 expectError: `docker image path "/foo" not valid`, 226 }, { 227 about: "invalid CAAS operator docker image path - extra /", 228 config: controller.Config{ 229 controller.CACertKey: testing.CACert, 230 controller.CAASOperatorImagePath: "foo//bar", 231 }, 232 expectError: `docker image path "foo//bar" not valid`, 233 }, { 234 about: "negative controller-api-port", 235 config: controller.Config{ 236 controller.CACertKey: testing.CACert, 237 controller.ControllerAPIPort: -5, 238 }, 239 expectError: `non-positive integer for controller-api-port not valid`, 240 }, { 241 about: "controller-api-port matching api-port", 242 config: controller.Config{ 243 controller.APIPort: 12345, 244 controller.CACertKey: testing.CACert, 245 controller.ControllerAPIPort: 12345, 246 }, 247 expectError: `controller-api-port matching api-port not valid`, 248 }, { 249 about: "controller-api-port matching state-port", 250 config: controller.Config{ 251 controller.APIPort: 12345, 252 controller.StatePort: 54321, 253 controller.CACertKey: testing.CACert, 254 controller.ControllerAPIPort: 54321, 255 }, 256 expectError: `controller-api-port matching state-port not valid`, 257 }, { 258 about: "api-port-open-delay not a duration", 259 config: controller.Config{ 260 controller.CACertKey: testing.CACert, 261 controller.APIPortOpenDelay: "15", 262 }, 263 expectError: `api-port-open-delay value "15" must be a valid duration`, 264 }, { 265 about: "txn-prune-sleep-time not a duration", 266 config: controller.Config{ 267 controller.CACertKey: testing.CACert, 268 controller.PruneTxnSleepTime: "15", 269 }, 270 expectError: `prune-txn-sleep-time must be a valid duration \(eg "10ms"\): time: missing unit in duration 15`, 271 }} 272 273 func (s *ConfigSuite) TestValidate(c *gc.C) { 274 for i, test := range validateTests { 275 c.Logf("test %d: %v", i, test.about) 276 err := test.config.Validate() 277 if test.expectError != "" { 278 c.Check(err, gc.ErrorMatches, test.expectError) 279 } else { 280 c.Check(err, jc.ErrorIsNil) 281 } 282 } 283 } 284 285 func (s *ConfigSuite) TestAPIPortDefaults(c *gc.C) { 286 cfg, err := controller.NewConfig(testing.ControllerTag.Id(), testing.CACert, nil) 287 c.Assert(err, jc.ErrorIsNil) 288 c.Assert(cfg.APIPortOpenDelay(), gc.Equals, 2*time.Second) 289 } 290 291 func (s *ConfigSuite) TestLogConfigDefaults(c *gc.C) { 292 cfg, err := controller.NewConfig(testing.ControllerTag.Id(), testing.CACert, nil) 293 c.Assert(err, jc.ErrorIsNil) 294 c.Assert(cfg.MaxLogsAge(), gc.Equals, 72*time.Hour) 295 c.Assert(cfg.MaxLogSizeMB(), gc.Equals, 4096) 296 } 297 298 func (s *ConfigSuite) TestLogConfigValues(c *gc.C) { 299 c.Assert(controller.AllowedUpdateConfigAttributes.Contains(controller.MaxLogsAge), jc.IsTrue) 300 c.Assert(controller.AllowedUpdateConfigAttributes.Contains(controller.MaxLogsSize), jc.IsTrue) 301 302 cfg, err := controller.NewConfig( 303 testing.ControllerTag.Id(), 304 testing.CACert, 305 map[string]interface{}{ 306 "max-logs-size": "8G", 307 "max-logs-age": "96h", 308 }, 309 ) 310 c.Assert(err, jc.ErrorIsNil) 311 c.Assert(cfg.MaxLogsAge(), gc.Equals, 96*time.Hour) 312 c.Assert(cfg.MaxLogSizeMB(), gc.Equals, 8192) 313 } 314 315 func (s *ConfigSuite) TestTxnLogConfigDefault(c *gc.C) { 316 cfg, err := controller.NewConfig(testing.ControllerTag.Id(), testing.CACert, nil) 317 c.Assert(err, jc.ErrorIsNil) 318 c.Assert(cfg.MaxTxnLogSizeMB(), gc.Equals, 10) 319 } 320 321 func (s *ConfigSuite) TestTxnLogConfigValue(c *gc.C) { 322 cfg, err := controller.NewConfig( 323 testing.ControllerTag.Id(), 324 testing.CACert, 325 map[string]interface{}{ 326 "max-txn-log-size": "8G", 327 }, 328 ) 329 c.Assert(err, jc.ErrorIsNil) 330 c.Assert(cfg.MaxTxnLogSizeMB(), gc.Equals, 8192) 331 } 332 333 func (s *ConfigSuite) TestMaxPruneTxnConfigDefault(c *gc.C) { 334 cfg, err := controller.NewConfig(testing.ControllerTag.Id(), testing.CACert, nil) 335 c.Assert(err, jc.ErrorIsNil) 336 c.Check(cfg.MaxPruneTxnBatchSize(), gc.Equals, 1*1000*1000) 337 c.Check(cfg.MaxPruneTxnPasses(), gc.Equals, 100) 338 } 339 340 func (s *ConfigSuite) TestMaxPruneTxnConfigValue(c *gc.C) { 341 cfg, err := controller.NewConfig( 342 testing.ControllerTag.Id(), 343 testing.CACert, 344 map[string]interface{}{ 345 "max-prune-txn-batch-size": "12345678", 346 "max-prune-txn-passes": "10", 347 }, 348 ) 349 c.Assert(err, jc.ErrorIsNil) 350 c.Check(cfg.MaxPruneTxnBatchSize(), gc.Equals, 12345678) 351 c.Check(cfg.MaxPruneTxnPasses(), gc.Equals, 10) 352 } 353 354 func (s *ConfigSuite) TestPruneTxnQueryCount(c *gc.C) { 355 cfg, err := controller.NewConfig( 356 testing.ControllerTag.Id(), 357 testing.CACert, 358 map[string]interface{}{ 359 "prune-txn-query-count": "500", 360 "prune-txn-sleep-time": "5ms", 361 }, 362 ) 363 c.Assert(err, jc.ErrorIsNil) 364 c.Check(cfg.PruneTxnQueryCount(), gc.Equals, 500) 365 c.Check(cfg.PruneTxnSleepTime(), gc.Equals, 5*time.Millisecond) 366 } 367 368 func (s *ConfigSuite) TestNetworkSpaceConfigValues(c *gc.C) { 369 haSpace := "space1" 370 managementSpace := "space2" 371 372 cfg, err := controller.NewConfig( 373 testing.ControllerTag.Id(), 374 testing.CACert, 375 map[string]interface{}{ 376 controller.JujuHASpace: haSpace, 377 controller.JujuManagementSpace: managementSpace, 378 }, 379 ) 380 c.Assert(err, jc.ErrorIsNil) 381 c.Assert(cfg.JujuHASpace(), gc.Equals, haSpace) 382 c.Assert(cfg.JujuManagementSpace(), gc.Equals, managementSpace) 383 } 384 385 func (s *ConfigSuite) TestNetworkSpaceConfigDefaults(c *gc.C) { 386 cfg, err := controller.NewConfig( 387 testing.ControllerTag.Id(), 388 testing.CACert, 389 map[string]interface{}{}, 390 ) 391 c.Assert(err, jc.ErrorIsNil) 392 c.Assert(cfg.JujuHASpace(), gc.Equals, "") 393 c.Assert(cfg.JujuManagementSpace(), gc.Equals, "") 394 } 395 396 func (s *ConfigSuite) TestAuditLogDefaults(c *gc.C) { 397 cfg, err := controller.NewConfig(testing.ControllerTag.Id(), testing.CACert, nil) 398 c.Assert(err, jc.ErrorIsNil) 399 c.Assert(cfg.AuditingEnabled(), gc.Equals, true) 400 c.Assert(cfg.AuditLogCaptureArgs(), gc.Equals, false) 401 c.Assert(cfg.AuditLogMaxSizeMB(), gc.Equals, 300) 402 c.Assert(cfg.AuditLogMaxBackups(), gc.Equals, 10) 403 c.Assert(cfg.AuditLogExcludeMethods(), gc.DeepEquals, 404 set.NewStrings(controller.DefaultAuditLogExcludeMethods...)) 405 } 406 407 func (s *ConfigSuite) TestAuditLogValues(c *gc.C) { 408 cfg, err := controller.NewConfig( 409 testing.ControllerTag.Id(), 410 testing.CACert, 411 map[string]interface{}{ 412 "auditing-enabled": false, 413 "audit-log-capture-args": true, 414 "audit-log-max-size": "100M", 415 "audit-log-max-backups": 10.0, 416 "audit-log-exclude-methods": []string{"Fleet.Foxes", "King.Gizzard", "ReadOnlyMethods"}, 417 }, 418 ) 419 c.Assert(err, jc.ErrorIsNil) 420 c.Assert(cfg.AuditingEnabled(), gc.Equals, false) 421 c.Assert(cfg.AuditLogCaptureArgs(), gc.Equals, true) 422 c.Assert(cfg.AuditLogMaxSizeMB(), gc.Equals, 100) 423 c.Assert(cfg.AuditLogMaxBackups(), gc.Equals, 10) 424 c.Assert(cfg.AuditLogExcludeMethods(), gc.DeepEquals, set.NewStrings( 425 "Fleet.Foxes", 426 "King.Gizzard", 427 "ReadOnlyMethods", 428 )) 429 } 430 431 func (s *ConfigSuite) TestAuditLogExcludeMethodsType(c *gc.C) { 432 _, err := controller.NewConfig( 433 testing.ControllerTag.Id(), 434 testing.CACert, 435 map[string]interface{}{ 436 "audit-log-exclude-methods": []int{2, 3, 4}, 437 }, 438 ) 439 c.Assert(err, gc.ErrorMatches, `audit-log-exclude-methods\[0\]: expected string, got int\(2\)`) 440 } 441 442 func (s *ConfigSuite) TestAuditLogFloatBackupsLoadedDirectly(c *gc.C) { 443 // We still need to be able to handle floats in data loaded from the DB. 444 cfg := controller.Config{ 445 controller.AuditLogMaxBackups: 10.0, 446 } 447 c.Assert(cfg.AuditLogMaxBackups(), gc.Equals, 10) 448 } 449 450 func (s *ConfigSuite) TestConfigManagementSpaceAsConstraint(c *gc.C) { 451 managementSpace := "management-space" 452 cfg, err := controller.NewConfig( 453 testing.ControllerTag.Id(), 454 testing.CACert, 455 map[string]interface{}{controller.JujuHASpace: managementSpace}, 456 ) 457 c.Assert(err, jc.ErrorIsNil) 458 c.Check(*cfg.AsSpaceConstraints(nil), gc.DeepEquals, []string{managementSpace}) 459 } 460 461 func (s *ConfigSuite) TestConfigHASpaceAsConstraint(c *gc.C) { 462 haSpace := "ha-space" 463 cfg, err := controller.NewConfig( 464 testing.ControllerTag.Id(), 465 testing.CACert, 466 map[string]interface{}{controller.JujuHASpace: haSpace}, 467 ) 468 c.Assert(err, jc.ErrorIsNil) 469 c.Check(*cfg.AsSpaceConstraints(nil), gc.DeepEquals, []string{haSpace}) 470 } 471 472 func (s *ConfigSuite) TestConfigAllSpacesAsMergedConstraints(c *gc.C) { 473 haSpace := "ha-space" 474 managementSpace := "management-space" 475 constraintSpace := "constraint-space" 476 477 cfg, err := controller.NewConfig( 478 testing.ControllerTag.Id(), 479 testing.CACert, 480 map[string]interface{}{ 481 controller.JujuHASpace: haSpace, 482 controller.JujuManagementSpace: managementSpace, 483 }, 484 ) 485 c.Assert(err, jc.ErrorIsNil) 486 487 got := *cfg.AsSpaceConstraints(&[]string{constraintSpace}) 488 c.Check(got, gc.DeepEquals, []string{constraintSpace, haSpace, managementSpace}) 489 } 490 491 func (s *ConfigSuite) TestConfigNoSpacesNilSpaceConfigPreserved(c *gc.C) { 492 cfg, err := controller.NewConfig( 493 testing.ControllerTag.Id(), 494 testing.CACert, 495 map[string]interface{}{}, 496 ) 497 c.Assert(err, jc.ErrorIsNil) 498 c.Check(cfg.AsSpaceConstraints(nil), gc.IsNil) 499 } 500 501 func (s *ConfigSuite) TestCAASOperatorImagePath(c *gc.C) { 502 for _, imagePath := range []string{ 503 "juju-operator-image", 504 "registry.foo.com/juju-operator-image", 505 "registry.foo.com/me/juju-operator-image", 506 "juju-operator-image:latest", 507 "juju-operator-image:2.4-beta1", 508 "registry.foo.com/juju-operator-image:2.4-beta1", 509 "registry.foo.com/me/juju-operator-image:2.4-beta1", 510 } { 511 cfg, err := controller.NewConfig( 512 testing.ControllerTag.Id(), 513 testing.CACert, 514 map[string]interface{}{ 515 controller.CAASOperatorImagePath: imagePath, 516 }, 517 ) 518 c.Assert(err, jc.ErrorIsNil) 519 c.Assert(cfg.CAASOperatorImagePath(), gc.Equals, imagePath) 520 } 521 } 522 523 func (s *ConfigSuite) TestCharmstoreURLDefault(c *gc.C) { 524 cfg, err := controller.NewConfig( 525 testing.ControllerTag.Id(), 526 testing.CACert, 527 map[string]interface{}{}, 528 ) 529 c.Assert(err, jc.ErrorIsNil) 530 c.Check(cfg.CharmStoreURL(), gc.Equals, csclient.ServerURL) 531 } 532 533 func (s *ConfigSuite) TestCharmstoreURLSettingValue(c *gc.C) { 534 csURL := "http://homestarrunner.com/charmstore" 535 cfg, err := controller.NewConfig( 536 testing.ControllerTag.Id(), 537 testing.CACert, 538 map[string]interface{}{ 539 controller.CharmStoreURL: csURL, 540 }, 541 ) 542 c.Assert(err, jc.ErrorIsNil) 543 c.Assert(cfg.CharmStoreURL(), gc.Equals, csURL) 544 } 545 546 func (s *ConfigSuite) TestMeteringURLDefault(c *gc.C) { 547 cfg, err := controller.NewConfig( 548 testing.ControllerTag.Id(), 549 testing.CACert, 550 map[string]interface{}{}, 551 ) 552 c.Assert(err, jc.ErrorIsNil) 553 c.Check(cfg.MeteringURL(), gc.Equals, romulus.DefaultAPIRoot) 554 } 555 556 func (s *ConfigSuite) TestMeteringURLSettingValue(c *gc.C) { 557 mURL := "http://homestarrunner.com/metering" 558 cfg, err := controller.NewConfig( 559 testing.ControllerTag.Id(), 560 testing.CACert, 561 map[string]interface{}{ 562 controller.MeteringURL: mURL, 563 }, 564 ) 565 c.Assert(err, jc.ErrorIsNil) 566 c.Assert(cfg.MeteringURL(), gc.Equals, mURL) 567 }