github.com/ezbercih/terraform@v0.1.1-0.20140729011846-3c33865e0839/builtin/providers/heroku/resource_heroku_app.go (about) 1 package heroku 2 3 import ( 4 "fmt" 5 "log" 6 7 "github.com/bgentry/heroku-go" 8 "github.com/hashicorp/terraform/flatmap" 9 "github.com/hashicorp/terraform/helper/config" 10 "github.com/hashicorp/terraform/helper/diff" 11 "github.com/hashicorp/terraform/helper/multierror" 12 "github.com/hashicorp/terraform/terraform" 13 ) 14 15 // type application is used to store all the details of a heroku app 16 type application struct { 17 Id string // Id of the resource 18 19 App *heroku.App // The heroku application 20 Client *heroku.Client // Client to interact with the heroku API 21 Vars map[string]string // The vars on the application 22 } 23 24 // Updates the application to have the latest from remote 25 func (a *application) Update() error { 26 var errs []error 27 var err error 28 29 a.App, err = a.Client.AppInfo(a.Id) 30 if err != nil { 31 errs = append(errs, err) 32 } 33 34 a.Vars, err = retrieve_config_vars(a.Id, a.Client) 35 if err != nil { 36 errs = append(errs, err) 37 } 38 39 if len(errs) > 0 { 40 return &multierror.Error{Errors: errs} 41 } 42 43 return nil 44 } 45 46 func resource_heroku_app_create( 47 s *terraform.ResourceState, 48 d *terraform.ResourceDiff, 49 meta interface{}) (*terraform.ResourceState, error) { 50 p := meta.(*ResourceProvider) 51 client := p.client 52 53 // Merge the diff into the state so that we have all the attributes 54 // properly. 55 rs := s.MergeDiff(d) 56 57 // Build up our creation options 58 opts := heroku.AppCreateOpts{} 59 60 if attr := rs.Attributes["name"]; attr != "" { 61 opts.Name = &attr 62 } 63 64 if attr := rs.Attributes["region"]; attr != "" { 65 opts.Region = &attr 66 } 67 68 if attr := rs.Attributes["stack"]; attr != "" { 69 opts.Stack = &attr 70 } 71 72 log.Printf("[DEBUG] App create configuration: %#v", opts) 73 74 a, err := client.AppCreate(&opts) 75 if err != nil { 76 return s, err 77 } 78 79 rs.ID = a.Name 80 log.Printf("[INFO] App ID: %s", rs.ID) 81 82 if attr, ok := rs.Attributes["config_vars.#"]; ok && attr == "1" { 83 vs := flatmap.Expand( 84 rs.Attributes, "config_vars").([]interface{}) 85 86 err = update_config_vars(rs.ID, vs, client) 87 if err != nil { 88 return rs, err 89 } 90 } 91 92 app, err := resource_heroku_app_retrieve(rs.ID, client) 93 if err != nil { 94 return rs, err 95 } 96 97 return resource_heroku_app_update_state(rs, app) 98 } 99 100 func resource_heroku_app_update( 101 s *terraform.ResourceState, 102 d *terraform.ResourceDiff, 103 meta interface{}) (*terraform.ResourceState, error) { 104 p := meta.(*ResourceProvider) 105 client := p.client 106 rs := s.MergeDiff(d) 107 108 if attr, ok := d.Attributes["name"]; ok { 109 opts := heroku.AppUpdateOpts{ 110 Name: &attr.New, 111 } 112 113 renamedApp, err := client.AppUpdate(rs.ID, &opts) 114 115 if err != nil { 116 return s, err 117 } 118 119 // Store the new ID 120 rs.ID = renamedApp.Name 121 } 122 123 attr, ok := s.Attributes["config_vars.#"] 124 125 // If the config var block was removed, nuke all config vars 126 if ok && attr == "1" { 127 vs := flatmap.Expand( 128 rs.Attributes, "config_vars").([]interface{}) 129 130 err := update_config_vars(rs.ID, vs, client) 131 if err != nil { 132 return rs, err 133 } 134 } else if ok && attr == "0" { 135 log.Println("[INFO] Config vars removed, removing all vars") 136 137 err := update_config_vars(rs.ID, make([]interface{}, 0), client) 138 139 if err != nil { 140 return rs, err 141 } 142 } 143 144 app, err := resource_heroku_app_retrieve(rs.ID, client) 145 if err != nil { 146 return rs, err 147 } 148 149 return resource_heroku_app_update_state(rs, app) 150 } 151 152 func resource_heroku_app_destroy( 153 s *terraform.ResourceState, 154 meta interface{}) error { 155 p := meta.(*ResourceProvider) 156 client := p.client 157 158 log.Printf("[INFO] Deleting App: %s", s.ID) 159 160 // Destroy the app 161 err := client.AppDelete(s.ID) 162 163 if err != nil { 164 return fmt.Errorf("Error deleting App: %s", err) 165 } 166 167 return nil 168 } 169 170 func resource_heroku_app_refresh( 171 s *terraform.ResourceState, 172 meta interface{}) (*terraform.ResourceState, error) { 173 p := meta.(*ResourceProvider) 174 client := p.client 175 176 app, err := resource_heroku_app_retrieve(s.ID, client) 177 if err != nil { 178 return nil, err 179 } 180 181 return resource_heroku_app_update_state(s, app) 182 } 183 184 func resource_heroku_app_diff( 185 s *terraform.ResourceState, 186 c *terraform.ResourceConfig, 187 meta interface{}) (*terraform.ResourceDiff, error) { 188 189 b := &diff.ResourceBuilder{ 190 Attrs: map[string]diff.AttrType{ 191 "name": diff.AttrTypeUpdate, 192 "region": diff.AttrTypeUpdate, 193 "stack": diff.AttrTypeCreate, 194 "config_vars": diff.AttrTypeUpdate, 195 }, 196 197 ComputedAttrs: []string{ 198 "name", 199 "region", 200 "stack", 201 "git_url", 202 "web_url", 203 "id", 204 "config_vars", 205 }, 206 207 ComputedAttrsUpdate: []string{ 208 "heroku_hostname", 209 }, 210 } 211 212 return b.Diff(s, c) 213 } 214 215 func resource_heroku_app_update_state( 216 s *terraform.ResourceState, 217 app *application) (*terraform.ResourceState, error) { 218 219 s.Attributes["name"] = app.App.Name 220 s.Attributes["stack"] = app.App.Stack.Name 221 s.Attributes["region"] = app.App.Region.Name 222 s.Attributes["git_url"] = app.App.GitURL 223 s.Attributes["web_url"] = app.App.WebURL 224 225 // We know that the hostname on heroku will be the name+herokuapp.com 226 // You need this to do things like create DNS CNAME records 227 s.Attributes["heroku_hostname"] = fmt.Sprintf("%s.herokuapp.com", app.App.Name) 228 229 toFlatten := make(map[string]interface{}) 230 231 if len(app.Vars) > 0 { 232 toFlatten["config_vars"] = []map[string]string{app.Vars} 233 } 234 235 for k, v := range flatmap.Flatten(toFlatten) { 236 s.Attributes[k] = v 237 } 238 239 return s, nil 240 } 241 242 func resource_heroku_app_retrieve(id string, client *heroku.Client) (*application, error) { 243 app := application{Id: id, Client: client} 244 245 err := app.Update() 246 247 if err != nil { 248 return nil, fmt.Errorf("Error retrieving app: %s", err) 249 } 250 251 return &app, nil 252 } 253 254 func resource_heroku_app_validation() *config.Validator { 255 return &config.Validator{ 256 Required: []string{}, 257 Optional: []string{ 258 "name", 259 "region", 260 "stack", 261 "config_vars.*", 262 }, 263 } 264 } 265 266 func retrieve_config_vars(id string, client *heroku.Client) (map[string]string, error) { 267 vars, err := client.ConfigVarInfo(id) 268 269 if err != nil { 270 return nil, err 271 } 272 273 return vars, nil 274 } 275 276 // Updates the config vars for from an expanded (prior to assertion) 277 // []map[string]string config 278 func update_config_vars(id string, vs []interface{}, client *heroku.Client) error { 279 vars := make(map[string]*string) 280 281 for k, v := range vs[0].(map[string]interface{}) { 282 val := v.(string) 283 vars[k] = &val 284 } 285 286 log.Printf("[INFO] Updating config vars: *%#v", vars) 287 288 _, err := client.ConfigVarUpdate(id, vars) 289 290 if err != nil { 291 return fmt.Errorf("Error updating config vars: %s", err) 292 } 293 294 return nil 295 }