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