func parseRestartPolicy(final **structs.RestartPolicy, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) > 1 { return fmt.Errorf("only one 'restart' block allowed") } // Get our job object obj := list.Items[0] var m map[string]interface{} if err := hcl.DecodeObject(&m, obj.Val); err != nil { return err } var result structs.RestartPolicy dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.StringToTimeDurationHookFunc(), WeaklyTypedInput: true, Result: &result, }) if err != nil { return err } if err := dec.Decode(m); err != nil { return err } *final = &result return nil }
func parseUpdate(result *structs.UpdateStrategy, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) > 1 { return fmt.Errorf("only one 'update' block allowed per job") } // Get our resource object o := list.Items[0] var m map[string]interface{} if err := hcl.DecodeObject(&m, o.Val); err != nil { return err } // Check for invalid keys valid := []string{ "stagger", "max_parallel", } if err := checkHCLKeys(o.Val, valid); err != nil { return err } dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.StringToTimeDurationHookFunc(), WeaklyTypedInput: true, Result: result, }) if err != nil { return err } return dec.Decode(m) }
func parseTelemetry(result **Telemetry, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) > 1 { return fmt.Errorf("only one 'telemetry' block allowed") } // Get our telemetry object listVal := list.Items[0].Val // Check for invalid keys valid := []string{ "statsite_address", "statsd_address", "disable_hostname", } if err := checkHCLKeys(listVal, valid); err != nil { return err } var m map[string]interface{} if err := hcl.DecodeObject(&m, listVal); err != nil { return err } var telemetry Telemetry if err := mapstructure.WeakDecode(m, &telemetry); err != nil { return err } *result = &telemetry return nil }
func parseLocalDisk(result **structs.LocalDisk, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) > 1 { return fmt.Errorf("only one 'local_disk' block allowed") } // Get our local_disk object obj := list.Items[0] // Check for invalid keys valid := []string{ "sticky", "disk", } if err := checkHCLKeys(obj.Val, valid); err != nil { return err } var m map[string]interface{} if err := hcl.DecodeObject(&m, obj.Val); err != nil { return err } var localDisk structs.LocalDisk if err := mapstructure.WeakDecode(m, &localDisk); err != nil { return err } *result = &localDisk return nil }
func parseAtlas(result **AtlasConfig, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) > 1 { return fmt.Errorf("only one 'atlas' block allowed") } // Get our atlas object listVal := list.Items[0].Val // Check for invalid keys valid := []string{ "infrastructure", "token", "join", "endpoint", } if err := checkHCLKeys(listVal, valid); err != nil { return err } var m map[string]interface{} if err := hcl.DecodeObject(&m, listVal); err != nil { return err } var atlas AtlasConfig if err := mapstructure.WeakDecode(m, &atlas); err != nil { return err } *result = &atlas return nil }
func parseAdvertise(result **AdvertiseAddrs, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) > 1 { return fmt.Errorf("only one 'advertise' block allowed") } // Get our advertise object listVal := list.Items[0].Val // Check for invalid keys valid := []string{ "http", "rpc", "serf", } if err := checkHCLKeys(listVal, valid); err != nil { return err } var m map[string]interface{} if err := hcl.DecodeObject(&m, listVal); err != nil { return err } var advertise AdvertiseAddrs if err := mapstructure.WeakDecode(m, &advertise); err != nil { return err } *result = &advertise return nil }
func parseResources(result *structs.Resources, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) == 0 { return nil } if len(list.Items) > 1 { return fmt.Errorf("only one 'resource' block allowed per task") } // Get our resource object o := list.Items[0] // We need this later var listVal *ast.ObjectList if ot, ok := o.Val.(*ast.ObjectType); ok { listVal = ot.List } else { return fmt.Errorf("resource: should be an object") } var m map[string]interface{} if err := hcl.DecodeObject(&m, o.Val); err != nil { return err } delete(m, "network") if err := mapstructure.WeakDecode(m, result); err != nil { return err } // Parse the network resources if o := listVal.Filter("network"); len(o.Items) > 0 { if len(o.Items) > 1 { return fmt.Errorf("only one 'network' resource allowed") } var r structs.NetworkResource var m map[string]interface{} if err := hcl.DecodeObject(&m, o.Items[0].Val); err != nil { return err } if err := mapstructure.WeakDecode(m, &r); err != nil { return err } var networkObj *ast.ObjectList if ot, ok := o.Items[0].Val.(*ast.ObjectType); ok { networkObj = ot.List } else { return fmt.Errorf("resource: should be an object") } if err := parsePorts(networkObj, &r); err != nil { return err } result.Networks = []*structs.NetworkResource{&r} } return nil }
func parseTLSConfig(result **config.TLSConfig, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) > 1 { return fmt.Errorf("only one 'tls' block allowed") } // Get the TLS object listVal := list.Items[0].Val valid := []string{ "http", "rpc", "verify_server_hostname", "ca_file", "cert_file", "key_file", } if err := checkHCLKeys(listVal, valid); err != nil { return err } var m map[string]interface{} if err := hcl.DecodeObject(&m, listVal); err != nil { return err } var tlsConfig config.TLSConfig if err := mapstructure.WeakDecode(m, &tlsConfig); err != nil { return err } *result = &tlsConfig return nil }
func parseTelemetry(result **Telemetry, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) > 1 { return fmt.Errorf("only one 'telemetry' block allowed") } // Get our telemetry object listVal := list.Items[0].Val // Check for invalid keys valid := []string{ "statsite_address", "statsd_address", "disable_hostname", "collection_interval", "publish_allocation_metrics", "publish_node_metrics", "datadog_address", "circonus_api_token", "circonus_api_app", "circonus_api_url", "circonus_submission_interval", "circonus_submission_url", "circonus_check_id", "circonus_check_force_metric_activation", "circonus_check_instance_id", "circonus_check_search_tag", "circonus_check_display_name", "circonus_check_tags", "circonus_broker_id", "circonus_broker_select_tag", } if err := checkHCLKeys(listVal, valid); err != nil { return err } var m map[string]interface{} if err := hcl.DecodeObject(&m, listVal); err != nil { return err } var telemetry Telemetry if err := mapstructure.WeakDecode(m, &telemetry); err != nil { return err } if telemetry.CollectionInterval != "" { if dur, err := time.ParseDuration(telemetry.CollectionInterval); err != nil { return fmt.Errorf("error parsing value of %q: %v", "collection_interval", err) } else { telemetry.collectionInterval = dur } } *result = &telemetry return nil }
func parseConsulConfig(result **config.ConsulConfig, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) > 1 { return fmt.Errorf("only one 'consul' block allowed") } // Get our Consul object listVal := list.Items[0].Val // Check for invalid keys valid := []string{ "address", "auth", "auto_advertise", "ca_file", "cert_file", "checks_use_advertise", "client_auto_join", "client_service_name", "key_file", "server_auto_join", "server_service_name", "ssl", "timeout", "token", "verify_ssl", } if err := checkHCLKeys(listVal, valid); err != nil { return err } var m map[string]interface{} if err := hcl.DecodeObject(&m, listVal); err != nil { return err } consulConfig := config.DefaultConsulConfig() dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.StringToTimeDurationHookFunc(), WeaklyTypedInput: true, Result: &consulConfig, }) if err != nil { return err } if err := dec.Decode(m); err != nil { return err } *result = consulConfig return nil }
func parseConstraints(result *[]*structs.Constraint, list *ast.ObjectList) error { for _, o := range list.Elem().Items { var m map[string]interface{} if err := hcl.DecodeObject(&m, o.Val); err != nil { return err } m["LTarget"] = m["attribute"] m["RTarget"] = m["value"] m["Operand"] = m["operator"] // If "version" is provided, set the operand // to "version" and the value to the "RTarget" if constraint, ok := m[structs.ConstraintVersion]; ok { m["Operand"] = structs.ConstraintVersion m["RTarget"] = constraint } // If "regexp" is provided, set the operand // to "regexp" and the value to the "RTarget" if constraint, ok := m[structs.ConstraintRegex]; ok { m["Operand"] = structs.ConstraintRegex m["RTarget"] = constraint } if value, ok := m[structs.ConstraintDistinctHosts]; ok { enabled, err := parseBool(value) if err != nil { return fmt.Errorf("distinct_hosts should be set to true or false; %v", err) } // If it is not enabled, skip the constraint. if !enabled { continue } m["Operand"] = structs.ConstraintDistinctHosts } // Build the constraint var c structs.Constraint if err := mapstructure.WeakDecode(m, &c); err != nil { return err } if c.Operand == "" { c.Operand = "=" } *result = append(*result, &c) } return nil }
func parseServer(result **ServerConfig, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) > 1 { return fmt.Errorf("only one 'server' block allowed") } // Get our server object obj := list.Items[0] // Value should be an object var listVal *ast.ObjectList if ot, ok := obj.Val.(*ast.ObjectType); ok { listVal = ot.List } else { return fmt.Errorf("client value: should be an object") } // Check for invalid keys valid := []string{ "enabled", "bootstrap_expect", "data_dir", "protocol_version", "num_schedulers", "enabled_schedulers", "node_gc_threshold", "heartbeat_grace", "start_join", "retry_join", "retry_max", "retry_interval", "rejoin_after_leave", "encrypt", } if err := checkHCLKeys(listVal, valid); err != nil { return err } var m map[string]interface{} if err := hcl.DecodeObject(&m, listVal); err != nil { return err } var config ServerConfig if err := mapstructure.WeakDecode(m, &config); err != nil { return err } *result = &config return nil }
func parsePeriodic(result **structs.PeriodicConfig, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) > 1 { return fmt.Errorf("only one 'periodic' block allowed per job") } // Get our resource object o := list.Items[0] var m map[string]interface{} if err := hcl.DecodeObject(&m, o.Val); err != nil { return err } // Check for invalid keys valid := []string{ "enabled", "cron", "prohibit_overlap", } if err := checkHCLKeys(o.Val, valid); err != nil { return err } // Enabled by default if the periodic block exists. if value, ok := m["enabled"]; !ok { m["Enabled"] = true } else { enabled, err := parseBool(value) if err != nil { return fmt.Errorf("periodic.enabled should be set to true or false; %v", err) } m["Enabled"] = enabled } // If "cron" is provided, set the type to "cron" and store the spec. if cron, ok := m["cron"]; ok { m["SpecType"] = structs.PeriodicSpecCron m["Spec"] = cron } // Build the constraint var p structs.PeriodicConfig if err := mapstructure.WeakDecode(m, &p); err != nil { return err } *result = &p return nil }
func parseArtifacts(result *[]*structs.TaskArtifact, list *ast.ObjectList) error { for _, o := range list.Elem().Items { // Check for invalid keys valid := []string{ "source", "options", "destination", } if err := checkHCLKeys(o.Val, valid); err != nil { return err } var m map[string]interface{} if err := hcl.DecodeObject(&m, o.Val); err != nil { return err } delete(m, "options") // Default to downloading to the local directory. if _, ok := m["destination"]; !ok { m["destination"] = "local/" } var ta structs.TaskArtifact if err := mapstructure.WeakDecode(m, &ta); err != nil { return err } var optionList *ast.ObjectList if ot, ok := o.Val.(*ast.ObjectType); ok { optionList = ot.List } else { return fmt.Errorf("artifact should be an object") } if oo := optionList.Filter("options"); len(oo.Items) > 0 { options := make(map[string]string) if err := parseArtifactOption(options, oo); err != nil { return multierror.Prefix(err, "options: ") } ta.GetterOptions = options } *result = append(*result, &ta) } return nil }
func parseVaultConfig(result **config.VaultConfig, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) > 1 { return fmt.Errorf("only one 'vault' block allowed") } // Get our Vault object listVal := list.Items[0].Val // Check for invalid keys valid := []string{ "address", "allow_unauthenticated", "enabled", "task_token_ttl", "ca_file", "ca_path", "cert_file", "key_file", "tls_server_name", "tls_skip_verify", "token", } if err := checkHCLKeys(listVal, valid); err != nil { return err } var m map[string]interface{} if err := hcl.DecodeObject(&m, listVal); err != nil { return err } vaultConfig := config.DefaultVaultConfig() dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.StringToTimeDurationHookFunc(), WeaklyTypedInput: true, Result: &vaultConfig, }) if err != nil { return err } if err := dec.Decode(m); err != nil { return err } *result = vaultConfig return nil }
func parseReserved(result **Resources, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) > 1 { return fmt.Errorf("only one 'reserved' block allowed") } // Get our reserved object obj := list.Items[0] // Value should be an object var listVal *ast.ObjectList if ot, ok := obj.Val.(*ast.ObjectType); ok { listVal = ot.List } else { return fmt.Errorf("client value: should be an object") } // Check for invalid keys valid := []string{ "cpu", "memory", "disk", "iops", "reserved_ports", } if err := checkHCLKeys(listVal, valid); err != nil { return err } var m map[string]interface{} if err := hcl.DecodeObject(&m, listVal); err != nil { return err } var reserved Resources if err := mapstructure.WeakDecode(m, &reserved); err != nil { return err } if err := reserved.ParseReserved(); err != nil { return err } *result = &reserved return nil }
func parseVault(result *structs.Vault, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) == 0 { return nil } if len(list.Items) > 1 { return fmt.Errorf("only one 'vault' block allowed per task") } // Get our resource object o := list.Items[0] // We need this later var listVal *ast.ObjectList if ot, ok := o.Val.(*ast.ObjectType); ok { listVal = ot.List } else { return fmt.Errorf("vault: should be an object") } // Check for invalid keys valid := []string{ "policies", "env", } if err := checkHCLKeys(listVal, valid); err != nil { return multierror.Prefix(err, "vault ->") } var m map[string]interface{} if err := hcl.DecodeObject(&m, o.Val); err != nil { return err } // Default the env bool if _, ok := m["env"]; !ok { m["env"] = true } if err := mapstructure.WeakDecode(m, result); err != nil { return err } return nil }
func ParseStringList(o *ast.ObjectList, context string) ([]string, error) { result := []string{} for _, o := range o.Elem().Items { if olit, ok := o.Val.(*ast.LiteralType); ok && olit.Token.Type == token.STRING { result = append(result, olit.Token.Value().(string)) } else if list, ok := o.Val.(*ast.ListType); ok { for _, n := range list.List { if olit, ok := n.(*ast.LiteralType); ok && olit.Token.Type == token.STRING { result = append(result, olit.Token.Value().(string)) } else { return nil, maskAny(errgo.WithCausef(nil, ValidationError, "element of %s is not a string but %v", context, n)) } } } else { return nil, maskAny(errgo.WithCausef(nil, ValidationError, "%s is not a string or array", context)) } } return result, nil }
func parseConsulConfig(result **ConsulConfig, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) > 1 { return fmt.Errorf("only one 'consul' block allowed") } // Get our consul object listVal := list.Items[0].Val // Check for invalid keys valid := []string{ "server_service_name", "client_service_name", "auto_register", "addr", "token", "auth", "ssl", "verify_ssl", "ca_file", "cert_file", "key_file", "client_auto_join", "server_auto_join", } if err := checkHCLKeys(listVal, valid); err != nil { return err } var m map[string]interface{} if err := hcl.DecodeObject(&m, listVal); err != nil { return err } var consulConfig ConsulConfig if err := mapstructure.WeakDecode(m, &consulConfig); err != nil { return err } *result = &consulConfig return nil }
func parseArtifactOption(result map[string]string, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) > 1 { return fmt.Errorf("only one 'options' block allowed per artifact") } // Get our resource object o := list.Items[0] var m map[string]interface{} if err := hcl.DecodeObject(&m, o.Val); err != nil { return err } if err := mapstructure.WeakDecode(m, &result); err != nil { return err } return nil }
func parseTemplates(result *[]*structs.Template, list *ast.ObjectList) error { for _, o := range list.Elem().Items { // Check for invalid keys valid := []string{ "source", "destination", "data", "change_mode", "change_signal", "splay", "once", } if err := checkHCLKeys(o.Val, valid); err != nil { return err } var m map[string]interface{} if err := hcl.DecodeObject(&m, o.Val); err != nil { return err } templ := structs.DefaultTemplate() dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.StringToTimeDurationHookFunc(), WeaklyTypedInput: true, Result: templ, }) if err != nil { return err } if err := dec.Decode(m); err != nil { return err } *result = append(*result, templ) } return nil }
func parseStats(result **StatsConfig, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) > 1 { return fmt.Errorf("only one 'stats' block allowed") } // Get our stats object obj := list.Items[0] var listVal *ast.ObjectList if ot, ok := obj.Val.(*ast.ObjectType); ok { listVal = ot.List } else { return fmt.Errorf("client value: should be an object") } // check for invalid keys valid := []string{ "data_points", "collection_interval", } if err := checkHCLKeys(listVal, valid); err != nil { return err } var m map[string]interface{} if err := hcl.DecodeObject(&m, listVal); err != nil { return err } var stats StatsConfig if err := mapstructure.WeakDecode(m, &stats); err != nil { return err } *result = &stats return nil }
func parseResources(result *structs.Resources, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) == 0 { return nil } if len(list.Items) > 1 { return fmt.Errorf("only one 'resource' block allowed per task") } // Get our resource object o := list.Items[0] // We need this later var listVal *ast.ObjectList if ot, ok := o.Val.(*ast.ObjectType); ok { listVal = ot.List } else { return fmt.Errorf("resource: should be an object") } var m map[string]interface{} if err := hcl.DecodeObject(&m, o.Val); err != nil { return err } delete(m, "network") if err := mapstructure.WeakDecode(m, result); err != nil { return err } // Parse the network resources if o := listVal.Filter("network"); len(o.Items) > 0 { if len(o.Items) > 1 { return fmt.Errorf("only one 'network' resource allowed") } var r structs.NetworkResource var m map[string]interface{} if err := hcl.DecodeObject(&m, o.Items[0].Val); err != nil { return err } if err := mapstructure.WeakDecode(m, &r); err != nil { return err } // Keep track of labels we've already seen so we can ensure there // are no collisions when we turn them into environment variables. // lowercase:NomalCase so we can get the first for the error message seenLabel := map[string]string{} for _, label := range r.DynamicPorts { if !reDynamicPorts.MatchString(label) { return errDynamicPorts } first, seen := seenLabel[strings.ToLower(label)] if seen { return fmt.Errorf("Found a port label collision: `%s` overlaps with previous `%s`", label, first) } else { seenLabel[strings.ToLower(label)] = label } } result.Networks = []*structs.NetworkResource{&r} } return nil }
func parseResources(result *structs.Resources, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) == 0 { return nil } if len(list.Items) > 1 { return fmt.Errorf("only one 'resource' block allowed per task") } // Get our resource object o := list.Items[0] // We need this later var listVal *ast.ObjectList if ot, ok := o.Val.(*ast.ObjectType); ok { listVal = ot.List } else { return fmt.Errorf("resource: should be an object") } // Check for invalid keys valid := []string{ "cpu", "iops", "memory", "network", } if err := checkHCLKeys(listVal, valid); err != nil { return multierror.Prefix(err, "resources ->") } var m map[string]interface{} if err := hcl.DecodeObject(&m, o.Val); err != nil { return err } delete(m, "network") if err := mapstructure.WeakDecode(m, result); err != nil { return err } // Parse the network resources if o := listVal.Filter("network"); len(o.Items) > 0 { if len(o.Items) > 1 { return fmt.Errorf("only one 'network' resource allowed") } // Check for invalid keys valid := []string{ "mbits", "port", } if err := checkHCLKeys(o.Items[0].Val, valid); err != nil { return multierror.Prefix(err, "resources, network ->") } var r structs.NetworkResource var m map[string]interface{} if err := hcl.DecodeObject(&m, o.Items[0].Val); err != nil { return err } if err := mapstructure.WeakDecode(m, &r); err != nil { return err } var networkObj *ast.ObjectList if ot, ok := o.Items[0].Val.(*ast.ObjectType); ok { networkObj = ot.List } else { return fmt.Errorf("resource: should be an object") } if err := parsePorts(networkObj, &r); err != nil { return multierror.Prefix(err, "resources, network, ports ->") } result.Networks = []*structs.NetworkResource{&r} } // Combine the parsed resources with a default resource block. min := structs.DefaultResources() min.Merge(result) *result = *min return nil }
func parseClient(result **ClientConfig, list *ast.ObjectList) error { list = list.Elem() if len(list.Items) > 1 { return fmt.Errorf("only one 'client' block allowed") } // Get our client object obj := list.Items[0] // Value should be an object var listVal *ast.ObjectList if ot, ok := obj.Val.(*ast.ObjectType); ok { listVal = ot.List } else { return fmt.Errorf("client value: should be an object") } // Check for invalid keys valid := []string{ "enabled", "state_dir", "alloc_dir", "servers", "node_class", "options", "meta", "network_interface", "network_speed", "max_kill_timeout", "client_max_port", "client_min_port", "reserved", "stats", } if err := checkHCLKeys(listVal, valid); err != nil { return err } var m map[string]interface{} if err := hcl.DecodeObject(&m, listVal); err != nil { return err } delete(m, "options") delete(m, "meta") delete(m, "reserved") delete(m, "stats") var config ClientConfig if err := mapstructure.WeakDecode(m, &config); err != nil { return err } // Parse out options fields. These are in HCL as a list so we need to // iterate over them and merge them. if optionsO := listVal.Filter("options"); len(optionsO.Items) > 0 { for _, o := range optionsO.Elem().Items { var m map[string]interface{} if err := hcl.DecodeObject(&m, o.Val); err != nil { return err } if err := mapstructure.WeakDecode(m, &config.Options); err != nil { return err } } } // Parse out options meta. These are in HCL as a list so we need to // iterate over them and merge them. if metaO := listVal.Filter("meta"); len(metaO.Items) > 0 { for _, o := range metaO.Elem().Items { var m map[string]interface{} if err := hcl.DecodeObject(&m, o.Val); err != nil { return err } if err := mapstructure.WeakDecode(m, &config.Meta); err != nil { return err } } } // Parse reserved config if o := listVal.Filter("reserved"); len(o.Items) > 0 { if err := parseReserved(&config.Reserved, o); err != nil { return multierror.Prefix(err, "reserved ->") } } // Parse stats config if o := listVal.Filter("stats"); len(o.Items) > 0 { if err := parseStats(&config.StatsConfig, o); err != nil { return multierror.Prefix(err, "stats ->") } } *result = &config return nil }