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