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  }