github.com/ewbankkit/terraform@v0.7.7/terraform/variables.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "os" 6 "strings" 7 8 "github.com/hashicorp/terraform/config" 9 "github.com/hashicorp/terraform/config/module" 10 ) 11 12 // Variables returns the fully loaded set of variables to use with 13 // ContextOpts and NewContext, loading any additional variables from 14 // the environment or any other sources. 15 // 16 // The given module tree doesn't need to be loaded. 17 func Variables( 18 m *module.Tree, 19 override map[string]interface{}) (map[string]interface{}, error) { 20 result := make(map[string]interface{}) 21 22 // Variables are loaded in the following sequence. Each additional step 23 // will override conflicting variable keys from prior steps: 24 // 25 // * Take default values from config 26 // * Take values from TF_VAR_x env vars 27 // * Take values specified in the "override" param which is usually 28 // from -var, -var-file, etc. 29 // 30 31 // First load from the config 32 for _, v := range m.Config().Variables { 33 // If the var has no default, ignore 34 if v.Default == nil { 35 continue 36 } 37 38 // If the type isn't a string, we use it as-is since it is a rich type 39 if v.Type() != config.VariableTypeString { 40 result[v.Name] = v.Default 41 continue 42 } 43 44 // v.Default has already been parsed as HCL but it may be an int type 45 switch typedDefault := v.Default.(type) { 46 case string: 47 if typedDefault == "" { 48 continue 49 } 50 result[v.Name] = typedDefault 51 case int, int64: 52 result[v.Name] = fmt.Sprintf("%d", typedDefault) 53 case float32, float64: 54 result[v.Name] = fmt.Sprintf("%f", typedDefault) 55 case bool: 56 result[v.Name] = fmt.Sprintf("%t", typedDefault) 57 default: 58 panic(fmt.Sprintf( 59 "Unknown default var type: %T\n\n"+ 60 "THIS IS A BUG. Please report it.", 61 v.Default)) 62 } 63 } 64 65 // Load from env vars 66 for _, v := range os.Environ() { 67 if !strings.HasPrefix(v, VarEnvPrefix) { 68 continue 69 } 70 71 // Strip off the prefix and get the value after the first "=" 72 idx := strings.Index(v, "=") 73 k := v[len(VarEnvPrefix):idx] 74 v = v[idx+1:] 75 76 // Override the configuration-default values. Note that *not* finding the variable 77 // in configuration is OK, as we don't want to preclude people from having multiple 78 // sets of TF_VAR_whatever in their environment even if it is a little weird. 79 for _, schema := range m.Config().Variables { 80 if schema.Name != k { 81 continue 82 } 83 84 varType := schema.Type() 85 varVal, err := parseVariableAsHCL(k, v, varType) 86 if err != nil { 87 return nil, err 88 } 89 90 switch varType { 91 case config.VariableTypeMap: 92 varSetMap(result, k, varVal) 93 default: 94 result[k] = varVal 95 } 96 } 97 } 98 99 // Load from overrides 100 for k, v := range override { 101 for _, schema := range m.Config().Variables { 102 if schema.Name != k { 103 continue 104 } 105 106 switch schema.Type() { 107 case config.VariableTypeMap: 108 varSetMap(result, k, v) 109 default: 110 result[k] = v 111 } 112 } 113 } 114 115 return result, nil 116 } 117 118 // varSetMap sets or merges the map in "v" with the key "k" in the 119 // "current" set of variables. This is just a private function to remove 120 // duplicate logic in Variables 121 func varSetMap(current map[string]interface{}, k string, v interface{}) { 122 existing, ok := current[k] 123 if !ok { 124 current[k] = v 125 return 126 } 127 128 existingMap, ok := existing.(map[string]interface{}) 129 if !ok { 130 panic(fmt.Sprintf("%s is not a map, this is a bug in Terraform.", k)) 131 } 132 133 switch typedV := v.(type) { 134 case []map[string]interface{}: 135 for newKey, newVal := range typedV[0] { 136 existingMap[newKey] = newVal 137 } 138 case map[string]interface{}: 139 for newKey, newVal := range typedV { 140 existingMap[newKey] = newVal 141 } 142 default: 143 panic(fmt.Sprintf("%s is not a map, this is a bug in Terraform.", k)) 144 } 145 }