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