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