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