github.com/manicqin/nomad@v0.9.5/command/agent/config_test.go (about)

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