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