github.com/hs0210/hashicorp-terraform@v0.11.12-beta1/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 // ResourceAddress returns the address of the resource that the receiver is describing. 92 func (i *InstanceInfo) ResourceAddress() *ResourceAddress { 93 // GROSS: for tainted and deposed instances, their status gets appended 94 // to i.Id to create a unique id for the graph node. Historically these 95 // ids were displayed to the user, so it's designed to be human-readable: 96 // "aws_instance.bar.0 (deposed #0)" 97 // 98 // So here we detect such suffixes and try to interpret them back to 99 // their original meaning so we can then produce a ResourceAddress 100 // with a suitable InstanceType. 101 id := i.Id 102 instanceType := TypeInvalid 103 if idx := strings.Index(id, " ("); idx != -1 { 104 remain := id[idx:] 105 id = id[:idx] 106 107 switch { 108 case strings.Contains(remain, "tainted"): 109 instanceType = TypeTainted 110 case strings.Contains(remain, "deposed"): 111 instanceType = TypeDeposed 112 } 113 } 114 115 addr, err := parseResourceAddressInternal(id) 116 if err != nil { 117 // should never happen, since that would indicate a bug in the 118 // code that constructed this InstanceInfo. 119 panic(fmt.Errorf("InstanceInfo has invalid Id %s", id)) 120 } 121 if len(i.ModulePath) > 1 { 122 addr.Path = i.ModulePath[1:] // trim off "root" prefix, which is implied 123 } 124 if instanceType != TypeInvalid { 125 addr.InstanceTypeSet = true 126 addr.InstanceType = instanceType 127 } 128 return addr 129 } 130 131 func (i *InstanceInfo) uniqueId() string { 132 prefix := i.HumanId() 133 if v := i.uniqueExtra; v != "" { 134 prefix += " " + v 135 } 136 137 return prefix 138 } 139 140 // ResourceConfig holds the configuration given for a resource. This is 141 // done instead of a raw `map[string]interface{}` type so that rich 142 // methods can be added to it to make dealing with it easier. 143 type ResourceConfig struct { 144 ComputedKeys []string 145 Raw map[string]interface{} 146 Config map[string]interface{} 147 148 raw *config.RawConfig 149 } 150 151 // NewResourceConfig creates a new ResourceConfig from a config.RawConfig. 152 func NewResourceConfig(c *config.RawConfig) *ResourceConfig { 153 result := &ResourceConfig{raw: c} 154 result.interpolateForce() 155 return result 156 } 157 158 // DeepCopy performs a deep copy of the configuration. This makes it safe 159 // to modify any of the structures that are part of the resource config without 160 // affecting the original configuration. 161 func (c *ResourceConfig) DeepCopy() *ResourceConfig { 162 // DeepCopying a nil should return a nil to avoid panics 163 if c == nil { 164 return nil 165 } 166 167 // Copy, this will copy all the exported attributes 168 copy, err := copystructure.Config{Lock: true}.Copy(c) 169 if err != nil { 170 panic(err) 171 } 172 173 // Force the type 174 result := copy.(*ResourceConfig) 175 176 // For the raw configuration, we can just use its own copy method 177 result.raw = c.raw.Copy() 178 179 return result 180 } 181 182 // Equal checks the equality of two resource configs. 183 func (c *ResourceConfig) Equal(c2 *ResourceConfig) bool { 184 // If either are nil, then they're only equal if they're both nil 185 if c == nil || c2 == nil { 186 return c == c2 187 } 188 189 // Sort the computed keys so they're deterministic 190 sort.Strings(c.ComputedKeys) 191 sort.Strings(c2.ComputedKeys) 192 193 // Two resource configs if their exported properties are equal. 194 // We don't compare "raw" because it is never used again after 195 // initialization and for all intents and purposes they are equal 196 // if the exported properties are equal. 197 check := [][2]interface{}{ 198 {c.ComputedKeys, c2.ComputedKeys}, 199 {c.Raw, c2.Raw}, 200 {c.Config, c2.Config}, 201 } 202 for _, pair := range check { 203 if !reflect.DeepEqual(pair[0], pair[1]) { 204 return false 205 } 206 } 207 208 return true 209 } 210 211 // CheckSet checks that the given list of configuration keys is 212 // properly set. If not, errors are returned for each unset key. 213 // 214 // This is useful to be called in the Validate method of a ResourceProvider. 215 func (c *ResourceConfig) CheckSet(keys []string) []error { 216 var errs []error 217 218 for _, k := range keys { 219 if !c.IsSet(k) { 220 errs = append(errs, fmt.Errorf("%s must be set", k)) 221 } 222 } 223 224 return errs 225 } 226 227 // Get looks up a configuration value by key and returns the value. 228 // 229 // The second return value is true if the get was successful. Get will 230 // return the raw value if the key is computed, so you should pair this 231 // with IsComputed. 232 func (c *ResourceConfig) Get(k string) (interface{}, bool) { 233 // We aim to get a value from the configuration. If it is computed, 234 // then we return the pure raw value. 235 source := c.Config 236 if c.IsComputed(k) { 237 source = c.Raw 238 } 239 240 return c.get(k, source) 241 } 242 243 // GetRaw looks up a configuration value by key and returns the value, 244 // from the raw, uninterpolated config. 245 // 246 // The second return value is true if the get was successful. Get will 247 // not succeed if the value is being computed. 248 func (c *ResourceConfig) GetRaw(k string) (interface{}, bool) { 249 return c.get(k, c.Raw) 250 } 251 252 // IsComputed returns whether the given key is computed or not. 253 func (c *ResourceConfig) IsComputed(k string) bool { 254 // The next thing we do is check the config if we get a computed 255 // value out of it. 256 v, ok := c.get(k, c.Config) 257 if !ok { 258 return false 259 } 260 261 // If value is nil, then it isn't computed 262 if v == nil { 263 return false 264 } 265 266 // Test if the value contains an unknown value 267 var w unknownCheckWalker 268 if err := reflectwalk.Walk(v, &w); err != nil { 269 panic(err) 270 } 271 272 return w.Unknown 273 } 274 275 // IsSet checks if the key in the configuration is set. A key is set if 276 // it has a value or the value is being computed (is unknown currently). 277 // 278 // This function should be used rather than checking the keys of the 279 // raw configuration itself, since a key may be omitted from the raw 280 // configuration if it is being computed. 281 func (c *ResourceConfig) IsSet(k string) bool { 282 if c == nil { 283 return false 284 } 285 286 if c.IsComputed(k) { 287 return true 288 } 289 290 if _, ok := c.Get(k); ok { 291 return true 292 } 293 294 return false 295 } 296 297 func (c *ResourceConfig) get( 298 k string, raw map[string]interface{}) (interface{}, bool) { 299 parts := strings.Split(k, ".") 300 if len(parts) == 1 && parts[0] == "" { 301 parts = nil 302 } 303 304 var current interface{} = raw 305 var previous interface{} = nil 306 for i, part := range parts { 307 if current == nil { 308 return nil, false 309 } 310 311 cv := reflect.ValueOf(current) 312 switch cv.Kind() { 313 case reflect.Map: 314 previous = current 315 v := cv.MapIndex(reflect.ValueOf(part)) 316 if !v.IsValid() { 317 if i > 0 && i != (len(parts)-1) { 318 tryKey := strings.Join(parts[i:], ".") 319 v := cv.MapIndex(reflect.ValueOf(tryKey)) 320 if !v.IsValid() { 321 return nil, false 322 } 323 324 return v.Interface(), true 325 } 326 327 return nil, false 328 } 329 330 current = v.Interface() 331 case reflect.Slice: 332 previous = current 333 334 if part == "#" { 335 // If any value in a list is computed, this whole thing 336 // is computed and we can't read any part of it. 337 for i := 0; i < cv.Len(); i++ { 338 if v := cv.Index(i).Interface(); v == unknownValue() { 339 return v, true 340 } 341 } 342 343 current = cv.Len() 344 } else { 345 i, err := strconv.ParseInt(part, 0, 0) 346 if err != nil { 347 return nil, false 348 } 349 if int(i) < 0 || int(i) >= cv.Len() { 350 return nil, false 351 } 352 current = cv.Index(int(i)).Interface() 353 } 354 case reflect.String: 355 // This happens when map keys contain "." and have a common 356 // prefix so were split as path components above. 357 actualKey := strings.Join(parts[i-1:], ".") 358 if prevMap, ok := previous.(map[string]interface{}); ok { 359 v, ok := prevMap[actualKey] 360 return v, ok 361 } 362 363 return nil, false 364 default: 365 panic(fmt.Sprintf("Unknown kind: %s", cv.Kind())) 366 } 367 } 368 369 return current, true 370 } 371 372 // interpolateForce is a temporary thing. We want to get rid of interpolate 373 // above and likewise this, but it can only be done after the f-ast-graph 374 // refactor is complete. 375 func (c *ResourceConfig) interpolateForce() { 376 if c.raw == nil { 377 var err error 378 c.raw, err = config.NewRawConfig(make(map[string]interface{})) 379 if err != nil { 380 panic(err) 381 } 382 } 383 384 c.ComputedKeys = c.raw.UnknownKeys() 385 c.Raw = c.raw.RawMap() 386 c.Config = c.raw.Config() 387 } 388 389 // unknownCheckWalker 390 type unknownCheckWalker struct { 391 Unknown bool 392 } 393 394 func (w *unknownCheckWalker) Primitive(v reflect.Value) error { 395 if v.Interface() == unknownValue() { 396 w.Unknown = true 397 } 398 399 return nil 400 }