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