github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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 "encoding/base64" 8 "encoding/json" 9 "fmt" 10 stdtesting "testing" 11 "time" 12 13 "github.com/juju/collections/set" 14 "github.com/juju/loggo" 15 "github.com/juju/romulus" 16 jc "github.com/juju/testing/checkers" 17 "go.uber.org/mock/gomock" 18 gc "gopkg.in/check.v1" 19 20 "github.com/juju/juju/controller" 21 "github.com/juju/juju/docker" 22 "github.com/juju/juju/docker/registry" 23 "github.com/juju/juju/docker/registry/mocks" 24 "github.com/juju/juju/testing" 25 ) 26 27 func Test(t *stdtesting.T) { 28 gc.TestingT(t) 29 } 30 31 type ConfigSuite struct { 32 testing.FakeJujuXDGDataHomeSuite 33 } 34 35 var _ = gc.Suite(&ConfigSuite{}) 36 37 func (s *ConfigSuite) SetUpTest(c *gc.C) { 38 s.FakeJujuXDGDataHomeSuite.SetUpTest(c) 39 // Make sure that the defaults are used, which 40 // is <root>=WARNING 41 loggo.DefaultContext().ResetLoggerLevels() 42 } 43 44 var validateTests = []struct { 45 about string 46 config controller.Config 47 expectError string 48 }{{ 49 about: "missing CA cert", 50 expectError: `missing CA certificate`, 51 }, { 52 about: "bad CA cert", 53 config: controller.Config{ 54 controller.CACertKey: "xxx", 55 }, 56 expectError: `bad CA certificate in configuration: no certificates in pem bundle`, 57 }, { 58 about: "bad controller UUID", 59 config: controller.Config{ 60 controller.ControllerUUIDKey: "xxx", 61 controller.CACertKey: testing.CACert, 62 }, 63 expectError: `controller-uuid: expected UUID, got string\("xxx"\)`, 64 }} 65 66 func (s *ConfigSuite) TestValidate(c *gc.C) { 67 // Normally Validate is only called as part of the NewConfig call, which 68 // also does schema coercing. The NewConfig method takes the controller uuid 69 // and cacert as separate args, so to get invalid ones, we skip that part. 70 for i, test := range validateTests { 71 c.Logf("test %d: %v", i, test.about) 72 err := test.config.Validate() 73 if test.expectError != "" { 74 c.Check(err, gc.ErrorMatches, test.expectError) 75 } else { 76 c.Check(err, jc.ErrorIsNil) 77 } 78 } 79 } 80 81 var newConfigTests = []struct { 82 about string 83 config controller.Config 84 expectError string 85 }{{ 86 about: "HTTPS identity URL OK", 87 config: controller.Config{ 88 controller.IdentityURL: "https://0.1.2.3/foo", 89 }, 90 }, { 91 about: "HTTP identity URL requires public key", 92 config: controller.Config{ 93 controller.IdentityURL: "http://0.1.2.3/foo", 94 }, 95 expectError: `URL needs to be https when identity-public-key not provided`, 96 }, { 97 about: "HTTP identity URL OK if public key is provided", 98 config: controller.Config{ 99 controller.IdentityPublicKey: `o/yOqSNWncMo1GURWuez/dGR30TscmmuIxgjztpoHEY=`, 100 controller.IdentityURL: "http://0.1.2.3/foo", 101 }, 102 }, { 103 about: "feature flags mys be a list", 104 config: controller.Config{ 105 controller.Features: "foo", 106 }, 107 expectError: `features: expected list, got string\("foo"\)`, 108 }, { 109 about: "invalid identity public key", 110 config: controller.Config{ 111 controller.IdentityPublicKey: `xxxx`, 112 }, 113 expectError: `invalid identity public key: wrong length for key, got 3 want 32`, 114 }, { 115 about: "invalid management space name - whitespace", 116 config: controller.Config{ 117 controller.JujuManagementSpace: " ", 118 }, 119 expectError: `juju mgmt space name " " not valid`, 120 }, { 121 about: "invalid management space name - caps", 122 config: controller.Config{ 123 controller.JujuManagementSpace: "CAPS", 124 }, 125 expectError: `juju mgmt space name "CAPS" not valid`, 126 }, { 127 about: "invalid management space name - carriage return", 128 config: controller.Config{ 129 controller.JujuManagementSpace: "\n", 130 }, 131 expectError: `juju mgmt space name "\\n" not valid`, 132 }, { 133 about: "invalid HA space name - number", 134 config: controller.Config{ 135 controller.JujuHASpace: 666, 136 }, 137 expectError: `juju-ha-space: expected string, got int\(666\)`, 138 }, { 139 about: "invalid HA space name - bool", 140 config: controller.Config{ 141 controller.JujuHASpace: true, 142 }, 143 expectError: `juju-ha-space: expected string, got bool\(true\)`, 144 }, { 145 about: "invalid audit log max size", 146 config: controller.Config{ 147 controller.AuditLogMaxSize: "abcd", 148 }, 149 expectError: `invalid audit log max size in configuration: expected a non-negative number, got "abcd"`, 150 }, { 151 about: "zero audit log max size", 152 config: controller.Config{ 153 controller.AuditingEnabled: true, 154 controller.AuditLogMaxSize: "0M", 155 }, 156 expectError: `invalid audit log max size: can't be 0 if auditing is enabled`, 157 }, { 158 about: "invalid audit log max backups", 159 config: controller.Config{ 160 controller.AuditLogMaxBackups: -10, 161 }, 162 expectError: `invalid audit log max backups: should be a number of files \(or 0 to keep all\), got -10`, 163 }, { 164 about: "invalid audit log exclude", 165 config: controller.Config{ 166 controller.AuditLogExcludeMethods: []interface{}{"Dap.Kings", "ReadOnlyMethods", "Sharon Jones"}, 167 }, 168 expectError: `invalid audit log exclude methods: should be a list of "Facade.Method" names \(or "ReadOnlyMethods"\), got "Sharon Jones" at position 3`, 169 }, { 170 about: "invalid model log max size", 171 config: controller.Config{ 172 controller.ModelLogsSize: "abcd", 173 }, 174 expectError: `invalid model logs size in configuration: expected a non-negative number, got "abcd"`, 175 }, { 176 about: "zero model log max size", 177 config: controller.Config{ 178 controller.ModelLogsSize: "0", 179 }, 180 expectError: "model logs size less than 1 MB not valid", 181 }, { 182 about: "negative controller-api-port", 183 config: controller.Config{ 184 controller.ControllerAPIPort: -5, 185 }, 186 expectError: `non-positive integer for controller-api-port not valid`, 187 }, { 188 about: "controller-api-port matching api-port", 189 config: controller.Config{ 190 controller.APIPort: 12345, 191 controller.ControllerAPIPort: 12345, 192 }, 193 expectError: `controller-api-port matching api-port not valid`, 194 }, { 195 about: "controller-api-port matching state-port", 196 config: controller.Config{ 197 controller.APIPort: 12345, 198 controller.StatePort: 54321, 199 controller.ControllerAPIPort: 54321, 200 }, 201 expectError: `controller-api-port matching state-port not valid`, 202 }, { 203 about: "api-port-open-delay not a duration", 204 config: controller.Config{ 205 controller.APIPortOpenDelay: "15", 206 }, 207 expectError: `api-port-open-delay: conversion to duration: time: missing unit in duration "15"`, 208 }, { 209 about: "txn-prune-sleep-time not a duration", 210 config: controller.Config{ 211 controller.PruneTxnSleepTime: "15", 212 }, 213 expectError: `prune-txn-sleep-time: conversion to duration: time: missing unit in duration "15"`, 214 }, { 215 about: "mongo-memory-profile not valid", 216 config: controller.Config{ 217 controller.MongoMemoryProfile: "not-valid", 218 }, 219 expectError: `mongo-memory-profile: expected one of "low" or "default" got string\("not-valid"\)`, 220 }, { 221 about: "max-debug-log-duration not valid", 222 config: controller.Config{ 223 controller.MaxDebugLogDuration: time.Duration(0), 224 }, 225 expectError: `max-debug-log-duration cannot be zero`, 226 }, { 227 about: "agent-logfile-max-backups not valid", 228 config: controller.Config{ 229 controller.AgentLogfileMaxBackups: -1, 230 }, 231 expectError: `negative agent-logfile-max-backups not valid`, 232 }, { 233 about: "agent-logfile-max-size not valid", 234 config: controller.Config{ 235 controller.AgentLogfileMaxSize: "0", 236 }, 237 expectError: `agent-logfile-max-size less than 1 MB not valid`, 238 }, { 239 about: "model-logfile-max-backups not valid", 240 config: controller.Config{ 241 controller.ModelLogfileMaxBackups: -1, 242 }, 243 expectError: `negative model-logfile-max-backups not valid`, 244 }, { 245 about: "model-logfile-max-size not valid", 246 config: controller.Config{ 247 controller.ModelLogfileMaxSize: "0", 248 }, 249 expectError: `model-logfile-max-size less than 1 MB not valid`, 250 }, { 251 about: "agent-ratelimit-max non-int", 252 config: controller.Config{ 253 controller.AgentRateLimitMax: "ten", 254 }, 255 expectError: `agent-ratelimit-max: expected number, got string\("ten"\)`, 256 }, { 257 about: "agent-ratelimit-max negative", 258 config: controller.Config{ 259 controller.AgentRateLimitMax: "-5", 260 }, 261 expectError: `negative agent-ratelimit-max \(-5\) not valid`, 262 }, { 263 about: "agent-ratelimit-rate missing unit", 264 config: controller.Config{ 265 controller.AgentRateLimitRate: "150", 266 }, 267 expectError: `agent-ratelimit-rate: conversion to duration: time: missing unit in duration "?150"?`, 268 }, { 269 about: "agent-ratelimit-rate bad type, int", 270 config: controller.Config{ 271 controller.AgentRateLimitRate: 150, 272 }, 273 expectError: `agent-ratelimit-rate: expected string or time.Duration, got int\(150\)`, 274 }, { 275 about: "agent-ratelimit-rate zero", 276 config: controller.Config{ 277 controller.AgentRateLimitRate: "0s", 278 }, 279 expectError: `agent-ratelimit-rate cannot be zero`, 280 }, { 281 about: "agent-ratelimit-rate negative", 282 config: controller.Config{ 283 controller.AgentRateLimitRate: "-5s", 284 }, 285 expectError: `agent-ratelimit-rate cannot be negative`, 286 }, { 287 about: "agent-ratelimit-rate too large", 288 config: controller.Config{ 289 controller.AgentRateLimitRate: "4h", 290 }, 291 expectError: `agent-ratelimit-rate must be between 0..1m`, 292 }, { 293 about: "max-charm-state-size non-int", 294 config: controller.Config{ 295 controller.MaxCharmStateSize: "ten", 296 }, 297 expectError: `max-charm-state-size: expected number, got string\("ten"\)`, 298 }, { 299 about: "max-charm-state-size cannot be negative", 300 config: controller.Config{ 301 controller.MaxCharmStateSize: "-42", 302 }, 303 expectError: `invalid max charm state size: should be a number of bytes \(or 0 to disable limit\), got -42`, 304 }, { 305 about: "max-agent-state-size non-int", 306 config: controller.Config{ 307 controller.MaxAgentStateSize: "ten", 308 }, 309 expectError: `max-agent-state-size: expected number, got string\("ten"\)`, 310 }, { 311 about: "max-agent-state-size cannot be negative", 312 config: controller.Config{ 313 controller.MaxAgentStateSize: "-42", 314 }, 315 expectError: `invalid max agent state size: should be a number of bytes \(or 0 to disable limit\), got -42`, 316 }, { 317 about: "combined charm/agent state cannot exceed mongo's 16M limit/doc", 318 config: controller.Config{ 319 controller.MaxCharmStateSize: "14000000", 320 controller.MaxAgentStateSize: "3000000", 321 }, 322 expectError: `invalid max charm/agent state sizes: combined value should not exceed mongo's 16M per-document limit, got 17000000`, 323 }, { 324 about: "public-dns-address: expect string, got number", 325 config: controller.Config{ 326 controller.PublicDNSAddress: 42, 327 }, 328 expectError: `public-dns-address: expected string, got int\(42\)`, 329 }, { 330 about: "migration-agent-wait-time not a duration", 331 config: controller.Config{ 332 controller.MigrationMinionWaitMax: "15", 333 }, 334 expectError: `migration-agent-wait-time: conversion to duration: time: missing unit in duration "15"`, 335 }, { 336 about: "application-resource-download-limit cannot be negative", 337 config: controller.Config{ 338 controller.ApplicationResourceDownloadLimit: "-42", 339 }, 340 expectError: `negative application-resource-download-limit \(-42\) not valid, use 0 to disable the limit`, 341 }, { 342 about: "controller-resource-download-limit cannot be negative", 343 config: controller.Config{ 344 controller.ControllerResourceDownloadLimit: "-42", 345 }, 346 expectError: `negative controller-resource-download-limit \(-42\) not valid, use 0 to disable the limit`, 347 }, { 348 about: "login token refresh url", 349 config: controller.Config{ 350 controller.LoginTokenRefreshURL: `https://xxxx`, 351 }, 352 }, { 353 about: "invalid login token refresh url", 354 config: controller.Config{ 355 controller.LoginTokenRefreshURL: `xxxx`, 356 }, 357 expectError: `logic token refresh URL "xxxx" not valid`, 358 }, { 359 about: "invalid query tracing value", 360 config: controller.Config{ 361 controller.QueryTracingEnabled: "invalid", 362 }, 363 expectError: `query-tracing-enabled: expected bool, got string\("invalid"\)`, 364 }, { 365 about: "invalid query tracing threshold value", 366 config: controller.Config{ 367 controller.QueryTracingThreshold: "invalid", 368 }, 369 expectError: `query-tracing-threshold: conversion to duration: time: invalid duration "invalid"`, 370 }, { 371 about: "negative query tracing threshold duration", 372 config: controller.Config{ 373 controller.QueryTracingThreshold: "-1s", 374 }, 375 expectError: `query-tracing-threshold value "-1s" must be a positive duration`, 376 }, { 377 about: "invalid jujud-controller-snap-source value", 378 config: controller.Config{ 379 controller.JujudControllerSnapSource: "latest/stable", 380 }, 381 expectError: `jujud-controller-snap-source value "latest/stable" must be one of legacy, snapstore, local or local-dangerous.`, 382 }} 383 384 func (s *ConfigSuite) TestNewConfig(c *gc.C) { 385 for i, test := range newConfigTests { 386 c.Logf("test %d: %v", i, test.about) 387 _, err := controller.NewConfig(testing.ControllerTag.Id(), testing.CACert, test.config) 388 if test.expectError != "" { 389 c.Check(err, gc.ErrorMatches, test.expectError) 390 } else { 391 c.Check(err, jc.ErrorIsNil) 392 } 393 } 394 } 395 396 func (s *ConfigSuite) TestAPIPortDefaults(c *gc.C) { 397 cfg, err := controller.NewConfig(testing.ControllerTag.Id(), testing.CACert, nil) 398 c.Assert(err, jc.ErrorIsNil) 399 c.Assert(cfg.APIPortOpenDelay(), gc.Equals, 2*time.Second) 400 } 401 402 func (s *ConfigSuite) TestLogConfigDefaults(c *gc.C) { 403 cfg, err := controller.NewConfig(testing.ControllerTag.Id(), testing.CACert, nil) 404 c.Assert(err, jc.ErrorIsNil) 405 c.Assert(cfg.ModelLogsSizeMB(), gc.Equals, 20) 406 } 407 408 func (s *ConfigSuite) TestResourceDownloadLimits(c *gc.C) { 409 cfg, err := controller.NewConfig( 410 testing.ControllerTag.Id(), 411 testing.CACert, 412 map[string]interface{}{ 413 "application-resource-download-limit": "42", 414 "controller-resource-download-limit": "666", 415 }, 416 ) 417 c.Assert(err, jc.ErrorIsNil) 418 c.Assert(cfg.ApplicationResourceDownloadLimit(), gc.Equals, 42) 419 c.Assert(cfg.ControllerResourceDownloadLimit(), gc.Equals, 666) 420 } 421 422 func (s *ConfigSuite) TestLogConfigValues(c *gc.C) { 423 c.Assert(controller.AllowedUpdateConfigAttributes.Contains(controller.ModelLogsSize), jc.IsTrue) 424 425 cfg, err := controller.NewConfig( 426 testing.ControllerTag.Id(), 427 testing.CACert, 428 map[string]interface{}{ 429 "max-logs-size": "8G", 430 "max-logs-age": "96h", 431 "model-logs-size": "35M", 432 }, 433 ) 434 c.Assert(err, jc.ErrorIsNil) 435 c.Assert(cfg.ModelLogsSizeMB(), gc.Equals, 35) 436 } 437 438 func (s *ConfigSuite) TestTxnLogConfigDefault(c *gc.C) { 439 cfg, err := controller.NewConfig(testing.ControllerTag.Id(), testing.CACert, nil) 440 c.Assert(err, jc.ErrorIsNil) 441 c.Assert(cfg.MaxTxnLogSizeMB(), gc.Equals, 10) 442 } 443 444 func (s *ConfigSuite) TestTxnLogConfigValue(c *gc.C) { 445 cfg, err := controller.NewConfig( 446 testing.ControllerTag.Id(), 447 testing.CACert, 448 map[string]interface{}{ 449 "max-txn-log-size": "8G", 450 }, 451 ) 452 c.Assert(err, jc.ErrorIsNil) 453 c.Assert(cfg.MaxTxnLogSizeMB(), gc.Equals, 8192) 454 } 455 456 func (s *ConfigSuite) TestMaxPruneTxnConfigDefault(c *gc.C) { 457 cfg, err := controller.NewConfig(testing.ControllerTag.Id(), testing.CACert, nil) 458 c.Assert(err, jc.ErrorIsNil) 459 c.Check(cfg.MaxPruneTxnBatchSize(), gc.Equals, 1*1000*1000) 460 c.Check(cfg.MaxPruneTxnPasses(), gc.Equals, 100) 461 } 462 463 func (s *ConfigSuite) TestMaxPruneTxnConfigValue(c *gc.C) { 464 cfg, err := controller.NewConfig( 465 testing.ControllerTag.Id(), 466 testing.CACert, 467 map[string]interface{}{ 468 "max-prune-txn-batch-size": "12345678", 469 "max-prune-txn-passes": "10", 470 }, 471 ) 472 c.Assert(err, jc.ErrorIsNil) 473 c.Check(cfg.MaxPruneTxnBatchSize(), gc.Equals, 12345678) 474 c.Check(cfg.MaxPruneTxnPasses(), gc.Equals, 10) 475 } 476 477 func (s *ConfigSuite) TestPruneTxnQueryCount(c *gc.C) { 478 cfg, err := controller.NewConfig( 479 testing.ControllerTag.Id(), 480 testing.CACert, 481 map[string]interface{}{ 482 "prune-txn-query-count": "500", 483 "prune-txn-sleep-time": "5ms", 484 }, 485 ) 486 c.Assert(err, jc.ErrorIsNil) 487 c.Check(cfg.PruneTxnQueryCount(), gc.Equals, 500) 488 c.Check(cfg.PruneTxnSleepTime(), gc.Equals, 5*time.Millisecond) 489 } 490 491 func (s *ConfigSuite) TestPublicDNSAddressConfigValue(c *gc.C) { 492 cfg, err := controller.NewConfig( 493 testing.ControllerTag.Id(), 494 testing.CACert, 495 map[string]interface{}{ 496 "public-dns-address": "controller.test.com:12345", 497 }, 498 ) 499 c.Assert(err, jc.ErrorIsNil) 500 c.Check(cfg.PublicDNSAddress(), gc.Equals, "controller.test.com:12345") 501 } 502 503 func (s *ConfigSuite) TestNetworkSpaceConfigValues(c *gc.C) { 504 haSpace := "space1" 505 managementSpace := "space2" 506 507 cfg, err := controller.NewConfig( 508 testing.ControllerTag.Id(), 509 testing.CACert, 510 map[string]interface{}{ 511 controller.JujuHASpace: haSpace, 512 controller.JujuManagementSpace: managementSpace, 513 }, 514 ) 515 c.Assert(err, jc.ErrorIsNil) 516 c.Assert(cfg.JujuHASpace(), gc.Equals, haSpace) 517 c.Assert(cfg.JujuManagementSpace(), gc.Equals, managementSpace) 518 } 519 520 func (s *ConfigSuite) TestNetworkSpaceConfigDefaults(c *gc.C) { 521 cfg, err := controller.NewConfig( 522 testing.ControllerTag.Id(), 523 testing.CACert, 524 map[string]interface{}{}, 525 ) 526 c.Assert(err, jc.ErrorIsNil) 527 c.Assert(cfg.JujuHASpace(), gc.Equals, "") 528 c.Assert(cfg.JujuManagementSpace(), gc.Equals, "") 529 } 530 531 func (s *ConfigSuite) TestAuditLogDefaults(c *gc.C) { 532 cfg, err := controller.NewConfig(testing.ControllerTag.Id(), testing.CACert, nil) 533 c.Assert(err, jc.ErrorIsNil) 534 c.Assert(cfg.AuditingEnabled(), gc.Equals, true) 535 c.Assert(cfg.AuditLogCaptureArgs(), gc.Equals, false) 536 c.Assert(cfg.AuditLogMaxSizeMB(), gc.Equals, 300) 537 c.Assert(cfg.AuditLogMaxBackups(), gc.Equals, 10) 538 c.Assert(cfg.AuditLogExcludeMethods(), gc.DeepEquals, 539 set.NewStrings(controller.DefaultAuditLogExcludeMethods...)) 540 } 541 542 func (s *ConfigSuite) TestAuditLogValues(c *gc.C) { 543 cfg, err := controller.NewConfig( 544 testing.ControllerTag.Id(), 545 testing.CACert, 546 map[string]interface{}{ 547 "auditing-enabled": false, 548 "audit-log-capture-args": true, 549 "audit-log-max-size": "100M", 550 "audit-log-max-backups": 10.0, 551 "audit-log-exclude-methods": []string{"Fleet.Foxes", "King.Gizzard", "ReadOnlyMethods"}, 552 }, 553 ) 554 c.Assert(err, jc.ErrorIsNil) 555 c.Assert(cfg.AuditingEnabled(), gc.Equals, false) 556 c.Assert(cfg.AuditLogCaptureArgs(), gc.Equals, true) 557 c.Assert(cfg.AuditLogMaxSizeMB(), gc.Equals, 100) 558 c.Assert(cfg.AuditLogMaxBackups(), gc.Equals, 10) 559 c.Assert(cfg.AuditLogExcludeMethods(), gc.DeepEquals, set.NewStrings( 560 "Fleet.Foxes", 561 "King.Gizzard", 562 "ReadOnlyMethods", 563 )) 564 } 565 566 func (s *ConfigSuite) TestAuditLogExcludeMethodsType(c *gc.C) { 567 _, err := controller.NewConfig( 568 testing.ControllerTag.Id(), 569 testing.CACert, 570 map[string]interface{}{ 571 "audit-log-exclude-methods": []int{2, 3, 4}, 572 }, 573 ) 574 c.Assert(err, gc.ErrorMatches, `audit-log-exclude-methods\[0\]: expected string, got int\(2\)`) 575 } 576 577 func (s *ConfigSuite) TestAuditLogFloatBackupsLoadedDirectly(c *gc.C) { 578 // We still need to be able to handle floats in data loaded from the DB. 579 cfg := controller.Config{ 580 controller.AuditLogMaxBackups: 10.0, 581 } 582 c.Assert(cfg.AuditLogMaxBackups(), gc.Equals, 10) 583 } 584 585 func (s *ConfigSuite) TestConfigManagementSpaceAsConstraint(c *gc.C) { 586 managementSpace := "management-space" 587 cfg, err := controller.NewConfig( 588 testing.ControllerTag.Id(), 589 testing.CACert, 590 map[string]interface{}{controller.JujuHASpace: managementSpace}, 591 ) 592 c.Assert(err, jc.ErrorIsNil) 593 c.Check(*cfg.AsSpaceConstraints(nil), gc.DeepEquals, []string{managementSpace}) 594 } 595 596 func (s *ConfigSuite) TestConfigHASpaceAsConstraint(c *gc.C) { 597 haSpace := "ha-space" 598 cfg, err := controller.NewConfig( 599 testing.ControllerTag.Id(), 600 testing.CACert, 601 map[string]interface{}{controller.JujuHASpace: haSpace}, 602 ) 603 c.Assert(err, jc.ErrorIsNil) 604 c.Check(*cfg.AsSpaceConstraints(nil), gc.DeepEquals, []string{haSpace}) 605 } 606 607 func (s *ConfigSuite) TestConfigAllSpacesAsMergedConstraints(c *gc.C) { 608 haSpace := "ha-space" 609 managementSpace := "management-space" 610 constraintSpace := "constraint-space" 611 612 cfg, err := controller.NewConfig( 613 testing.ControllerTag.Id(), 614 testing.CACert, 615 map[string]interface{}{ 616 controller.JujuHASpace: haSpace, 617 controller.JujuManagementSpace: managementSpace, 618 }, 619 ) 620 c.Assert(err, jc.ErrorIsNil) 621 622 got := *cfg.AsSpaceConstraints(&[]string{constraintSpace}) 623 c.Check(got, gc.DeepEquals, []string{constraintSpace, haSpace, managementSpace}) 624 } 625 626 func (s *ConfigSuite) TestConfigNoSpacesNilSpaceConfigPreserved(c *gc.C) { 627 cfg, err := controller.NewConfig( 628 testing.ControllerTag.Id(), 629 testing.CACert, 630 map[string]interface{}{}, 631 ) 632 c.Assert(err, jc.ErrorIsNil) 633 c.Check(cfg.AsSpaceConstraints(nil), gc.IsNil) 634 } 635 636 func (s *ConfigSuite) TestCAASImageRepo(c *gc.C) { 637 ctrl := gomock.NewController(c) 638 defer ctrl.Finish() 639 640 // Ensure no requests are made from controller config code. 641 mockRoundTripper := mocks.NewMockRoundTripper(ctrl) 642 s.PatchValue(®istry.DefaultTransport, mockRoundTripper) 643 644 type tc struct { 645 content string 646 expected string 647 } 648 for _, imageRepo := range []tc{ 649 //used to reset since we don't have a --reset option 650 {content: "", expected: ""}, 651 {content: "docker.io/juju-operator-repo", expected: ""}, 652 {content: "registry.foo.com/jujuqa", expected: ""}, 653 {content: "ghcr.io/jujuqa", expected: ""}, 654 {content: "registry.gitlab.com/jujuqa", expected: ""}, 655 { 656 content: fmt.Sprintf(` 657 { 658 "serveraddress": "ghcr.io", 659 "auth": "%s", 660 "repository": "ghcr.io/test-account" 661 }`, base64.StdEncoding.EncodeToString([]byte("username:pwd"))), 662 expected: "ghcr.io/test-account"}, 663 } { 664 c.Logf("testing %#v", imageRepo) 665 if imageRepo.expected == "" { 666 imageRepo.expected = imageRepo.content 667 } 668 cfg, err := controller.NewConfig( 669 testing.ControllerTag.Id(), 670 testing.CACert, 671 map[string]interface{}{ 672 controller.CAASImageRepo: imageRepo.content, 673 }, 674 ) 675 c.Check(err, jc.ErrorIsNil) 676 imageRepoDetails, err := docker.NewImageRepoDetails(cfg.CAASImageRepo()) 677 c.Check(err, jc.ErrorIsNil) 678 c.Check(imageRepoDetails.Repository, gc.Equals, imageRepo.expected) 679 } 680 } 681 682 func (s *ConfigSuite) TestControllerNameDefault(c *gc.C) { 683 cfg := controller.Config{} 684 c.Check(cfg.ControllerName(), gc.Equals, "") 685 } 686 687 func (s *ConfigSuite) TestControllerNameSetGet(c *gc.C) { 688 cfg, err := controller.NewConfig( 689 testing.ControllerTag.Id(), 690 testing.CACert, 691 map[string]interface{}{ 692 controller.ControllerName: "test", 693 }, 694 ) 695 c.Assert(err, jc.ErrorIsNil) 696 c.Check(cfg.ControllerName(), gc.Equals, "test") 697 } 698 699 func (s *ConfigSuite) TestMeteringURLDefault(c *gc.C) { 700 cfg, err := controller.NewConfig( 701 testing.ControllerTag.Id(), 702 testing.CACert, 703 map[string]interface{}{}, 704 ) 705 c.Assert(err, jc.ErrorIsNil) 706 c.Check(cfg.MeteringURL(), gc.Equals, romulus.DefaultAPIRoot) 707 } 708 709 func (s *ConfigSuite) TestMeteringURLSettingValue(c *gc.C) { 710 mURL := "http://homestarrunner.com/metering" 711 cfg, err := controller.NewConfig( 712 testing.ControllerTag.Id(), 713 testing.CACert, 714 map[string]interface{}{ 715 controller.MeteringURL: mURL, 716 }, 717 ) 718 c.Assert(err, jc.ErrorIsNil) 719 c.Assert(cfg.MeteringURL(), gc.Equals, mURL) 720 } 721 722 func (s *ConfigSuite) TestMaxDebugLogDuration(c *gc.C) { 723 cfg, err := controller.NewConfig( 724 testing.ControllerTag.Id(), 725 testing.CACert, 726 map[string]interface{}{ 727 "max-debug-log-duration": "90m", 728 }, 729 ) 730 c.Assert(err, jc.ErrorIsNil) 731 c.Assert(cfg.MaxDebugLogDuration(), gc.Equals, 90*time.Minute) 732 } 733 734 func (s *ConfigSuite) TestMaxDebugLogDurationSchemaCoerce(c *gc.C) { 735 _, err := controller.NewConfig( 736 testing.ControllerTag.Id(), 737 testing.CACert, 738 map[string]interface{}{ 739 "max-debug-log-duration": "12", 740 }, 741 ) 742 c.Assert(err, gc.ErrorMatches, `max-debug-log-duration: conversion to duration: time: missing unit in duration "?12"?`) 743 } 744 745 func (s *ConfigSuite) TestFeatureFlags(c *gc.C) { 746 cfg, err := controller.NewConfig( 747 testing.ControllerTag.Id(), 748 testing.CACert, 749 map[string]interface{}{ 750 controller.Features: `["foo","bar"]`, 751 }, 752 ) 753 c.Assert(err, jc.ErrorIsNil) 754 c.Check(cfg.Features().Values(), jc.SameContents, []string{"foo", "bar"}) 755 } 756 757 func (s *ConfigSuite) TestDefaults(c *gc.C) { 758 cfg, err := controller.NewConfig( 759 testing.ControllerTag.Id(), 760 testing.CACert, 761 map[string]interface{}{}, 762 ) 763 c.Assert(err, jc.ErrorIsNil) 764 c.Assert(cfg.AgentRateLimitMax(), gc.Equals, controller.DefaultAgentRateLimitMax) 765 c.Assert(cfg.AgentRateLimitRate(), gc.Equals, controller.DefaultAgentRateLimitRate) 766 c.Assert(cfg.MaxDebugLogDuration(), gc.Equals, controller.DefaultMaxDebugLogDuration) 767 c.Assert(cfg.AgentLogfileMaxBackups(), gc.Equals, controller.DefaultAgentLogfileMaxBackups) 768 c.Assert(cfg.AgentLogfileMaxSizeMB(), gc.Equals, controller.DefaultAgentLogfileMaxSize) 769 c.Assert(cfg.ModelLogfileMaxBackups(), gc.Equals, controller.DefaultModelLogfileMaxBackups) 770 c.Assert(cfg.ModelLogfileMaxSizeMB(), gc.Equals, controller.DefaultModelLogfileMaxSize) 771 c.Assert(cfg.ApplicationResourceDownloadLimit(), gc.Equals, controller.DefaultApplicationResourceDownloadLimit) 772 c.Assert(cfg.ControllerResourceDownloadLimit(), gc.Equals, controller.DefaultControllerResourceDownloadLimit) 773 c.Assert(cfg.QueryTracingEnabled(), gc.Equals, controller.DefaultQueryTracingEnabled) 774 c.Assert(cfg.QueryTracingThreshold(), gc.Equals, controller.DefaultQueryTracingThreshold) 775 } 776 777 func (s *ConfigSuite) TestAgentLogfile(c *gc.C) { 778 cfg, err := controller.NewConfig( 779 testing.ControllerTag.Id(), 780 testing.CACert, 781 map[string]interface{}{ 782 "agent-logfile-max-size": "35M", 783 "agent-logfile-max-backups": "17", 784 }, 785 ) 786 c.Assert(err, jc.ErrorIsNil) 787 c.Assert(cfg.AgentLogfileMaxBackups(), gc.Equals, 17) 788 c.Assert(cfg.AgentLogfileMaxSizeMB(), gc.Equals, 35) 789 } 790 791 func (s *ConfigSuite) TestAgentLogfileBackupErr(c *gc.C) { 792 _, err := controller.NewConfig( 793 testing.ControllerTag.Id(), 794 testing.CACert, 795 map[string]interface{}{ 796 "agent-logfile-max-backups": "two", 797 }, 798 ) 799 c.Assert(err.Error(), gc.Equals, `agent-logfile-max-backups: expected number, got string("two")`) 800 } 801 802 func (s *ConfigSuite) TestModelLogfile(c *gc.C) { 803 cfg, err := controller.NewConfig( 804 testing.ControllerTag.Id(), 805 testing.CACert, 806 map[string]interface{}{ 807 "model-logfile-max-size": "25M", 808 "model-logfile-max-backups": "15", 809 }, 810 ) 811 c.Assert(err, jc.ErrorIsNil) 812 c.Assert(cfg.ModelLogfileMaxBackups(), gc.Equals, 15) 813 c.Assert(cfg.ModelLogfileMaxSizeMB(), gc.Equals, 25) 814 } 815 816 func (s *ConfigSuite) TestModelLogfileBackupErr(c *gc.C) { 817 _, err := controller.NewConfig( 818 testing.ControllerTag.Id(), 819 testing.CACert, 820 map[string]interface{}{ 821 "model-logfile-max-backups": "two", 822 }, 823 ) 824 c.Assert(err.Error(), gc.Equals, `model-logfile-max-backups: expected number, got string("two")`) 825 } 826 827 func (s *ConfigSuite) TestAgentRateLimitMax(c *gc.C) { 828 cfg, err := controller.NewConfig( 829 testing.ControllerTag.Id(), 830 testing.CACert, 831 map[string]interface{}{ 832 "agent-ratelimit-max": "0", 833 }, 834 ) 835 c.Assert(err, jc.ErrorIsNil) 836 c.Assert(cfg.AgentRateLimitMax(), gc.Equals, 0) 837 } 838 839 func (s *ConfigSuite) TestAgentRateLimitRate(c *gc.C) { 840 cfg, err := controller.NewConfig( 841 testing.ControllerTag.Id(), 842 testing.CACert, nil) 843 c.Assert(err, jc.ErrorIsNil) 844 c.Assert(cfg.AgentRateLimitRate(), gc.Equals, controller.DefaultAgentRateLimitRate) 845 846 cfg[controller.AgentRateLimitRate] = time.Second 847 c.Assert(cfg.AgentRateLimitRate(), gc.Equals, time.Second) 848 849 cfg[controller.AgentRateLimitRate] = "500ms" 850 c.Assert(cfg.AgentRateLimitRate(), gc.Equals, 500*time.Millisecond) 851 } 852 853 func (s *ConfigSuite) TestJujuDBSnapChannel(c *gc.C) { 854 cfg, err := controller.NewConfig( 855 testing.ControllerTag.Id(), 856 testing.CACert, 857 map[string]interface{}{}, 858 ) 859 c.Assert(err, jc.ErrorIsNil) 860 c.Assert(cfg.JujuDBSnapChannel(), gc.Equals, controller.DefaultJujuDBSnapChannel) 861 862 cfg, err = controller.NewConfig( 863 testing.ControllerTag.Id(), 864 testing.CACert, 865 map[string]interface{}{ 866 "juju-db-snap-channel": "latest/candidate", 867 }, 868 ) 869 c.Assert(err, jc.ErrorIsNil) 870 c.Assert(cfg.JujuDBSnapChannel(), gc.Equals, "latest/candidate") 871 } 872 873 func (s *ConfigSuite) TestMigrationMinionWaitMax(c *gc.C) { 874 cfg, err := controller.NewConfig( 875 testing.ControllerTag.Id(), 876 testing.CACert, nil) 877 c.Assert(err, jc.ErrorIsNil) 878 879 c.Assert(cfg.MigrationMinionWaitMax(), gc.Equals, controller.DefaultMigrationMinionWaitMax) 880 881 cfg[controller.MigrationMinionWaitMax] = "500ms" 882 c.Assert(cfg.MigrationMinionWaitMax(), gc.Equals, 500*time.Millisecond) 883 } 884 885 func (s *ConfigSuite) TestQueryTraceEnabled(c *gc.C) { 886 cfg, err := controller.NewConfig( 887 testing.ControllerTag.Id(), 888 testing.CACert, nil) 889 c.Assert(err, jc.ErrorIsNil) 890 891 c.Assert(cfg.QueryTracingEnabled(), gc.Equals, controller.DefaultQueryTracingEnabled) 892 893 cfg[controller.QueryTracingEnabled] = true 894 c.Assert(cfg.QueryTracingEnabled(), gc.Equals, true) 895 } 896 897 func (s *ConfigSuite) TestQueryTraceThreshold(c *gc.C) { 898 cfg, err := controller.NewConfig( 899 testing.ControllerTag.Id(), 900 testing.CACert, nil) 901 c.Assert(err, jc.ErrorIsNil) 902 903 c.Assert(cfg.QueryTracingThreshold(), gc.Equals, controller.DefaultQueryTracingThreshold) 904 905 cfg[controller.QueryTracingThreshold] = time.Second * 10 906 c.Assert(cfg.QueryTracingThreshold(), gc.Equals, time.Second*10) 907 908 d := time.Second * 10 909 cfg[controller.QueryTracingThreshold] = d.String() 910 911 bytes, err := json.Marshal(cfg) 912 c.Assert(err, jc.ErrorIsNil) 913 914 var cfg2 controller.Config 915 err = json.Unmarshal(bytes, &cfg2) 916 c.Assert(err, jc.ErrorIsNil) 917 918 c.Assert(cfg2.QueryTracingThreshold(), gc.Equals, time.Second*10) 919 }