// Start starts the mock driver func (m *MockDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { var driverConfig MockDriverConfig dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.StringToTimeDurationHookFunc(), WeaklyTypedInput: true, Result: &driverConfig, }) if err != nil { return nil, err } if err := dec.Decode(task.Config); err != nil { return nil, err } h := mockDriverHandle{ taskName: task.Name, runFor: driverConfig.RunFor, killAfter: driverConfig.KillAfter, killTimeout: task.KillTimeout, exitCode: driverConfig.ExitCode, exitSignal: driverConfig.ExitSignal, logger: m.logger, doneCh: make(chan struct{}), waitCh: make(chan *dstructs.WaitResult, 1), } if driverConfig.ExitErrMsg != "" { h.exitErr = errors.New(driverConfig.ExitErrMsg) } m.logger.Printf("[DEBUG] driver.mock: starting task %q", task.Name) go h.run() return &h, nil }
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 }
// customDecodeHook adds the additional functions of parsing durations from strings // as well as parsing strings of the format "[thing1, thing2, thing3]" into string slices // Note that whitespace around slice elements is removed func customDecodeHook() mapstructure.DecodeHookFunc { durationHook := mapstructure.StringToTimeDurationHookFunc() return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { dur, err := mapstructure.DecodeHookExec(durationHook, f, t, data) if err == nil { if _, ok := dur.(time.Duration); ok { return dur, nil } } if f.Kind() != reflect.String { return data, nil } raw := data.(string) l := len(raw) if raw[0] == '[' && raw[l-1] == ']' { slice := strings.Split(raw[1:l-1], ",") for i, v := range slice { slice[i] = strings.TrimSpace(v) } return slice, nil } return data, 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 parseChecks(service *structs.Service, checkObjs *ast.ObjectList) error { service.Checks = make([]*structs.ServiceCheck, len(checkObjs.Items)) for idx, co := range checkObjs.Items { var check structs.ServiceCheck var cm map[string]interface{} if err := hcl.DecodeObject(&cm, co.Val); err != nil { return err } dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.StringToTimeDurationHookFunc(), WeaklyTypedInput: true, Result: &check, }) if err != nil { return err } if err := dec.Decode(cm); err != nil { return err } service.Checks[idx] = &check } return nil }
// defaultDecoderConfig returns default mapsstructure.DecoderConfig with suppot // of time.Duration values func defaultDecoderConfig(output interface{}) *mapstructure.DecoderConfig { return &mapstructure.DecoderConfig{ Metadata: nil, Result: output, WeaklyTypedInput: true, DecodeHook: mapstructure.StringToTimeDurationHookFunc(), } }
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 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 (r *rawTemplate) decoder( result interface{}, md *mapstructure.Metadata) *mapstructure.Decoder { d, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.StringToTimeDurationHookFunc(), Metadata: md, Result: result, }) if err != nil { // This really shouldn't happen since we have firm control over // all the arguments and they're all unit tested. So we use a // panic here to note this would definitely be a bug. panic(err) } return d }
func transform(source map[string]interface{}, target interface{}) error { data := mapstructure.Metadata{} config := &mapstructure.DecoderConfig{ DecodeHook: mapstructure.ComposeDecodeHookFunc( transformHook, mapstructure.StringToTimeDurationHookFunc()), Result: target, Metadata: &data, } decoder, err := mapstructure.NewDecoder(config) if err != nil { return err } err = decoder.Decode(source) // TODO: log unused keys return err }
func unmarshal(rawVal interface{}) error { config := &mapstructure.DecoderConfig{ DecodeHook: mapstructure.StringToTimeDurationHookFunc(), Metadata: nil, Result: rawVal, WeaklyTypedInput: true, } decoder, err := mapstructure.NewDecoder(config) if err != nil { return err } err = decoder.Decode(viper.AllSettings()) if err != nil { return err } return nil }
func parseChecks(service *structs.Service, checkObjs *ast.ObjectList) error { service.Checks = make([]*structs.ServiceCheck, len(checkObjs.Items)) for idx, co := range checkObjs.Items { // Check for invalid keys valid := []string{ "name", "type", "interval", "timeout", "path", "protocol", "port", "command", "args", "initial_status", } if err := checkHCLKeys(co.Val, valid); err != nil { return multierror.Prefix(err, "check ->") } var check structs.ServiceCheck var cm map[string]interface{} if err := hcl.DecodeObject(&cm, co.Val); err != nil { return err } dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.StringToTimeDurationHookFunc(), WeaklyTypedInput: true, Result: &check, }) if err != nil { return err } if err := dec.Decode(cm); err != nil { return err } service.Checks[idx] = &check } 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 parseTasks(jobName string, taskGroupName string, result *[]*structs.Task, list *ast.ObjectList) error { list = list.Children() if len(list.Items) == 0 { return nil } // Go through each object and turn it into an actual result. seen := make(map[string]struct{}) for _, item := range list.Items { n := item.Keys[0].Token.Value().(string) // Make sure we haven't already found this if _, ok := seen[n]; ok { return fmt.Errorf("task '%s' defined more than once", n) } seen[n] = struct{}{} // We need this later var listVal *ast.ObjectList if ot, ok := item.Val.(*ast.ObjectType); ok { listVal = ot.List } else { return fmt.Errorf("group '%s': should be an object", n) } var m map[string]interface{} if err := hcl.DecodeObject(&m, item.Val); err != nil { return err } delete(m, "config") delete(m, "env") delete(m, "constraint") delete(m, "service") delete(m, "meta") delete(m, "resources") // Build the task var t structs.Task t.Name = n if taskGroupName == "" { taskGroupName = n } dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.StringToTimeDurationHookFunc(), WeaklyTypedInput: true, Result: &t, }) if err != nil { return err } if err := dec.Decode(m); err != nil { return err } // If we have env, then parse them if o := listVal.Filter("env"); len(o.Items) > 0 { for _, o := range o.Elem().Items { var m map[string]interface{} if err := hcl.DecodeObject(&m, o.Val); err != nil { return err } if err := mapstructure.WeakDecode(m, &t.Env); err != nil { return err } } } if o := listVal.Filter("service"); len(o.Items) > 0 { if err := parseServices(jobName, taskGroupName, &t, o); err != nil { return err } } // If we have config, then parse that if o := listVal.Filter("config"); len(o.Items) > 0 { for _, o := range o.Elem().Items { var m map[string]interface{} if err := hcl.DecodeObject(&m, o.Val); err != nil { return err } if err := mapstructure.WeakDecode(m, &t.Config); err != nil { return err } } } // Parse constraints if o := listVal.Filter("constraint"); len(o.Items) > 0 { if err := parseConstraints(&t.Constraints, o); err != nil { return err } } // Parse out meta fields. 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, &t.Meta); err != nil { return err } } } // If we have resources, then parse that if o := listVal.Filter("resources"); len(o.Items) > 0 { var r structs.Resources if err := parseResources(&r, o); err != nil { return fmt.Errorf("task '%s': %s", t.Name, err) } t.Resources = &r } *result = append(*result, &t) } return nil }
func parseTasks(jobName string, taskGroupName string, result *[]*structs.Task, list *ast.ObjectList) error { list = list.Children() if len(list.Items) == 0 { return nil } // Go through each object and turn it into an actual result. seen := make(map[string]struct{}) for _, item := range list.Items { n := item.Keys[0].Token.Value().(string) // Make sure we haven't already found this if _, ok := seen[n]; ok { return fmt.Errorf("task '%s' defined more than once", n) } seen[n] = struct{}{} // We need this later var listVal *ast.ObjectList if ot, ok := item.Val.(*ast.ObjectType); ok { listVal = ot.List } else { return fmt.Errorf("group '%s': should be an object", n) } // Check for invalid keys valid := []string{ "artifact", "config", "constraint", "driver", "env", "kill_timeout", "logs", "meta", "resources", "service", "user", "vault", } if err := checkHCLKeys(listVal, valid); err != nil { return multierror.Prefix(err, fmt.Sprintf("'%s' ->", n)) } var m map[string]interface{} if err := hcl.DecodeObject(&m, item.Val); err != nil { return err } delete(m, "artifact") delete(m, "config") delete(m, "constraint") delete(m, "env") delete(m, "logs") delete(m, "meta") delete(m, "resources") delete(m, "service") delete(m, "vault") // Build the task var t structs.Task t.Name = n if taskGroupName == "" { taskGroupName = n } dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.StringToTimeDurationHookFunc(), WeaklyTypedInput: true, Result: &t, }) if err != nil { return err } if err := dec.Decode(m); err != nil { return err } // If we have env, then parse them if o := listVal.Filter("env"); len(o.Items) > 0 { for _, o := range o.Elem().Items { var m map[string]interface{} if err := hcl.DecodeObject(&m, o.Val); err != nil { return err } if err := mapstructure.WeakDecode(m, &t.Env); err != nil { return err } } } if o := listVal.Filter("service"); len(o.Items) > 0 { if err := parseServices(jobName, taskGroupName, &t, o); err != nil { return multierror.Prefix(err, fmt.Sprintf("'%s',", n)) } } // If we have config, then parse that if o := listVal.Filter("config"); len(o.Items) > 0 { for _, o := range o.Elem().Items { var m map[string]interface{} if err := hcl.DecodeObject(&m, o.Val); err != nil { return err } if err := mapstructure.WeakDecode(m, &t.Config); err != nil { return err } } // Instantiate a driver to validate the configuration d, err := driver.NewDriver( t.Driver, driver.NewEmptyDriverContext(), ) if err != nil { return multierror.Prefix(err, fmt.Sprintf("'%s', config ->", n)) } if err := d.Validate(t.Config); err != nil { return multierror.Prefix(err, fmt.Sprintf("'%s', config ->", n)) } } // Parse constraints if o := listVal.Filter("constraint"); len(o.Items) > 0 { if err := parseConstraints(&t.Constraints, o); err != nil { return multierror.Prefix(err, fmt.Sprintf( "'%s', constraint ->", n)) } } // Parse out meta fields. 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, &t.Meta); err != nil { return err } } } // If we have resources, then parse that if o := listVal.Filter("resources"); len(o.Items) > 0 { var r structs.Resources if err := parseResources(&r, o); err != nil { return multierror.Prefix(err, fmt.Sprintf("'%s',", n)) } t.Resources = &r } // If we have logs then parse that logConfig := structs.DefaultLogConfig() if o := listVal.Filter("logs"); len(o.Items) > 0 { if len(o.Items) > 1 { return fmt.Errorf("only one logs block is allowed in a Task. Number of logs block found: %d", len(o.Items)) } var m map[string]interface{} logsBlock := o.Items[0] // Check for invalid keys valid := []string{ "max_files", "max_file_size", } if err := checkHCLKeys(logsBlock.Val, valid); err != nil { return multierror.Prefix(err, fmt.Sprintf("'%s', logs ->", n)) } if err := hcl.DecodeObject(&m, logsBlock.Val); err != nil { return err } if err := mapstructure.WeakDecode(m, &logConfig); err != nil { return err } } t.LogConfig = logConfig // Parse artifacts if o := listVal.Filter("artifact"); len(o.Items) > 0 { if err := parseArtifacts(&t.Artifacts, o); err != nil { return multierror.Prefix(err, fmt.Sprintf("'%s', artifact ->", n)) } } // If we have a vault block, then parse that if o := listVal.Filter("vault"); len(o.Items) > 0 { var v structs.Vault if err := parseVault(&v, o); err != nil { return multierror.Prefix(err, fmt.Sprintf("'%s', vault ->", n)) } t.Vault = &v } *result = append(*result, &t) } return nil }
// g reads the configuration file at the given path and returns a new // Config struct with the data populated. func ParseConfig(path string) (*Config, error) { var errs *multierror.Error // Read the contents of the file contents, err := ioutil.ReadFile(path) if err != nil { return nil, fmt.Errorf("error reading config at %q: %s", path, err) } // Parse the file (could be HCL or JSON) var shadow interface{} if err := hcl.Decode(&shadow, string(contents)); err != nil { return nil, fmt.Errorf("error decoding config at %q: %s", path, err) } // Convert to a map and flatten the keys we want to flatten parsed, ok := shadow.(map[string]interface{}) if !ok { return nil, fmt.Errorf("error converting config at %q", path) } flattenKeys(parsed, []string{"auth", "ssl", "syslog"}) // Create a new, empty config c := new(Config) // Use mapstructure to populate the basic config fields metadata := new(mapstructure.Metadata) decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.ComposeDecodeHookFunc( config.StringToWaitDurationHookFunc(), mapstructure.StringToSliceHookFunc(","), mapstructure.StringToTimeDurationHookFunc(), ), ErrorUnused: true, Metadata: metadata, Result: c, }) if err != nil { errs = multierror.Append(errs, err) return nil, errs.ErrorOrNil() } if err := decoder.Decode(parsed); err != nil { errs = multierror.Append(errs, err) return nil, errs.ErrorOrNil() } // Store a reference to the path where this config was read from c.Path = path // Parse the prefix sources for _, prefix := range c.Prefixes { parsed, err := dep.NewKVListQuery(prefix.Source) if err != nil { errs = multierror.Append(errs, err) continue } prefix.Dependency = parsed // If no destination was given, default to the prefix if prefix.Destination == "" { prefix.Destination = prefix.Source } } // Update the list of set keys if c.setKeys == nil { c.setKeys = make(map[string]struct{}) } for _, key := range metadata.Keys { if _, ok := c.setKeys[key]; !ok { c.setKeys[key] = struct{}{} } } c.setKeys["path"] = struct{}{} d := DefaultConfig() d.Merge(c) c = d return c, errs.ErrorOrNil() }
// ParseConfig reads the configuration file at the given path and returns a new // Config struct with the data populated. func ParseConfig(path string) (*Config, error) { var errs *multierror.Error // Read the contents of the file contents, err := ioutil.ReadFile(path) if err != nil { return nil, fmt.Errorf("error reading config at %q: %s", path, err) } // Parse the file (could be HCL or JSON) var shadow interface{} if err := hcl.Decode(&shadow, string(contents)); err != nil { return nil, fmt.Errorf("error decoding config at %q: %s", path, err) } // Convert to a map and flatten the keys we want to flatten parsed, ok := shadow.(map[string]interface{}) if !ok { return nil, fmt.Errorf("error converting config at %q", path) } flattenKeys(parsed, []string{"auth", "ssl", "syslog"}) // Parse the prefixes if raw, ok := parsed["prefixes"]; ok { if typed, ok := raw.([]interface{}); !ok { err = fmt.Errorf("error converting prefixes to []interface{} at %q, was %T", path, raw) errs = multierror.Append(errs, err) delete(parsed, "prefixes") } else { prefixes := make([]*dep.StoreKeyPrefix, 0, len(typed)) for _, p := range typed { if s, ok := p.(string); ok { if prefix, err := dep.ParseStoreKeyPrefix(s); err != nil { err = fmt.Errorf("error parsing prefix %q at %q: %s", p, path, err) errs = multierror.Append(errs, err) } else { prefixes = append(prefixes, prefix) } } else { err = fmt.Errorf("error converting %T to string", p) errs = multierror.Append(errs, err) delete(parsed, "prefixes") } } parsed["prefixes"] = prefixes } } // Parse the wait component if raw, ok := parsed["wait"]; ok { if typed, ok := raw.(string); !ok { err = fmt.Errorf("error converting wait to string at %q", path) errs = multierror.Append(errs, err) delete(parsed, "wait") } else { if wait, err := watch.ParseWait(typed); err != nil { err = fmt.Errorf("error parsing wait at %q: %s", path, err) errs = multierror.Append(errs, err) delete(parsed, "wait") } else { parsed["wait"] = map[string]time.Duration{ "min": wait.Min, "max": wait.Max, } } } } // Create a new, empty config config := new(Config) // Use mapstructure to populate the basic config fields metadata := new(mapstructure.Metadata) decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.ComposeDecodeHookFunc( mapstructure.StringToSliceHookFunc(","), mapstructure.StringToTimeDurationHookFunc(), ), ErrorUnused: true, Metadata: metadata, Result: config, }) if err != nil { errs = multierror.Append(errs, err) return nil, errs.ErrorOrNil() } if err := decoder.Decode(parsed); err != nil { errs = multierror.Append(errs, err) return nil, errs.ErrorOrNil() } // Store a reference to the path where this config was read from config.Path = path // Update the list of set keys if config.setKeys == nil { config.setKeys = make(map[string]struct{}) } for _, key := range metadata.Keys { if _, ok := config.setKeys[key]; !ok { config.setKeys[key] = struct{}{} } } config.setKeys["path"] = struct{}{} d := DefaultConfig() d.Merge(config) config = d return config, errs.ErrorOrNil() }
// Parse parses the given string contents as a config func Parse(s string) (*Config, error) { var errs *multierror.Error // Parse the file (could be HCL or JSON) var shadow interface{} if err := hcl.Decode(&shadow, s); err != nil { return nil, fmt.Errorf("error decoding config: %s", err) } // Convert to a map and flatten the keys we want to flatten parsed, ok := shadow.(map[string]interface{}) if !ok { return nil, fmt.Errorf("error converting config") } flattenKeys(parsed, []string{ "auth", "ssl", "syslog", "exec", "vault", "deduplicate", }) // Deprecations if vault, ok := parsed["vault"].(map[string]interface{}); ok { if val, ok := vault["renew"]; ok { log.Println(`[WARN] vault.renew has been renamed to vault.renew_token. ` + `Update your configuration files and change "renew" to "renew_token".`) vault["renew_token"] = val delete(vault, "renew") } } // Create a new, empty config config := new(Config) // Use mapstructure to populate the basic config fields metadata := new(mapstructure.Metadata) decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.ComposeDecodeHookFunc( StringToFileModeFunc(), signals.StringToSignalFunc(), watch.StringToWaitDurationHookFunc(), mapstructure.StringToSliceHookFunc(","), mapstructure.StringToTimeDurationHookFunc(), ), ErrorUnused: true, Metadata: metadata, Result: config, }) if err != nil { errs = multierror.Append(errs, err) return nil, errs.ErrorOrNil() } if err := decoder.Decode(parsed); err != nil { errs = multierror.Append(errs, err) return nil, errs.ErrorOrNil() } // Explicitly check for the nil signal and set the value back to nil if config.ReloadSignal == signals.SIGNIL { config.ReloadSignal = nil } if config.DumpSignal == signals.SIGNIL { config.DumpSignal = nil } if config.KillSignal == signals.SIGNIL { config.KillSignal = nil } if config.Exec != nil { if config.Exec.ReloadSignal == signals.SIGNIL { config.Exec.ReloadSignal = nil } if config.Exec.KillSignal == signals.SIGNIL { config.Exec.KillSignal = nil } } // Setup default values for templates for _, t := range config.ConfigTemplates { // Ensure there's a default value for the template's file permissions if t.Perms == 0000 { t.Perms = DefaultFilePerms } // Ensure we have a default command timeout if t.CommandTimeout == 0 { t.CommandTimeout = DefaultCommandTimeout } // Set up a default zero wait, which disables it for this // template. if t.Wait == nil { t.Wait = &watch.Wait{} } } // Update the list of set keys if config.setKeys == nil { config.setKeys = make(map[string]struct{}) } for _, key := range metadata.Keys { if _, ok := config.setKeys[key]; !ok { config.setKeys[key] = struct{}{} } } config.setKeys["path"] = struct{}{} d := DefaultConfig() d.Merge(config) config = d return config, errs.ErrorOrNil() }
// Execute takes the callback and executes it via the LIFX protocol func Execute(callback map[string]interface{}) { var ( cb lifxCallback decoder *mapstructure.Decoder err error ) log.WithField(`request`, callback).Debug(`Sending to LIFX`) decoder, err = mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.StringToTimeDurationHookFunc(), Result: &cb, }) if err != nil { log.WithField(`error`, err).Error(`Initializing LIFX callback decoder`) return } if err = decoder.Decode(callback); err != nil { log.WithFields(log.Fields{ `request`: callback, `error`: err, }).Error(`Decoding LIFX callback`) return } boblights.cancel(&cb) if _, ok := callback[`power`]; ok { if len(cb.Lights) == 0 && len(cb.Groups) == 0 { if err = client.SetPowerDuration(cb.Power, cb.PowerDuration); err != nil { log.WithField(`error`, err).Error(`Setting power`) } } if len(cb.Lights) > 0 { for _, label := range cb.Lights { light, e := client.GetLightByLabel(label) if e != nil { log.WithFields(log.Fields{ `label`: label, `error`: e, }).Error(`Finding light`) continue } if e = light.SetPowerDuration(cb.Power, cb.PowerDuration); e != nil { log.WithFields(log.Fields{ `label`: label, `error`: e, }).Error(`Setting power`) continue } } } if len(cb.Groups) > 0 { for _, label := range cb.Groups { group, e := client.GetGroupByLabel(label) if e != nil { log.WithFields(log.Fields{ `label`: label, `error`: e, }).Error(`Finding group`) continue } if e = group.SetPowerDuration(cb.Power, cb.PowerDuration); e != nil { log.WithFields(log.Fields{ `label`: label, `error`: e, }).Error(`Setting power`) continue } } } } if _, ok := callback[`color`]; ok { if len(cb.Lights) == 0 && len(cb.Groups) == 0 { if err = client.SetColor(cb.Color, cb.ColorDuration); err != nil { log.WithField(`error`, err).Error(`Setting color`) } } if len(cb.Lights) > 0 { for _, label := range cb.Lights { light, err := client.GetLightByLabel(label) if err != nil { log.WithFields(log.Fields{ `label`: label, `error`: err, }).Error(`Finding light`) continue } if err = light.SetColor(cb.Color, cb.ColorDuration); err != nil { log.WithFields(log.Fields{ `label`: label, `error`: err, }).Error(`Setting color`) continue } } } if len(cb.Groups) > 0 { for _, label := range cb.Groups { group, err := client.GetGroupByLabel(label) if err != nil { log.WithFields(log.Fields{ `label`: label, `error`: err, }).Error(`Finding group`) continue } if err = group.SetColor(cb.Color, cb.ColorDuration); err != nil { log.WithFields(log.Fields{ `label`: label, `error`: err, }).Error(`Setting color`) continue } } } } if _, ok := callback[`boblight`]; ok { // Recommended LIFX device rate limit rateLimit := time.Second / 20 // If requested rate limit is longer than minimum, use it if cb.Boblight.RateLimit > rateLimit { rateLimit = cb.Boblight.RateLimit } if len(cb.Lights) == 0 && len(cb.Groups) == 0 { lights, err := client.GetLights() if err != nil { return } for _, light := range lights { boblights.add(light, cb.Boblight.Lights, rateLimit) } } if len(cb.Lights) > 0 { for _, label := range cb.Lights { light, err := client.GetLightByLabel(label) if err != nil { log.WithFields(log.Fields{ `label`: label, `error`: err, }).Error(`Finding light`) continue } boblights.add(light, cb.Boblight.Lights, rateLimit) } } if len(cb.Groups) > 0 { for _, label := range cb.Groups { group, err := client.GetGroupByLabel(label) if err != nil { log.WithFields(log.Fields{ `label`: label, `error`: err, }).Error(`Finding group`) continue } for _, light := range group.Lights() { boblights.add(light, cb.Boblight.Lights, rateLimit) } } } } }
// Parse parses the given string contents as a config func Parse(s string) (*Config, error) { var shadow interface{} if err := hcl.Decode(&shadow, s); err != nil { return nil, errors.Wrap(err, "error decoding config") } // Convert to a map and flatten the keys we want to flatten parsed, ok := shadow.(map[string]interface{}) if !ok { return nil, errors.New("error converting config") } flattenKeys(parsed, []string{ "auth", "consul", "consul.auth", "consul.retry", "consul.ssl", "deduplicate", "env", "exec", "exec.env", "ssl", "syslog", "vault", "vault.retry", "vault.ssl", "wait", }) // FlattenFlatten keys belonging to the templates. We cannot do this above // because it is an array of tmeplates. if templates, ok := parsed["template"].([]map[string]interface{}); ok { for _, template := range templates { flattenKeys(template, []string{ "env", "exec", "exec.env", "wait", }) } } // TODO: Deprecations if vault, ok := parsed["vault"].(map[string]interface{}); ok { if val, ok := vault["renew"]; ok { log.Println(`[WARN] vault.renew has been renamed to vault.renew_token. ` + `Update your configuration files and change "renew" to "renew_token".`) vault["renew_token"] = val delete(vault, "renew") } } if auth, ok := parsed["auth"].(map[string]interface{}); ok { log.Println("[WARN] auth has been moved under the consul stanza. " + "Update your configuration files and place auth inside consul { }.") if _, ok := parsed["consul"]; !ok { parsed["consul"] = make(map[string]interface{}) } parsed["consul"].(map[string]interface{})["auth"] = auth delete(parsed, "auth") } if retry, ok := parsed["retry"].(string); ok { log.Println("[WARN] retry has been moved under the consul stanza. " + "Update your configuration files and place retry inside consul { }.") if _, ok := parsed["consul"]; !ok { parsed["consul"] = make(map[string]interface{}) } parsed["consul"].(map[string]interface{})["retry"] = map[string]interface{}{ "backoff": retry, } delete(parsed, "retry") } if ssl, ok := parsed["ssl"].(map[string]interface{}); ok { log.Println("[WARN] ssl has been moved under the consul stanza. " + "Update your configuration files and place ssl inside consul { }.") if _, ok := parsed["consul"]; !ok { parsed["consul"] = make(map[string]interface{}) } parsed["consul"].(map[string]interface{})["ssl"] = ssl delete(parsed, "ssl") } if token, ok := parsed["token"].(string); ok { log.Println("[WARN] token has been moved under the consul stanza. " + "Update your configuration files and place token inside consul { }.") if _, ok := parsed["consul"]; !ok { parsed["consul"] = make(map[string]interface{}) } parsed["consul"].(map[string]interface{})["token"] = token delete(parsed, "token") } // Create a new, empty config var c Config // Use mapstructure to populate the basic config fields var md mapstructure.Metadata decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.ComposeDecodeHookFunc( ConsulStringToStructFunc(), StringToFileModeFunc(), signals.StringToSignalFunc(), StringToWaitDurationHookFunc(), mapstructure.StringToSliceHookFunc(","), mapstructure.StringToTimeDurationHookFunc(), ), ErrorUnused: true, Metadata: &md, Result: &c, }) if err != nil { return nil, errors.Wrap(err, "mapstructure decoder creation failed") } if err := decoder.Decode(parsed); err != nil { return nil, errors.Wrap(err, "mapstructure decode failed") } return &c, nil }
// ParseConfig reads the configuration file at the given path and returns a new // Config struct with the data populated. func ParseConfig(path string) (*Config, error) { var errs *multierror.Error // Read the contents of the file contents, err := ioutil.ReadFile(path) if err != nil { return nil, fmt.Errorf("error reading config at %q: %s", path, err) } // Parse the file (could be HCL or JSON) var shadow interface{} if err := hcl.Decode(&shadow, string(contents)); err != nil { return nil, fmt.Errorf("error decoding config at %q: %s", path, err) } // Convert to a map and flatten the keys we want to flatten parsed, ok := shadow.(map[string]interface{}) if !ok { return nil, fmt.Errorf("error converting config at %q", path) } flattenKeys(parsed, []string{"auth", "ssl", "syslog", "vault"}) // Create a new, empty config config := new(Config) // Use mapstructure to populate the basic config fields metadata := new(mapstructure.Metadata) decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.ComposeDecodeHookFunc( watch.StringToWaitDurationHookFunc(), mapstructure.StringToSliceHookFunc(","), mapstructure.StringToTimeDurationHookFunc(), ), ErrorUnused: true, Metadata: metadata, Result: config, }) if err != nil { errs = multierror.Append(errs, err) return nil, errs.ErrorOrNil() } if err := decoder.Decode(parsed); err != nil { errs = multierror.Append(errs, err) return nil, errs.ErrorOrNil() } // Store a reference to the path where this config was read from config.Path = path // Handle deprecations if len(config.PrefixesOld) > 0 { log.Printf(`[WARN] Specifying the key "prefixes" in the configuration is `+ `no longer supported. Please specify each prefix individually using `+ `the key "prefix" (config at %s)`, path) prefixes := make([]*ConfigPrefix, 0, len(config.PrefixesOld)) for _, prefix := range config.PrefixesOld { prefixes = append(prefixes, &ConfigPrefix{ Path: prefix, }) } config.Prefixes = append(prefixes, config.Prefixes...) config.PrefixesOld = nil } // Update the list of set keys if config.setKeys == nil { config.setKeys = make(map[string]struct{}) } for _, key := range metadata.Keys { if _, ok := config.setKeys[key]; !ok { config.setKeys[key] = struct{}{} } } config.setKeys["path"] = struct{}{} d := DefaultConfig() d.Merge(config) config = d return config, errs.ErrorOrNil() }
// Decode decodes the configuration into the target and optionally // automatically interpolates all the configuration as it goes. func Decode(target interface{}, config *DecodeOpts, raws ...interface{}) error { if config == nil { config = &DecodeOpts{Interpolate: true} } // Interpolate first if config.Interpolate { // Detect user variables from the raws and merge them into our context ctx, err := DetectContext(raws...) if err != nil { return err } if config.InterpolateContext == nil { config.InterpolateContext = ctx } else { config.InterpolateContext.TemplatePath = ctx.TemplatePath config.InterpolateContext.UserVariables = ctx.UserVariables } ctx = config.InterpolateContext // Render everything for i, raw := range raws { m, err := interpolate.RenderMap(raw, ctx, config.InterpolateFilter) if err != nil { return err } raws[i] = m } } // Build our decoder var md mapstructure.Metadata decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ Result: target, Metadata: &md, WeaklyTypedInput: true, DecodeHook: mapstructure.ComposeDecodeHookFunc( uint8ToStringHook, mapstructure.StringToSliceHookFunc(","), mapstructure.StringToTimeDurationHookFunc(), ), }) if err != nil { return err } for _, raw := range raws { if err := decoder.Decode(raw); err != nil { return err } } // Set the metadata if it is set if config.Metadata != nil { *config.Metadata = md } // If we have unused keys, it is an error if len(md.Unused) > 0 { var err error sort.Strings(md.Unused) for _, unused := range md.Unused { if unused != "type" && !strings.HasPrefix(unused, "packer_") { err = multierror.Append(err, fmt.Errorf( "unknown configuration key: %q", unused)) } } if err != nil { return err } } return nil }
// ParseConfig reads the configuration file at the given path and returns a new // Config struct with the data populated. func ParseConfig(path string) (*Config, error) { var errs *multierror.Error // Read the contents of the file contents, err := ioutil.ReadFile(path) if err != nil { return nil, fmt.Errorf("error reading config at %q: %s", path, err) } // Parse the file (could be HCL or JSON) var shadow interface{} if err := hcl.Decode(&shadow, string(contents)); err != nil { return nil, fmt.Errorf("error decoding config at %q: %s", path, err) } // Convert to a map and flatten the keys we want to flatten parsed, ok := shadow.(map[string]interface{}) if !ok { return nil, fmt.Errorf("error converting config at %q", path) } flattenKeys(parsed, []string{"auth", "ssl", "syslog", "vault", "deduplicate"}) // Create a new, empty config config := new(Config) // Use mapstructure to populate the basic config fields metadata := new(mapstructure.Metadata) decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.ComposeDecodeHookFunc( watch.StringToWaitDurationHookFunc(), mapstructure.StringToSliceHookFunc(","), mapstructure.StringToTimeDurationHookFunc(), ), ErrorUnused: true, Metadata: metadata, Result: config, }) if err != nil { errs = multierror.Append(errs, err) return nil, errs.ErrorOrNil() } if err := decoder.Decode(parsed); err != nil { errs = multierror.Append(errs, err) return nil, errs.ErrorOrNil() } // Store a reference to the path where this config was read from config.Path = path // Ensure there's a default value for the template's file permissions for _, t := range config.ConfigTemplates { if t.Perms == 0000 { t.Perms = defaultFilePerms } } // Update the list of set keys if config.setKeys == nil { config.setKeys = make(map[string]struct{}) } for _, key := range metadata.Keys { if _, ok := config.setKeys[key]; !ok { config.setKeys[key] = struct{}{} } } config.setKeys["path"] = struct{}{} d := DefaultConfig() d.Merge(config) config = d return config, errs.ErrorOrNil() }
// Execute takes the callback and executes it via the LIFX protocol func Execute(callback map[string]interface{}) { var ( cb lifxCallback decoder *mapstructure.Decoder err error ) log.WithField(`request`, callback).Debug(`Sending to LIFX`) decoder, err = mapstructure.NewDecoder(&mapstructure.DecoderConfig{ DecodeHook: mapstructure.StringToTimeDurationHookFunc(), Result: &cb, }) if err != nil { log.WithField(`error`, err).Error(`Initializing LIFX callback decoder`) return } if err = decoder.Decode(callback); err != nil { log.WithFields(log.Fields{ `request`: callback, `error`: err, }).Error(`Decoding LIFX callback`) return } if _, ok := callback[`power`]; ok { if len(cb.Lights) == 0 { if err = client.SetPowerDuration(cb.Power, cb.PowerDuration); err != nil { log.WithField(`error`, err).Error(`Setting power`) } } else { for _, label := range cb.Lights { light, err := client.GetLightByLabel(label) if err != nil { log.WithFields(log.Fields{ `label`: label, `error`: err, }).Error(`Finding light`) continue } if err = light.SetPowerDuration(cb.Power, cb.PowerDuration); err != nil { log.WithFields(log.Fields{ `label`: label, `error`: err, }).Error(`Setting power`) continue } } } } if _, ok := callback[`color`]; ok { if len(cb.Lights) == 0 { if err = client.SetColor(cb.Color, cb.ColorDuration); err != nil { log.WithField(`error`, err).Error(`Setting color`) } } else { for _, label := range cb.Lights { light, err := client.GetLightByLabel(label) if err != nil { log.WithFields(log.Fields{ `label`: label, `error`: err, }).Error(`Finding light`) continue } if err = light.SetColor(cb.Color, cb.ColorDuration); err != nil { log.WithFields(log.Fields{ `label`: label, `error`: err, }).Error(`Setting color`) continue } } } } }