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