github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/openstack/resource_openstack_lb_pool_v2.go (about)

     1  package openstack
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"time"
     7  
     8  	"github.com/hashicorp/terraform/helper/resource"
     9  	"github.com/hashicorp/terraform/helper/schema"
    10  
    11  	"github.com/gophercloud/gophercloud"
    12  	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
    13  )
    14  
    15  func resourcePoolV2() *schema.Resource {
    16  	return &schema.Resource{
    17  		Create: resourcePoolV2Create,
    18  		Read:   resourcePoolV2Read,
    19  		Update: resourcePoolV2Update,
    20  		Delete: resourcePoolV2Delete,
    21  
    22  		Timeouts: &schema.ResourceTimeout{
    23  			Create: schema.DefaultTimeout(10 * time.Minute),
    24  			Delete: schema.DefaultTimeout(10 * time.Minute),
    25  		},
    26  
    27  		Schema: map[string]*schema.Schema{
    28  			"region": &schema.Schema{
    29  				Type:        schema.TypeString,
    30  				Required:    true,
    31  				ForceNew:    true,
    32  				DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
    33  			},
    34  
    35  			"tenant_id": &schema.Schema{
    36  				Type:     schema.TypeString,
    37  				Optional: true,
    38  				Computed: true,
    39  				ForceNew: true,
    40  			},
    41  
    42  			"name": &schema.Schema{
    43  				Type:     schema.TypeString,
    44  				Optional: true,
    45  			},
    46  
    47  			"description": &schema.Schema{
    48  				Type:     schema.TypeString,
    49  				Optional: true,
    50  			},
    51  
    52  			"protocol": &schema.Schema{
    53  				Type:     schema.TypeString,
    54  				Required: true,
    55  				ForceNew: true,
    56  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    57  					value := v.(string)
    58  					if value != "TCP" && value != "HTTP" && value != "HTTPS" {
    59  						errors = append(errors, fmt.Errorf(
    60  							"Only 'TCP', 'HTTP', and 'HTTPS' are supported values for 'protocol'"))
    61  					}
    62  					return
    63  				},
    64  			},
    65  
    66  			// One of loadbalancer_id or listener_id must be provided
    67  			"loadbalancer_id": &schema.Schema{
    68  				Type:     schema.TypeString,
    69  				Optional: true,
    70  				ForceNew: true,
    71  			},
    72  
    73  			// One of loadbalancer_id or listener_id must be provided
    74  			"listener_id": &schema.Schema{
    75  				Type:     schema.TypeString,
    76  				Optional: true,
    77  				ForceNew: true,
    78  			},
    79  
    80  			"lb_method": &schema.Schema{
    81  				Type:     schema.TypeString,
    82  				Required: true,
    83  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    84  					value := v.(string)
    85  					if value != "ROUND_ROBIN" && value != "LEAST_CONNECTIONS" && value != "SOURCE_IP" {
    86  						errors = append(errors, fmt.Errorf(
    87  							"Only 'ROUND_ROBIN', 'LEAST_CONNECTIONS', and 'SOURCE_IP' are supported values for 'lb_method'"))
    88  					}
    89  					return
    90  				},
    91  			},
    92  
    93  			"persistence": &schema.Schema{
    94  				Type:     schema.TypeList,
    95  				Optional: true,
    96  				ForceNew: true,
    97  				Elem: &schema.Resource{
    98  					Schema: map[string]*schema.Schema{
    99  						"type": &schema.Schema{
   100  							Type:     schema.TypeString,
   101  							Required: true,
   102  							ForceNew: true,
   103  							ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
   104  								value := v.(string)
   105  								if value != "SOURCE_IP" && value != "HTTP_COOKIE" && value != "APP_COOKIE" {
   106  									errors = append(errors, fmt.Errorf(
   107  										"Only 'SOURCE_IP', 'HTTP_COOKIE', and 'APP_COOKIE' are supported values for 'persistence'"))
   108  								}
   109  								return
   110  							},
   111  						},
   112  
   113  						"cookie_name": &schema.Schema{
   114  							Type:     schema.TypeString,
   115  							Required: true,
   116  							ForceNew: true,
   117  						},
   118  					},
   119  				},
   120  			},
   121  
   122  			"admin_state_up": &schema.Schema{
   123  				Type:     schema.TypeBool,
   124  				Default:  true,
   125  				Optional: true,
   126  			},
   127  
   128  			"id": &schema.Schema{
   129  				Type:     schema.TypeString,
   130  				Optional: true,
   131  				Computed: true,
   132  			},
   133  		},
   134  	}
   135  }
   136  
   137  func resourcePoolV2Create(d *schema.ResourceData, meta interface{}) error {
   138  	config := meta.(*Config)
   139  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   140  	if err != nil {
   141  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   142  	}
   143  
   144  	adminStateUp := d.Get("admin_state_up").(bool)
   145  	var persistence pools.SessionPersistence
   146  	if p, ok := d.GetOk("persistence"); ok {
   147  		pV := (p.([]interface{}))[0].(map[string]interface{})
   148  
   149  		persistence = pools.SessionPersistence{
   150  			Type:       pV["type"].(string),
   151  			CookieName: pV["cookie_name"].(string),
   152  		}
   153  	}
   154  	createOpts := pools.CreateOpts{
   155  		TenantID:       d.Get("tenant_id").(string),
   156  		Name:           d.Get("name").(string),
   157  		Description:    d.Get("description").(string),
   158  		Protocol:       pools.Protocol(d.Get("protocol").(string)),
   159  		LoadbalancerID: d.Get("loadbalancer_id").(string),
   160  		ListenerID:     d.Get("listener_id").(string),
   161  		LBMethod:       pools.LBMethod(d.Get("lb_method").(string)),
   162  		AdminStateUp:   &adminStateUp,
   163  	}
   164  	// Must omit if not set
   165  	if persistence != (pools.SessionPersistence{}) {
   166  		createOpts.Persistence = &persistence
   167  	}
   168  
   169  	log.Printf("[DEBUG] Create Options: %#v", createOpts)
   170  
   171  	var pool *pools.Pool
   172  	err = resource.Retry(d.Timeout(schema.TimeoutCreate), func() *resource.RetryError {
   173  		var err error
   174  		log.Printf("[DEBUG] Attempting to create LBaaSV2 pool")
   175  		pool, err = pools.Create(networkingClient, createOpts).Extract()
   176  		if err != nil {
   177  			switch errCode := err.(type) {
   178  			case gophercloud.ErrDefault500:
   179  				log.Printf("[DEBUG] OpenStack LBaaSV2 pool is still creating.")
   180  				return resource.RetryableError(err)
   181  			case gophercloud.ErrUnexpectedResponseCode:
   182  				if errCode.Actual == 409 {
   183  					log.Printf("[DEBUG] OpenStack LBaaSV2 pool is still creating.")
   184  					return resource.RetryableError(err)
   185  				}
   186  			default:
   187  				return resource.NonRetryableError(err)
   188  			}
   189  		}
   190  		return nil
   191  	})
   192  
   193  	if err != nil {
   194  		return fmt.Errorf("Error creating OpenStack LBaaSV2 pool: %s", err)
   195  	}
   196  
   197  	log.Printf("[INFO] pool ID: %s", pool.ID)
   198  
   199  	log.Printf("[DEBUG] Waiting for Openstack LBaaSV2 pool (%s) to become available.", pool.ID)
   200  
   201  	stateConf := &resource.StateChangeConf{
   202  		Pending:    []string{"PENDING_CREATE"},
   203  		Target:     []string{"ACTIVE"},
   204  		Refresh:    waitForPoolActive(networkingClient, pool.ID),
   205  		Timeout:    d.Timeout(schema.TimeoutCreate),
   206  		Delay:      5 * time.Second,
   207  		MinTimeout: 3 * time.Second,
   208  	}
   209  
   210  	_, err = stateConf.WaitForState()
   211  	if err != nil {
   212  		return err
   213  	}
   214  
   215  	d.SetId(pool.ID)
   216  
   217  	return resourcePoolV2Read(d, meta)
   218  }
   219  
   220  func resourcePoolV2Read(d *schema.ResourceData, meta interface{}) error {
   221  	config := meta.(*Config)
   222  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   223  	if err != nil {
   224  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   225  	}
   226  
   227  	pool, err := pools.Get(networkingClient, d.Id()).Extract()
   228  	if err != nil {
   229  		return CheckDeleted(d, err, "LBV2 Pool")
   230  	}
   231  
   232  	log.Printf("[DEBUG] Retrieved OpenStack LBaaSV2 Pool %s: %+v", d.Id(), pool)
   233  
   234  	d.Set("lb_method", pool.LBMethod)
   235  	d.Set("protocol", pool.Protocol)
   236  	d.Set("description", pool.Description)
   237  	d.Set("tenant_id", pool.TenantID)
   238  	d.Set("admin_state_up", pool.AdminStateUp)
   239  	d.Set("name", pool.Name)
   240  	d.Set("id", pool.ID)
   241  	d.Set("persistence", pool.Persistence)
   242  
   243  	return nil
   244  }
   245  
   246  func resourcePoolV2Update(d *schema.ResourceData, meta interface{}) error {
   247  	config := meta.(*Config)
   248  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   249  	if err != nil {
   250  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   251  	}
   252  
   253  	var updateOpts pools.UpdateOpts
   254  	if d.HasChange("lb_method") {
   255  		updateOpts.LBMethod = pools.LBMethod(d.Get("lb_method").(string))
   256  	}
   257  	if d.HasChange("name") {
   258  		updateOpts.Name = d.Get("name").(string)
   259  	}
   260  	if d.HasChange("description") {
   261  		updateOpts.Description = d.Get("description").(string)
   262  	}
   263  	if d.HasChange("admin_state_up") {
   264  		asu := d.Get("admin_state_up").(bool)
   265  		updateOpts.AdminStateUp = &asu
   266  	}
   267  
   268  	log.Printf("[DEBUG] Updating OpenStack LBaaSV2 Pool %s with options: %+v", d.Id(), updateOpts)
   269  
   270  	_, err = pools.Update(networkingClient, d.Id(), updateOpts).Extract()
   271  	if err != nil {
   272  		return fmt.Errorf("Error updating OpenStack LBaaSV2 Pool: %s", err)
   273  	}
   274  
   275  	return resourcePoolV2Read(d, meta)
   276  }
   277  
   278  func resourcePoolV2Delete(d *schema.ResourceData, meta interface{}) error {
   279  	config := meta.(*Config)
   280  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   281  	if err != nil {
   282  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   283  	}
   284  
   285  	stateConf := &resource.StateChangeConf{
   286  		Pending:    []string{"ACTIVE", "PENDING_DELETE"},
   287  		Target:     []string{"DELETED"},
   288  		Refresh:    waitForPoolDelete(networkingClient, d.Id()),
   289  		Timeout:    d.Timeout(schema.TimeoutDelete),
   290  		Delay:      5 * time.Second,
   291  		MinTimeout: 3 * time.Second,
   292  	}
   293  
   294  	_, err = stateConf.WaitForState()
   295  	if err != nil {
   296  		return fmt.Errorf("Error deleting OpenStack LBaaSV2 Pool: %s", err)
   297  	}
   298  
   299  	d.SetId("")
   300  	return nil
   301  }
   302  
   303  func waitForPoolActive(networkingClient *gophercloud.ServiceClient, poolID string) resource.StateRefreshFunc {
   304  	return func() (interface{}, string, error) {
   305  		pool, err := pools.Get(networkingClient, poolID).Extract()
   306  		if err != nil {
   307  			return nil, "", err
   308  		}
   309  
   310  		// The pool resource has no Status attribute, so a successful Get is the best we can do
   311  		log.Printf("[DEBUG] OpenStack LBaaSV2 Pool: %+v", pool)
   312  		return pool, "ACTIVE", nil
   313  	}
   314  }
   315  
   316  func waitForPoolDelete(networkingClient *gophercloud.ServiceClient, poolID string) resource.StateRefreshFunc {
   317  	return func() (interface{}, string, error) {
   318  		log.Printf("[DEBUG] Attempting to delete OpenStack LBaaSV2 Pool %s", poolID)
   319  
   320  		pool, err := pools.Get(networkingClient, poolID).Extract()
   321  		if err != nil {
   322  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   323  				log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Pool %s", poolID)
   324  				return pool, "DELETED", nil
   325  			}
   326  			return pool, "ACTIVE", err
   327  		}
   328  
   329  		log.Printf("[DEBUG] Openstack LBaaSV2 Pool: %+v", pool)
   330  		err = pools.Delete(networkingClient, poolID).ExtractErr()
   331  		if err != nil {
   332  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   333  				log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Pool %s", poolID)
   334  				return pool, "DELETED", nil
   335  			}
   336  
   337  			if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok {
   338  				if errCode.Actual == 409 {
   339  					log.Printf("[DEBUG] OpenStack LBaaSV2 Pool (%s) is still in use.", poolID)
   340  					return pool, "ACTIVE", nil
   341  				}
   342  			}
   343  
   344  			return pool, "ACTIVE", err
   345  		}
   346  
   347  		log.Printf("[DEBUG] OpenStack LBaaSV2 Pool %s still active.", poolID)
   348  		return pool, "ACTIVE", nil
   349  	}
   350  }