github.com/memsql/terraform@v0.7.0-rc2.0.20160706152241-21e2173e0a32/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 const ( 52 FlagPrimary ResourceFlag = 1 << iota 53 FlagTainted 54 FlagOrphan 55 FlagReplacePrimary 56 FlagDeposed 57 ) 58 59 // InstanceInfo is used to hold information about the instance and/or 60 // resource being modified. 61 type InstanceInfo struct { 62 // Id is a unique name to represent this instance. This is not related 63 // to InstanceState.ID in any way. 64 Id string 65 66 // ModulePath is the complete path of the module containing this 67 // instance. 68 ModulePath []string 69 70 // Type is the resource type of this instance 71 Type string 72 } 73 74 // HumanId is a unique Id that is human-friendly and useful for UI elements. 75 func (i *InstanceInfo) HumanId() string { 76 if len(i.ModulePath) <= 1 { 77 return i.Id 78 } 79 80 return fmt.Sprintf( 81 "module.%s.%s", 82 strings.Join(i.ModulePath[1:], "."), 83 i.Id) 84 } 85 86 // ResourceConfig holds the configuration given for a resource. This is 87 // done instead of a raw `map[string]interface{}` type so that rich 88 // methods can be added to it to make dealing with it easier. 89 type ResourceConfig struct { 90 ComputedKeys []string 91 Raw map[string]interface{} 92 Config map[string]interface{} 93 94 raw *config.RawConfig 95 } 96 97 // NewResourceConfig creates a new ResourceConfig from a config.RawConfig. 98 func NewResourceConfig(c *config.RawConfig) *ResourceConfig { 99 result := &ResourceConfig{raw: c} 100 result.interpolateForce() 101 return result 102 } 103 104 // CheckSet checks that the given list of configuration keys is 105 // properly set. If not, errors are returned for each unset key. 106 // 107 // This is useful to be called in the Validate method of a ResourceProvider. 108 func (c *ResourceConfig) CheckSet(keys []string) []error { 109 var errs []error 110 111 for _, k := range keys { 112 if !c.IsSet(k) { 113 errs = append(errs, fmt.Errorf("%s must be set", k)) 114 } 115 } 116 117 return errs 118 } 119 120 // Get looks up a configuration value by key and returns the value. 121 // 122 // The second return value is true if the get was successful. Get will 123 // not succeed if the value is being computed. 124 func (c *ResourceConfig) Get(k string) (interface{}, bool) { 125 // First try to get it from c.Config since that has interpolated values 126 result, ok := c.get(k, c.Config) 127 if ok { 128 return result, ok 129 } 130 131 // Otherwise, just get it from the raw config 132 return c.get(k, c.Raw) 133 } 134 135 // GetRaw looks up a configuration value by key and returns the value, 136 // from the raw, uninterpolated config. 137 // 138 // The second return value is true if the get was successful. Get will 139 // not succeed if the value is being computed. 140 func (c *ResourceConfig) GetRaw(k string) (interface{}, bool) { 141 return c.get(k, c.Raw) 142 } 143 144 // IsComputed returns whether the given key is computed or not. 145 func (c *ResourceConfig) IsComputed(k string) bool { 146 _, ok := c.get(k, c.Config) 147 _, okRaw := c.get(k, c.Raw) 148 return !ok && okRaw 149 } 150 151 // IsSet checks if the key in the configuration is set. A key is set if 152 // it has a value or the value is being computed (is unknown currently). 153 // 154 // This function should be used rather than checking the keys of the 155 // raw configuration itself, since a key may be omitted from the raw 156 // configuration if it is being computed. 157 func (c *ResourceConfig) IsSet(k string) bool { 158 if c == nil { 159 return false 160 } 161 162 for _, ck := range c.ComputedKeys { 163 if ck == k { 164 return true 165 } 166 } 167 168 if _, ok := c.Get(k); ok { 169 return true 170 } 171 172 return false 173 } 174 175 func (c *ResourceConfig) get( 176 k string, raw map[string]interface{}) (interface{}, bool) { 177 parts := strings.Split(k, ".") 178 if len(parts) == 1 && parts[0] == "" { 179 parts = nil 180 } 181 182 var current interface{} = raw 183 for _, part := range parts { 184 if current == nil { 185 return nil, false 186 } 187 188 cv := reflect.ValueOf(current) 189 switch cv.Kind() { 190 case reflect.Map: 191 v := cv.MapIndex(reflect.ValueOf(part)) 192 if !v.IsValid() { 193 return nil, false 194 } 195 current = v.Interface() 196 case reflect.Slice: 197 if part == "#" { 198 current = cv.Len() 199 } else { 200 i, err := strconv.ParseInt(part, 0, 0) 201 if err != nil { 202 return nil, false 203 } 204 if i >= int64(cv.Len()) { 205 return nil, false 206 } 207 current = cv.Index(int(i)).Interface() 208 } 209 default: 210 panic(fmt.Sprintf("Unknown kind: %s", cv.Kind())) 211 } 212 } 213 214 return current, true 215 } 216 217 // interpolateForce is a temporary thing. We want to get rid of interpolate 218 // above and likewise this, but it can only be done after the f-ast-graph 219 // refactor is complete. 220 func (c *ResourceConfig) interpolateForce() { 221 if c.raw == nil { 222 var err error 223 c.raw, err = config.NewRawConfig(make(map[string]interface{})) 224 if err != nil { 225 panic(err) 226 } 227 } 228 229 c.ComputedKeys = c.raw.UnknownKeys() 230 c.Raw = c.raw.Raw 231 c.Config = c.raw.Config() 232 }