github.com/hspak/nomad@v0.7.2-0.20180309000617-bc4ae22a39a5/command/agent/config_test.go (about) 1 package agent 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "net" 7 "os" 8 "path/filepath" 9 "reflect" 10 "testing" 11 "time" 12 13 "github.com/hashicorp/consul/lib/freeport" 14 "github.com/hashicorp/nomad/nomad/structs" 15 "github.com/hashicorp/nomad/nomad/structs/config" 16 ) 17 18 var ( 19 // trueValue/falseValue are used to get a pointer to a boolean 20 trueValue = true 21 falseValue = false 22 ) 23 24 func TestConfig_Merge(t *testing.T) { 25 c0 := &Config{} 26 27 c1 := &Config{ 28 Telemetry: &Telemetry{}, 29 Client: &ClientConfig{}, 30 Server: &ServerConfig{}, 31 ACL: &ACLConfig{}, 32 Ports: &Ports{}, 33 Addresses: &Addresses{}, 34 AdvertiseAddrs: &AdvertiseAddrs{}, 35 Vault: &config.VaultConfig{}, 36 Consul: &config.ConsulConfig{}, 37 Sentinel: &config.SentinelConfig{}, 38 Autopilot: &config.AutopilotConfig{}, 39 } 40 41 c2 := &Config{ 42 Region: "global", 43 Datacenter: "dc1", 44 NodeName: "node1", 45 DataDir: "/tmp/dir1", 46 LogLevel: "INFO", 47 EnableDebug: false, 48 LeaveOnInt: false, 49 LeaveOnTerm: false, 50 EnableSyslog: false, 51 SyslogFacility: "local0.info", 52 DisableUpdateCheck: false, 53 DisableAnonymousSignature: false, 54 BindAddr: "127.0.0.1", 55 Telemetry: &Telemetry{ 56 StatsiteAddr: "127.0.0.1:8125", 57 StatsdAddr: "127.0.0.1:8125", 58 DataDogAddr: "127.0.0.1:8125", 59 DataDogTags: []string{"cat1:tag1", "cat2:tag2"}, 60 PrometheusMetrics: true, 61 DisableHostname: false, 62 DisableTaggedMetrics: true, 63 BackwardsCompatibleMetrics: true, 64 CirconusAPIToken: "0", 65 CirconusAPIApp: "nomadic", 66 CirconusAPIURL: "http://api.circonus.com/v2", 67 CirconusSubmissionInterval: "60s", 68 CirconusCheckSubmissionURL: "https://someplace.com/metrics", 69 CirconusCheckID: "0", 70 CirconusCheckForceMetricActivation: "true", 71 CirconusCheckInstanceID: "node1:nomadic", 72 CirconusCheckSearchTag: "service:nomadic", 73 CirconusCheckDisplayName: "node1:nomadic", 74 CirconusCheckTags: "cat1:tag1,cat2:tag2", 75 CirconusBrokerID: "0", 76 CirconusBrokerSelectTag: "dc:dc1", 77 }, 78 Client: &ClientConfig{ 79 Enabled: false, 80 StateDir: "/tmp/state1", 81 AllocDir: "/tmp/alloc1", 82 NodeClass: "class1", 83 Options: map[string]string{ 84 "foo": "bar", 85 }, 86 NetworkSpeed: 100, 87 CpuCompute: 100, 88 MaxKillTimeout: "20s", 89 ClientMaxPort: 19996, 90 Reserved: &Resources{ 91 CPU: 10, 92 MemoryMB: 10, 93 DiskMB: 10, 94 IOPS: 10, 95 ReservedPorts: "1,10-30,55", 96 ParsedReservedPorts: []int{1, 2, 4}, 97 }, 98 }, 99 Server: &ServerConfig{ 100 Enabled: false, 101 AuthoritativeRegion: "global", 102 BootstrapExpect: 1, 103 DataDir: "/tmp/data1", 104 ProtocolVersion: 1, 105 RaftProtocol: 1, 106 NumSchedulers: 1, 107 NodeGCThreshold: "1h", 108 HeartbeatGrace: 30 * time.Second, 109 MinHeartbeatTTL: 30 * time.Second, 110 MaxHeartbeatsPerSecond: 30.0, 111 RedundancyZone: "foo", 112 UpgradeVersion: "foo", 113 }, 114 ACL: &ACLConfig{ 115 Enabled: true, 116 TokenTTL: 60 * time.Second, 117 PolicyTTL: 60 * time.Second, 118 ReplicationToken: "foo", 119 }, 120 Ports: &Ports{ 121 HTTP: 4646, 122 RPC: 4647, 123 Serf: 4648, 124 }, 125 Addresses: &Addresses{ 126 HTTP: "127.0.0.1", 127 RPC: "127.0.0.1", 128 Serf: "127.0.0.1", 129 }, 130 AdvertiseAddrs: &AdvertiseAddrs{ 131 RPC: "127.0.0.1", 132 Serf: "127.0.0.1", 133 }, 134 HTTPAPIResponseHeaders: map[string]string{ 135 "Access-Control-Allow-Origin": "*", 136 }, 137 Vault: &config.VaultConfig{ 138 Token: "1", 139 AllowUnauthenticated: &falseValue, 140 TaskTokenTTL: "1", 141 Addr: "1", 142 TLSCaFile: "1", 143 TLSCaPath: "1", 144 TLSCertFile: "1", 145 TLSKeyFile: "1", 146 TLSSkipVerify: &falseValue, 147 TLSServerName: "1", 148 }, 149 Consul: &config.ConsulConfig{ 150 ServerServiceName: "1", 151 ClientServiceName: "1", 152 AutoAdvertise: &falseValue, 153 Addr: "1", 154 Timeout: 1 * time.Second, 155 Token: "1", 156 Auth: "1", 157 EnableSSL: &falseValue, 158 VerifySSL: &falseValue, 159 CAFile: "1", 160 CertFile: "1", 161 KeyFile: "1", 162 ServerAutoJoin: &falseValue, 163 ClientAutoJoin: &falseValue, 164 ChecksUseAdvertise: &falseValue, 165 }, 166 Autopilot: &config.AutopilotConfig{ 167 CleanupDeadServers: &falseValue, 168 ServerStabilizationTime: 1 * time.Second, 169 LastContactThreshold: 1 * time.Second, 170 MaxTrailingLogs: 1, 171 EnableRedundancyZones: &falseValue, 172 DisableUpgradeMigration: &falseValue, 173 EnableCustomUpgrades: &falseValue, 174 }, 175 } 176 177 c3 := &Config{ 178 Region: "region2", 179 Datacenter: "dc2", 180 NodeName: "node2", 181 DataDir: "/tmp/dir2", 182 LogLevel: "DEBUG", 183 EnableDebug: true, 184 LeaveOnInt: true, 185 LeaveOnTerm: true, 186 EnableSyslog: true, 187 SyslogFacility: "local0.debug", 188 DisableUpdateCheck: true, 189 DisableAnonymousSignature: true, 190 BindAddr: "127.0.0.2", 191 Telemetry: &Telemetry{ 192 StatsiteAddr: "127.0.0.2:8125", 193 StatsdAddr: "127.0.0.2:8125", 194 DataDogAddr: "127.0.0.1:8125", 195 DataDogTags: []string{"cat1:tag1", "cat2:tag2"}, 196 PrometheusMetrics: true, 197 DisableHostname: true, 198 PublishNodeMetrics: true, 199 PublishAllocationMetrics: true, 200 DisableTaggedMetrics: true, 201 BackwardsCompatibleMetrics: true, 202 CirconusAPIToken: "1", 203 CirconusAPIApp: "nomad", 204 CirconusAPIURL: "https://api.circonus.com/v2", 205 CirconusSubmissionInterval: "10s", 206 CirconusCheckSubmissionURL: "https://example.com/metrics", 207 CirconusCheckID: "1", 208 CirconusCheckForceMetricActivation: "false", 209 CirconusCheckInstanceID: "node2:nomad", 210 CirconusCheckSearchTag: "service:nomad", 211 CirconusCheckDisplayName: "node2:nomad", 212 CirconusCheckTags: "cat1:tag1,cat2:tag2", 213 CirconusBrokerID: "1", 214 CirconusBrokerSelectTag: "dc:dc2", 215 }, 216 Client: &ClientConfig{ 217 Enabled: true, 218 StateDir: "/tmp/state2", 219 AllocDir: "/tmp/alloc2", 220 NodeClass: "class2", 221 Servers: []string{"server2"}, 222 Meta: map[string]string{ 223 "baz": "zip", 224 }, 225 Options: map[string]string{ 226 "foo": "bar", 227 "baz": "zip", 228 }, 229 ChrootEnv: map[string]string{}, 230 ClientMaxPort: 20000, 231 ClientMinPort: 22000, 232 NetworkSpeed: 105, 233 CpuCompute: 105, 234 MaxKillTimeout: "50s", 235 Reserved: &Resources{ 236 CPU: 15, 237 MemoryMB: 15, 238 DiskMB: 15, 239 IOPS: 15, 240 ReservedPorts: "2,10-30,55", 241 ParsedReservedPorts: []int{1, 2, 3}, 242 }, 243 GCInterval: 6 * time.Second, 244 GCParallelDestroys: 6, 245 GCDiskUsageThreshold: 71, 246 GCInodeUsageThreshold: 86, 247 }, 248 Server: &ServerConfig{ 249 Enabled: true, 250 AuthoritativeRegion: "global2", 251 BootstrapExpect: 2, 252 DataDir: "/tmp/data2", 253 ProtocolVersion: 2, 254 RaftProtocol: 2, 255 NumSchedulers: 2, 256 EnabledSchedulers: []string{structs.JobTypeBatch}, 257 NodeGCThreshold: "12h", 258 HeartbeatGrace: 2 * time.Minute, 259 MinHeartbeatTTL: 2 * time.Minute, 260 MaxHeartbeatsPerSecond: 200.0, 261 RejoinAfterLeave: true, 262 StartJoin: []string{"1.1.1.1"}, 263 RetryJoin: []string{"1.1.1.1"}, 264 RetryInterval: "10s", 265 retryInterval: time.Second * 10, 266 NonVotingServer: true, 267 RedundancyZone: "bar", 268 UpgradeVersion: "bar", 269 }, 270 ACL: &ACLConfig{ 271 Enabled: true, 272 TokenTTL: 20 * time.Second, 273 PolicyTTL: 20 * time.Second, 274 ReplicationToken: "foobar", 275 }, 276 Ports: &Ports{ 277 HTTP: 20000, 278 RPC: 21000, 279 Serf: 22000, 280 }, 281 Addresses: &Addresses{ 282 HTTP: "127.0.0.2", 283 RPC: "127.0.0.2", 284 Serf: "127.0.0.2", 285 }, 286 AdvertiseAddrs: &AdvertiseAddrs{ 287 RPC: "127.0.0.2", 288 Serf: "127.0.0.2", 289 }, 290 HTTPAPIResponseHeaders: map[string]string{ 291 "Access-Control-Allow-Origin": "*", 292 "Access-Control-Allow-Methods": "GET, POST, OPTIONS", 293 }, 294 Vault: &config.VaultConfig{ 295 Token: "2", 296 AllowUnauthenticated: &trueValue, 297 TaskTokenTTL: "2", 298 Addr: "2", 299 TLSCaFile: "2", 300 TLSCaPath: "2", 301 TLSCertFile: "2", 302 TLSKeyFile: "2", 303 TLSSkipVerify: &trueValue, 304 TLSServerName: "2", 305 }, 306 Consul: &config.ConsulConfig{ 307 ServerServiceName: "2", 308 ClientServiceName: "2", 309 AutoAdvertise: &trueValue, 310 Addr: "2", 311 Timeout: 2 * time.Second, 312 Token: "2", 313 Auth: "2", 314 EnableSSL: &trueValue, 315 VerifySSL: &trueValue, 316 CAFile: "2", 317 CertFile: "2", 318 KeyFile: "2", 319 ServerAutoJoin: &trueValue, 320 ClientAutoJoin: &trueValue, 321 ChecksUseAdvertise: &trueValue, 322 }, 323 Sentinel: &config.SentinelConfig{ 324 Imports: []*config.SentinelImport{ 325 { 326 Name: "foo", 327 Path: "foo", 328 Args: []string{"a", "b", "c"}, 329 }, 330 }, 331 }, 332 Autopilot: &config.AutopilotConfig{ 333 CleanupDeadServers: &trueValue, 334 ServerStabilizationTime: 2 * time.Second, 335 LastContactThreshold: 2 * time.Second, 336 MaxTrailingLogs: 2, 337 EnableRedundancyZones: &trueValue, 338 DisableUpgradeMigration: &trueValue, 339 EnableCustomUpgrades: &trueValue, 340 }, 341 } 342 343 result := c0.Merge(c1) 344 result = result.Merge(c2) 345 result = result.Merge(c3) 346 if !reflect.DeepEqual(result, c3) { 347 t.Fatalf("bad:\n%#v\n%#v", result, c3) 348 } 349 } 350 351 func TestConfig_ParseConfigFile(t *testing.T) { 352 // Fails if the file doesn't exist 353 if _, err := ParseConfigFile("/unicorns/leprechauns"); err == nil { 354 t.Fatalf("expected error, got nothing") 355 } 356 357 fh, err := ioutil.TempFile("", "nomad") 358 if err != nil { 359 t.Fatalf("err: %s", err) 360 } 361 defer os.RemoveAll(fh.Name()) 362 363 // Invalid content returns error 364 if _, err := fh.WriteString("nope;!!!"); err != nil { 365 t.Fatalf("err: %s", err) 366 } 367 if _, err := ParseConfigFile(fh.Name()); err == nil { 368 t.Fatalf("expected load error, got nothing") 369 } 370 371 // Valid content parses successfully 372 if err := fh.Truncate(0); err != nil { 373 t.Fatalf("err: %s", err) 374 } 375 if _, err := fh.Seek(0, 0); err != nil { 376 t.Fatalf("err: %s", err) 377 } 378 if _, err := fh.WriteString(`{"region":"west"}`); err != nil { 379 t.Fatalf("err: %s", err) 380 } 381 382 config, err := ParseConfigFile(fh.Name()) 383 if err != nil { 384 t.Fatalf("err: %s", err) 385 } 386 if config.Region != "west" { 387 t.Fatalf("bad region: %q", config.Region) 388 } 389 } 390 391 func TestConfig_LoadConfigDir(t *testing.T) { 392 // Fails if the dir doesn't exist. 393 if _, err := LoadConfigDir("/unicorns/leprechauns"); err == nil { 394 t.Fatalf("expected error, got nothing") 395 } 396 397 dir, err := ioutil.TempDir("", "nomad") 398 if err != nil { 399 t.Fatalf("err: %s", err) 400 } 401 defer os.RemoveAll(dir) 402 403 // Returns empty config on empty dir 404 config, err := LoadConfig(dir) 405 if err != nil { 406 t.Fatalf("err: %s", err) 407 } 408 if config == nil { 409 t.Fatalf("should not be nil") 410 } 411 412 file1 := filepath.Join(dir, "conf1.hcl") 413 err = ioutil.WriteFile(file1, []byte(`{"region":"west"}`), 0600) 414 if err != nil { 415 t.Fatalf("err: %s", err) 416 } 417 418 file2 := filepath.Join(dir, "conf2.hcl") 419 err = ioutil.WriteFile(file2, []byte(`{"datacenter":"sfo"}`), 0600) 420 if err != nil { 421 t.Fatalf("err: %s", err) 422 } 423 424 file3 := filepath.Join(dir, "conf3.hcl") 425 err = ioutil.WriteFile(file3, []byte(`nope;!!!`), 0600) 426 if err != nil { 427 t.Fatalf("err: %s", err) 428 } 429 430 // Fails if we have a bad config file 431 if _, err := LoadConfigDir(dir); err == nil { 432 t.Fatalf("expected load error, got nothing") 433 } 434 435 if err := os.Remove(file3); err != nil { 436 t.Fatalf("err: %s", err) 437 } 438 439 // Works if configs are valid 440 config, err = LoadConfigDir(dir) 441 if err != nil { 442 t.Fatalf("err: %s", err) 443 } 444 if config.Region != "west" || config.Datacenter != "sfo" { 445 t.Fatalf("bad: %#v", config) 446 } 447 } 448 449 func TestConfig_LoadConfig(t *testing.T) { 450 // Fails if the target doesn't exist 451 if _, err := LoadConfig("/unicorns/leprechauns"); err == nil { 452 t.Fatalf("expected error, got nothing") 453 } 454 455 fh, err := ioutil.TempFile("", "nomad") 456 if err != nil { 457 t.Fatalf("err: %s", err) 458 } 459 defer os.Remove(fh.Name()) 460 461 if _, err := fh.WriteString(`{"region":"west"}`); err != nil { 462 t.Fatalf("err: %s", err) 463 } 464 465 // Works on a config file 466 config, err := LoadConfig(fh.Name()) 467 if err != nil { 468 t.Fatalf("err: %s", err) 469 } 470 if config.Region != "west" { 471 t.Fatalf("bad: %#v", config) 472 } 473 474 expectedConfigFiles := []string{fh.Name()} 475 if !reflect.DeepEqual(config.Files, expectedConfigFiles) { 476 t.Errorf("Loaded configs don't match\nExpected\n%+vGot\n%+v\n", 477 expectedConfigFiles, config.Files) 478 } 479 480 dir, err := ioutil.TempDir("", "nomad") 481 if err != nil { 482 t.Fatalf("err: %s", err) 483 } 484 defer os.RemoveAll(dir) 485 486 file1 := filepath.Join(dir, "config1.hcl") 487 err = ioutil.WriteFile(file1, []byte(`{"datacenter":"sfo"}`), 0600) 488 if err != nil { 489 t.Fatalf("err: %s", err) 490 } 491 492 // Works on config dir 493 config, err = LoadConfig(dir) 494 if err != nil { 495 t.Fatalf("err: %s", err) 496 } 497 if config.Datacenter != "sfo" { 498 t.Fatalf("bad: %#v", config) 499 } 500 501 expectedConfigFiles = []string{file1} 502 if !reflect.DeepEqual(config.Files, expectedConfigFiles) { 503 t.Errorf("Loaded configs don't match\nExpected\n%+vGot\n%+v\n", 504 expectedConfigFiles, config.Files) 505 } 506 } 507 508 func TestConfig_LoadConfigsFileOrder(t *testing.T) { 509 config1, err := LoadConfigDir("test-resources/etcnomad") 510 if err != nil { 511 t.Fatalf("Failed to load config: %s", err) 512 } 513 514 config2, err := LoadConfig("test-resources/myconf") 515 if err != nil { 516 t.Fatalf("Failed to load config: %s", err) 517 } 518 519 expected := []string{ 520 // filepath.FromSlash changes these to backslash \ on Windows 521 filepath.FromSlash("test-resources/etcnomad/common.hcl"), 522 filepath.FromSlash("test-resources/etcnomad/server.json"), 523 filepath.FromSlash("test-resources/myconf"), 524 } 525 526 config := config1.Merge(config2) 527 528 if !reflect.DeepEqual(config.Files, expected) { 529 t.Errorf("Loaded configs don't match\nwant: %+v\n got: %+v\n", 530 expected, config.Files) 531 } 532 } 533 534 func TestConfig_Listener(t *testing.T) { 535 config := DefaultConfig() 536 537 // Fails on invalid input 538 if ln, err := config.Listener("tcp", "nope", 8080); err == nil { 539 ln.Close() 540 t.Fatalf("expected addr error") 541 } 542 if ln, err := config.Listener("nope", "127.0.0.1", 8080); err == nil { 543 ln.Close() 544 t.Fatalf("expected protocol err") 545 } 546 if ln, err := config.Listener("tcp", "127.0.0.1", -1); err == nil { 547 ln.Close() 548 t.Fatalf("expected port error") 549 } 550 551 // Works with valid inputs 552 ports := freeport.GetT(t, 2) 553 ln, err := config.Listener("tcp", "127.0.0.1", ports[0]) 554 if err != nil { 555 t.Fatalf("err: %s", err) 556 } 557 ln.Close() 558 559 if net := ln.Addr().Network(); net != "tcp" { 560 t.Fatalf("expected tcp, got: %q", net) 561 } 562 want := fmt.Sprintf("127.0.0.1:%d", ports[0]) 563 if addr := ln.Addr().String(); addr != want { 564 t.Fatalf("expected %q, got: %q", want, addr) 565 } 566 567 // Falls back to default bind address if non provided 568 config.BindAddr = "0.0.0.0" 569 ln, err = config.Listener("tcp4", "", ports[1]) 570 if err != nil { 571 t.Fatalf("err: %s", err) 572 } 573 ln.Close() 574 575 want = fmt.Sprintf("0.0.0.0:%d", ports[1]) 576 if addr := ln.Addr().String(); addr != want { 577 t.Fatalf("expected %q, got: %q", want, addr) 578 } 579 } 580 581 // TestConfig_normalizeAddrs_DevMode asserts that normalizeAddrs allows 582 // advertising localhost in dev mode. 583 func TestConfig_normalizeAddrs_DevMode(t *testing.T) { 584 // allow to advertise 127.0.0.1 if dev-mode is enabled 585 c := &Config{ 586 BindAddr: "127.0.0.1", 587 Ports: &Ports{ 588 HTTP: 4646, 589 RPC: 4647, 590 Serf: 4648, 591 }, 592 Addresses: &Addresses{}, 593 AdvertiseAddrs: &AdvertiseAddrs{}, 594 DevMode: true, 595 } 596 597 if err := c.normalizeAddrs(); err != nil { 598 t.Fatalf("unable to normalize addresses: %s", err) 599 } 600 601 if c.BindAddr != "127.0.0.1" { 602 t.Fatalf("expected BindAddr 127.0.0.1, got %s", c.BindAddr) 603 } 604 605 if c.normalizedAddrs.HTTP != "127.0.0.1:4646" { 606 t.Fatalf("expected HTTP address 127.0.0.1:4646, got %s", c.normalizedAddrs.HTTP) 607 } 608 609 if c.normalizedAddrs.RPC != "127.0.0.1:4647" { 610 t.Fatalf("expected RPC address 127.0.0.1:4647, got %s", c.normalizedAddrs.RPC) 611 } 612 613 if c.normalizedAddrs.Serf != "127.0.0.1:4648" { 614 t.Fatalf("expected Serf address 127.0.0.1:4648, got %s", c.normalizedAddrs.Serf) 615 } 616 617 if c.AdvertiseAddrs.HTTP != "127.0.0.1:4646" { 618 t.Fatalf("expected HTTP advertise address 127.0.0.1:4646, got %s", c.AdvertiseAddrs.HTTP) 619 } 620 621 if c.AdvertiseAddrs.RPC != "127.0.0.1:4647" { 622 t.Fatalf("expected RPC advertise address 127.0.0.1:4647, got %s", c.AdvertiseAddrs.RPC) 623 } 624 625 // Client mode, no Serf address defined 626 if c.AdvertiseAddrs.Serf != "" { 627 t.Fatalf("expected unset Serf advertise address, got %s", c.AdvertiseAddrs.Serf) 628 } 629 } 630 631 // TestConfig_normalizeAddrs_NoAdvertise asserts that normalizeAddrs will 632 // fail if no valid advertise address available in non-dev mode. 633 func TestConfig_normalizeAddrs_NoAdvertise(t *testing.T) { 634 c := &Config{ 635 BindAddr: "127.0.0.1", 636 Ports: &Ports{ 637 HTTP: 4646, 638 RPC: 4647, 639 Serf: 4648, 640 }, 641 Addresses: &Addresses{}, 642 AdvertiseAddrs: &AdvertiseAddrs{}, 643 DevMode: false, 644 } 645 646 if err := c.normalizeAddrs(); err == nil { 647 t.Fatalf("expected an error when no valid advertise address is available") 648 } 649 650 if c.AdvertiseAddrs.HTTP == "127.0.0.1:4646" { 651 t.Fatalf("expected non-localhost HTTP advertise address, got %s", c.AdvertiseAddrs.HTTP) 652 } 653 654 if c.AdvertiseAddrs.RPC == "127.0.0.1:4647" { 655 t.Fatalf("expected non-localhost RPC advertise address, got %s", c.AdvertiseAddrs.RPC) 656 } 657 658 if c.AdvertiseAddrs.Serf == "127.0.0.1:4648" { 659 t.Fatalf("expected non-localhost Serf advertise address, got %s", c.AdvertiseAddrs.Serf) 660 } 661 } 662 663 // TestConfig_normalizeAddrs_AdvertiseLocalhost asserts localhost can be 664 // advertised if it's explicitly set in the config. 665 func TestConfig_normalizeAddrs_AdvertiseLocalhost(t *testing.T) { 666 c := &Config{ 667 BindAddr: "127.0.0.1", 668 Ports: &Ports{ 669 HTTP: 4646, 670 RPC: 4647, 671 Serf: 4648, 672 }, 673 Addresses: &Addresses{}, 674 AdvertiseAddrs: &AdvertiseAddrs{ 675 HTTP: "127.0.0.1", 676 RPC: "127.0.0.1", 677 Serf: "127.0.0.1", 678 }, 679 DevMode: false, 680 Server: &ServerConfig{Enabled: true}, 681 } 682 683 if err := c.normalizeAddrs(); err != nil { 684 t.Fatalf("unexpected error when manually setting bind mode: %v", err) 685 } 686 687 if c.AdvertiseAddrs.HTTP != "127.0.0.1:4646" { 688 t.Errorf("expected localhost HTTP advertise address, got %s", c.AdvertiseAddrs.HTTP) 689 } 690 691 if c.AdvertiseAddrs.RPC != "127.0.0.1:4647" { 692 t.Errorf("expected localhost RPC advertise address, got %s", c.AdvertiseAddrs.RPC) 693 } 694 695 if c.AdvertiseAddrs.Serf != "127.0.0.1:4648" { 696 t.Errorf("expected localhost Serf advertise address, got %s", c.AdvertiseAddrs.Serf) 697 } 698 } 699 700 // TestConfig_normalizeAddrs_IPv6Loopback asserts that an IPv6 loopback address 701 // is normalized properly. See #2739 702 func TestConfig_normalizeAddrs_IPv6Loopback(t *testing.T) { 703 c := &Config{ 704 BindAddr: "::1", 705 Ports: &Ports{ 706 HTTP: 4646, 707 RPC: 4647, 708 }, 709 Addresses: &Addresses{}, 710 AdvertiseAddrs: &AdvertiseAddrs{ 711 HTTP: "::1", 712 RPC: "::1", 713 }, 714 DevMode: false, 715 } 716 717 if err := c.normalizeAddrs(); err != nil { 718 t.Fatalf("unexpected error when manually setting bind mode: %v", err) 719 } 720 721 if c.Addresses.HTTP != "::1" { 722 t.Errorf("expected ::1 HTTP address, got %s", c.Addresses.HTTP) 723 } 724 725 if c.Addresses.RPC != "::1" { 726 t.Errorf("expected ::1 RPC address, got %s", c.Addresses.RPC) 727 } 728 729 if c.AdvertiseAddrs.HTTP != "[::1]:4646" { 730 t.Errorf("expected [::1] HTTP advertise address, got %s", c.AdvertiseAddrs.HTTP) 731 } 732 733 if c.AdvertiseAddrs.RPC != "[::1]:4647" { 734 t.Errorf("expected [::1] RPC advertise address, got %s", c.AdvertiseAddrs.RPC) 735 } 736 } 737 738 func TestConfig_normalizeAddrs(t *testing.T) { 739 c := &Config{ 740 BindAddr: "169.254.1.5", 741 Ports: &Ports{ 742 HTTP: 4646, 743 RPC: 4647, 744 Serf: 4648, 745 }, 746 Addresses: &Addresses{ 747 HTTP: "169.254.1.10", 748 }, 749 AdvertiseAddrs: &AdvertiseAddrs{ 750 RPC: "169.254.1.40", 751 }, 752 Server: &ServerConfig{ 753 Enabled: true, 754 }, 755 } 756 757 if err := c.normalizeAddrs(); err != nil { 758 t.Fatalf("unable to normalize addresses: %s", err) 759 } 760 761 if c.BindAddr != "169.254.1.5" { 762 t.Fatalf("expected BindAddr 169.254.1.5, got %s", c.BindAddr) 763 } 764 765 if c.AdvertiseAddrs.HTTP != "169.254.1.10:4646" { 766 t.Fatalf("expected HTTP advertise address 169.254.1.10:4646, got %s", c.AdvertiseAddrs.HTTP) 767 } 768 769 if c.AdvertiseAddrs.RPC != "169.254.1.40:4647" { 770 t.Fatalf("expected RPC advertise address 169.254.1.40:4647, got %s", c.AdvertiseAddrs.RPC) 771 } 772 773 if c.AdvertiseAddrs.Serf != "169.254.1.5:4648" { 774 t.Fatalf("expected Serf advertise address 169.254.1.5:4648, got %s", c.AdvertiseAddrs.Serf) 775 } 776 777 c = &Config{ 778 BindAddr: "{{ GetPrivateIP }}", 779 Ports: &Ports{ 780 HTTP: 4646, 781 RPC: 4647, 782 Serf: 4648, 783 }, 784 Addresses: &Addresses{}, 785 AdvertiseAddrs: &AdvertiseAddrs{ 786 RPC: "{{ GetPrivateIP }}", 787 }, 788 Server: &ServerConfig{ 789 Enabled: true, 790 }, 791 } 792 793 if err := c.normalizeAddrs(); err != nil { 794 t.Fatalf("unable to normalize addresses: %s", err) 795 } 796 797 exp := net.JoinHostPort(c.BindAddr, "4646") 798 if c.AdvertiseAddrs.HTTP != exp { 799 t.Fatalf("expected HTTP advertise address %s, got %s", exp, c.AdvertiseAddrs.HTTP) 800 } 801 802 exp = net.JoinHostPort(c.BindAddr, "4647") 803 if c.AdvertiseAddrs.RPC != exp { 804 t.Fatalf("expected RPC advertise address %s, got %s", exp, c.AdvertiseAddrs.RPC) 805 } 806 807 exp = net.JoinHostPort(c.BindAddr, "4648") 808 if c.AdvertiseAddrs.Serf != exp { 809 t.Fatalf("expected Serf advertise address %s, got %s", exp, c.AdvertiseAddrs.Serf) 810 } 811 812 // allow to advertise 127.0.0.1 in non-dev mode, if explicitly configured to do so 813 c = &Config{ 814 BindAddr: "127.0.0.1", 815 Ports: &Ports{ 816 HTTP: 4646, 817 RPC: 4647, 818 Serf: 4648, 819 }, 820 Addresses: &Addresses{}, 821 AdvertiseAddrs: &AdvertiseAddrs{ 822 HTTP: "127.0.0.1:4646", 823 RPC: "127.0.0.1:4647", 824 Serf: "127.0.0.1:4648", 825 }, 826 DevMode: false, 827 Server: &ServerConfig{ 828 Enabled: true, 829 }, 830 } 831 832 if err := c.normalizeAddrs(); err != nil { 833 t.Fatalf("unable to normalize addresses: %s", err) 834 } 835 836 if c.AdvertiseAddrs.HTTP != "127.0.0.1:4646" { 837 t.Fatalf("expected HTTP advertise address 127.0.0.1:4646, got %s", c.AdvertiseAddrs.HTTP) 838 } 839 840 if c.AdvertiseAddrs.RPC != "127.0.0.1:4647" { 841 t.Fatalf("expected RPC advertise address 127.0.0.1:4647, got %s", c.AdvertiseAddrs.RPC) 842 } 843 844 if c.AdvertiseAddrs.RPC != "127.0.0.1:4647" { 845 t.Fatalf("expected RPC advertise address 127.0.0.1:4647, got %s", c.AdvertiseAddrs.RPC) 846 } 847 } 848 849 func TestResources_ParseReserved(t *testing.T) { 850 cases := []struct { 851 Input string 852 Parsed []int 853 Err bool 854 }{ 855 { 856 "1,2,3", 857 []int{1, 2, 3}, 858 false, 859 }, 860 { 861 "3,1,2,1,2,3,1-3", 862 []int{1, 2, 3}, 863 false, 864 }, 865 { 866 "3-1", 867 nil, 868 true, 869 }, 870 { 871 "1-3,2-4", 872 []int{1, 2, 3, 4}, 873 false, 874 }, 875 { 876 "1-3,4,5-5,6,7,8-10", 877 []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 878 false, 879 }, 880 } 881 882 for i, tc := range cases { 883 r := &Resources{ReservedPorts: tc.Input} 884 err := r.ParseReserved() 885 if (err != nil) != tc.Err { 886 t.Fatalf("test case %d: %v", i, err) 887 continue 888 } 889 890 if !reflect.DeepEqual(r.ParsedReservedPorts, tc.Parsed) { 891 t.Fatalf("test case %d: \n\n%#v\n\n%#v", i, r.ParsedReservedPorts, tc.Parsed) 892 } 893 894 } 895 } 896 897 func TestIsMissingPort(t *testing.T) { 898 _, _, err := net.SplitHostPort("localhost") 899 if missing := isMissingPort(err); !missing { 900 t.Errorf("expected missing port error, but got %v", err) 901 } 902 _, _, err = net.SplitHostPort("localhost:9000") 903 if missing := isMissingPort(err); missing { 904 t.Errorf("expected no error, but got %v", err) 905 } 906 }