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