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