github.com/adamar/terraform@v0.2.2-0.20141016210445-2e703afdad0e/config/raw_config.go (about) 1 package config 2 3 import ( 4 "bytes" 5 "encoding/gob" 6 7 "github.com/mitchellh/copystructure" 8 "github.com/mitchellh/reflectwalk" 9 ) 10 11 // UnknownVariableValue is a sentinel value that can be used 12 // to denote that the value of a variable is unknown at this time. 13 // RawConfig uses this information to build up data about 14 // unknown keys. 15 const UnknownVariableValue = "74D93920-ED26-11E3-AC10-0800200C9A66" 16 17 // RawConfig is a structure that holds a piece of configuration 18 // where te overall structure is unknown since it will be used 19 // to configure a plugin or some other similar external component. 20 // 21 // RawConfigs can be interpolated with variables that come from 22 // other resources, user variables, etc. 23 // 24 // RawConfig supports a query-like interface to request 25 // information from deep within the structure. 26 type RawConfig struct { 27 Key string 28 Raw map[string]interface{} 29 Interpolations []Interpolation 30 Variables map[string]InterpolatedVariable 31 32 config map[string]interface{} 33 unknownKeys []string 34 } 35 36 // NewRawConfig creates a new RawConfig structure and populates the 37 // publicly readable struct fields. 38 func NewRawConfig(raw map[string]interface{}) (*RawConfig, error) { 39 result := &RawConfig{Raw: raw} 40 if err := result.init(); err != nil { 41 return nil, err 42 } 43 44 return result, nil 45 } 46 47 // Value returns the value of the configuration if this configuration 48 // has a Key set. If this does not have a Key set, nil will be returned. 49 func (r *RawConfig) Value() interface{} { 50 if c := r.Config(); c != nil { 51 if v, ok := c[r.Key]; ok { 52 return v 53 } 54 } 55 56 return r.Raw[r.Key] 57 } 58 59 // Config returns the entire configuration with the variables 60 // interpolated from any call to Interpolate. 61 // 62 // If any interpolated variables are unknown (value set to 63 // UnknownVariableValue), the first non-container (map, slice, etc.) element 64 // will be removed from the config. The keys of unknown variables 65 // can be found using the UnknownKeys function. 66 // 67 // By pruning out unknown keys from the configuration, the raw 68 // structure will always successfully decode into its ultimate 69 // structure using something like mapstructure. 70 func (r *RawConfig) Config() map[string]interface{} { 71 return r.config 72 } 73 74 // Interpolate uses the given mapping of variable values and uses 75 // those as the values to replace any variables in this raw 76 // configuration. 77 // 78 // Any prior calls to Interpolate are replaced with this one. 79 // 80 // If a variable key is missing, this will panic. 81 func (r *RawConfig) Interpolate(vs map[string]string) error { 82 return r.interpolate(func(i Interpolation) (string, error) { 83 return i.Interpolate(vs) 84 }) 85 } 86 87 func (r *RawConfig) init() error { 88 r.config = r.Raw 89 r.Interpolations = nil 90 r.Variables = nil 91 92 fn := func(i Interpolation) (string, error) { 93 r.Interpolations = append(r.Interpolations, i) 94 95 for k, v := range i.Variables() { 96 if r.Variables == nil { 97 r.Variables = make(map[string]InterpolatedVariable) 98 } 99 100 r.Variables[k] = v 101 } 102 103 return "", nil 104 } 105 106 walker := &interpolationWalker{F: fn} 107 if err := reflectwalk.Walk(r.Raw, walker); err != nil { 108 return err 109 } 110 111 return nil 112 } 113 114 func (r *RawConfig) interpolate(fn interpolationWalkerFunc) error { 115 config, err := copystructure.Copy(r.Raw) 116 if err != nil { 117 return err 118 } 119 r.config = config.(map[string]interface{}) 120 121 w := &interpolationWalker{F: fn, Replace: true} 122 err = reflectwalk.Walk(r.config, w) 123 if err != nil { 124 return err 125 } 126 127 r.unknownKeys = w.unknownKeys 128 return nil 129 } 130 131 func (r *RawConfig) merge(r2 *RawConfig) *RawConfig { 132 rawRaw, err := copystructure.Copy(r.Raw) 133 if err != nil { 134 panic(err) 135 } 136 137 raw := rawRaw.(map[string]interface{}) 138 for k, v := range r2.Raw { 139 raw[k] = v 140 } 141 142 result, err := NewRawConfig(raw) 143 if err != nil { 144 panic(err) 145 } 146 147 return result 148 } 149 150 // UnknownKeys returns the keys of the configuration that are unknown 151 // because they had interpolated variables that must be computed. 152 func (r *RawConfig) UnknownKeys() []string { 153 return r.unknownKeys 154 } 155 156 // See GobEncode 157 func (r *RawConfig) GobDecode(b []byte) error { 158 var data gobRawConfig 159 err := gob.NewDecoder(bytes.NewReader(b)).Decode(&data) 160 if err != nil { 161 return err 162 } 163 164 r.Key = data.Key 165 r.Raw = data.Raw 166 167 return r.init() 168 } 169 170 // GobEncode is a custom Gob encoder to use so that we only include the 171 // raw configuration. Interpolated variables and such are lost and the 172 // tree of interpolated variables is recomputed on decode, since it is 173 // referentially transparent. 174 func (r *RawConfig) GobEncode() ([]byte, error) { 175 data := gobRawConfig{ 176 Key: r.Key, 177 Raw: r.Raw, 178 } 179 180 var buf bytes.Buffer 181 if err := gob.NewEncoder(&buf).Encode(data); err != nil { 182 return nil, err 183 } 184 185 return buf.Bytes(), nil 186 } 187 188 type gobRawConfig struct { 189 Key string 190 Raw map[string]interface{} 191 }