github.com/bradfeehan/terraform@v0.7.0-rc3.0.20170529055808-34b45c5ad841/builtin/providers/heroku/resource_heroku_addon.go (about)

     1  package heroku
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log"
     7  	"strings"
     8  	"sync"
     9  	"time"
    10  
    11  	"github.com/cyberdelia/heroku-go/v3"
    12  	"github.com/hashicorp/terraform/helper/resource"
    13  	"github.com/hashicorp/terraform/helper/schema"
    14  )
    15  
    16  // Global lock to prevent parallelism for heroku_addon since
    17  // the Heroku API cannot handle a single application requesting
    18  // multiple addons simultaneously.
    19  var addonLock sync.Mutex
    20  
    21  func resourceHerokuAddon() *schema.Resource {
    22  	return &schema.Resource{
    23  		Create: resourceHerokuAddonCreate,
    24  		Read:   resourceHerokuAddonRead,
    25  		Update: resourceHerokuAddonUpdate,
    26  		Delete: resourceHerokuAddonDelete,
    27  
    28  		Importer: &schema.ResourceImporter{
    29  			State: schema.ImportStatePassthrough,
    30  		},
    31  
    32  		Schema: map[string]*schema.Schema{
    33  			"app": {
    34  				Type:     schema.TypeString,
    35  				Required: true,
    36  				ForceNew: true,
    37  			},
    38  
    39  			"plan": {
    40  				Type:     schema.TypeString,
    41  				Required: true,
    42  			},
    43  
    44  			"config": {
    45  				Type:     schema.TypeList,
    46  				Optional: true,
    47  				ForceNew: true,
    48  				Elem: &schema.Schema{
    49  					Type: schema.TypeMap,
    50  				},
    51  			},
    52  
    53  			"provider_id": {
    54  				Type:     schema.TypeString,
    55  				Computed: true,
    56  			},
    57  
    58  			"config_vars": {
    59  				Type:     schema.TypeList,
    60  				Computed: true,
    61  				Elem: &schema.Schema{
    62  					Type: schema.TypeString,
    63  				},
    64  			},
    65  		},
    66  	}
    67  }
    68  
    69  func resourceHerokuAddonCreate(d *schema.ResourceData, meta interface{}) error {
    70  	addonLock.Lock()
    71  	defer addonLock.Unlock()
    72  
    73  	client := meta.(*heroku.Service)
    74  
    75  	app := d.Get("app").(string)
    76  	opts := heroku.AddOnCreateOpts{Plan: d.Get("plan").(string)}
    77  
    78  	if v := d.Get("config"); v != nil {
    79  		config := make(map[string]string)
    80  		for _, v := range v.([]interface{}) {
    81  			for k, v := range v.(map[string]interface{}) {
    82  				config[k] = v.(string)
    83  			}
    84  		}
    85  
    86  		opts.Config = &config
    87  	}
    88  
    89  	log.Printf("[DEBUG] Addon create configuration: %#v, %#v", app, opts)
    90  	a, err := client.AddOnCreate(context.TODO(), app, opts)
    91  	if err != nil {
    92  		return err
    93  	}
    94  
    95  	d.SetId(a.ID)
    96  	log.Printf("[INFO] Addon ID: %s", d.Id())
    97  
    98  	// Wait for the Addon to be provisioned
    99  	log.Printf("[DEBUG] Waiting for Addon (%s) to be provisioned", d.Id())
   100  	stateConf := &resource.StateChangeConf{
   101  		Pending: []string{"provisioning"},
   102  		Target:  []string{"provisioned"},
   103  		Refresh: AddOnStateRefreshFunc(client, app, d.Id()),
   104  		Timeout: 20 * time.Minute,
   105  	}
   106  
   107  	if _, err := stateConf.WaitForState(); err != nil {
   108  		return fmt.Errorf("Error waiting for Addon (%s) to be provisioned: %s", d.Id(), err)
   109  	}
   110  	log.Printf("[INFO] Addon provisioned: %s", d.Id())
   111  
   112  	return resourceHerokuAddonRead(d, meta)
   113  }
   114  
   115  func resourceHerokuAddonRead(d *schema.ResourceData, meta interface{}) error {
   116  	client := meta.(*heroku.Service)
   117  
   118  	addon, err := resourceHerokuAddonRetrieve(d.Id(), client)
   119  	if err != nil {
   120  		return err
   121  	}
   122  
   123  	// Determine the plan. If we were configured without a specific plan,
   124  	// then just avoid the plan altogether (accepting anything that
   125  	// Heroku sends down).
   126  	plan := addon.Plan.Name
   127  	if v := d.Get("plan").(string); v != "" {
   128  		if idx := strings.IndexRune(v, ':'); idx == -1 {
   129  			idx = strings.IndexRune(plan, ':')
   130  			if idx > -1 {
   131  				plan = plan[:idx]
   132  			}
   133  		}
   134  	}
   135  
   136  	d.Set("name", addon.Name)
   137  	d.Set("plan", plan)
   138  	d.Set("provider_id", addon.ProviderID)
   139  	if err := d.Set("config_vars", addon.ConfigVars); err != nil {
   140  		return err
   141  	}
   142  
   143  	return nil
   144  }
   145  
   146  func resourceHerokuAddonUpdate(d *schema.ResourceData, meta interface{}) error {
   147  	client := meta.(*heroku.Service)
   148  
   149  	app := d.Get("app").(string)
   150  
   151  	if d.HasChange("plan") {
   152  		ad, err := client.AddOnUpdate(
   153  			context.TODO(), app, d.Id(), heroku.AddOnUpdateOpts{Plan: d.Get("plan").(string)})
   154  		if err != nil {
   155  			return err
   156  		}
   157  
   158  		// Store the new ID
   159  		d.SetId(ad.ID)
   160  	}
   161  
   162  	return resourceHerokuAddonRead(d, meta)
   163  }
   164  
   165  func resourceHerokuAddonDelete(d *schema.ResourceData, meta interface{}) error {
   166  	client := meta.(*heroku.Service)
   167  
   168  	log.Printf("[INFO] Deleting Addon: %s", d.Id())
   169  
   170  	// Destroy the app
   171  	_, err := client.AddOnDelete(context.TODO(), d.Get("app").(string), d.Id())
   172  	if err != nil {
   173  		return fmt.Errorf("Error deleting addon: %s", err)
   174  	}
   175  
   176  	d.SetId("")
   177  	return nil
   178  }
   179  
   180  func resourceHerokuAddonRetrieve(id string, client *heroku.Service) (*heroku.AddOn, error) {
   181  	addon, err := client.AddOnInfo(context.TODO(), id)
   182  
   183  	if err != nil {
   184  		return nil, fmt.Errorf("Error retrieving addon: %s", err)
   185  	}
   186  
   187  	return addon, nil
   188  }
   189  
   190  func resourceHerokuAddonRetrieveByApp(app string, id string, client *heroku.Service) (*heroku.AddOn, error) {
   191  	addon, err := client.AddOnInfoByApp(context.TODO(), app, id)
   192  
   193  	if err != nil {
   194  		return nil, fmt.Errorf("Error retrieving addon: %s", err)
   195  	}
   196  
   197  	return addon, nil
   198  }
   199  
   200  // AddOnStateRefreshFunc returns a resource.StateRefreshFunc that is used to
   201  // watch an AddOn.
   202  func AddOnStateRefreshFunc(client *heroku.Service, appID, addOnID string) resource.StateRefreshFunc {
   203  	return func() (interface{}, string, error) {
   204  		addon, err := resourceHerokuAddonRetrieveByApp(appID, addOnID, client)
   205  
   206  		if err != nil {
   207  			return nil, "", err
   208  		}
   209  
   210  		// The type conversion here can be dropped when the vendored version of
   211  		// heroku-go is updated.
   212  		return (*heroku.AddOn)(addon), addon.State, nil
   213  	}
   214  }