github.com/smithx10/nomad@v0.9.1-rc1/command/agent/config_parse.go (about) 1 package agent 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 "os" 8 "path/filepath" 9 "time" 10 11 multierror "github.com/hashicorp/go-multierror" 12 version "github.com/hashicorp/go-version" 13 "github.com/hashicorp/hcl" 14 "github.com/hashicorp/hcl/hcl/ast" 15 "github.com/hashicorp/nomad/helper" 16 "github.com/hashicorp/nomad/helper/tlsutil" 17 "github.com/hashicorp/nomad/nomad/structs/config" 18 "github.com/mitchellh/mapstructure" 19 ) 20 21 // ParseConfigFile parses the given path as a config file. 22 func ParseConfigFile(path string) (*Config, error) { 23 path, err := filepath.Abs(path) 24 if err != nil { 25 return nil, err 26 } 27 28 f, err := os.Open(path) 29 if err != nil { 30 return nil, err 31 } 32 defer f.Close() 33 34 config, err := ParseConfig(f) 35 if err != nil { 36 return nil, err 37 } 38 39 return config, nil 40 } 41 42 // ParseConfig parses the config from the given io.Reader. 43 // 44 // Due to current internal limitations, the entire contents of the 45 // io.Reader will be copied into memory first before parsing. 46 func ParseConfig(r io.Reader) (*Config, error) { 47 // Copy the reader into an in-memory buffer first since HCL requires it. 48 var buf bytes.Buffer 49 if _, err := io.Copy(&buf, r); err != nil { 50 return nil, err 51 } 52 53 // Parse the buffer 54 root, err := hcl.Parse(buf.String()) 55 if err != nil { 56 return nil, fmt.Errorf("error parsing: %s", err) 57 } 58 buf.Reset() 59 60 // Top-level item should be a list 61 list, ok := root.Node.(*ast.ObjectList) 62 if !ok { 63 return nil, fmt.Errorf("error parsing: root should be an object") 64 } 65 66 var config Config 67 if err := parseConfig(&config, list); err != nil { 68 return nil, fmt.Errorf("error parsing 'config': %v", err) 69 } 70 71 return &config, nil 72 } 73 74 func parseConfig(result *Config, list *ast.ObjectList) error { 75 // Check for invalid keys 76 valid := []string{ 77 "region", 78 "datacenter", 79 "name", 80 "data_dir", 81 "plugin_dir", 82 "log_level", 83 "log_json", 84 "bind_addr", 85 "enable_debug", 86 "ports", 87 "addresses", 88 "interfaces", 89 "advertise", 90 "client", 91 "server", 92 "telemetry", 93 "leave_on_interrupt", 94 "leave_on_terminate", 95 "enable_syslog", 96 "syslog_facility", 97 "disable_update_check", 98 "disable_anonymous_signature", 99 "consul", 100 "vault", 101 "tls", 102 "http_api_response_headers", 103 "acl", 104 "sentinel", 105 "autopilot", 106 "plugin", 107 } 108 if err := helper.CheckHCLKeys(list, valid); err != nil { 109 return multierror.Prefix(err, "config:") 110 } 111 112 // Decode the full thing into a map[string]interface for ease 113 var m map[string]interface{} 114 if err := hcl.DecodeObject(&m, list); err != nil { 115 return err 116 } 117 delete(m, "ports") 118 delete(m, "addresses") 119 delete(m, "interfaces") 120 delete(m, "advertise") 121 delete(m, "client") 122 delete(m, "server") 123 delete(m, "telemetry") 124 delete(m, "consul") 125 delete(m, "vault") 126 delete(m, "tls") 127 delete(m, "http_api_response_headers") 128 delete(m, "acl") 129 delete(m, "sentinel") 130 delete(m, "autopilot") 131 delete(m, "plugin") 132 133 // Decode the rest 134 if err := mapstructure.WeakDecode(m, result); err != nil { 135 return err 136 } 137 138 // Parse ports 139 if o := list.Filter("ports"); len(o.Items) > 0 { 140 if err := parsePorts(&result.Ports, o); err != nil { 141 return multierror.Prefix(err, "ports ->") 142 } 143 } 144 145 // Parse addresses 146 if o := list.Filter("addresses"); len(o.Items) > 0 { 147 if err := parseAddresses(&result.Addresses, o); err != nil { 148 return multierror.Prefix(err, "addresses ->") 149 } 150 } 151 152 // Parse advertise 153 if o := list.Filter("advertise"); len(o.Items) > 0 { 154 if err := parseAdvertise(&result.AdvertiseAddrs, o); err != nil { 155 return multierror.Prefix(err, "advertise ->") 156 } 157 } 158 159 // Parse client config 160 if o := list.Filter("client"); len(o.Items) > 0 { 161 if err := parseClient(&result.Client, o); err != nil { 162 return multierror.Prefix(err, "client ->") 163 } 164 } 165 166 // Parse server config 167 if o := list.Filter("server"); len(o.Items) > 0 { 168 if err := parseServer(&result.Server, o); err != nil { 169 return multierror.Prefix(err, "server ->") 170 } 171 } 172 173 // Parse ACL config 174 if o := list.Filter("acl"); len(o.Items) > 0 { 175 if err := parseACL(&result.ACL, o); err != nil { 176 return multierror.Prefix(err, "acl ->") 177 } 178 } 179 180 // Parse telemetry config 181 if o := list.Filter("telemetry"); len(o.Items) > 0 { 182 if err := parseTelemetry(&result.Telemetry, o); err != nil { 183 return multierror.Prefix(err, "telemetry ->") 184 } 185 } 186 187 // Parse the consul config 188 if o := list.Filter("consul"); len(o.Items) > 0 { 189 if err := parseConsulConfig(&result.Consul, o); err != nil { 190 return multierror.Prefix(err, "consul ->") 191 } 192 } 193 194 // Parse the vault config 195 if o := list.Filter("vault"); len(o.Items) > 0 { 196 if err := parseVaultConfig(&result.Vault, o); err != nil { 197 return multierror.Prefix(err, "vault ->") 198 } 199 } 200 201 // Parse the TLS config 202 if o := list.Filter("tls"); len(o.Items) > 0 { 203 if err := parseTLSConfig(&result.TLSConfig, o); err != nil { 204 return multierror.Prefix(err, "tls ->") 205 } 206 } 207 208 // Parse Sentinel config 209 if o := list.Filter("sentinel"); len(o.Items) > 0 { 210 if err := parseSentinel(&result.Sentinel, o); err != nil { 211 return multierror.Prefix(err, "sentinel->") 212 } 213 } 214 215 // Parse Autopilot config 216 if o := list.Filter("autopilot"); len(o.Items) > 0 { 217 if err := parseAutopilot(&result.Autopilot, o); err != nil { 218 return multierror.Prefix(err, "autopilot->") 219 } 220 } 221 222 // Parse Plugin configs 223 if o := list.Filter("plugin"); len(o.Items) > 0 { 224 if err := parsePlugins(&result.Plugins, o); err != nil { 225 return multierror.Prefix(err, "plugin->") 226 } 227 } 228 229 // Parse out http_api_response_headers fields. These are in HCL as a list so 230 // we need to iterate over them and merge them. 231 if headersO := list.Filter("http_api_response_headers"); len(headersO.Items) > 0 { 232 for _, o := range headersO.Elem().Items { 233 var m map[string]interface{} 234 if err := hcl.DecodeObject(&m, o.Val); err != nil { 235 return err 236 } 237 if err := mapstructure.WeakDecode(m, &result.HTTPAPIResponseHeaders); err != nil { 238 return err 239 } 240 } 241 } 242 243 return nil 244 } 245 246 func parsePorts(result **Ports, list *ast.ObjectList) error { 247 list = list.Elem() 248 if len(list.Items) > 1 { 249 return fmt.Errorf("only one 'ports' block allowed") 250 } 251 252 // Get our ports object 253 listVal := list.Items[0].Val 254 255 // Check for invalid keys 256 valid := []string{ 257 "http", 258 "rpc", 259 "serf", 260 } 261 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 262 return err 263 } 264 265 var m map[string]interface{} 266 if err := hcl.DecodeObject(&m, listVal); err != nil { 267 return err 268 } 269 270 var ports Ports 271 if err := mapstructure.WeakDecode(m, &ports); err != nil { 272 return err 273 } 274 *result = &ports 275 return nil 276 } 277 278 func parseAddresses(result **Addresses, list *ast.ObjectList) error { 279 list = list.Elem() 280 if len(list.Items) > 1 { 281 return fmt.Errorf("only one 'addresses' block allowed") 282 } 283 284 // Get our addresses object 285 listVal := list.Items[0].Val 286 287 // Check for invalid keys 288 valid := []string{ 289 "http", 290 "rpc", 291 "serf", 292 } 293 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 294 return err 295 } 296 297 var m map[string]interface{} 298 if err := hcl.DecodeObject(&m, listVal); err != nil { 299 return err 300 } 301 302 var addresses Addresses 303 if err := mapstructure.WeakDecode(m, &addresses); err != nil { 304 return err 305 } 306 *result = &addresses 307 return nil 308 } 309 310 func parseAdvertise(result **AdvertiseAddrs, list *ast.ObjectList) error { 311 list = list.Elem() 312 if len(list.Items) > 1 { 313 return fmt.Errorf("only one 'advertise' block allowed") 314 } 315 316 // Get our advertise object 317 listVal := list.Items[0].Val 318 319 // Check for invalid keys 320 valid := []string{ 321 "http", 322 "rpc", 323 "serf", 324 } 325 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 326 return err 327 } 328 329 var m map[string]interface{} 330 if err := hcl.DecodeObject(&m, listVal); err != nil { 331 return err 332 } 333 334 var advertise AdvertiseAddrs 335 if err := mapstructure.WeakDecode(m, &advertise); err != nil { 336 return err 337 } 338 *result = &advertise 339 return nil 340 } 341 342 func parseClient(result **ClientConfig, list *ast.ObjectList) error { 343 list = list.Elem() 344 if len(list.Items) > 1 { 345 return fmt.Errorf("only one 'client' block allowed") 346 } 347 348 // Get our client object 349 obj := list.Items[0] 350 351 // Value should be an object 352 var listVal *ast.ObjectList 353 if ot, ok := obj.Val.(*ast.ObjectType); ok { 354 listVal = ot.List 355 } else { 356 return fmt.Errorf("client value: should be an object") 357 } 358 359 // Check for invalid keys 360 valid := []string{ 361 "enabled", 362 "state_dir", 363 "alloc_dir", 364 "servers", 365 "node_class", 366 "options", 367 "meta", 368 "chroot_env", 369 "network_interface", 370 "network_speed", 371 "memory_total_mb", 372 "cpu_total_compute", 373 "max_kill_timeout", 374 "client_max_port", 375 "client_min_port", 376 "reserved", 377 "stats", 378 "gc_interval", 379 "gc_disk_usage_threshold", 380 "gc_inode_usage_threshold", 381 "gc_parallel_destroys", 382 "gc_max_allocs", 383 "no_host_uuid", 384 "server_join", 385 } 386 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 387 return err 388 } 389 390 var m map[string]interface{} 391 if err := hcl.DecodeObject(&m, listVal); err != nil { 392 return err 393 } 394 395 delete(m, "options") 396 delete(m, "meta") 397 delete(m, "chroot_env") 398 delete(m, "reserved") 399 delete(m, "stats") 400 delete(m, "server_join") 401 402 var config ClientConfig 403 dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 404 DecodeHook: mapstructure.StringToTimeDurationHookFunc(), 405 WeaklyTypedInput: true, 406 Result: &config, 407 }) 408 if err != nil { 409 return err 410 } 411 if err := dec.Decode(m); err != nil { 412 return err 413 } 414 415 // Parse out options fields. These are in HCL as a list so we need to 416 // iterate over them and merge them. 417 if optionsO := listVal.Filter("options"); len(optionsO.Items) > 0 { 418 for _, o := range optionsO.Elem().Items { 419 var m map[string]interface{} 420 if err := hcl.DecodeObject(&m, o.Val); err != nil { 421 return err 422 } 423 if err := mapstructure.WeakDecode(m, &config.Options); err != nil { 424 return err 425 } 426 } 427 } 428 429 // Parse out options meta. These are in HCL as a list so we need to 430 // iterate over them and merge them. 431 if metaO := listVal.Filter("meta"); len(metaO.Items) > 0 { 432 for _, o := range metaO.Elem().Items { 433 var m map[string]interface{} 434 if err := hcl.DecodeObject(&m, o.Val); err != nil { 435 return err 436 } 437 if err := mapstructure.WeakDecode(m, &config.Meta); err != nil { 438 return err 439 } 440 } 441 } 442 443 // Parse out chroot_env fields. These are in HCL as a list so we need to 444 // iterate over them and merge them. 445 if chrootEnvO := listVal.Filter("chroot_env"); len(chrootEnvO.Items) > 0 { 446 for _, o := range chrootEnvO.Elem().Items { 447 var m map[string]interface{} 448 if err := hcl.DecodeObject(&m, o.Val); err != nil { 449 return err 450 } 451 if err := mapstructure.WeakDecode(m, &config.ChrootEnv); err != nil { 452 return err 453 } 454 } 455 } 456 457 // Parse reserved config 458 if o := listVal.Filter("reserved"); len(o.Items) > 0 { 459 if err := parseReserved(&config.Reserved, o); err != nil { 460 return multierror.Prefix(err, "reserved ->") 461 } 462 } 463 464 // Parse ServerJoin config 465 if o := listVal.Filter("server_join"); len(o.Items) > 0 { 466 if err := parseServerJoin(&config.ServerJoin, o); err != nil { 467 return multierror.Prefix(err, "server_join->") 468 } 469 } 470 471 *result = &config 472 return nil 473 } 474 475 func parseReserved(result **Resources, list *ast.ObjectList) error { 476 list = list.Elem() 477 if len(list.Items) > 1 { 478 return fmt.Errorf("only one 'reserved' block allowed") 479 } 480 481 // Get our reserved object 482 obj := list.Items[0] 483 484 // Value should be an object 485 var listVal *ast.ObjectList 486 if ot, ok := obj.Val.(*ast.ObjectType); ok { 487 listVal = ot.List 488 } else { 489 return fmt.Errorf("client value: should be an object") 490 } 491 492 // Check for invalid keys 493 valid := []string{ 494 "cpu", 495 "memory", 496 "disk", 497 "reserved_ports", 498 } 499 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 500 return err 501 } 502 503 var m map[string]interface{} 504 if err := hcl.DecodeObject(&m, listVal); err != nil { 505 return err 506 } 507 508 var reserved Resources 509 if err := mapstructure.WeakDecode(m, &reserved); err != nil { 510 return err 511 } 512 if err := reserved.CanParseReserved(); err != nil { 513 return err 514 } 515 516 *result = &reserved 517 return nil 518 } 519 520 func parseServer(result **ServerConfig, list *ast.ObjectList) error { 521 list = list.Elem() 522 if len(list.Items) > 1 { 523 return fmt.Errorf("only one 'server' block allowed") 524 } 525 526 // Get our server object 527 obj := list.Items[0] 528 529 // Value should be an object 530 var listVal *ast.ObjectList 531 if ot, ok := obj.Val.(*ast.ObjectType); ok { 532 listVal = ot.List 533 } else { 534 return fmt.Errorf("client value: should be an object") 535 } 536 537 // Check for invalid keys 538 valid := []string{ 539 "enabled", 540 "bootstrap_expect", 541 "data_dir", 542 "protocol_version", 543 "raft_protocol", 544 "num_schedulers", 545 "enabled_schedulers", 546 "node_gc_threshold", 547 "eval_gc_threshold", 548 "job_gc_threshold", 549 "deployment_gc_threshold", 550 "heartbeat_grace", 551 "min_heartbeat_ttl", 552 "max_heartbeats_per_second", 553 "rejoin_after_leave", 554 "encrypt", 555 "authoritative_region", 556 "non_voting_server", 557 "redundancy_zone", 558 "upgrade_version", 559 560 "server_join", 561 562 // For backwards compatibility 563 "start_join", 564 "retry_join", 565 "retry_max", 566 "retry_interval", 567 } 568 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 569 return err 570 } 571 572 var m map[string]interface{} 573 if err := hcl.DecodeObject(&m, listVal); err != nil { 574 return err 575 } 576 577 delete(m, "server_join") 578 579 var config ServerConfig 580 dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 581 DecodeHook: mapstructure.StringToTimeDurationHookFunc(), 582 WeaklyTypedInput: true, 583 Result: &config, 584 }) 585 if err != nil { 586 return err 587 } 588 if err := dec.Decode(m); err != nil { 589 return err 590 } 591 592 if config.UpgradeVersion != "" { 593 if _, err := version.NewVersion(config.UpgradeVersion); err != nil { 594 return fmt.Errorf("error parsing upgrade_version: %v", err) 595 } 596 } 597 598 // Parse ServerJoin config 599 if o := listVal.Filter("server_join"); len(o.Items) > 0 { 600 if err := parseServerJoin(&config.ServerJoin, o); err != nil { 601 return multierror.Prefix(err, "server_join->") 602 } 603 } 604 605 *result = &config 606 return nil 607 } 608 609 func parseServerJoin(result **ServerJoin, list *ast.ObjectList) error { 610 list = list.Elem() 611 if len(list.Items) > 1 { 612 return fmt.Errorf("only one 'server_join' block allowed") 613 } 614 615 // Get our object 616 listVal := list.Items[0].Val 617 618 // Check for invalid keys 619 valid := []string{ 620 "start_join", 621 "retry_join", 622 "retry_max", 623 "retry_interval", 624 } 625 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 626 return err 627 } 628 629 var m map[string]interface{} 630 if err := hcl.DecodeObject(&m, listVal); err != nil { 631 return err 632 } 633 634 var serverJoinInfo ServerJoin 635 dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 636 DecodeHook: mapstructure.StringToTimeDurationHookFunc(), 637 WeaklyTypedInput: true, 638 Result: &serverJoinInfo, 639 }) 640 if err != nil { 641 return err 642 } 643 if err := dec.Decode(m); err != nil { 644 return err 645 } 646 647 *result = &serverJoinInfo 648 return nil 649 } 650 651 func parseACL(result **ACLConfig, list *ast.ObjectList) error { 652 list = list.Elem() 653 if len(list.Items) > 1 { 654 return fmt.Errorf("only one 'acl' block allowed") 655 } 656 657 // Get our server object 658 obj := list.Items[0] 659 660 // Value should be an object 661 var listVal *ast.ObjectList 662 if ot, ok := obj.Val.(*ast.ObjectType); ok { 663 listVal = ot.List 664 } else { 665 return fmt.Errorf("acl value: should be an object") 666 } 667 668 // Check for invalid keys 669 valid := []string{ 670 "enabled", 671 "token_ttl", 672 "policy_ttl", 673 "replication_token", 674 } 675 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 676 return err 677 } 678 679 var m map[string]interface{} 680 if err := hcl.DecodeObject(&m, listVal); err != nil { 681 return err 682 } 683 684 var config ACLConfig 685 dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 686 DecodeHook: mapstructure.StringToTimeDurationHookFunc(), 687 WeaklyTypedInput: true, 688 Result: &config, 689 }) 690 if err != nil { 691 return err 692 } 693 if err := dec.Decode(m); err != nil { 694 return err 695 } 696 697 *result = &config 698 return nil 699 } 700 701 func parseTelemetry(result **Telemetry, list *ast.ObjectList) error { 702 list = list.Elem() 703 if len(list.Items) > 1 { 704 return fmt.Errorf("only one 'telemetry' block allowed") 705 } 706 707 // Get our telemetry object 708 listVal := list.Items[0].Val 709 710 // Check for invalid keys 711 valid := []string{ 712 "statsite_address", 713 "statsd_address", 714 "disable_hostname", 715 "use_node_name", 716 "collection_interval", 717 "publish_allocation_metrics", 718 "publish_node_metrics", 719 "datadog_address", 720 "datadog_tags", 721 "prometheus_metrics", 722 "circonus_api_token", 723 "circonus_api_app", 724 "circonus_api_url", 725 "circonus_submission_interval", 726 "circonus_submission_url", 727 "circonus_check_id", 728 "circonus_check_force_metric_activation", 729 "circonus_check_instance_id", 730 "circonus_check_search_tag", 731 "circonus_check_display_name", 732 "circonus_check_tags", 733 "circonus_broker_id", 734 "circonus_broker_select_tag", 735 "disable_tagged_metrics", 736 "backwards_compatible_metrics", 737 "prefix_filter", 738 "filter_default", 739 "disable_dispatched_job_summary_metrics", 740 } 741 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 742 return err 743 } 744 745 var m map[string]interface{} 746 if err := hcl.DecodeObject(&m, listVal); err != nil { 747 return err 748 } 749 750 var telemetry Telemetry 751 if err := mapstructure.WeakDecode(m, &telemetry); err != nil { 752 return err 753 } 754 if telemetry.CollectionInterval != "" { 755 if dur, err := time.ParseDuration(telemetry.CollectionInterval); err != nil { 756 return fmt.Errorf("error parsing value of %q: %v", "collection_interval", err) 757 } else { 758 telemetry.collectionInterval = dur 759 } 760 } 761 *result = &telemetry 762 return nil 763 } 764 765 func parseConsulConfig(result **config.ConsulConfig, list *ast.ObjectList) error { 766 list = list.Elem() 767 if len(list.Items) > 1 { 768 return fmt.Errorf("only one 'consul' block allowed") 769 } 770 771 // Get our Consul object 772 listVal := list.Items[0].Val 773 774 // Check for invalid keys 775 valid := []string{ 776 "address", 777 "auth", 778 "auto_advertise", 779 "ca_file", 780 "cert_file", 781 "checks_use_advertise", 782 "client_auto_join", 783 "client_service_name", 784 "client_http_check_name", 785 "key_file", 786 "server_auto_join", 787 "server_service_name", 788 "server_http_check_name", 789 "server_serf_check_name", 790 "server_rpc_check_name", 791 "ssl", 792 "timeout", 793 "token", 794 "verify_ssl", 795 } 796 797 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 798 return err 799 } 800 801 var m map[string]interface{} 802 if err := hcl.DecodeObject(&m, listVal); err != nil { 803 return err 804 } 805 806 consulConfig := config.DefaultConsulConfig() 807 dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 808 DecodeHook: mapstructure.StringToTimeDurationHookFunc(), 809 WeaklyTypedInput: true, 810 Result: &consulConfig, 811 }) 812 if err != nil { 813 return err 814 } 815 if err := dec.Decode(m); err != nil { 816 return err 817 } 818 819 *result = consulConfig 820 return nil 821 } 822 823 func parseTLSConfig(result **config.TLSConfig, list *ast.ObjectList) error { 824 list = list.Elem() 825 if len(list.Items) > 1 { 826 return fmt.Errorf("only one 'tls' block allowed") 827 } 828 829 // Get the TLS object 830 listVal := list.Items[0].Val 831 832 valid := []string{ 833 "http", 834 "rpc", 835 "verify_server_hostname", 836 "rpc_upgrade_mode", 837 "ca_file", 838 "cert_file", 839 "key_file", 840 "verify_https_client", 841 "tls_cipher_suites", 842 "tls_min_version", 843 "tls_prefer_server_cipher_suites", 844 } 845 846 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 847 return err 848 } 849 850 var m map[string]interface{} 851 if err := hcl.DecodeObject(&m, listVal); err != nil { 852 return err 853 } 854 855 var tlsConfig config.TLSConfig 856 if err := mapstructure.WeakDecode(m, &tlsConfig); err != nil { 857 return err 858 } 859 860 if _, err := tlsutil.ParseCiphers(&tlsConfig); err != nil { 861 return err 862 } 863 864 if _, err := tlsutil.ParseMinVersion(tlsConfig.TLSMinVersion); err != nil { 865 return err 866 } 867 868 *result = &tlsConfig 869 return nil 870 } 871 872 func parseVaultConfig(result **config.VaultConfig, list *ast.ObjectList) error { 873 list = list.Elem() 874 if len(list.Items) > 1 { 875 return fmt.Errorf("only one 'vault' block allowed") 876 } 877 878 // Get our Vault object 879 listVal := list.Items[0].Val 880 881 // Check for invalid keys 882 valid := []string{ 883 "address", 884 "allow_unauthenticated", 885 "enabled", 886 "task_token_ttl", 887 "ca_file", 888 "ca_path", 889 "cert_file", 890 "create_from_role", 891 "key_file", 892 "tls_server_name", 893 "tls_skip_verify", 894 "token", 895 } 896 897 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 898 return err 899 } 900 901 var m map[string]interface{} 902 if err := hcl.DecodeObject(&m, listVal); err != nil { 903 return err 904 } 905 906 vaultConfig := config.DefaultVaultConfig() 907 dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 908 DecodeHook: mapstructure.StringToTimeDurationHookFunc(), 909 WeaklyTypedInput: true, 910 Result: &vaultConfig, 911 }) 912 if err != nil { 913 return err 914 } 915 if err := dec.Decode(m); err != nil { 916 return err 917 } 918 919 *result = vaultConfig 920 return nil 921 } 922 923 func parseSentinel(result **config.SentinelConfig, list *ast.ObjectList) error { 924 list = list.Elem() 925 if len(list.Items) > 1 { 926 return fmt.Errorf("only one 'sentinel' block allowed") 927 } 928 929 // Get our sentinel object 930 obj := list.Items[0] 931 932 // Value should be an object 933 var listVal *ast.ObjectList 934 if ot, ok := obj.Val.(*ast.ObjectType); ok { 935 listVal = ot.List 936 } else { 937 return fmt.Errorf("sentinel value: should be an object") 938 } 939 940 // Check for invalid keys 941 valid := []string{ 942 "import", 943 } 944 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 945 return err 946 } 947 948 var config config.SentinelConfig 949 if err := hcl.DecodeObject(&config, listVal); err != nil { 950 return err 951 } 952 953 *result = &config 954 return nil 955 } 956 957 func parseAutopilot(result **config.AutopilotConfig, list *ast.ObjectList) error { 958 list = list.Elem() 959 if len(list.Items) > 1 { 960 return fmt.Errorf("only one 'autopilot' block allowed") 961 } 962 963 // Get our Autopilot object 964 listVal := list.Items[0].Val 965 966 // Check for invalid keys 967 valid := []string{ 968 "cleanup_dead_servers", 969 "server_stabilization_time", 970 "last_contact_threshold", 971 "max_trailing_logs", 972 "enable_redundancy_zones", 973 "disable_upgrade_migration", 974 "enable_custom_upgrades", 975 } 976 977 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 978 return err 979 } 980 981 var m map[string]interface{} 982 if err := hcl.DecodeObject(&m, listVal); err != nil { 983 return err 984 } 985 986 autopilotConfig := config.DefaultAutopilotConfig() 987 dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 988 DecodeHook: mapstructure.StringToTimeDurationHookFunc(), 989 WeaklyTypedInput: true, 990 Result: &autopilotConfig, 991 }) 992 if err != nil { 993 return err 994 } 995 if err := dec.Decode(m); err != nil { 996 return err 997 } 998 999 *result = autopilotConfig 1000 return nil 1001 } 1002 1003 func parsePlugins(result *[]*config.PluginConfig, list *ast.ObjectList) error { 1004 listLen := len(list.Items) 1005 plugins := make([]*config.PluginConfig, listLen) 1006 1007 // Check for invalid keys 1008 valid := []string{ 1009 "args", 1010 "config", 1011 } 1012 1013 for i := 0; i < listLen; i++ { 1014 // Get the current plugin object 1015 listVal := list.Items[i] 1016 1017 // Deal with json->hcl AST parsing incorrectness when directly nested 1018 // items show up as additional keys. 1019 // TODO(preetha): Add additional tests and fix other places that have the same issue 1020 unwrapLegacyHCLObjectKeysFromJSON(listVal, 1) 1021 if err := helper.CheckHCLKeys(listVal.Val, valid); err != nil { 1022 return fmt.Errorf("invalid keys in plugin config %d: %v", i+1, err) 1023 } 1024 1025 // Ensure there is a key 1026 if len(listVal.Keys) != 1 { 1027 return fmt.Errorf("plugin config %d doesn't incude a name key", i+1) 1028 } 1029 1030 var plugin config.PluginConfig 1031 if err := hcl.DecodeObject(&plugin, listVal); err != nil { 1032 return fmt.Errorf("error decoding plugin config %d: %v", i+1, err) 1033 } 1034 1035 plugins[i] = &plugin 1036 } 1037 1038 *result = plugins 1039 return nil 1040 } 1041 1042 // unwrapLegacyHCLObjectKeysFromJSON cleans up an edge case that can occur when 1043 // parsing JSON as input: if we're parsing JSON then directly nested 1044 // items will show up as additional "keys". 1045 // 1046 // For objects that expect a fixed number of keys, this breaks the 1047 // decoding process. This function unwraps the object into what it would've 1048 // looked like if it came directly from HCL by specifying the number of keys 1049 // you expect. 1050 // 1051 // Example: 1052 // 1053 // { "foo": { "baz": {} } } 1054 // 1055 // Will show up with Keys being: []string{"foo", "baz"} 1056 // when we really just want the first two. This function will fix this. 1057 func unwrapLegacyHCLObjectKeysFromJSON(item *ast.ObjectItem, depth int) { 1058 if len(item.Keys) > depth && item.Keys[0].Token.JSON { 1059 for len(item.Keys) > depth { 1060 // Pop off the last key 1061 n := len(item.Keys) 1062 key := item.Keys[n-1] 1063 item.Keys[n-1] = nil 1064 item.Keys = item.Keys[:n-1] 1065 1066 // Wrap our value in a list 1067 item.Val = &ast.ObjectType{ 1068 List: &ast.ObjectList{ 1069 Items: []*ast.ObjectItem{ 1070 { 1071 Keys: []*ast.ObjectKey{key}, 1072 Val: item.Val, 1073 }, 1074 }, 1075 }, 1076 } 1077 } 1078 } 1079 }