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