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