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 parseDetect(result *Config, obj *hclobj.Object) error { // 从map中获取所有实际对象的key值 objects := make([]*hclobj.Object, 0, 2) for _, o1 := range obj.Elem(false) { for _, o2 := range o1.Elem(true) { objects = append(objects, o2) } } if len(objects) == 0 { return nil } // 检查每个对象,返回实际结果 collection := make([]*Detector, 0, len(objects)) for _, o := range objects { var m map[string]interface{} if err := hcl.DecodeObject(&m, o); err != nil { return err } var d Detector if err := mapstructure.WeakDecode(m, &d); err != nil { return fmt.Errorf("解析detector错误 '%s' : %s", o.Key, err) } d.Type = o.Key collection = append(collection, &d) } result.Detectors = collection return nil }
func parseDetect(result *Config, obj *hclobj.Object) error { // Get all the maps of keys to the actual object objects := make([]*hclobj.Object, 0, 2) for _, o1 := range obj.Elem(false) { for _, o2 := range o1.Elem(true) { objects = append(objects, o2) } } if len(objects) == 0 { return nil } // Go through each object and turn it into an actual result. collection := make([]*Detector, 0, len(objects)) for _, o := range objects { var m map[string]interface{} if err := hcl.DecodeObject(&m, o); err != nil { return err } var d Detector if err := mapstructure.WeakDecode(m, &d); err != nil { return fmt.Errorf( "error parsing detector '%s': %s", o.Key, err) } d.Type = o.Key collection = append(collection, &d) } result.Detectors = collection return nil }
func parseConstraints(result *[]*structs.Constraint, obj *hclobj.Object) error { for _, o := range obj.Elem(false) { var m map[string]interface{} if err := hcl.DecodeObject(&m, o); err != nil { return err } m["LTarget"] = m["attribute"] m["RTarget"] = m["value"] m["Operand"] = m["operator"] // Default constraint to being hard if _, ok := m["hard"]; !ok { m["hard"] = true } // 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 parseRawModules(objModules *hclObj.Object, errors *multierror.Error) (output []modules.ModuleSpec) { //iterate over each module for _, ms := range objModules.Elem(true) { // lets build the mod params ... rawParams := ms.Elem(true) params := params.NewModuleParams() for _, p := range rawParams { switch p.Value.(type) { case string: params[p.Key] = p.Value case int: params[p.Key] = p.Value case []*hclObj.Object: propertyValues := make([]interface{}, 0) for _, pv := range p.Elem(true) { propertyValues = append(propertyValues, pv.Value) } params[p.Key] = propertyValues } } // build the module module := modprobe.Find(ms.Key, params) output = append(output, module) } return output }
func parseInfra(result *File, obj *hclobj.Object) error { // Get all the maps of keys to the actual object objects := make(map[string]*hclobj.Object) for _, o1 := range obj.Elem(false) { for _, o2 := range o1.Elem(true) { if _, ok := objects[o2.Key]; ok { return fmt.Errorf( "infrastructure '%s' defined more than once", o2.Key) } objects[o2.Key] = o2 } } if len(objects) == 0 { return nil } // Go through each object and turn it into an actual result. collection := make([]*Infrastructure, 0, len(objects)) for n, o := range objects { // Check for invalid keys valid := []string{"name", "type", "flavor", "foundation"} if err := checkHCLKeys(o, valid); err != nil { return multierror.Prefix(err, fmt.Sprintf( "infrastructure '%s':", n)) } var m map[string]interface{} if err := hcl.DecodeObject(&m, o); err != nil { return err } var infra Infrastructure if err := mapstructure.WeakDecode(m, &infra); err != nil { return fmt.Errorf( "error parsing infrastructure '%s': %s", n, err) } infra.Name = n if infra.Type == "" { infra.Type = infra.Name } // Parse the foundations if we have any if o2 := o.Get("foundation", false); o != nil { if err := parseFoundations(&infra, o2); err != nil { return fmt.Errorf("error parsing 'foundation': %s", err) } } collection = append(collection, &infra) } result.Infrastructure = collection return nil }
func parseConstraints(result *[]*structs.Constraint, obj *hclobj.Object) error { for _, o := range obj.Elem(false) { var m map[string]interface{} if err := hcl.DecodeObject(&m, o); err != nil { return err } m["LTarget"] = m["attribute"] m["RTarget"] = m["value"] m["Operand"] = m["operator"] // Default constraint to being hard if _, ok := m["hard"]; !ok { m["hard"] = true } // 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 := strconv.ParseBool(value.(string)) if err != nil { return 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 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 }
// LoadProvidersHcl recurses into the given HCL object and turns // it into a mapping of provider configs. func loadProvidersHcl(os *hclobj.Object) ([]*ProviderConfig, error) { var objects []*hclobj.Object // Iterate over all the "provider" blocks and get the keys along with // their raw configuration objects. We'll parse those later. for _, o1 := range os.Elem(false) { for _, o2 := range o1.Elem(true) { objects = append(objects, o2) } } if len(objects) == 0 { return nil, nil } // Go through each object and turn it into an actual result. result := make([]*ProviderConfig, 0, len(objects)) for _, o := range objects { var config map[string]interface{} if err := hcl.DecodeObject(&config, o); err != nil { return nil, err } delete(config, "alias") rawConfig, err := NewRawConfig(config) if err != nil { return nil, fmt.Errorf( "Error reading config for provider config %s: %s", o.Key, err) } // If we have an alias field, then add those in var alias string if a := o.Get("alias", false); a != nil { err := hcl.DecodeObject(&alias, a) if err != nil { return nil, fmt.Errorf( "Error reading alias for provider[%s]: %s", o.Key, err) } } result = append(result, &ProviderConfig{ Name: o.Key, Alias: alias, RawConfig: rawConfig, }) } return result, nil }
func checkHCLKeys(obj *hclobj.Object, valid []string) error { validMap := make(map[string]struct{}, len(valid)) for _, v := range valid { validMap[v] = struct{}{} } var result error for _, o := range obj.Elem(true) { if _, ok := validMap[o.Key]; !ok { result = multierror.Append(result, fmt.Errorf( "invald key: %s", o.Key)) } } return result }
func (d *decoder) decodeSlice(name string, o *hcl.Object, result reflect.Value) error { // If we have an interface, then we can address the interface, // but not the slice itself, so get the element but set the interface set := result if result.Kind() == reflect.Interface { result = result.Elem() } // Create the slice if it isn't nil resultType := result.Type() resultElemType := resultType.Elem() if result.IsNil() { resultSliceType := reflect.SliceOf(resultElemType) result = reflect.MakeSlice( resultSliceType, 0, 0) } // Determine how we're doing this expand := true switch o.Type { case hcl.ValueTypeObject: expand = false default: // Array or anything else: we expand values and take it all } i := 0 for _, o := range o.Elem(expand) { fieldName := fmt.Sprintf("%s[%d]", name, i) // Decode val := reflect.Indirect(reflect.New(resultElemType)) if err := d.decode(fieldName, o, val); err != nil { return err } // Append it onto the slice result = reflect.Append(result, val) i += 1 } set.Set(result) return nil }
func loadListeners(os *hclobj.Object) ([]*Listener, error) { var allNames []*hclobj.Object // Really confusing iteration. The key is the false/true parameter // of whether we're expanding or not. We first iterate over all // the "listeners" for _, o1 := range os.Elem(false) { // Iterate expand to get the list of types for _, o2 := range o1.Elem(true) { switch o2.Type { case hclobj.ValueTypeList: // This switch is for JSON, to allow them to do this: // // "tcp": [{ ... }, { ... }] // // To configure multiple listeners of the same type. for _, o3 := range o2.Elem(true) { o3.Key = o2.Key allNames = append(allNames, o3) } case hclobj.ValueTypeObject: // This is for the standard `listener "tcp" { ... }` syntax allNames = append(allNames, o2) } } } if len(allNames) == 0 { return nil, nil } // Now go over all the types and their children in order to get // all of the actual resources. result := make([]*Listener, 0, len(allNames)) for _, obj := range allNames { k := obj.Key var config map[string]string if err := hcl.DecodeObject(&config, obj); err != nil { return nil, fmt.Errorf( "Error reading config for %s: %s", k, err) } result = append(result, &Listener{ Type: k, Config: config, }) } return result, nil }
// LoadProvidersHcl recurses into the given HCL object and turns // it into a mapping of provider configs. func loadProvidersHcl(os *hclobj.Object) ([]*ProviderConfig, error) { objects := make(map[string]*hclobj.Object) // Iterate over all the "provider" blocks and get the keys along with // their raw configuration objects. We'll parse those later. for _, o1 := range os.Elem(false) { for _, o2 := range o1.Elem(true) { objects[o2.Key] = o2 } } if len(objects) == 0 { return nil, nil } // Go through each object and turn it into an actual result. result := make([]*ProviderConfig, 0, len(objects)) for n, o := range objects { var config map[string]interface{} if err := hcl.DecodeObject(&config, o); err != nil { return nil, err } rawConfig, err := NewRawConfig(config) if err != nil { return nil, fmt.Errorf( "Error reading config for provider config %s: %s", n, err) } result = append(result, &ProviderConfig{ Name: n, RawConfig: rawConfig, }) } return result, nil }
func parseImport(result *File, obj *hclobj.Object) error { // Get all the maps of keys to the actual object objects := make([]*hclobj.Object, 0, 3) set := make(map[string]struct{}) for _, o1 := range obj.Elem(false) { for _, o2 := range o1.Elem(true) { if _, ok := set[o2.Key]; ok { return fmt.Errorf( "imported '%s' more than once", o2.Key) } objects = append(objects, o2) set[o2.Key] = struct{}{} } } if len(objects) == 0 { return nil } // Go through each object and turn it into an actual result. collection := make([]*Import, 0, len(objects)) for _, o := range objects { // Check for invalid keys if err := checkHCLKeys(o, nil); err != nil { return multierror.Prefix(err, fmt.Sprintf( "import '%s':", o.Key)) } collection = append(collection, &Import{ Source: o.Key, }) } result.Imports = collection return nil }
func parseFoundations(result *Infrastructure, obj *hclobj.Object) error { // Get all the maps of keys to the actual object objects := make(map[string]*hclobj.Object) for _, o1 := range obj.Elem(false) { for _, o2 := range o1.Elem(true) { if _, ok := objects[o2.Key]; ok { return fmt.Errorf( "foundation '%s' defined more than once", o2.Key) } objects[o2.Key] = o2 } } if len(objects) == 0 { return nil } // Go through each object and turn it into an actual result. collection := make([]*Foundation, 0, len(objects)) for n, o := range objects { var m map[string]interface{} if err := hcl.DecodeObject(&m, o); err != nil { return err } var f Foundation f.Name = n f.Config = m collection = append(collection, &f) } // Set the results result.Foundations = collection return nil }
func parseCustomizations(result *File, obj *hclobj.Object) error { // Get all the maps of keys to the actual object objects := make(map[string]*hclobj.Object) for _, o1 := range obj.Elem(false) { for _, o2 := range o1.Elem(true) { if _, ok := objects[o2.Key]; ok { return fmt.Errorf( "customization '%s' defined more than once", o2.Key) } objects[o2.Key] = o2 } } if len(objects) == 0 { return nil } // Go through each object and turn it into an actual result. collection := make([]*Customization, 0, len(objects)) for n, o := range objects { var m map[string]interface{} if err := hcl.DecodeObject(&m, o); err != nil { return err } var c Customization c.Type = strings.ToLower(n) c.Config = m collection = append(collection, &c) } result.Customization = &CustomizationSet{Raw: collection} return nil }
func loadBackend(os *hclobj.Object) (*Backend, error) { var allNames []*hclobj.Object // See loadListeners for _, o1 := range os.Elem(false) { // Iterate expand to get the list of types for _, o2 := range o1.Elem(true) { // Iterate non-expand to get the full list of types for _, o3 := range o2.Elem(false) { allNames = append(allNames, o3) } } } if len(allNames) == 0 { return nil, nil } if len(allNames) > 1 { keys := make([]string, 0, len(allNames)) for _, o := range allNames { keys = append(keys, o.Key) } return nil, fmt.Errorf( "Multiple backends declared. Only one is allowed: %v", keys) } // Now go over all the types and their children in order to get // all of the actual resources. var result Backend obj := allNames[0] result.Type = obj.Key var config map[string]string if err := hcl.DecodeObject(&config, obj); err != nil { return nil, fmt.Errorf( "Error reading config for backend %s: %s", result.Type, err) } if v, ok := config["advertise_addr"]; ok { result.AdvertiseAddr = v delete(config, "advertise_addr") } result.Config = config return &result, nil }
// Given a handle to a HCL object, this recurses into the structure // and pulls out a list of resources. // // The resulting resources may not be unique, but each resource // represents exactly one resource definition in the HCL configuration. // We leave it up to another pass to merge them together. func loadResourcesHcl(os *hclobj.Object) ([]*Resource, error) { var allTypes []*hclobj.Object // HCL object iteration is really nasty. Below is likely to make // no sense to anyone approaching this code. Luckily, it is very heavily // tested. If working on a bug fix or feature, we recommend writing a // test first then doing whatever you want to the code below. If you // break it, the tests will catch it. Likewise, if you change this, // MAKE SURE you write a test for your change, because its fairly impossible // to reason about this mess. // // Functionally, what the code does below is get the libucl.Objects // for all the TYPES, such as "aws_security_group". for _, o1 := range os.Elem(false) { // Iterate the inner to get the list of types for _, o2 := range o1.Elem(true) { // Iterate all of this type to get _all_ the types for _, o3 := range o2.Elem(false) { allTypes = append(allTypes, o3) } } } // Where all the results will go var result []*Resource // Now go over all the types and their children in order to get // all of the actual resources. for _, t := range allTypes { for _, obj := range t.Elem(true) { k := obj.Key var config map[string]interface{} if err := hcl.DecodeObject(&config, obj); err != nil { return nil, fmt.Errorf( "Error reading config for %s[%s]: %s", t.Key, k, err) } // Remove the fields we handle specially delete(config, "connection") delete(config, "count") delete(config, "depends_on") delete(config, "provisioner") delete(config, "lifecycle") rawConfig, err := NewRawConfig(config) if err != nil { return nil, fmt.Errorf( "Error reading config for %s[%s]: %s", t.Key, k, err) } // If we have a count, then figure it out var count string = "1" if o := obj.Get("count", false); o != nil { err = hcl.DecodeObject(&count, o) if err != nil { return nil, fmt.Errorf( "Error parsing count for %s[%s]: %s", t.Key, k, err) } } countConfig, err := NewRawConfig(map[string]interface{}{ "count": count, }) if err != nil { return nil, err } countConfig.Key = "count" // If we have depends fields, then add those in var dependsOn []string if o := obj.Get("depends_on", false); o != nil { err := hcl.DecodeObject(&dependsOn, o) if err != nil { return nil, fmt.Errorf( "Error reading depends_on for %s[%s]: %s", t.Key, k, err) } } // If we have connection info, then parse those out var connInfo map[string]interface{} if o := obj.Get("connection", false); o != nil { err := hcl.DecodeObject(&connInfo, o) if err != nil { return nil, fmt.Errorf( "Error reading connection info for %s[%s]: %s", t.Key, k, err) } } // If we have provisioners, then parse those out var provisioners []*Provisioner if os := obj.Get("provisioner", false); os != nil { var err error provisioners, err = loadProvisionersHcl(os, connInfo) if err != nil { return nil, fmt.Errorf( "Error reading provisioners for %s[%s]: %s", t.Key, k, err) } } // Check if the resource should be re-created before // destroying the existing instance var lifecycle ResourceLifecycle if o := obj.Get("lifecycle", false); o != nil { err = hcl.DecodeObject(&lifecycle, o) if err != nil { return nil, fmt.Errorf( "Error parsing lifecycle for %s[%s]: %s", t.Key, k, err) } } result = append(result, &Resource{ Name: k, Type: t.Key, RawCount: countConfig, RawConfig: rawConfig, Provisioners: provisioners, DependsOn: dependsOn, Lifecycle: lifecycle, }) } } return result, nil }
// Given a handle to a HCL object, this recurses into the structure // and pulls out a list of modules. // // The resulting modules may not be unique, but each module // represents exactly one module definition in the HCL configuration. // We leave it up to another pass to merge them together. func loadModulesHcl(os *hclobj.Object) ([]*Module, error) { var allNames []*hclobj.Object // See loadResourcesHcl for why this exists. Don't touch this. for _, o1 := range os.Elem(false) { // Iterate the inner to get the list of types for _, o2 := range o1.Elem(true) { // Iterate all of this type to get _all_ the types for _, o3 := range o2.Elem(false) { allNames = append(allNames, o3) } } } // Where all the results will go var result []*Module // Now go over all the types and their children in order to get // all of the actual resources. for _, obj := range allNames { k := obj.Key var config map[string]interface{} if err := hcl.DecodeObject(&config, obj); err != nil { return nil, fmt.Errorf( "Error reading config for %s: %s", k, err) } // Remove the fields we handle specially delete(config, "source") rawConfig, err := NewRawConfig(config) if err != nil { return nil, fmt.Errorf( "Error reading config for %s: %s", k, err) } // If we have a count, then figure it out var source string if o := obj.Get("source", false); o != nil { err = hcl.DecodeObject(&source, o) if err != nil { return nil, fmt.Errorf( "Error parsing source for %s: %s", k, err) } } result = append(result, &Module{ Name: k, Source: source, RawConfig: rawConfig, }) } 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 parseTasks(result *[]*structs.Task, obj *hclobj.Object) error { // Get all the maps of keys to the actual object objects := make([]*hclobj.Object, 0, 5) set := make(map[string]struct{}) for _, o1 := range obj.Elem(false) { for _, o2 := range o1.Elem(true) { if _, ok := set[o2.Key]; ok { return fmt.Errorf( "group '%s' defined more than once", o2.Key) } objects = append(objects, o2) set[o2.Key] = struct{}{} } } if len(objects) == 0 { return nil } for _, o := range objects { var m map[string]interface{} if err := hcl.DecodeObject(&m, o); err != nil { return err } delete(m, "config") delete(m, "constraint") delete(m, "meta") delete(m, "resources") // Build the task var t structs.Task t.Name = o.Key if err := mapstructure.WeakDecode(m, &t); err != nil { return err } // If we have config, then parse that if o := o.Get("config", false); o != nil { for _, o := range o.Elem(false) { var m map[string]interface{} if err := hcl.DecodeObject(&m, o); err != nil { return err } if err := mapstructure.WeakDecode(m, &t.Config); err != nil { return err } } } // Parse constraints if o := o.Get("constraint", false); o != nil { 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 := o.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, &t.Meta); err != nil { return err } } } // If we have resources, then parse that if o := o.Get("resources", false); o != nil { 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 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 parseGroups(result *structs.Job, obj *hclobj.Object) error { // Get all the maps of keys to the actual object objects := make(map[string]*hclobj.Object) for _, o1 := range obj.Elem(false) { for _, o2 := range o1.Elem(true) { if _, ok := objects[o2.Key]; ok { return fmt.Errorf( "group '%s' defined more than once", o2.Key) } objects[o2.Key] = o2 } } if len(objects) == 0 { return nil } // Go through each object and turn it into an actual result. collection := make([]*structs.TaskGroup, 0, len(objects)) for n, o := range objects { var m map[string]interface{} if err := hcl.DecodeObject(&m, o); err != nil { return err } delete(m, "constraint") delete(m, "meta") delete(m, "task") // Default count to 1 if not specified if _, ok := m["count"]; !ok { m["count"] = 1 } // Build the group with the basic decode var g structs.TaskGroup g.Name = n if err := mapstructure.WeakDecode(m, &g); err != nil { return err } // Parse constraints if o := o.Get("constraint", false); o != nil { if err := parseConstraints(&g.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 := o.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, &g.Meta); err != nil { return err } } } // Parse tasks if o := o.Get("task", false); o != nil { if err := parseTasks(&g.Tasks, o); err != nil { return err } } collection = append(collection, &g) } result.TaskGroups = append(result.TaskGroups, collection...) return nil }
func (d *decoder) decodeMap(name string, o *hcl.Object, result reflect.Value) error { if o.Type != hcl.ValueTypeObject { return fmt.Errorf("%s: not an object type for map (%s)", name, o.Type) } // If we have an interface, then we can address the interface, // but not the slice itself, so get the element but set the interface set := result if result.Kind() == reflect.Interface { result = result.Elem() } resultType := result.Type() resultElemType := resultType.Elem() resultKeyType := resultType.Key() if resultKeyType.Kind() != reflect.String { return fmt.Errorf( "%s: map must have string keys", name) } // Make a map if it is nil resultMap := result if result.IsNil() { resultMap = reflect.MakeMap( reflect.MapOf(resultKeyType, resultElemType)) } // Go through each element and decode it. for _, o := range o.Elem(false) { if o.Value == nil { continue } for _, o := range o.Elem(true) { // Make the field name fieldName := fmt.Sprintf("%s.%s", name, o.Key) // Get the key/value as reflection values key := reflect.ValueOf(o.Key) val := reflect.Indirect(reflect.New(resultElemType)) // If we have a pre-existing value in the map, use that oldVal := resultMap.MapIndex(key) if oldVal.IsValid() { val.Set(oldVal) } // Decode! if err := d.decode(fieldName, o, val); err != nil { return err } // Set the value on the map resultMap.SetMapIndex(key, val) } } // Set the final map if we can set.Set(resultMap) return nil }