1package server 2 3import ( 4 "errors" 5 "fmt" 6 "io" 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 "strconv" 11 "strings" 12 "time" 13 14 "github.com/hashicorp/errwrap" 15 16 "github.com/hashicorp/go-multierror" 17 "github.com/hashicorp/hcl" 18 "github.com/hashicorp/hcl/hcl/ast" 19 "github.com/hashicorp/vault/sdk/helper/parseutil" 20) 21 22const ( 23 prometheusDefaultRetentionTime = 24 * time.Hour 24) 25 26// Config is the configuration for the vault server. 27type Config struct { 28 Listeners []*Listener `hcl:"-"` 29 Storage *Storage `hcl:"-"` 30 HAStorage *Storage `hcl:"-"` 31 32 Seals []*Seal `hcl:"-"` 33 34 CacheSize int `hcl:"cache_size"` 35 DisableCache bool `hcl:"-"` 36 DisableCacheRaw interface{} `hcl:"disable_cache"` 37 DisableMlock bool `hcl:"-"` 38 DisableMlockRaw interface{} `hcl:"disable_mlock"` 39 DisablePrintableCheck bool `hcl:"-"` 40 DisablePrintableCheckRaw interface{} `hcl:"disable_printable_check"` 41 42 EnableUI bool `hcl:"-"` 43 EnableUIRaw interface{} `hcl:"ui"` 44 45 Telemetry *Telemetry `hcl:"telemetry"` 46 47 MaxLeaseTTL time.Duration `hcl:"-"` 48 MaxLeaseTTLRaw interface{} `hcl:"max_lease_ttl"` 49 DefaultLeaseTTL time.Duration `hcl:"-"` 50 DefaultLeaseTTLRaw interface{} `hcl:"default_lease_ttl"` 51 52 DefaultMaxRequestDuration time.Duration `hcl:"-"` 53 DefaultMaxRequestDurationRaw interface{} `hcl:"default_max_request_duration"` 54 55 ClusterName string `hcl:"cluster_name"` 56 ClusterCipherSuites string `hcl:"cluster_cipher_suites"` 57 58 PluginDirectory string `hcl:"plugin_directory"` 59 60 LogLevel string `hcl:"log_level"` 61 62 // LogFormat specifies the log format. Valid values are "standard" and "json". The values are case-insenstive. 63 // If no log format is specified, then standard format will be used. 64 LogFormat string `hcl:"log_format"` 65 66 PidFile string `hcl:"pid_file"` 67 EnableRawEndpoint bool `hcl:"-"` 68 EnableRawEndpointRaw interface{} `hcl:"raw_storage_endpoint"` 69 70 APIAddr string `hcl:"api_addr"` 71 ClusterAddr string `hcl:"cluster_addr"` 72 DisableClustering bool `hcl:"-"` 73 DisableClusteringRaw interface{} `hcl:"disable_clustering"` 74 75 DisablePerformanceStandby bool `hcl:"-"` 76 DisablePerformanceStandbyRaw interface{} `hcl:"disable_performance_standby"` 77 78 DisableSealWrap bool `hcl:"-"` 79 DisableSealWrapRaw interface{} `hcl:"disable_sealwrap"` 80 81 DisableIndexing bool `hcl:"-"` 82 DisableIndexingRaw interface{} `hcl:"disable_indexing"` 83} 84 85// DevConfig is a Config that is used for dev mode of Vault. 86func DevConfig(storageType string) *Config { 87 ret := &Config{ 88 DisableMlock: true, 89 EnableRawEndpoint: true, 90 91 Storage: &Storage{ 92 Type: storageType, 93 }, 94 95 Listeners: []*Listener{ 96 &Listener{ 97 Type: "tcp", 98 Config: map[string]interface{}{ 99 "address": "127.0.0.1:8200", 100 "tls_disable": true, 101 "proxy_protocol_behavior": "allow_authorized", 102 "proxy_protocol_authorized_addrs": "127.0.0.1:8200", 103 }, 104 }, 105 }, 106 107 EnableUI: true, 108 109 Telemetry: &Telemetry{ 110 PrometheusRetentionTime: prometheusDefaultRetentionTime, 111 DisableHostname: true, 112 }, 113 } 114 115 return ret 116} 117 118// Listener is the listener configuration for the server. 119type Listener struct { 120 Type string 121 Config map[string]interface{} 122} 123 124func (l *Listener) GoString() string { 125 return fmt.Sprintf("*%#v", *l) 126} 127 128// Storage is the underlying storage configuration for the server. 129type Storage struct { 130 Type string 131 RedirectAddr string 132 ClusterAddr string 133 DisableClustering bool 134 Config map[string]string 135} 136 137func (b *Storage) GoString() string { 138 return fmt.Sprintf("*%#v", *b) 139} 140 141// Seal contains Seal configuration for the server 142type Seal struct { 143 Type string 144 Disabled bool 145 Config map[string]string 146} 147 148func (h *Seal) GoString() string { 149 return fmt.Sprintf("*%#v", *h) 150} 151 152// Telemetry is the telemetry configuration for the server 153type Telemetry struct { 154 StatsiteAddr string `hcl:"statsite_address"` 155 StatsdAddr string `hcl:"statsd_address"` 156 157 DisableHostname bool `hcl:"disable_hostname"` 158 159 // Circonus: see https://github.com/circonus-labs/circonus-gometrics 160 // for more details on the various configuration options. 161 // Valid configuration combinations: 162 // - CirconusAPIToken 163 // metric management enabled (search for existing check or create a new one) 164 // - CirconusSubmissionUrl 165 // metric management disabled (use check with specified submission_url, 166 // broker must be using a public SSL certificate) 167 // - CirconusAPIToken + CirconusCheckSubmissionURL 168 // metric management enabled (use check with specified submission_url) 169 // - CirconusAPIToken + CirconusCheckID 170 // metric management enabled (use check with specified id) 171 172 // CirconusAPIToken is a valid API Token used to create/manage check. If provided, 173 // metric management is enabled. 174 // Default: none 175 CirconusAPIToken string `hcl:"circonus_api_token"` 176 // CirconusAPIApp is an app name associated with API token. 177 // Default: "consul" 178 CirconusAPIApp string `hcl:"circonus_api_app"` 179 // CirconusAPIURL is the base URL to use for contacting the Circonus API. 180 // Default: "https://api.circonus.com/v2" 181 CirconusAPIURL string `hcl:"circonus_api_url"` 182 // CirconusSubmissionInterval is the interval at which metrics are submitted to Circonus. 183 // Default: 10s 184 CirconusSubmissionInterval string `hcl:"circonus_submission_interval"` 185 // CirconusCheckSubmissionURL is the check.config.submission_url field from a 186 // previously created HTTPTRAP check. 187 // Default: none 188 CirconusCheckSubmissionURL string `hcl:"circonus_submission_url"` 189 // CirconusCheckID is the check id (not check bundle id) from a previously created 190 // HTTPTRAP check. The numeric portion of the check._cid field. 191 // Default: none 192 CirconusCheckID string `hcl:"circonus_check_id"` 193 // CirconusCheckForceMetricActivation will force enabling metrics, as they are encountered, 194 // if the metric already exists and is NOT active. If check management is enabled, the default 195 // behavior is to add new metrics as they are encountered. If the metric already exists in the 196 // check, it will *NOT* be activated. This setting overrides that behavior. 197 // Default: "false" 198 CirconusCheckForceMetricActivation string `hcl:"circonus_check_force_metric_activation"` 199 // CirconusCheckInstanceID serves to uniquely identify the metrics coming from this "instance". 200 // It can be used to maintain metric continuity with transient or ephemeral instances as 201 // they move around within an infrastructure. 202 // Default: hostname:app 203 CirconusCheckInstanceID string `hcl:"circonus_check_instance_id"` 204 // CirconusCheckSearchTag is a special tag which, when coupled with the instance id, helps to 205 // narrow down the search results when neither a Submission URL or Check ID is provided. 206 // Default: service:app (e.g. service:consul) 207 CirconusCheckSearchTag string `hcl:"circonus_check_search_tag"` 208 // CirconusCheckTags is a comma separated list of tags to apply to the check. Note that 209 // the value of CirconusCheckSearchTag will always be added to the check. 210 // Default: none 211 CirconusCheckTags string `mapstructure:"circonus_check_tags"` 212 // CirconusCheckDisplayName is the name for the check which will be displayed in the Circonus UI. 213 // Default: value of CirconusCheckInstanceID 214 CirconusCheckDisplayName string `mapstructure:"circonus_check_display_name"` 215 // CirconusBrokerID is an explicit broker to use when creating a new check. The numeric portion 216 // of broker._cid. If metric management is enabled and neither a Submission URL nor Check ID 217 // is provided, an attempt will be made to search for an existing check using Instance ID and 218 // Search Tag. If one is not found, a new HTTPTRAP check will be created. 219 // Default: use Select Tag if provided, otherwise, a random Enterprise Broker associated 220 // with the specified API token or the default Circonus Broker. 221 // Default: none 222 CirconusBrokerID string `hcl:"circonus_broker_id"` 223 // CirconusBrokerSelectTag is a special tag which will be used to select a broker when 224 // a Broker ID is not provided. The best use of this is to as a hint for which broker 225 // should be used based on *where* this particular instance is running. 226 // (e.g. a specific geo location or datacenter, dc:sfo) 227 // Default: none 228 CirconusBrokerSelectTag string `hcl:"circonus_broker_select_tag"` 229 230 // Dogstats: 231 // DogStatsdAddr is the address of a dogstatsd instance. If provided, 232 // metrics will be sent to that instance 233 DogStatsDAddr string `hcl:"dogstatsd_addr"` 234 235 // DogStatsdTags are the global tags that should be sent with each packet to dogstatsd 236 // It is a list of strings, where each string looks like "my_tag_name:my_tag_value" 237 DogStatsDTags []string `hcl:"dogstatsd_tags"` 238 239 // Prometheus: 240 // PrometheusRetentionTime is the retention time for prometheus metrics if greater than 0. 241 // Default: 24h 242 PrometheusRetentionTime time.Duration `hcl:-` 243 PrometheusRetentionTimeRaw interface{} `hcl:"prometheus_retention_time"` 244} 245 246func (s *Telemetry) GoString() string { 247 return fmt.Sprintf("*%#v", *s) 248} 249 250// Merge merges two configurations. 251func (c *Config) Merge(c2 *Config) *Config { 252 if c2 == nil { 253 return c 254 } 255 256 result := new(Config) 257 for _, l := range c.Listeners { 258 result.Listeners = append(result.Listeners, l) 259 } 260 for _, l := range c2.Listeners { 261 result.Listeners = append(result.Listeners, l) 262 } 263 264 result.Storage = c.Storage 265 if c2.Storage != nil { 266 result.Storage = c2.Storage 267 } 268 269 result.HAStorage = c.HAStorage 270 if c2.HAStorage != nil { 271 result.HAStorage = c2.HAStorage 272 } 273 274 for _, s := range c.Seals { 275 result.Seals = append(result.Seals, s) 276 } 277 for _, s := range c2.Seals { 278 result.Seals = append(result.Seals, s) 279 } 280 281 result.Telemetry = c.Telemetry 282 if c2.Telemetry != nil { 283 result.Telemetry = c2.Telemetry 284 } 285 286 result.CacheSize = c.CacheSize 287 if c2.CacheSize != 0 { 288 result.CacheSize = c2.CacheSize 289 } 290 291 // merging these booleans via an OR operation 292 result.DisableCache = c.DisableCache 293 if c2.DisableCache { 294 result.DisableCache = c2.DisableCache 295 } 296 297 result.DisableMlock = c.DisableMlock 298 if c2.DisableMlock { 299 result.DisableMlock = c2.DisableMlock 300 } 301 302 result.DisablePrintableCheck = c.DisablePrintableCheck 303 if c2.DisablePrintableCheckRaw != nil { 304 result.DisablePrintableCheck = c2.DisablePrintableCheck 305 } 306 307 // merge these integers via a MAX operation 308 result.MaxLeaseTTL = c.MaxLeaseTTL 309 if c2.MaxLeaseTTL > result.MaxLeaseTTL { 310 result.MaxLeaseTTL = c2.MaxLeaseTTL 311 } 312 313 result.DefaultLeaseTTL = c.DefaultLeaseTTL 314 if c2.DefaultLeaseTTL > result.DefaultLeaseTTL { 315 result.DefaultLeaseTTL = c2.DefaultLeaseTTL 316 } 317 318 result.DefaultMaxRequestDuration = c.DefaultMaxRequestDuration 319 if c2.DefaultMaxRequestDuration > result.DefaultMaxRequestDuration { 320 result.DefaultMaxRequestDuration = c2.DefaultMaxRequestDuration 321 } 322 323 result.LogLevel = c.LogLevel 324 if c2.LogLevel != "" { 325 result.LogLevel = c2.LogLevel 326 } 327 328 result.LogFormat = c.LogFormat 329 if c2.LogFormat != "" { 330 result.LogFormat = c2.LogFormat 331 } 332 333 result.ClusterName = c.ClusterName 334 if c2.ClusterName != "" { 335 result.ClusterName = c2.ClusterName 336 } 337 338 result.ClusterCipherSuites = c.ClusterCipherSuites 339 if c2.ClusterCipherSuites != "" { 340 result.ClusterCipherSuites = c2.ClusterCipherSuites 341 } 342 343 result.EnableUI = c.EnableUI 344 if c2.EnableUI { 345 result.EnableUI = c2.EnableUI 346 } 347 348 result.EnableRawEndpoint = c.EnableRawEndpoint 349 if c2.EnableRawEndpoint { 350 result.EnableRawEndpoint = c2.EnableRawEndpoint 351 } 352 353 result.APIAddr = c.APIAddr 354 if c2.APIAddr != "" { 355 result.APIAddr = c2.APIAddr 356 } 357 358 result.ClusterAddr = c.ClusterAddr 359 if c2.ClusterAddr != "" { 360 result.ClusterAddr = c2.ClusterAddr 361 } 362 363 // Retain raw value so that it can be assigned to storage objects 364 result.DisableClustering = c.DisableClustering 365 result.DisableClusteringRaw = c.DisableClusteringRaw 366 if c2.DisableClusteringRaw != nil { 367 result.DisableClustering = c2.DisableClustering 368 result.DisableClusteringRaw = c2.DisableClusteringRaw 369 } 370 371 result.PluginDirectory = c.PluginDirectory 372 if c2.PluginDirectory != "" { 373 result.PluginDirectory = c2.PluginDirectory 374 } 375 376 result.PidFile = c.PidFile 377 if c2.PidFile != "" { 378 result.PidFile = c2.PidFile 379 } 380 381 result.DisablePerformanceStandby = c.DisablePerformanceStandby 382 if c2.DisablePerformanceStandby { 383 result.DisablePerformanceStandby = c2.DisablePerformanceStandby 384 } 385 386 result.DisableSealWrap = c.DisableSealWrap 387 if c2.DisableSealWrap { 388 result.DisableSealWrap = c2.DisableSealWrap 389 } 390 391 result.DisableIndexing = c.DisableIndexing 392 if c2.DisableIndexing { 393 result.DisableIndexing = c2.DisableIndexing 394 } 395 396 // Use values from top-level configuration for storage if set 397 if storage := result.Storage; storage != nil { 398 if result.APIAddr != "" { 399 storage.RedirectAddr = result.APIAddr 400 } 401 if result.ClusterAddr != "" { 402 storage.ClusterAddr = result.ClusterAddr 403 } 404 if result.DisableClusteringRaw != nil { 405 storage.DisableClustering = result.DisableClustering 406 } 407 } 408 409 if haStorage := result.HAStorage; haStorage != nil { 410 if result.APIAddr != "" { 411 haStorage.RedirectAddr = result.APIAddr 412 } 413 if result.ClusterAddr != "" { 414 haStorage.ClusterAddr = result.ClusterAddr 415 } 416 if result.DisableClusteringRaw != nil { 417 haStorage.DisableClustering = result.DisableClustering 418 } 419 } 420 421 return result 422} 423 424// LoadConfig loads the configuration at the given path, regardless if 425// its a file or directory. 426func LoadConfig(path string) (*Config, error) { 427 fi, err := os.Stat(path) 428 if err != nil { 429 return nil, err 430 } 431 432 if fi.IsDir() { 433 return LoadConfigDir(path) 434 } 435 return LoadConfigFile(path) 436} 437 438// LoadConfigFile loads the configuration from the given file. 439func LoadConfigFile(path string) (*Config, error) { 440 // Read the file 441 d, err := ioutil.ReadFile(path) 442 if err != nil { 443 return nil, err 444 } 445 return ParseConfig(string(d)) 446} 447 448func ParseConfig(d string) (*Config, error) { 449 // Parse! 450 obj, err := hcl.Parse(d) 451 if err != nil { 452 return nil, err 453 } 454 455 // Start building the result 456 var result Config 457 if err := hcl.DecodeObject(&result, obj); err != nil { 458 return nil, err 459 } 460 461 if result.MaxLeaseTTLRaw != nil { 462 if result.MaxLeaseTTL, err = parseutil.ParseDurationSecond(result.MaxLeaseTTLRaw); err != nil { 463 return nil, err 464 } 465 } 466 if result.DefaultLeaseTTLRaw != nil { 467 if result.DefaultLeaseTTL, err = parseutil.ParseDurationSecond(result.DefaultLeaseTTLRaw); err != nil { 468 return nil, err 469 } 470 } 471 472 if result.DefaultMaxRequestDurationRaw != nil { 473 if result.DefaultMaxRequestDuration, err = parseutil.ParseDurationSecond(result.DefaultMaxRequestDurationRaw); err != nil { 474 return nil, err 475 } 476 } 477 478 if result.EnableUIRaw != nil { 479 if result.EnableUI, err = parseutil.ParseBool(result.EnableUIRaw); err != nil { 480 return nil, err 481 } 482 } 483 484 if result.DisableCacheRaw != nil { 485 if result.DisableCache, err = parseutil.ParseBool(result.DisableCacheRaw); err != nil { 486 return nil, err 487 } 488 } 489 490 if result.DisableMlockRaw != nil { 491 if result.DisableMlock, err = parseutil.ParseBool(result.DisableMlockRaw); err != nil { 492 return nil, err 493 } 494 } 495 496 if result.DisablePrintableCheckRaw != nil { 497 if result.DisablePrintableCheck, err = parseutil.ParseBool(result.DisablePrintableCheckRaw); err != nil { 498 return nil, err 499 } 500 } 501 502 if result.EnableRawEndpointRaw != nil { 503 if result.EnableRawEndpoint, err = parseutil.ParseBool(result.EnableRawEndpointRaw); err != nil { 504 return nil, err 505 } 506 } 507 508 if result.DisableClusteringRaw != nil { 509 if result.DisableClustering, err = parseutil.ParseBool(result.DisableClusteringRaw); err != nil { 510 return nil, err 511 } 512 } 513 514 if result.DisablePerformanceStandbyRaw != nil { 515 if result.DisablePerformanceStandby, err = parseutil.ParseBool(result.DisablePerformanceStandbyRaw); err != nil { 516 return nil, err 517 } 518 } 519 520 if result.DisableSealWrapRaw != nil { 521 if result.DisableSealWrap, err = parseutil.ParseBool(result.DisableSealWrapRaw); err != nil { 522 return nil, err 523 } 524 } 525 526 if result.DisableIndexingRaw != nil { 527 if result.DisableIndexing, err = parseutil.ParseBool(result.DisableIndexingRaw); err != nil { 528 return nil, err 529 } 530 } 531 532 list, ok := obj.Node.(*ast.ObjectList) 533 if !ok { 534 return nil, fmt.Errorf("error parsing: file doesn't contain a root object") 535 } 536 537 // Look for storage but still support old backend 538 if o := list.Filter("storage"); len(o.Items) > 0 { 539 if err := ParseStorage(&result, o, "storage"); err != nil { 540 return nil, errwrap.Wrapf("error parsing 'storage': {{err}}", err) 541 } 542 } else { 543 if o := list.Filter("backend"); len(o.Items) > 0 { 544 if err := ParseStorage(&result, o, "backend"); err != nil { 545 return nil, errwrap.Wrapf("error parsing 'backend': {{err}}", err) 546 } 547 } 548 } 549 550 if o := list.Filter("ha_storage"); len(o.Items) > 0 { 551 if err := parseHAStorage(&result, o, "ha_storage"); err != nil { 552 return nil, errwrap.Wrapf("error parsing 'ha_storage': {{err}}", err) 553 } 554 } else { 555 if o := list.Filter("ha_backend"); len(o.Items) > 0 { 556 if err := parseHAStorage(&result, o, "ha_backend"); err != nil { 557 return nil, errwrap.Wrapf("error parsing 'ha_backend': {{err}}", err) 558 } 559 } 560 } 561 562 if o := list.Filter("hsm"); len(o.Items) > 0 { 563 if err := parseSeals(&result, o, "hsm"); err != nil { 564 return nil, errwrap.Wrapf("error parsing 'hsm': {{err}}", err) 565 } 566 } 567 568 if o := list.Filter("seal"); len(o.Items) > 0 { 569 if err := parseSeals(&result, o, "seal"); err != nil { 570 return nil, errwrap.Wrapf("error parsing 'seal': {{err}}", err) 571 } 572 } 573 574 if o := list.Filter("listener"); len(o.Items) > 0 { 575 if err := parseListeners(&result, o); err != nil { 576 return nil, errwrap.Wrapf("error parsing 'listener': {{err}}", err) 577 } 578 } 579 580 if o := list.Filter("telemetry"); len(o.Items) > 0 { 581 if err := parseTelemetry(&result, o); err != nil { 582 return nil, errwrap.Wrapf("error parsing 'telemetry': {{err}}", err) 583 } 584 } 585 586 return &result, nil 587} 588 589// LoadConfigDir loads all the configurations in the given directory 590// in alphabetical order. 591func LoadConfigDir(dir string) (*Config, error) { 592 f, err := os.Open(dir) 593 if err != nil { 594 return nil, err 595 } 596 defer f.Close() 597 598 fi, err := f.Stat() 599 if err != nil { 600 return nil, err 601 } 602 if !fi.IsDir() { 603 return nil, fmt.Errorf("configuration path must be a directory: %q", dir) 604 } 605 606 var files []string 607 err = nil 608 for err != io.EOF { 609 var fis []os.FileInfo 610 fis, err = f.Readdir(128) 611 if err != nil && err != io.EOF { 612 return nil, err 613 } 614 615 for _, fi := range fis { 616 // Ignore directories 617 if fi.IsDir() { 618 continue 619 } 620 621 // Only care about files that are valid to load. 622 name := fi.Name() 623 skip := true 624 if strings.HasSuffix(name, ".hcl") { 625 skip = false 626 } else if strings.HasSuffix(name, ".json") { 627 skip = false 628 } 629 if skip || isTemporaryFile(name) { 630 continue 631 } 632 633 path := filepath.Join(dir, name) 634 files = append(files, path) 635 } 636 } 637 638 var result *Config 639 for _, f := range files { 640 config, err := LoadConfigFile(f) 641 if err != nil { 642 return nil, errwrap.Wrapf(fmt.Sprintf("error loading %q: {{err}}", f), err) 643 } 644 645 if result == nil { 646 result = config 647 } else { 648 result = result.Merge(config) 649 } 650 } 651 652 return result, nil 653} 654 655// isTemporaryFile returns true or false depending on whether the 656// provided file name is a temporary file for the following editors: 657// emacs or vim. 658func isTemporaryFile(name string) bool { 659 return strings.HasSuffix(name, "~") || // vim 660 strings.HasPrefix(name, ".#") || // emacs 661 (strings.HasPrefix(name, "#") && strings.HasSuffix(name, "#")) // emacs 662} 663 664func ParseStorage(result *Config, list *ast.ObjectList, name string) error { 665 if len(list.Items) > 1 { 666 return fmt.Errorf("only one %q block is permitted", name) 667 } 668 669 // Get our item 670 item := list.Items[0] 671 672 key := name 673 if len(item.Keys) > 0 { 674 key = item.Keys[0].Token.Value().(string) 675 } 676 677 var m map[string]string 678 if err := hcl.DecodeObject(&m, item.Val); err != nil { 679 return multierror.Prefix(err, fmt.Sprintf("%s.%s:", name, key)) 680 } 681 682 // Pull out the redirect address since it's common to all backends 683 var redirectAddr string 684 if v, ok := m["redirect_addr"]; ok { 685 redirectAddr = v 686 delete(m, "redirect_addr") 687 } else if v, ok := m["advertise_addr"]; ok { 688 redirectAddr = v 689 delete(m, "advertise_addr") 690 } 691 692 // Pull out the cluster address since it's common to all backends 693 var clusterAddr string 694 if v, ok := m["cluster_addr"]; ok { 695 clusterAddr = v 696 delete(m, "cluster_addr") 697 } 698 699 var disableClustering bool 700 var err error 701 if v, ok := m["disable_clustering"]; ok { 702 disableClustering, err = strconv.ParseBool(v) 703 if err != nil { 704 return multierror.Prefix(err, fmt.Sprintf("%s.%s:", name, key)) 705 } 706 delete(m, "disable_clustering") 707 } 708 709 // Override with top-level values if they are set 710 if result.APIAddr != "" { 711 redirectAddr = result.APIAddr 712 } 713 714 if result.ClusterAddr != "" { 715 clusterAddr = result.ClusterAddr 716 } 717 718 if result.DisableClusteringRaw != nil { 719 disableClustering = result.DisableClustering 720 } 721 722 result.Storage = &Storage{ 723 RedirectAddr: redirectAddr, 724 ClusterAddr: clusterAddr, 725 DisableClustering: disableClustering, 726 Type: strings.ToLower(key), 727 Config: m, 728 } 729 return nil 730} 731 732func parseHAStorage(result *Config, list *ast.ObjectList, name string) error { 733 if len(list.Items) > 1 { 734 return fmt.Errorf("only one %q block is permitted", name) 735 } 736 737 // Get our item 738 item := list.Items[0] 739 740 key := name 741 if len(item.Keys) > 0 { 742 key = item.Keys[0].Token.Value().(string) 743 } 744 745 var m map[string]string 746 if err := hcl.DecodeObject(&m, item.Val); err != nil { 747 return multierror.Prefix(err, fmt.Sprintf("%s.%s:", name, key)) 748 } 749 750 // Pull out the redirect address since it's common to all backends 751 var redirectAddr string 752 if v, ok := m["redirect_addr"]; ok { 753 redirectAddr = v 754 delete(m, "redirect_addr") 755 } else if v, ok := m["advertise_addr"]; ok { 756 redirectAddr = v 757 delete(m, "advertise_addr") 758 } 759 760 // Pull out the cluster address since it's common to all backends 761 var clusterAddr string 762 if v, ok := m["cluster_addr"]; ok { 763 clusterAddr = v 764 delete(m, "cluster_addr") 765 } 766 767 var disableClustering bool 768 var err error 769 if v, ok := m["disable_clustering"]; ok { 770 disableClustering, err = strconv.ParseBool(v) 771 if err != nil { 772 return multierror.Prefix(err, fmt.Sprintf("%s.%s:", name, key)) 773 } 774 delete(m, "disable_clustering") 775 } 776 777 // Override with top-level values if they are set 778 if result.APIAddr != "" { 779 redirectAddr = result.APIAddr 780 } 781 782 if result.ClusterAddr != "" { 783 clusterAddr = result.ClusterAddr 784 } 785 786 if result.DisableClusteringRaw != nil { 787 disableClustering = result.DisableClustering 788 } 789 790 result.HAStorage = &Storage{ 791 RedirectAddr: redirectAddr, 792 ClusterAddr: clusterAddr, 793 DisableClustering: disableClustering, 794 Type: strings.ToLower(key), 795 Config: m, 796 } 797 return nil 798} 799 800func parseSeals(result *Config, list *ast.ObjectList, blockName string) error { 801 if len(list.Items) > 2 { 802 return fmt.Errorf("only two or less %q blocks are permitted", blockName) 803 } 804 805 seals := make([]*Seal, 0, len(list.Items)) 806 for _, item := range list.Items { 807 key := "seal" 808 if len(item.Keys) > 0 { 809 key = item.Keys[0].Token.Value().(string) 810 } 811 812 var m map[string]string 813 if err := hcl.DecodeObject(&m, item.Val); err != nil { 814 return multierror.Prefix(err, fmt.Sprintf("seal.%s:", key)) 815 } 816 817 var disabled bool 818 var err error 819 if v, ok := m["disabled"]; ok { 820 disabled, err = strconv.ParseBool(v) 821 if err != nil { 822 return multierror.Prefix(err, fmt.Sprintf("%s.%s:", blockName, key)) 823 } 824 delete(m, "disabled") 825 } 826 seals = append(seals, &Seal{ 827 Type: strings.ToLower(key), 828 Disabled: disabled, 829 Config: m, 830 }) 831 } 832 833 if len(seals) == 2 && 834 (seals[0].Disabled && seals[1].Disabled || !seals[0].Disabled && !seals[1].Disabled) { 835 return errors.New("seals: two seals provided but both are disabled or neither are disabled") 836 } 837 838 result.Seals = seals 839 840 return nil 841} 842 843func parseListeners(result *Config, list *ast.ObjectList) error { 844 listeners := make([]*Listener, 0, len(list.Items)) 845 for _, item := range list.Items { 846 key := "listener" 847 if len(item.Keys) > 0 { 848 key = item.Keys[0].Token.Value().(string) 849 } 850 851 var m map[string]interface{} 852 if err := hcl.DecodeObject(&m, item.Val); err != nil { 853 return multierror.Prefix(err, fmt.Sprintf("listeners.%s:", key)) 854 } 855 856 lnType := strings.ToLower(key) 857 858 listeners = append(listeners, &Listener{ 859 Type: lnType, 860 Config: m, 861 }) 862 } 863 864 result.Listeners = listeners 865 return nil 866} 867 868func parseTelemetry(result *Config, list *ast.ObjectList) error { 869 if len(list.Items) > 1 { 870 return fmt.Errorf("only one 'telemetry' block is permitted") 871 } 872 873 // Get our one item 874 item := list.Items[0] 875 876 var t Telemetry 877 if err := hcl.DecodeObject(&t, item.Val); err != nil { 878 return multierror.Prefix(err, "telemetry:") 879 } 880 881 if result.Telemetry == nil { 882 result.Telemetry = &Telemetry{} 883 } 884 885 if err := hcl.DecodeObject(&result.Telemetry, item.Val); err != nil { 886 return multierror.Prefix(err, "telemetry:") 887 } 888 889 if result.Telemetry.PrometheusRetentionTimeRaw != nil { 890 var err error 891 if result.Telemetry.PrometheusRetentionTime, err = parseutil.ParseDurationSecond(result.Telemetry.PrometheusRetentionTimeRaw); err != nil { 892 return err 893 } 894 } else { 895 result.Telemetry.PrometheusRetentionTime = prometheusDefaultRetentionTime 896 } 897 898 return nil 899} 900