func parseProject(result *File, obj *hclobj.Object) error { if obj.Len() > 1 { return fmt.Errorf("only one 'project' block allowed") } // Check for invalid keys valid := []string{"name", "infrastructure"} if err := checkHCLKeys(obj, valid); err != nil { return multierror.Prefix(err, "project:") } var m map[string]interface{} if err := hcl.DecodeObject(&m, obj); err != nil { return err } // Parse the project var proj Project result.Project = &proj if err := mapstructure.WeakDecode(m, &proj); err != nil { return err } return nil }
func parseUpdate(result *structs.UpdateStrategy, obj *hclobj.Object) error { if obj.Len() > 1 { return fmt.Errorf("only one 'update' block allowed per job") } for _, o := range obj.Elem(false) { var m map[string]interface{} if err := hcl.DecodeObject(&m, o); err != nil { return err } for _, key := range []string{"stagger", "Stagger"} { if raw, ok := m[key]; ok { switch v := raw.(type) { case string: dur, err := time.ParseDuration(v) if err != nil { return fmt.Errorf("invalid stagger time '%s'", raw) } m[key] = dur case int: m[key] = time.Duration(v) * time.Second default: return fmt.Errorf("invalid type for stagger time '%s'", raw) } } } if err := mapstructure.WeakDecode(m, result); err != nil { return err } } return nil }
func parseUpdate(result *structs.UpdateStrategy, obj *hclobj.Object) error { if obj.Len() > 1 { return fmt.Errorf("only one 'update' block allowed per job") } for _, o := range obj.Elem(false) { var m map[string]interface{} if err := hcl.DecodeObject(&m, o); err != nil { return err } for _, key := range []string{"stagger", "Stagger"} { if raw, ok := m[key]; ok { staggerTime, err := toDuration(raw) if err != nil { return fmt.Errorf("Invalid stagger time: %v", err) } m[key] = staggerTime } } if err := mapstructure.WeakDecode(m, result); err != nil { return err } } return nil }
func parseResources(result *structs.Resources, obj *hclobj.Object) error { if obj.Len() > 1 { return fmt.Errorf("only one 'resource' block allowed per task") } for _, o := range obj.Elem(false) { var m map[string]interface{} if err := hcl.DecodeObject(&m, o); err != nil { return err } delete(m, "network") if err := mapstructure.WeakDecode(m, result); err != nil { return err } // Parse the network resources if o := o.Get("network", false); o != nil { if o.Len() > 1 { return fmt.Errorf("only one 'network' resource allowed") } var r structs.NetworkResource var m map[string]interface{} if err := hcl.DecodeObject(&m, o); 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 parseJob(jobNode *hclObj.Object, errors *multierror.Error) JobConfig { config := JobConfig{} if jobNode.Len() > 1 { errors = multierror.Append(errors, fmt.Errorf("job was specified more than once")) } else { if jobNode.Type != hclObj.ValueTypeObject { errors = multierror.Append(errors, fmt.Errorf("job was specified as an invalid type - expected object, found %s", jobNode.Type)) } else { config.Name = jobNode.Key config.Trigger = parseJobTrigger(jobNode, errors) config.Exec = parseExec(jobNode, errors) } } return config }
func parseApplication(result *File, obj *hclobj.Object) error { if obj.Len() > 1 { return fmt.Errorf("only one 'application' block allowed") } // Check for invalid keys valid := []string{"name", "type", "dependency"} if err := checkHCLKeys(obj, valid); err != nil { return multierror.Prefix(err, "application:") } var m map[string]interface{} if err := hcl.DecodeObject(&m, obj); err != nil { return err } var app Application result.Application = &app return mapstructure.WeakDecode(m, &app) }
func loadProvisionersHcl(os *hclobj.Object, connInfo map[string]interface{}) ([]*Provisioner, error) { pos := make([]*hclobj.Object, 0, int(os.Len())) // Accumulate all the actual provisioner configuration objects. We // have to iterate twice here: // // 1. The first iteration is of the list of `provisioner` blocks. // 2. The second iteration is of the dictionary within the // provisioner which will have only one element which is the // type of provisioner to use along with tis config. // // In JSON it looks kind of like this: // // [ // { // "shell": { // ... // } // } // ] // for _, o1 := range os.Elem(false) { for _, o2 := range o1.Elem(true) { pos = append(pos, o2) } } // Short-circuit if there are no items if len(pos) == 0 { return nil, nil } result := make([]*Provisioner, 0, len(pos)) for _, po := range pos { var config map[string]interface{} if err := hcl.DecodeObject(&config, po); err != nil { return nil, err } // Delete the "connection" section, handle seperately delete(config, "connection") rawConfig, err := NewRawConfig(config) if err != nil { return nil, err } // Check if we have a provisioner-level connection // block that overrides the resource-level var subConnInfo map[string]interface{} if o := po.Get("connection", false); o != nil { err := hcl.DecodeObject(&subConnInfo, o) if err != nil { return nil, err } } // Inherit from the resource connInfo any keys // that are not explicitly overriden. if connInfo != nil && subConnInfo != nil { for k, v := range connInfo { if _, ok := subConnInfo[k]; !ok { subConnInfo[k] = v } } } else if subConnInfo == nil { subConnInfo = connInfo } // Parse the connInfo connRaw, err := NewRawConfig(subConnInfo) if err != nil { return nil, err } result = append(result, &Provisioner{ Type: po.Key, RawConfig: rawConfig, ConnInfo: connRaw, }) } return result, nil }
func parseJob(result *structs.Job, obj *hclobj.Object) error { if obj.Len() > 1 { return fmt.Errorf("only one 'job' block allowed") } // Get our job object obj = obj.Elem(true)[0] // Decode the full thing into a map[string]interface for ease var m map[string]interface{} if err := hcl.DecodeObject(&m, obj); err != nil { return err } delete(m, "constraint") delete(m, "meta") delete(m, "update") // Set the ID and name to the object key result.ID = obj.Key result.Name = obj.Key // Defaults result.Priority = 50 result.Region = "global" result.Type = "service" // Decode the rest if err := mapstructure.WeakDecode(m, result); err != nil { return err } // Parse constraints if o := obj.Get("constraint", false); o != nil { if err := parseConstraints(&result.Constraints, o); err != nil { return err } } // If we have an update strategy, then parse that if o := obj.Get("update", false); o != nil { if err := parseUpdate(&result.Update, 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 := obj.Get("meta", false); metaO != nil { for _, o := range metaO.Elem(false) { var m map[string]interface{} if err := hcl.DecodeObject(&m, o); err != nil { return err } if err := mapstructure.WeakDecode(m, &result.Meta); err != nil { return err } } } // If we have tasks outside, do those if o := obj.Get("task", false); o != nil { var tasks []*structs.Task if err := parseTasks(&tasks, o); err != nil { return err } result.TaskGroups = make([]*structs.TaskGroup, len(tasks), len(tasks)*2) for i, t := range tasks { result.TaskGroups[i] = &structs.TaskGroup{ Name: t.Name, Count: 1, Tasks: []*structs.Task{t}, } } } // Parse the task groups if o := obj.Get("group", false); o != nil { if err := parseGroups(result, o); err != nil { return fmt.Errorf("error parsing 'group': %s", err) } } return nil }
func (d *decoder) decodeInterface(name string, o *hcl.Object, result reflect.Value) error { var set reflect.Value redecode := true switch o.Type { case hcl.ValueTypeObject: // If we're at the root or we're directly within a slice, then we // decode objects into map[string]interface{}, otherwise we decode // them into lists. if len(d.stack) == 0 || d.stack[len(d.stack)-1] == reflect.Slice { var temp map[string]interface{} tempVal := reflect.ValueOf(temp) result := reflect.MakeMap( reflect.MapOf( reflect.TypeOf(""), tempVal.Type().Elem())) set = result } else { var temp []map[string]interface{} tempVal := reflect.ValueOf(temp) result := reflect.MakeSlice( reflect.SliceOf(tempVal.Type().Elem()), 0, int(o.Len())) set = result } case hcl.ValueTypeList: var temp []interface{} tempVal := reflect.ValueOf(temp) result := reflect.MakeSlice( reflect.SliceOf(tempVal.Type().Elem()), 0, 0) set = result case hcl.ValueTypeBool: var result bool set = reflect.Indirect(reflect.New(reflect.TypeOf(result))) case hcl.ValueTypeFloat: var result float64 set = reflect.Indirect(reflect.New(reflect.TypeOf(result))) case hcl.ValueTypeInt: var result int set = reflect.Indirect(reflect.New(reflect.TypeOf(result))) case hcl.ValueTypeString: set = reflect.Indirect(reflect.New(reflect.TypeOf(""))) default: return fmt.Errorf( "%s: cannot decode into interface: %T", name, o) } // Set the result to what its supposed to be, then reset // result so we don't reflect into this method anymore. result.Set(set) if redecode { // Revisit the node so that we can use the newly instantiated // thing and populate it. if err := d.decode(name, o, result); err != nil { return err } } return nil }