1package server
2
3import (
4	"fmt"
5	"reflect"
6	"strings"
7	"testing"
8	"time"
9
10	"github.com/go-test/deep"
11	"github.com/hashicorp/hcl"
12	"github.com/hashicorp/hcl/hcl/ast"
13)
14
15func testLoadConfigFile_topLevel(t *testing.T, entropy *Entropy) {
16	config, err := LoadConfigFile("./test-fixtures/config2.hcl")
17	if err != nil {
18		t.Fatalf("err: %s", err)
19	}
20
21	expected := &Config{
22		Listeners: []*Listener{
23			&Listener{
24				Type: "tcp",
25				Config: map[string]interface{}{
26					"address": "127.0.0.1:443",
27				},
28			},
29		},
30
31		Storage: &Storage{
32			Type:         "consul",
33			RedirectAddr: "top_level_api_addr",
34			ClusterAddr:  "top_level_cluster_addr",
35			Config: map[string]string{
36				"foo": "bar",
37			},
38		},
39
40		HAStorage: &Storage{
41			Type:         "consul",
42			RedirectAddr: "top_level_api_addr",
43			ClusterAddr:  "top_level_cluster_addr",
44			Config: map[string]string{
45				"bar": "baz",
46			},
47			DisableClustering: true,
48		},
49
50		Telemetry: &Telemetry{
51			StatsdAddr:                 "bar",
52			StatsiteAddr:               "foo",
53			DisableHostname:            false,
54			DogStatsDAddr:              "127.0.0.1:7254",
55			DogStatsDTags:              []string{"tag_1:val_1", "tag_2:val_2"},
56			PrometheusRetentionTime:    30 * time.Second,
57			PrometheusRetentionTimeRaw: "30s",
58		},
59
60		DisableCache:    true,
61		DisableCacheRaw: true,
62		DisableMlock:    true,
63		DisableMlockRaw: true,
64		EnableUI:        true,
65		EnableUIRaw:     true,
66
67		EnableRawEndpoint:    true,
68		EnableRawEndpointRaw: true,
69
70		DisableSealWrap:    true,
71		DisableSealWrapRaw: true,
72
73		MaxLeaseTTL:        10 * time.Hour,
74		MaxLeaseTTLRaw:     "10h",
75		DefaultLeaseTTL:    10 * time.Hour,
76		DefaultLeaseTTLRaw: "10h",
77		ClusterName:        "testcluster",
78
79		PidFile: "./pidfile",
80
81		APIAddr:     "top_level_api_addr",
82		ClusterAddr: "top_level_cluster_addr",
83	}
84	if entropy != nil {
85		expected.Entropy = entropy
86	}
87	if !reflect.DeepEqual(config, expected) {
88		t.Fatalf("expected \n\n%#v\n\n to be \n\n%#v\n\n", config, expected)
89	}
90}
91
92func testLoadConfigFile_json2(t *testing.T, entropy *Entropy) {
93	config, err := LoadConfigFile("./test-fixtures/config2.hcl.json")
94	if err != nil {
95		t.Fatalf("err: %s", err)
96	}
97
98	expected := &Config{
99		Listeners: []*Listener{
100			&Listener{
101				Type: "tcp",
102				Config: map[string]interface{}{
103					"address": "127.0.0.1:443",
104				},
105			},
106			&Listener{
107				Type: "tcp",
108				Config: map[string]interface{}{
109					"address": "127.0.0.1:444",
110				},
111			},
112		},
113
114		Storage: &Storage{
115			Type: "consul",
116			Config: map[string]string{
117				"foo": "bar",
118			},
119		},
120
121		HAStorage: &Storage{
122			Type: "consul",
123			Config: map[string]string{
124				"bar": "baz",
125			},
126			DisableClustering: true,
127		},
128
129		CacheSize: 45678,
130
131		EnableUI:    true,
132		EnableUIRaw: true,
133
134		EnableRawEndpoint:    true,
135		EnableRawEndpointRaw: true,
136
137		DisableSealWrap:    true,
138		DisableSealWrapRaw: true,
139
140		Telemetry: &Telemetry{
141			StatsiteAddr:                       "foo",
142			StatsdAddr:                         "bar",
143			DisableHostname:                    true,
144			CirconusAPIToken:                   "0",
145			CirconusAPIApp:                     "vault",
146			CirconusAPIURL:                     "http://api.circonus.com/v2",
147			CirconusSubmissionInterval:         "10s",
148			CirconusCheckSubmissionURL:         "https://someplace.com/metrics",
149			CirconusCheckID:                    "0",
150			CirconusCheckForceMetricActivation: "true",
151			CirconusCheckInstanceID:            "node1:vault",
152			CirconusCheckSearchTag:             "service:vault",
153			CirconusCheckDisplayName:           "node1:vault",
154			CirconusCheckTags:                  "cat1:tag1,cat2:tag2",
155			CirconusBrokerID:                   "0",
156			CirconusBrokerSelectTag:            "dc:sfo",
157			PrometheusRetentionTime:            30 * time.Second,
158			PrometheusRetentionTimeRaw:         "30s",
159		},
160	}
161	if entropy != nil {
162		expected.Entropy = entropy
163	}
164	if !reflect.DeepEqual(config, expected) {
165		t.Fatalf("expected \n\n%#v\n\n to be \n\n%#v\n\n", config, expected)
166	}
167}
168
169func testParseEntropy(t *testing.T, oss bool) {
170	var tests = []struct {
171		inConfig   string
172		outErr     error
173		outEntropy Entropy
174	}{
175		{
176			inConfig: `entropy "seal" {
177				mode = "augmentation"
178				}`,
179			outErr:     nil,
180			outEntropy: Entropy{Augmentation},
181		},
182		{
183			inConfig: `entropy "seal" {
184				mode = "a_mode_that_is_not_supported"
185				}`,
186			outErr: fmt.Errorf("the specified entropy mode %q is not supported", "a_mode_that_is_not_supported"),
187		},
188		{
189			inConfig: `entropy "device_that_is_not_supported" {
190				mode = "augmentation"
191				}`,
192			outErr: fmt.Errorf("only the %q type of external entropy is supported", "seal"),
193		},
194		{
195			inConfig: `entropy "seal" {
196				mode = "augmentation"
197				}
198				entropy "seal" {
199				mode = "augmentation"
200				}`,
201			outErr: fmt.Errorf("only one %q block is permitted", "entropy"),
202		},
203	}
204
205	var config Config
206
207	for _, test := range tests {
208		obj, _ := hcl.Parse(strings.TrimSpace(test.inConfig))
209		list, _ := obj.Node.(*ast.ObjectList)
210		objList := list.Filter("entropy")
211		err := parseEntropy(&config, objList, "entropy")
212		// validate the error, both should be nil or have the same Error()
213		switch {
214		case oss:
215			if config.Entropy != nil {
216				t.Fatalf("parsing Entropy should not be possible in oss but got a non-nil config.Entropy: %#v", config.Entropy)
217			}
218		case err != nil && test.outErr != nil:
219			if err.Error() != test.outErr.Error() {
220				t.Fatalf("error mismatch: expected %#v got %#v", err, test.outErr)
221			}
222		case err != test.outErr:
223			t.Fatalf("error mismatch: expected %#v got %#v", err, test.outErr)
224		case err == nil && config.Entropy != nil && *config.Entropy != test.outEntropy:
225			fmt.Printf("\n config.Entropy: %#v", config.Entropy)
226			t.Fatalf("entropy config mismatch: expected %#v got %#v", test.outEntropy, *config.Entropy)
227		}
228	}
229}
230
231func testLoadConfigFile(t *testing.T) {
232	config, err := LoadConfigFile("./test-fixtures/config.hcl")
233	if err != nil {
234		t.Fatalf("err: %s", err)
235	}
236
237	expected := &Config{
238		Listeners: []*Listener{
239			&Listener{
240				Type: "tcp",
241				Config: map[string]interface{}{
242					"address": "127.0.0.1:443",
243				},
244			},
245		},
246
247		Storage: &Storage{
248			Type:         "consul",
249			RedirectAddr: "foo",
250			Config: map[string]string{
251				"foo": "bar",
252			},
253		},
254
255		HAStorage: &Storage{
256			Type:         "consul",
257			RedirectAddr: "snafu",
258			Config: map[string]string{
259				"bar": "baz",
260			},
261			DisableClustering: true,
262		},
263
264		Telemetry: &Telemetry{
265			StatsdAddr:              "bar",
266			StatsiteAddr:            "foo",
267			DisableHostname:         false,
268			DogStatsDAddr:           "127.0.0.1:7254",
269			DogStatsDTags:           []string{"tag_1:val_1", "tag_2:val_2"},
270			PrometheusRetentionTime: prometheusDefaultRetentionTime,
271		},
272
273		DisableCache:             true,
274		DisableCacheRaw:          true,
275		DisableMlock:             true,
276		DisableMlockRaw:          true,
277		DisablePrintableCheckRaw: true,
278		DisablePrintableCheck:    true,
279		EnableUI:                 true,
280		EnableUIRaw:              true,
281
282		EnableRawEndpoint:    true,
283		EnableRawEndpointRaw: true,
284
285		DisableSealWrap:    true,
286		DisableSealWrapRaw: true,
287
288		Entropy: nil,
289
290		MaxLeaseTTL:        10 * time.Hour,
291		MaxLeaseTTLRaw:     "10h",
292		DefaultLeaseTTL:    10 * time.Hour,
293		DefaultLeaseTTLRaw: "10h",
294		ClusterName:        "testcluster",
295
296		PidFile: "./pidfile",
297	}
298	if !reflect.DeepEqual(config, expected) {
299		t.Fatalf("expected \n\n%#v\n\n to be \n\n%#v\n\n", config, expected)
300	}
301}
302
303func testLoadConfigFile_json(t *testing.T) {
304	config, err := LoadConfigFile("./test-fixtures/config.hcl.json")
305	if err != nil {
306		t.Fatalf("err: %s", err)
307	}
308
309	expected := &Config{
310		Listeners: []*Listener{
311			&Listener{
312				Type: "tcp",
313				Config: map[string]interface{}{
314					"address": "127.0.0.1:443",
315				},
316			},
317		},
318
319		Storage: &Storage{
320			Type: "consul",
321			Config: map[string]string{
322				"foo": "bar",
323			},
324			DisableClustering: true,
325		},
326
327		ClusterCipherSuites: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
328
329		Telemetry: &Telemetry{
330			StatsiteAddr:                       "baz",
331			StatsdAddr:                         "",
332			DisableHostname:                    false,
333			CirconusAPIToken:                   "",
334			CirconusAPIApp:                     "",
335			CirconusAPIURL:                     "",
336			CirconusSubmissionInterval:         "",
337			CirconusCheckSubmissionURL:         "",
338			CirconusCheckID:                    "",
339			CirconusCheckForceMetricActivation: "",
340			CirconusCheckInstanceID:            "",
341			CirconusCheckSearchTag:             "",
342			CirconusCheckDisplayName:           "",
343			CirconusCheckTags:                  "",
344			CirconusBrokerID:                   "",
345			CirconusBrokerSelectTag:            "",
346			PrometheusRetentionTime:            prometheusDefaultRetentionTime,
347		},
348
349		MaxLeaseTTL:          10 * time.Hour,
350		MaxLeaseTTLRaw:       "10h",
351		DefaultLeaseTTL:      10 * time.Hour,
352		DefaultLeaseTTLRaw:   "10h",
353		ClusterName:          "testcluster",
354		DisableCacheRaw:      interface{}(nil),
355		DisableMlockRaw:      interface{}(nil),
356		EnableUI:             true,
357		EnableUIRaw:          true,
358		PidFile:              "./pidfile",
359		EnableRawEndpoint:    true,
360		EnableRawEndpointRaw: true,
361		DisableSealWrap:      true,
362		DisableSealWrapRaw:   true,
363		Entropy:              nil,
364	}
365	if !reflect.DeepEqual(config, expected) {
366		t.Fatalf("expected \n\n%#v\n\n to be \n\n%#v\n\n", config, expected)
367	}
368}
369
370func testLoadConfigDir(t *testing.T) {
371	config, err := LoadConfigDir("./test-fixtures/config-dir")
372	if err != nil {
373		t.Fatalf("err: %s", err)
374	}
375
376	expected := &Config{
377		DisableCache: true,
378		DisableMlock: true,
379
380		DisableClustering:    false,
381		DisableClusteringRaw: false,
382
383		APIAddr:     "https://vault.local",
384		ClusterAddr: "https://127.0.0.1:444",
385
386		Listeners: []*Listener{
387			&Listener{
388				Type: "tcp",
389				Config: map[string]interface{}{
390					"address": "127.0.0.1:443",
391				},
392			},
393		},
394
395		Storage: &Storage{
396			Type: "consul",
397			Config: map[string]string{
398				"foo": "bar",
399			},
400			RedirectAddr:      "https://vault.local",
401			ClusterAddr:       "https://127.0.0.1:444",
402			DisableClustering: false,
403		},
404
405		EnableUI: true,
406
407		EnableRawEndpoint: true,
408
409		Telemetry: &Telemetry{
410			StatsiteAddr:            "qux",
411			StatsdAddr:              "baz",
412			DisableHostname:         true,
413			PrometheusRetentionTime: prometheusDefaultRetentionTime,
414		},
415
416		MaxLeaseTTL:     10 * time.Hour,
417		DefaultLeaseTTL: 10 * time.Hour,
418		ClusterName:     "testcluster",
419	}
420	if !reflect.DeepEqual(config, expected) {
421		t.Fatalf("expected \n\n%#v\n\n to be \n\n%#v\n\n", config, expected)
422	}
423}
424
425func testConfig_Sanitized(t *testing.T) {
426	config, err := LoadConfigFile("./test-fixtures/config3.hcl")
427	if err != nil {
428		t.Fatalf("err: %s", err)
429	}
430	sanitizedConfig := config.Sanitized()
431
432	expected := map[string]interface{}{
433		"api_addr":                     "top_level_api_addr",
434		"cache_size":                   0,
435		"cluster_addr":                 "top_level_cluster_addr",
436		"cluster_cipher_suites":        "",
437		"cluster_name":                 "testcluster",
438		"default_lease_ttl":            10 * time.Hour,
439		"default_max_request_duration": 0 * time.Second,
440		"disable_cache":                true,
441		"disable_clustering":           false,
442		"disable_indexing":             false,
443		"disable_mlock":                true,
444		"disable_performance_standby":  false,
445		"disable_printable_check":      false,
446		"disable_sealwrap":             true,
447		"raw_storage_endpoint":         true,
448		"enable_ui":                    true,
449		"ha_storage": map[string]interface{}{
450			"cluster_addr":       "top_level_cluster_addr",
451			"disable_clustering": true,
452			"redirect_addr":      "top_level_api_addr",
453			"type":               "consul"},
454		"listeners": []interface{}{
455			map[string]interface{}{
456				"config": map[string]interface{}{
457					"address": "127.0.0.1:443",
458				},
459				"type": "tcp",
460			},
461		},
462		"log_format":       "",
463		"log_level":        "",
464		"max_lease_ttl":    10 * time.Hour,
465		"pid_file":         "./pidfile",
466		"plugin_directory": "",
467		"seals": []interface{}{
468			map[string]interface{}{
469				"disabled": false,
470				"type":     "awskms",
471			},
472		},
473		"storage": map[string]interface{}{
474			"cluster_addr":       "top_level_cluster_addr",
475			"disable_clustering": false,
476			"redirect_addr":      "top_level_api_addr",
477			"type":               "consul",
478		},
479		"telemetry": map[string]interface{}{
480			"circonus_api_app":                       "",
481			"circonus_api_token":                     "",
482			"circonus_api_url":                       "",
483			"circonus_broker_id":                     "",
484			"circonus_broker_select_tag":             "",
485			"circonus_check_display_name":            "",
486			"circonus_check_force_metric_activation": "",
487			"circonus_check_id":                      "",
488			"circonus_check_instance_id":             "",
489			"circonus_check_search_tag":              "",
490			"circonus_submission_url":                "",
491			"circonus_check_tags":                    "",
492			"circonus_submission_interval":           "",
493			"disable_hostname":                       false,
494			"dogstatsd_addr":                         "",
495			"dogstatsd_tags":                         []string(nil),
496			"prometheus_retention_time":              24 * time.Hour,
497			"stackdriver_location":                   "",
498			"stackdriver_namespace":                  "",
499			"stackdriver_project_id":                 "",
500			"statsd_address":                         "bar",
501			"statsite_address":                       ""},
502	}
503
504	if diff := deep.Equal(sanitizedConfig, expected); len(diff) > 0 {
505		t.Fatalf("bad, diff: %#v", diff)
506	}
507}
508
509func testParseListeners(t *testing.T) {
510	obj, _ := hcl.Parse(strings.TrimSpace(`
511listener "tcp" {
512	address = "127.0.0.1:443"
513	cluster_address = "127.0.0.1:8201"
514	tls_disable = false
515	tls_cert_file = "./certs/server.crt"
516	tls_key_file = "./certs/server.key"
517	tls_client_ca_file = "./certs/rootca.crt"
518	tls_min_version = "tls12"
519	tls_require_and_verify_client_cert = true
520	tls_disable_client_certs = true
521}`))
522
523	var config Config
524	list, _ := obj.Node.(*ast.ObjectList)
525	objList := list.Filter("listener")
526	parseListeners(&config, objList)
527	listeners := config.Listeners
528	if len(listeners) == 0 {
529		t.Fatalf("expected at least one listener in the config")
530	}
531	listener := listeners[0]
532	if listener.Type != "tcp" {
533		t.Fatalf("expected tcp listener in the config")
534	}
535
536	expected := &Config{
537		Listeners: []*Listener{
538			&Listener{
539				Type: "tcp",
540				Config: map[string]interface{}{
541					"address":                            "127.0.0.1:443",
542					"cluster_address":                    "127.0.0.1:8201",
543					"tls_disable":                        false,
544					"tls_cert_file":                      "./certs/server.crt",
545					"tls_key_file":                       "./certs/server.key",
546					"tls_client_ca_file":                 "./certs/rootca.crt",
547					"tls_min_version":                    "tls12",
548					"tls_require_and_verify_client_cert": true,
549					"tls_disable_client_certs":           true,
550				},
551			},
552		},
553	}
554
555	if !reflect.DeepEqual(config, *expected) {
556		t.Fatalf("expected \n\n%#v\n\n to be \n\n%#v\n\n", config, *expected)
557	}
558
559}
560