github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/terraform/resource.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "reflect" 6 "sort" 7 "strconv" 8 "strings" 9 10 "github.com/hashicorp/terraform/config" 11 "github.com/mitchellh/copystructure" 12 "github.com/mitchellh/reflectwalk" 13 ) 14 15 // ResourceProvisionerConfig is used to pair a provisioner 16 // with its provided configuration. This allows us to use singleton 17 // instances of each ResourceProvisioner and to keep the relevant 18 // configuration instead of instantiating a new Provisioner for each 19 // resource. 20 type ResourceProvisionerConfig struct { 21 Type string 22 Provisioner ResourceProvisioner 23 Config *ResourceConfig 24 RawConfig *config.RawConfig 25 ConnInfo *config.RawConfig 26 } 27 28 // Resource encapsulates a resource, its configuration, its provider, 29 // its current state, and potentially a desired diff from the state it 30 // wants to reach. 31 type Resource struct { 32 // These are all used by the new EvalNode stuff. 33 Name string 34 Type string 35 CountIndex int 36 37 // These aren't really used anymore anywhere, but we keep them around 38 // since we haven't done a proper cleanup yet. 39 Id string 40 Info *InstanceInfo 41 Config *ResourceConfig 42 Dependencies []string 43 Diff *InstanceDiff 44 Provider ResourceProvider 45 State *InstanceState 46 Provisioners []*ResourceProvisionerConfig 47 Flags ResourceFlag 48 } 49 50 // ResourceKind specifies what kind of instance we're working with, whether 51 // its a primary instance, a tainted instance, or an orphan. 52 type ResourceFlag byte 53 54 // InstanceInfo is used to hold information about the instance and/or 55 // resource being modified. 56 type InstanceInfo struct { 57 // Id is a unique name to represent this instance. This is not related 58 // to InstanceState.ID in any way. 59 Id string 60 61 // ModulePath is the complete path of the module containing this 62 // instance. 63 ModulePath []string 64 65 // Type is the resource type of this instance 66 Type string 67 68 // uniqueExtra is an internal field that can be populated to supply 69 // extra metadata that is used to identify a unique instance in 70 // the graph walk. This will be appended to HumanID when uniqueId 71 // is called. 72 uniqueExtra string 73 } 74 75 // HumanId is a unique Id that is human-friendly and useful for UI elements. 76 func (i *InstanceInfo) HumanId() string { 77 if i == nil { 78 return "<nil>" 79 } 80 81 if len(i.ModulePath) <= 1 { 82 return i.Id 83 } 84 85 return fmt.Sprintf( 86 "module.%s.%s", 87 strings.Join(i.ModulePath[1:], "."), 88 i.Id) 89 } 90 91 func (i *InstanceInfo) uniqueId() string { 92 prefix := i.HumanId() 93 if v := i.uniqueExtra; v != "" { 94 prefix += " " + v 95 } 96 97 return prefix 98 } 99 100 // ResourceConfig holds the configuration given for a resource. This is 101 // done instead of a raw `map[string]interface{}` type so that rich 102 // methods can be added to it to make dealing with it easier. 103 type ResourceConfig struct { 104 ComputedKeys []string 105 Raw map[string]interface{} 106 Config map[string]interface{} 107 108 raw *config.RawConfig 109 } 110 111 // NewResourceConfig creates a new ResourceConfig from a config.RawConfig. 112 func NewResourceConfig(c *config.RawConfig) *ResourceConfig { 113 result := &ResourceConfig{raw: c} 114 result.interpolateForce() 115 return result 116 } 117 118 // DeepCopy performs a deep copy of the configuration. This makes it safe 119 // to modify any of the structures that are part of the resource config without 120 // affecting the original configuration. 121 func (c *ResourceConfig) DeepCopy() *ResourceConfig { 122 // DeepCopying a nil should return a nil to avoid panics 123 if c == nil { 124 return nil 125 } 126 127 // Copy, this will copy all the exported attributes 128 copy, err := copystructure.Config{Lock: true}.Copy(c) 129 if err != nil { 130 panic(err) 131 } 132 133 // Force the type 134 result := copy.(*ResourceConfig) 135 136 // For the raw configuration, we can just use its own copy method 137 result.raw = c.raw.Copy() 138 139 return result 140 } 141 142 // Equal checks the equality of two resource configs. 143 func (c *ResourceConfig) Equal(c2 *ResourceConfig) bool { 144 // If either are nil, then they're only equal if they're both nil 145 if c == nil || c2 == nil { 146 return c == c2 147 } 148 149 // Sort the computed keys so they're deterministic 150 sort.Strings(c.ComputedKeys) 151 sort.Strings(c2.ComputedKeys) 152 153 // Two resource configs if their exported properties are equal. 154 // We don't compare "raw" because it is never used again after 155 // initialization and for all intents and purposes they are equal 156 // if the exported properties are equal. 157 check := [][2]interface{}{ 158 {c.ComputedKeys, c2.ComputedKeys}, 159 {c.Raw, c2.Raw}, 160 {c.Config, c2.Config}, 161 } 162 for _, pair := range check { 163 if !reflect.DeepEqual(pair[0], pair[1]) { 164 return false 165 } 166 } 167 168 return true 169 } 170 171 // CheckSet checks that the given list of configuration keys is 172 // properly set. If not, errors are returned for each unset key. 173 // 174 // This is useful to be called in the Validate method of a ResourceProvider. 175 func (c *ResourceConfig) CheckSet(keys []string) []error { 176 var errs []error 177 178 for _, k := range keys { 179 if !c.IsSet(k) { 180 errs = append(errs, fmt.Errorf("%s must be set", k)) 181 } 182 } 183 184 return errs 185 } 186 187 // Get looks up a configuration value by key and returns the value. 188 // 189 // The second return value is true if the get was successful. Get will 190 // return the raw value if the key is computed, so you should pair this 191 // with IsComputed. 192 func (c *ResourceConfig) Get(k string) (interface{}, bool) { 193 // We aim to get a value from the configuration. If it is computed, 194 // then we return the pure raw value. 195 source := c.Config 196 if c.IsComputed(k) { 197 source = c.Raw 198 } 199 200 return c.get(k, source) 201 } 202 203 // GetRaw looks up a configuration value by key and returns the value, 204 // from the raw, uninterpolated config. 205 // 206 // The second return value is true if the get was successful. Get will 207 // not succeed if the value is being computed. 208 func (c *ResourceConfig) GetRaw(k string) (interface{}, bool) { 209 return c.get(k, c.Raw) 210 } 211 212 // IsComputed returns whether the given key is computed or not. 213 func (c *ResourceConfig) IsComputed(k string) bool { 214 // The next thing we do is check the config if we get a computed 215 // value out of it. 216 v, ok := c.get(k, c.Config) 217 if !ok { 218 return false 219 } 220 221 // If value is nil, then it isn't computed 222 if v == nil { 223 return false 224 } 225 226 // Test if the value contains an unknown value 227 var w unknownCheckWalker 228 if err := reflectwalk.Walk(v, &w); err != nil { 229 panic(err) 230 } 231 232 return w.Unknown 233 } 234 235 // IsSet checks if the key in the configuration is set. A key is set if 236 // it has a value or the value is being computed (is unknown currently). 237 // 238 // This function should be used rather than checking the keys of the 239 // raw configuration itself, since a key may be omitted from the raw 240 // configuration if it is being computed. 241 func (c *ResourceConfig) IsSet(k string) bool { 242 if c == nil { 243 return false 244 } 245 246 if c.IsComputed(k) { 247 return true 248 } 249 250 if _, ok := c.Get(k); ok { 251 return true 252 } 253 254 return false 255 } 256 257 func (c *ResourceConfig) get( 258 k string, raw map[string]interface{}) (interface{}, bool) { 259 parts := strings.Split(k, ".") 260 if len(parts) == 1 && parts[0] == "" { 261 parts = nil 262 } 263 264 var current interface{} = raw 265 var previous interface{} = nil 266 for i, part := range parts { 267 if current == nil { 268 return nil, false 269 } 270 271 cv := reflect.ValueOf(current) 272 switch cv.Kind() { 273 case reflect.Map: 274 previous = current 275 v := cv.MapIndex(reflect.ValueOf(part)) 276 if !v.IsValid() { 277 if i > 0 && i != (len(parts)-1) { 278 tryKey := strings.Join(parts[i:], ".") 279 v := cv.MapIndex(reflect.ValueOf(tryKey)) 280 if !v.IsValid() { 281 return nil, false 282 } 283 284 return v.Interface(), true 285 } 286 287 return nil, false 288 } 289 290 current = v.Interface() 291 case reflect.Slice: 292 previous = current 293 294 if part == "#" { 295 // If any value in a list is computed, this whole thing 296 // is computed and we can't read any part of it. 297 for i := 0; i < cv.Len(); i++ { 298 if v := cv.Index(i).Interface(); v == unknownValue() { 299 return v, true 300 } 301 } 302 303 current = cv.Len() 304 } else { 305 i, err := strconv.ParseInt(part, 0, 0) 306 if err != nil { 307 return nil, false 308 } 309 if i >= int64(cv.Len()) { 310 return nil, false 311 } 312 current = cv.Index(int(i)).Interface() 313 } 314 case reflect.String: 315 // This happens when map keys contain "." and have a common 316 // prefix so were split as path components above. 317 actualKey := strings.Join(parts[i-1:], ".") 318 if prevMap, ok := previous.(map[string]interface{}); ok { 319 v, ok := prevMap[actualKey] 320 return v, ok 321 } 322 323 return nil, false 324 default: 325 panic(fmt.Sprintf("Unknown kind: %s", cv.Kind())) 326 } 327 } 328 329 return current, true 330 } 331 332 // interpolateForce is a temporary thing. We want to get rid of interpolate 333 // above and likewise this, but it can only be done after the f-ast-graph 334 // refactor is complete. 335 func (c *ResourceConfig) interpolateForce() { 336 if c.raw == nil { 337 var err error 338 c.raw, err = config.NewRawConfig(make(map[string]interface{})) 339 if err != nil { 340 panic(err) 341 } 342 } 343 344 c.ComputedKeys = c.raw.UnknownKeys() 345 c.Raw = c.raw.RawMap() 346 c.Config = c.raw.Config() 347 } 348 349 // unknownCheckWalker 350 type unknownCheckWalker struct { 351 Unknown bool 352 } 353 354 func (w *unknownCheckWalker) Primitive(v reflect.Value) error { 355 if v.Interface() == unknownValue() { 356 w.Unknown = true 357 } 358 359 return nil 360 }