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