github.com/djenriquez/nomad-1@v0.8.1/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 "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/nomad/structs/config" 17 "github.com/mitchellh/mapstructure" 18 ) 19 20 // ParseConfigFile parses the given path as a config file. 21 func ParseConfigFile(path string) (*Config, error) { 22 path, err := filepath.Abs(path) 23 if err != nil { 24 return nil, err 25 } 26 27 f, err := os.Open(path) 28 if err != nil { 29 return nil, err 30 } 31 defer f.Close() 32 33 config, err := ParseConfig(f) 34 if err != nil { 35 return nil, err 36 } 37 38 return config, nil 39 } 40 41 // ParseConfig parses the config from the given io.Reader. 42 // 43 // Due to current internal limitations, the entire contents of the 44 // io.Reader will be copied into memory first before parsing. 45 func ParseConfig(r io.Reader) (*Config, error) { 46 // Copy the reader into an in-memory buffer first since HCL requires it. 47 var buf bytes.Buffer 48 if _, err := io.Copy(&buf, r); err != nil { 49 return nil, err 50 } 51 52 // Parse the buffer 53 root, err := hcl.Parse(buf.String()) 54 if err != nil { 55 return nil, fmt.Errorf("error parsing: %s", err) 56 } 57 buf.Reset() 58 59 // Top-level item should be a list 60 list, ok := root.Node.(*ast.ObjectList) 61 if !ok { 62 return nil, fmt.Errorf("error parsing: root should be an object") 63 } 64 65 var config Config 66 if err := parseConfig(&config, list); err != nil { 67 return nil, fmt.Errorf("error parsing 'config': %v", err) 68 } 69 70 return &config, nil 71 } 72 73 func parseConfig(result *Config, list *ast.ObjectList) error { 74 // Check for invalid keys 75 valid := []string{ 76 "region", 77 "datacenter", 78 "name", 79 "data_dir", 80 "log_level", 81 "bind_addr", 82 "enable_debug", 83 "ports", 84 "addresses", 85 "interfaces", 86 "advertise", 87 "client", 88 "server", 89 "telemetry", 90 "leave_on_interrupt", 91 "leave_on_terminate", 92 "enable_syslog", 93 "syslog_facility", 94 "disable_update_check", 95 "disable_anonymous_signature", 96 "consul", 97 "vault", 98 "tls", 99 "http_api_response_headers", 100 "acl", 101 "sentinel", 102 "autopilot", 103 } 104 if err := helper.CheckHCLKeys(list, valid); err != nil { 105 return multierror.Prefix(err, "config:") 106 } 107 108 // Decode the full thing into a map[string]interface for ease 109 var m map[string]interface{} 110 if err := hcl.DecodeObject(&m, list); err != nil { 111 return err 112 } 113 delete(m, "ports") 114 delete(m, "addresses") 115 delete(m, "interfaces") 116 delete(m, "advertise") 117 delete(m, "client") 118 delete(m, "server") 119 delete(m, "telemetry") 120 delete(m, "consul") 121 delete(m, "vault") 122 delete(m, "tls") 123 delete(m, "http_api_response_headers") 124 delete(m, "acl") 125 delete(m, "sentinel") 126 delete(m, "autopilot") 127 128 // Decode the rest 129 if err := mapstructure.WeakDecode(m, result); err != nil { 130 return err 131 } 132 133 // Parse ports 134 if o := list.Filter("ports"); len(o.Items) > 0 { 135 if err := parsePorts(&result.Ports, o); err != nil { 136 return multierror.Prefix(err, "ports ->") 137 } 138 } 139 140 // Parse addresses 141 if o := list.Filter("addresses"); len(o.Items) > 0 { 142 if err := parseAddresses(&result.Addresses, o); err != nil { 143 return multierror.Prefix(err, "addresses ->") 144 } 145 } 146 147 // Parse advertise 148 if o := list.Filter("advertise"); len(o.Items) > 0 { 149 if err := parseAdvertise(&result.AdvertiseAddrs, o); err != nil { 150 return multierror.Prefix(err, "advertise ->") 151 } 152 } 153 154 // Parse client config 155 if o := list.Filter("client"); len(o.Items) > 0 { 156 if err := parseClient(&result.Client, o); err != nil { 157 return multierror.Prefix(err, "client ->") 158 } 159 } 160 161 // Parse server config 162 if o := list.Filter("server"); len(o.Items) > 0 { 163 if err := parseServer(&result.Server, o); err != nil { 164 return multierror.Prefix(err, "server ->") 165 } 166 } 167 168 // Parse ACL config 169 if o := list.Filter("acl"); len(o.Items) > 0 { 170 if err := parseACL(&result.ACL, o); err != nil { 171 return multierror.Prefix(err, "acl ->") 172 } 173 } 174 175 // Parse telemetry config 176 if o := list.Filter("telemetry"); len(o.Items) > 0 { 177 if err := parseTelemetry(&result.Telemetry, o); err != nil { 178 return multierror.Prefix(err, "telemetry ->") 179 } 180 } 181 182 // Parse the consul config 183 if o := list.Filter("consul"); len(o.Items) > 0 { 184 if err := parseConsulConfig(&result.Consul, o); err != nil { 185 return multierror.Prefix(err, "consul ->") 186 } 187 } 188 189 // Parse the vault config 190 if o := list.Filter("vault"); len(o.Items) > 0 { 191 if err := parseVaultConfig(&result.Vault, o); err != nil { 192 return multierror.Prefix(err, "vault ->") 193 } 194 } 195 196 // Parse the TLS config 197 if o := list.Filter("tls"); len(o.Items) > 0 { 198 if err := parseTLSConfig(&result.TLSConfig, o); err != nil { 199 return multierror.Prefix(err, "tls ->") 200 } 201 } 202 203 // Parse Sentinel config 204 if o := list.Filter("sentinel"); len(o.Items) > 0 { 205 if err := parseSentinel(&result.Sentinel, o); err != nil { 206 return multierror.Prefix(err, "sentinel->") 207 } 208 } 209 210 // Parse Autopilot config 211 if o := list.Filter("autopilot"); len(o.Items) > 0 { 212 if err := parseAutopilot(&result.Autopilot, o); err != nil { 213 return multierror.Prefix(err, "autopilot->") 214 } 215 } 216 217 // Parse out http_api_response_headers fields. These are in HCL as a list so 218 // we need to iterate over them and merge them. 219 if headersO := list.Filter("http_api_response_headers"); len(headersO.Items) > 0 { 220 for _, o := range headersO.Elem().Items { 221 var m map[string]interface{} 222 if err := hcl.DecodeObject(&m, o.Val); err != nil { 223 return err 224 } 225 if err := mapstructure.WeakDecode(m, &result.HTTPAPIResponseHeaders); err != nil { 226 return err 227 } 228 } 229 } 230 231 return nil 232 } 233 234 func parsePorts(result **Ports, list *ast.ObjectList) error { 235 list = list.Elem() 236 if len(list.Items) > 1 { 237 return fmt.Errorf("only one 'ports' block allowed") 238 } 239 240 // Get our ports object 241 listVal := list.Items[0].Val 242 243 // Check for invalid keys 244 valid := []string{ 245 "http", 246 "rpc", 247 "serf", 248 } 249 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 250 return err 251 } 252 253 var m map[string]interface{} 254 if err := hcl.DecodeObject(&m, listVal); err != nil { 255 return err 256 } 257 258 var ports Ports 259 if err := mapstructure.WeakDecode(m, &ports); err != nil { 260 return err 261 } 262 *result = &ports 263 return nil 264 } 265 266 func parseAddresses(result **Addresses, list *ast.ObjectList) error { 267 list = list.Elem() 268 if len(list.Items) > 1 { 269 return fmt.Errorf("only one 'addresses' block allowed") 270 } 271 272 // Get our addresses object 273 listVal := list.Items[0].Val 274 275 // Check for invalid keys 276 valid := []string{ 277 "http", 278 "rpc", 279 "serf", 280 } 281 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 282 return err 283 } 284 285 var m map[string]interface{} 286 if err := hcl.DecodeObject(&m, listVal); err != nil { 287 return err 288 } 289 290 var addresses Addresses 291 if err := mapstructure.WeakDecode(m, &addresses); err != nil { 292 return err 293 } 294 *result = &addresses 295 return nil 296 } 297 298 func parseAdvertise(result **AdvertiseAddrs, list *ast.ObjectList) error { 299 list = list.Elem() 300 if len(list.Items) > 1 { 301 return fmt.Errorf("only one 'advertise' block allowed") 302 } 303 304 // Get our advertise object 305 listVal := list.Items[0].Val 306 307 // Check for invalid keys 308 valid := []string{ 309 "http", 310 "rpc", 311 "serf", 312 } 313 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 314 return err 315 } 316 317 var m map[string]interface{} 318 if err := hcl.DecodeObject(&m, listVal); err != nil { 319 return err 320 } 321 322 var advertise AdvertiseAddrs 323 if err := mapstructure.WeakDecode(m, &advertise); err != nil { 324 return err 325 } 326 *result = &advertise 327 return nil 328 } 329 330 func parseClient(result **ClientConfig, list *ast.ObjectList) error { 331 list = list.Elem() 332 if len(list.Items) > 1 { 333 return fmt.Errorf("only one 'client' block allowed") 334 } 335 336 // Get our client object 337 obj := list.Items[0] 338 339 // Value should be an object 340 var listVal *ast.ObjectList 341 if ot, ok := obj.Val.(*ast.ObjectType); ok { 342 listVal = ot.List 343 } else { 344 return fmt.Errorf("client value: should be an object") 345 } 346 347 // Check for invalid keys 348 valid := []string{ 349 "enabled", 350 "state_dir", 351 "alloc_dir", 352 "servers", 353 "node_class", 354 "options", 355 "meta", 356 "chroot_env", 357 "network_interface", 358 "network_speed", 359 "memory_total_mb", 360 "cpu_total_compute", 361 "max_kill_timeout", 362 "client_max_port", 363 "client_min_port", 364 "reserved", 365 "stats", 366 "gc_interval", 367 "gc_disk_usage_threshold", 368 "gc_inode_usage_threshold", 369 "gc_parallel_destroys", 370 "gc_max_allocs", 371 "no_host_uuid", 372 } 373 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 374 return err 375 } 376 377 var m map[string]interface{} 378 if err := hcl.DecodeObject(&m, listVal); err != nil { 379 return err 380 } 381 382 delete(m, "options") 383 delete(m, "meta") 384 delete(m, "chroot_env") 385 delete(m, "reserved") 386 delete(m, "stats") 387 388 var config ClientConfig 389 dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 390 DecodeHook: mapstructure.StringToTimeDurationHookFunc(), 391 WeaklyTypedInput: true, 392 Result: &config, 393 }) 394 if err != nil { 395 return err 396 } 397 if err := dec.Decode(m); err != nil { 398 return err 399 } 400 401 // Parse out options fields. These are in HCL as a list so we need to 402 // iterate over them and merge them. 403 if optionsO := listVal.Filter("options"); len(optionsO.Items) > 0 { 404 for _, o := range optionsO.Elem().Items { 405 var m map[string]interface{} 406 if err := hcl.DecodeObject(&m, o.Val); err != nil { 407 return err 408 } 409 if err := mapstructure.WeakDecode(m, &config.Options); err != nil { 410 return err 411 } 412 } 413 } 414 415 // Parse out options meta. These are in HCL as a list so we need to 416 // iterate over them and merge them. 417 if metaO := listVal.Filter("meta"); len(metaO.Items) > 0 { 418 for _, o := range metaO.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.Meta); err != nil { 424 return err 425 } 426 } 427 } 428 429 // Parse out chroot_env fields. These are in HCL as a list so we need to 430 // iterate over them and merge them. 431 if chrootEnvO := listVal.Filter("chroot_env"); len(chrootEnvO.Items) > 0 { 432 for _, o := range chrootEnvO.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.ChrootEnv); err != nil { 438 return err 439 } 440 } 441 } 442 443 // Parse reserved config 444 if o := listVal.Filter("reserved"); len(o.Items) > 0 { 445 if err := parseReserved(&config.Reserved, o); err != nil { 446 return multierror.Prefix(err, "reserved ->") 447 } 448 } 449 450 *result = &config 451 return nil 452 } 453 454 func parseReserved(result **Resources, list *ast.ObjectList) error { 455 list = list.Elem() 456 if len(list.Items) > 1 { 457 return fmt.Errorf("only one 'reserved' block allowed") 458 } 459 460 // Get our reserved object 461 obj := list.Items[0] 462 463 // Value should be an object 464 var listVal *ast.ObjectList 465 if ot, ok := obj.Val.(*ast.ObjectType); ok { 466 listVal = ot.List 467 } else { 468 return fmt.Errorf("client value: should be an object") 469 } 470 471 // Check for invalid keys 472 valid := []string{ 473 "cpu", 474 "memory", 475 "disk", 476 "iops", 477 "reserved_ports", 478 } 479 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 480 return err 481 } 482 483 var m map[string]interface{} 484 if err := hcl.DecodeObject(&m, listVal); err != nil { 485 return err 486 } 487 488 var reserved Resources 489 if err := mapstructure.WeakDecode(m, &reserved); err != nil { 490 return err 491 } 492 if err := reserved.ParseReserved(); err != nil { 493 return err 494 } 495 496 *result = &reserved 497 return nil 498 } 499 500 func parseServer(result **ServerConfig, list *ast.ObjectList) error { 501 list = list.Elem() 502 if len(list.Items) > 1 { 503 return fmt.Errorf("only one 'server' block allowed") 504 } 505 506 // Get our server object 507 obj := list.Items[0] 508 509 // Value should be an object 510 var listVal *ast.ObjectList 511 if ot, ok := obj.Val.(*ast.ObjectType); ok { 512 listVal = ot.List 513 } else { 514 return fmt.Errorf("client value: should be an object") 515 } 516 517 // Check for invalid keys 518 valid := []string{ 519 "enabled", 520 "bootstrap_expect", 521 "data_dir", 522 "protocol_version", 523 "raft_protocol", 524 "num_schedulers", 525 "enabled_schedulers", 526 "node_gc_threshold", 527 "eval_gc_threshold", 528 "job_gc_threshold", 529 "deployment_gc_threshold", 530 "heartbeat_grace", 531 "min_heartbeat_ttl", 532 "max_heartbeats_per_second", 533 "start_join", 534 "retry_join", 535 "retry_max", 536 "retry_interval", 537 "rejoin_after_leave", 538 "encrypt", 539 "authoritative_region", 540 "non_voting_server", 541 "redundancy_zone", 542 "upgrade_version", 543 } 544 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 545 return err 546 } 547 548 var m map[string]interface{} 549 if err := hcl.DecodeObject(&m, listVal); err != nil { 550 return err 551 } 552 553 var config ServerConfig 554 dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 555 DecodeHook: mapstructure.StringToTimeDurationHookFunc(), 556 WeaklyTypedInput: true, 557 Result: &config, 558 }) 559 if err != nil { 560 return err 561 } 562 if err := dec.Decode(m); err != nil { 563 return err 564 } 565 566 if config.UpgradeVersion != "" { 567 if _, err := version.NewVersion(config.UpgradeVersion); err != nil { 568 return fmt.Errorf("error parsing upgrade_version: %v", err) 569 } 570 } 571 572 *result = &config 573 return nil 574 } 575 576 func parseACL(result **ACLConfig, list *ast.ObjectList) error { 577 list = list.Elem() 578 if len(list.Items) > 1 { 579 return fmt.Errorf("only one 'acl' block allowed") 580 } 581 582 // Get our server object 583 obj := list.Items[0] 584 585 // Value should be an object 586 var listVal *ast.ObjectList 587 if ot, ok := obj.Val.(*ast.ObjectType); ok { 588 listVal = ot.List 589 } else { 590 return fmt.Errorf("acl value: should be an object") 591 } 592 593 // Check for invalid keys 594 valid := []string{ 595 "enabled", 596 "token_ttl", 597 "policy_ttl", 598 "replication_token", 599 } 600 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 601 return err 602 } 603 604 var m map[string]interface{} 605 if err := hcl.DecodeObject(&m, listVal); err != nil { 606 return err 607 } 608 609 var config ACLConfig 610 dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 611 DecodeHook: mapstructure.StringToTimeDurationHookFunc(), 612 WeaklyTypedInput: true, 613 Result: &config, 614 }) 615 if err != nil { 616 return err 617 } 618 if err := dec.Decode(m); err != nil { 619 return err 620 } 621 622 *result = &config 623 return nil 624 } 625 626 func parseTelemetry(result **Telemetry, list *ast.ObjectList) error { 627 list = list.Elem() 628 if len(list.Items) > 1 { 629 return fmt.Errorf("only one 'telemetry' block allowed") 630 } 631 632 // Get our telemetry object 633 listVal := list.Items[0].Val 634 635 // Check for invalid keys 636 valid := []string{ 637 "statsite_address", 638 "statsd_address", 639 "disable_hostname", 640 "use_node_name", 641 "collection_interval", 642 "publish_allocation_metrics", 643 "publish_node_metrics", 644 "datadog_address", 645 "datadog_tags", 646 "prometheus_metrics", 647 "circonus_api_token", 648 "circonus_api_app", 649 "circonus_api_url", 650 "circonus_submission_interval", 651 "circonus_submission_url", 652 "circonus_check_id", 653 "circonus_check_force_metric_activation", 654 "circonus_check_instance_id", 655 "circonus_check_search_tag", 656 "circonus_check_display_name", 657 "circonus_check_tags", 658 "circonus_broker_id", 659 "circonus_broker_select_tag", 660 "disable_tagged_metrics", 661 "backwards_compatible_metrics", 662 } 663 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 664 return err 665 } 666 667 var m map[string]interface{} 668 if err := hcl.DecodeObject(&m, listVal); err != nil { 669 return err 670 } 671 672 var telemetry Telemetry 673 if err := mapstructure.WeakDecode(m, &telemetry); err != nil { 674 return err 675 } 676 if telemetry.CollectionInterval != "" { 677 if dur, err := time.ParseDuration(telemetry.CollectionInterval); err != nil { 678 return fmt.Errorf("error parsing value of %q: %v", "collection_interval", err) 679 } else { 680 telemetry.collectionInterval = dur 681 } 682 } 683 *result = &telemetry 684 return nil 685 } 686 687 func parseConsulConfig(result **config.ConsulConfig, list *ast.ObjectList) error { 688 list = list.Elem() 689 if len(list.Items) > 1 { 690 return fmt.Errorf("only one 'consul' block allowed") 691 } 692 693 // Get our Consul object 694 listVal := list.Items[0].Val 695 696 // Check for invalid keys 697 valid := []string{ 698 "address", 699 "auth", 700 "auto_advertise", 701 "ca_file", 702 "cert_file", 703 "checks_use_advertise", 704 "client_auto_join", 705 "client_service_name", 706 "client_http_check_name", 707 "key_file", 708 "server_auto_join", 709 "server_service_name", 710 "server_http_check_name", 711 "server_serf_check_name", 712 "server_rpc_check_name", 713 "ssl", 714 "timeout", 715 "token", 716 "verify_ssl", 717 } 718 719 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 720 return err 721 } 722 723 var m map[string]interface{} 724 if err := hcl.DecodeObject(&m, listVal); err != nil { 725 return err 726 } 727 728 consulConfig := config.DefaultConsulConfig() 729 dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 730 DecodeHook: mapstructure.StringToTimeDurationHookFunc(), 731 WeaklyTypedInput: true, 732 Result: &consulConfig, 733 }) 734 if err != nil { 735 return err 736 } 737 if err := dec.Decode(m); err != nil { 738 return err 739 } 740 741 *result = consulConfig 742 return nil 743 } 744 745 func parseTLSConfig(result **config.TLSConfig, list *ast.ObjectList) error { 746 list = list.Elem() 747 if len(list.Items) > 1 { 748 return fmt.Errorf("only one 'tls' block allowed") 749 } 750 751 // Get the TLS object 752 listVal := list.Items[0].Val 753 754 valid := []string{ 755 "http", 756 "rpc", 757 "verify_server_hostname", 758 "rpc_upgrade_mode", 759 "ca_file", 760 "cert_file", 761 "key_file", 762 "verify_https_client", 763 } 764 765 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 766 return err 767 } 768 769 var m map[string]interface{} 770 if err := hcl.DecodeObject(&m, listVal); err != nil { 771 return err 772 } 773 774 var tlsConfig config.TLSConfig 775 if err := mapstructure.WeakDecode(m, &tlsConfig); err != nil { 776 return err 777 } 778 779 *result = &tlsConfig 780 return nil 781 } 782 783 func parseVaultConfig(result **config.VaultConfig, list *ast.ObjectList) error { 784 list = list.Elem() 785 if len(list.Items) > 1 { 786 return fmt.Errorf("only one 'vault' block allowed") 787 } 788 789 // Get our Vault object 790 listVal := list.Items[0].Val 791 792 // Check for invalid keys 793 valid := []string{ 794 "address", 795 "allow_unauthenticated", 796 "enabled", 797 "task_token_ttl", 798 "ca_file", 799 "ca_path", 800 "cert_file", 801 "create_from_role", 802 "key_file", 803 "tls_server_name", 804 "tls_skip_verify", 805 "token", 806 } 807 808 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 809 return err 810 } 811 812 var m map[string]interface{} 813 if err := hcl.DecodeObject(&m, listVal); err != nil { 814 return err 815 } 816 817 vaultConfig := config.DefaultVaultConfig() 818 dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 819 DecodeHook: mapstructure.StringToTimeDurationHookFunc(), 820 WeaklyTypedInput: true, 821 Result: &vaultConfig, 822 }) 823 if err != nil { 824 return err 825 } 826 if err := dec.Decode(m); err != nil { 827 return err 828 } 829 830 *result = vaultConfig 831 return nil 832 } 833 834 func parseSentinel(result **config.SentinelConfig, list *ast.ObjectList) error { 835 list = list.Elem() 836 if len(list.Items) > 1 { 837 return fmt.Errorf("only one 'sentinel' block allowed") 838 } 839 840 // Get our sentinel object 841 obj := list.Items[0] 842 843 // Value should be an object 844 var listVal *ast.ObjectList 845 if ot, ok := obj.Val.(*ast.ObjectType); ok { 846 listVal = ot.List 847 } else { 848 return fmt.Errorf("sentinel value: should be an object") 849 } 850 851 // Check for invalid keys 852 valid := []string{ 853 "import", 854 } 855 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 856 return err 857 } 858 859 var config config.SentinelConfig 860 if err := hcl.DecodeObject(&config, listVal); err != nil { 861 return err 862 } 863 864 *result = &config 865 return nil 866 } 867 868 func parseAutopilot(result **config.AutopilotConfig, list *ast.ObjectList) error { 869 list = list.Elem() 870 if len(list.Items) > 1 { 871 return fmt.Errorf("only one 'autopilot' block allowed") 872 } 873 874 // Get our Autopilot object 875 listVal := list.Items[0].Val 876 877 // Check for invalid keys 878 valid := []string{ 879 "cleanup_dead_servers", 880 "server_stabilization_time", 881 "last_contact_threshold", 882 "max_trailing_logs", 883 "enable_redundancy_zones", 884 "disable_upgrade_migration", 885 "enable_custom_upgrades", 886 } 887 888 if err := helper.CheckHCLKeys(listVal, valid); err != nil { 889 return err 890 } 891 892 var m map[string]interface{} 893 if err := hcl.DecodeObject(&m, listVal); err != nil { 894 return err 895 } 896 897 autopilotConfig := config.DefaultAutopilotConfig() 898 dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ 899 DecodeHook: mapstructure.StringToTimeDurationHookFunc(), 900 WeaklyTypedInput: true, 901 Result: &autopilotConfig, 902 }) 903 if err != nil { 904 return err 905 } 906 if err := dec.Decode(m); err != nil { 907 return err 908 } 909 910 *result = autopilotConfig 911 return nil 912 }