github.com/maheshbr/terraform@v0.3.1-0.20141020033300-deec7194a3ea/builtin/providers/heroku/resource_heroku_app.go (about) 1 package heroku 2 3 import ( 4 "fmt" 5 "log" 6 7 "github.com/cyberdelia/heroku-go/v3" 8 "github.com/hashicorp/terraform/helper/multierror" 9 "github.com/hashicorp/terraform/helper/schema" 10 ) 11 12 // type application is used to store all the details of a heroku app 13 type application struct { 14 Id string // Id of the resource 15 16 App *heroku.App // The heroku application 17 Client *heroku.Service // Client to interact with the heroku API 18 Vars map[string]string // The vars on the application 19 } 20 21 // Updates the application to have the latest from remote 22 func (a *application) Update() error { 23 var errs []error 24 var err error 25 26 a.App, err = a.Client.AppInfo(a.Id) 27 if err != nil { 28 errs = append(errs, err) 29 } 30 31 a.Vars, err = retrieve_config_vars(a.Id, a.Client) 32 if err != nil { 33 errs = append(errs, err) 34 } 35 36 if len(errs) > 0 { 37 return &multierror.Error{Errors: errs} 38 } 39 40 return nil 41 } 42 43 func resourceHerokuApp() *schema.Resource { 44 return &schema.Resource{ 45 Create: switchHerokuAppCreate, 46 Read: resourceHerokuAppRead, 47 Update: resourceHerokuAppUpdate, 48 Delete: resourceHerokuAppDelete, 49 50 Schema: map[string]*schema.Schema{ 51 "name": &schema.Schema{ 52 Type: schema.TypeString, 53 Required: true, 54 }, 55 56 "region": &schema.Schema{ 57 Type: schema.TypeString, 58 Required: true, 59 ForceNew: true, 60 }, 61 62 "stack": &schema.Schema{ 63 Type: schema.TypeString, 64 Optional: true, 65 Computed: true, 66 ForceNew: true, 67 }, 68 69 "config_vars": &schema.Schema{ 70 Type: schema.TypeList, 71 Optional: true, 72 Elem: &schema.Schema{ 73 Type: schema.TypeMap, 74 }, 75 }, 76 77 "all_config_vars": &schema.Schema{ 78 Type: schema.TypeMap, 79 Computed: true, 80 }, 81 82 "git_url": &schema.Schema{ 83 Type: schema.TypeString, 84 Computed: true, 85 }, 86 87 "web_url": &schema.Schema{ 88 Type: schema.TypeString, 89 Computed: true, 90 }, 91 92 "heroku_hostname": &schema.Schema{ 93 Type: schema.TypeString, 94 Computed: true, 95 }, 96 97 "organization": &schema.Schema{ 98 Type: schema.TypeString, 99 Description: "Name of Organization to create application in. Leave blank for personal apps.", 100 Optional: true, 101 ForceNew: true, 102 }, 103 }, 104 } 105 } 106 107 func switchHerokuAppCreate(d *schema.ResourceData, meta interface{}) error { 108 if _, ok := d.GetOk("organization"); ok { 109 return resourceHerokuOrgAppCreate(d, meta) 110 } else { 111 return resourceHerokuAppCreate(d, meta) 112 } 113 } 114 115 func resourceHerokuAppCreate(d *schema.ResourceData, meta interface{}) error { 116 client := meta.(*heroku.Service) 117 118 // Build up our creation options 119 opts := heroku.AppCreateOpts{} 120 121 if v, ok := d.GetOk("name"); ok { 122 vs := v.(string) 123 log.Printf("[DEBUG] App name: %s", vs) 124 opts.Name = &vs 125 } 126 if v, ok := d.GetOk("region"); ok { 127 vs := v.(string) 128 log.Printf("[DEBUG] App region: %s", vs) 129 opts.Region = &vs 130 } 131 if v, ok := d.GetOk("stack"); ok { 132 vs := v.(string) 133 log.Printf("[DEBUG] App stack: %s", vs) 134 opts.Stack = &vs 135 } 136 137 log.Printf("[DEBUG] Creating Heroku app...") 138 a, err := client.AppCreate(opts) 139 if err != nil { 140 return err 141 } 142 143 d.SetId(a.Name) 144 log.Printf("[INFO] App ID: %s", d.Id()) 145 146 if v, ok := d.GetOk("config_vars"); ok { 147 err = update_config_vars(d.Id(), client, nil, v.([]interface{})) 148 if err != nil { 149 return err 150 } 151 } 152 153 return resourceHerokuAppRead(d, meta) 154 } 155 156 func resourceHerokuOrgAppCreate(d *schema.ResourceData, meta interface{}) error { 157 client := meta.(*heroku.Service) 158 // Build up our creation options 159 opts := heroku.OrganizationAppCreateOpts{} 160 if v, ok := d.GetOk("organization"); ok { 161 vs := v.(string) 162 log.Printf("[DEBUG] App name: %s", vs) 163 opts.Organization = &vs 164 } 165 if v, ok := d.GetOk("name"); ok { 166 vs := v.(string) 167 log.Printf("[DEBUG] App name: %s", vs) 168 opts.Name = &vs 169 } 170 if v, ok := d.GetOk("region"); ok { 171 vs := v.(string) 172 log.Printf("[DEBUG] App region: %s", vs) 173 opts.Region = &vs 174 } 175 if v, ok := d.GetOk("stack"); ok { 176 vs := v.(string) 177 log.Printf("[DEBUG] App stack: %s", vs) 178 opts.Stack = &vs 179 } 180 181 log.Printf("[DEBUG] Creating Heroku app...") 182 a, err := client.OrganizationAppCreate(opts) 183 if err != nil { 184 return err 185 } 186 187 d.SetId(a.Name) 188 log.Printf("[INFO] App ID: %s", d.Id()) 189 190 if v, ok := d.GetOk("config_vars"); ok { 191 err = update_config_vars(d.Id(), client, nil, v.([]interface{})) 192 if err != nil { 193 return err 194 } 195 } 196 197 return resourceHerokuAppRead(d, meta) 198 } 199 200 func resourceHerokuAppRead(d *schema.ResourceData, meta interface{}) error { 201 client := meta.(*heroku.Service) 202 app, err := resource_heroku_app_retrieve(d.Id(), client) 203 if err != nil { 204 return err 205 } 206 207 // Only set the config_vars that we have set in the configuration. 208 // The "all_config_vars" field has all of them. 209 configVars := make(map[string]string) 210 care := make(map[string]struct{}) 211 for _, v := range d.Get("config_vars").([]interface{}) { 212 for k, _ := range v.(map[string]interface{}) { 213 care[k] = struct{}{} 214 } 215 } 216 for k, v := range app.Vars { 217 if _, ok := care[k]; ok { 218 configVars[k] = v 219 } 220 } 221 var configVarsValue []map[string]string 222 if len(configVars) > 0 { 223 configVarsValue = []map[string]string{configVars} 224 } 225 226 d.Set("name", app.App.Name) 227 d.Set("stack", app.App.Stack.Name) 228 d.Set("region", app.App.Region.Name) 229 d.Set("git_url", app.App.GitURL) 230 d.Set("web_url", app.App.WebURL) 231 d.Set("config_vars", configVarsValue) 232 d.Set("all_config_vars", app.Vars) 233 234 // We know that the hostname on heroku will be the name+herokuapp.com 235 // You need this to do things like create DNS CNAME records 236 d.Set("heroku_hostname", fmt.Sprintf("%s.herokuapp.com", app.App.Name)) 237 238 return nil 239 } 240 241 func resourceHerokuAppUpdate(d *schema.ResourceData, meta interface{}) error { 242 client := meta.(*heroku.Service) 243 244 // If name changed, update it 245 if d.HasChange("name") { 246 v := d.Get("name").(string) 247 opts := heroku.AppUpdateOpts{ 248 Name: &v, 249 } 250 251 renamedApp, err := client.AppUpdate(d.Id(), opts) 252 if err != nil { 253 return err 254 } 255 256 // Store the new ID 257 d.SetId(renamedApp.Name) 258 } 259 260 // If the config vars changed, then recalculate those 261 if d.HasChange("config_vars") { 262 o, n := d.GetChange("config_vars") 263 if o == nil { 264 o = []interface{}{} 265 } 266 if n == nil { 267 n = []interface{}{} 268 } 269 270 err := update_config_vars( 271 d.Id(), client, o.([]interface{}), n.([]interface{})) 272 if err != nil { 273 return err 274 } 275 } 276 277 return resourceHerokuAppRead(d, meta) 278 } 279 280 func resourceHerokuAppDelete(d *schema.ResourceData, meta interface{}) error { 281 client := meta.(*heroku.Service) 282 283 log.Printf("[INFO] Deleting App: %s", d.Id()) 284 err := client.AppDelete(d.Id()) 285 if err != nil { 286 return fmt.Errorf("Error deleting App: %s", err) 287 } 288 289 d.SetId("") 290 return nil 291 } 292 293 func resource_heroku_app_retrieve(id string, client *heroku.Service) (*application, error) { 294 app := application{Id: id, Client: client} 295 296 err := app.Update() 297 298 if err != nil { 299 return nil, fmt.Errorf("Error retrieving app: %s", err) 300 } 301 302 return &app, nil 303 } 304 305 func retrieve_config_vars(id string, client *heroku.Service) (map[string]string, error) { 306 vars, err := client.ConfigVarInfo(id) 307 308 if err != nil { 309 return nil, err 310 } 311 312 return vars, nil 313 } 314 315 // Updates the config vars for from an expanded configuration. 316 func update_config_vars( 317 id string, 318 client *heroku.Service, 319 o []interface{}, 320 n []interface{}) error { 321 vars := make(map[string]*string) 322 323 for _, v := range o { 324 for k, _ := range v.(map[string]interface{}) { 325 vars[k] = nil 326 } 327 } 328 for _, v := range n { 329 for k, v := range v.(map[string]interface{}) { 330 val := v.(string) 331 vars[k] = &val 332 } 333 } 334 335 log.Printf("[INFO] Updating config vars: *%#v", vars) 336 if _, err := client.ConfigVarUpdate(id, vars); err != nil { 337 return fmt.Errorf("Error updating config vars: %s", err) 338 } 339 340 return nil 341 }