github.com/sixgill/terraform@v0.9.0-beta2.0.20170316214032-033f6226ae50/builtin/providers/openstack/resource_openstack_lb_member_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 resourceMemberV2() *schema.Resource {
    16  	return &schema.Resource{
    17  		Create: resourceMemberV2Create,
    18  		Read:   resourceMemberV2Read,
    19  		Update: resourceMemberV2Update,
    20  		Delete: resourceMemberV2Delete,
    21  
    22  		Schema: map[string]*schema.Schema{
    23  			"region": &schema.Schema{
    24  				Type:        schema.TypeString,
    25  				Required:    true,
    26  				ForceNew:    true,
    27  				DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
    28  			},
    29  
    30  			"name": &schema.Schema{
    31  				Type:     schema.TypeString,
    32  				Optional: true,
    33  			},
    34  
    35  			"tenant_id": &schema.Schema{
    36  				Type:     schema.TypeString,
    37  				Optional: true,
    38  				Computed: true,
    39  				ForceNew: true,
    40  			},
    41  
    42  			"address": &schema.Schema{
    43  				Type:     schema.TypeString,
    44  				Required: true,
    45  				ForceNew: true,
    46  			},
    47  
    48  			"protocol_port": &schema.Schema{
    49  				Type:     schema.TypeInt,
    50  				Required: true,
    51  				ForceNew: true,
    52  			},
    53  
    54  			"weight": &schema.Schema{
    55  				Type:     schema.TypeInt,
    56  				Optional: true,
    57  				Computed: true,
    58  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    59  					value := v.(int)
    60  					if value < 1 {
    61  						errors = append(errors, fmt.Errorf(
    62  							"Only numbers greater than 0 are supported values for 'weight'"))
    63  					}
    64  					return
    65  				},
    66  			},
    67  
    68  			"subnet_id": &schema.Schema{
    69  				Type:     schema.TypeString,
    70  				Required: true,
    71  				ForceNew: true,
    72  			},
    73  
    74  			"admin_state_up": &schema.Schema{
    75  				Type:     schema.TypeBool,
    76  				Default:  true,
    77  				Optional: true,
    78  			},
    79  
    80  			"pool_id": &schema.Schema{
    81  				Type:     schema.TypeString,
    82  				Required: true,
    83  				ForceNew: true,
    84  			},
    85  
    86  			"id": &schema.Schema{
    87  				Type:     schema.TypeString,
    88  				Optional: true,
    89  				Computed: true,
    90  			},
    91  		},
    92  	}
    93  }
    94  
    95  func resourceMemberV2Create(d *schema.ResourceData, meta interface{}) error {
    96  	config := meta.(*Config)
    97  	networkingClient, err := config.networkingV2Client(GetRegion(d))
    98  	if err != nil {
    99  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   100  	}
   101  
   102  	adminStateUp := d.Get("admin_state_up").(bool)
   103  	createOpts := pools.CreateMemberOpts{
   104  		Name:         d.Get("name").(string),
   105  		TenantID:     d.Get("tenant_id").(string),
   106  		Address:      d.Get("address").(string),
   107  		ProtocolPort: d.Get("protocol_port").(int),
   108  		Weight:       d.Get("weight").(int),
   109  		AdminStateUp: &adminStateUp,
   110  	}
   111  
   112  	// Must omit if not set
   113  	if v, ok := d.GetOk("subnet_id"); ok {
   114  		createOpts.SubnetID = v.(string)
   115  	}
   116  
   117  	poolID := d.Get("pool_id").(string)
   118  
   119  	log.Printf("[DEBUG] Create Options: %#v", createOpts)
   120  
   121  	var member *pools.Member
   122  	err = resource.Retry(10*time.Minute, func() *resource.RetryError {
   123  		var err error
   124  		log.Printf("[DEBUG] Attempting to create LBaaSV2 member")
   125  		member, err = pools.CreateMember(networkingClient, poolID, createOpts).Extract()
   126  		if err != nil {
   127  			switch errCode := err.(type) {
   128  			case gophercloud.ErrDefault500:
   129  				log.Printf("[DEBUG] OpenStack LBaaSV2 member is still creating.")
   130  				return resource.RetryableError(err)
   131  			case gophercloud.ErrUnexpectedResponseCode:
   132  				if errCode.Actual == 409 {
   133  					log.Printf("[DEBUG] OpenStack LBaaSV2 member is still creating.")
   134  					return resource.RetryableError(err)
   135  				}
   136  
   137  			default:
   138  				return resource.NonRetryableError(err)
   139  			}
   140  		}
   141  		return nil
   142  	})
   143  
   144  	if err != nil {
   145  		return fmt.Errorf("Error creating OpenStack LBaaSV2 member: %s", err)
   146  	}
   147  	log.Printf("[INFO] member ID: %s", member.ID)
   148  
   149  	log.Printf("[DEBUG] Waiting for Openstack LBaaSV2 member (%s) to become available.", member.ID)
   150  
   151  	stateConf := &resource.StateChangeConf{
   152  		Pending:    []string{"PENDING_CREATE"},
   153  		Target:     []string{"ACTIVE"},
   154  		Refresh:    waitForMemberActive(networkingClient, poolID, member.ID),
   155  		Timeout:    2 * time.Minute,
   156  		Delay:      5 * time.Second,
   157  		MinTimeout: 3 * time.Second,
   158  	}
   159  
   160  	_, err = stateConf.WaitForState()
   161  	if err != nil {
   162  		return err
   163  	}
   164  
   165  	d.SetId(member.ID)
   166  
   167  	return resourceMemberV2Read(d, meta)
   168  }
   169  
   170  func resourceMemberV2Read(d *schema.ResourceData, meta interface{}) error {
   171  	config := meta.(*Config)
   172  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   173  	if err != nil {
   174  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   175  	}
   176  
   177  	member, err := pools.GetMember(networkingClient, d.Get("pool_id").(string), d.Id()).Extract()
   178  	if err != nil {
   179  		return CheckDeleted(d, err, "LBV2 Member")
   180  	}
   181  
   182  	log.Printf("[DEBUG] Retrieved OpenStack LBaaSV2 Member %s: %+v", d.Id(), member)
   183  
   184  	d.Set("name", member.Name)
   185  	d.Set("weight", member.Weight)
   186  	d.Set("admin_state_up", member.AdminStateUp)
   187  	d.Set("tenant_id", member.TenantID)
   188  	d.Set("subnet_id", member.SubnetID)
   189  	d.Set("address", member.Address)
   190  	d.Set("protocol_port", member.ProtocolPort)
   191  	d.Set("id", member.ID)
   192  
   193  	return nil
   194  }
   195  
   196  func resourceMemberV2Update(d *schema.ResourceData, meta interface{}) error {
   197  	config := meta.(*Config)
   198  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   199  	if err != nil {
   200  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   201  	}
   202  
   203  	var updateOpts pools.UpdateMemberOpts
   204  	if d.HasChange("name") {
   205  		updateOpts.Name = d.Get("name").(string)
   206  	}
   207  	if d.HasChange("weight") {
   208  		updateOpts.Weight = d.Get("weight").(int)
   209  	}
   210  	if d.HasChange("admin_state_up") {
   211  		asu := d.Get("admin_state_up").(bool)
   212  		updateOpts.AdminStateUp = &asu
   213  	}
   214  
   215  	log.Printf("[DEBUG] Updating OpenStack LBaaSV2 Member %s with options: %+v", d.Id(), updateOpts)
   216  
   217  	_, err = pools.UpdateMember(networkingClient, d.Get("pool_id").(string), d.Id(), updateOpts).Extract()
   218  	if err != nil {
   219  		return fmt.Errorf("Error updating OpenStack LBaaSV2 Member: %s", err)
   220  	}
   221  
   222  	return resourceMemberV2Read(d, meta)
   223  }
   224  
   225  func resourceMemberV2Delete(d *schema.ResourceData, meta interface{}) error {
   226  	config := meta.(*Config)
   227  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   228  	if err != nil {
   229  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   230  	}
   231  
   232  	stateConf := &resource.StateChangeConf{
   233  		Pending:    []string{"ACTIVE", "PENDING_DELETE"},
   234  		Target:     []string{"DELETED"},
   235  		Refresh:    waitForMemberDelete(networkingClient, d.Get("pool_id").(string), d.Id()),
   236  		Timeout:    2 * time.Minute,
   237  		Delay:      5 * time.Second,
   238  		MinTimeout: 3 * time.Second,
   239  	}
   240  
   241  	_, err = stateConf.WaitForState()
   242  	if err != nil {
   243  		return fmt.Errorf("Error deleting OpenStack LBaaSV2 Member: %s", err)
   244  	}
   245  
   246  	d.SetId("")
   247  	return nil
   248  }
   249  
   250  func waitForMemberActive(networkingClient *gophercloud.ServiceClient, poolID string, memberID string) resource.StateRefreshFunc {
   251  	return func() (interface{}, string, error) {
   252  		member, err := pools.GetMember(networkingClient, poolID, memberID).Extract()
   253  		if err != nil {
   254  			return nil, "", err
   255  		}
   256  
   257  		// The member resource has no Status attribute, so a successful Get is the best we can do
   258  		log.Printf("[DEBUG] OpenStack LBaaSV2 Member: %+v", member)
   259  		return member, "ACTIVE", nil
   260  	}
   261  }
   262  
   263  func waitForMemberDelete(networkingClient *gophercloud.ServiceClient, poolID string, memberID string) resource.StateRefreshFunc {
   264  	return func() (interface{}, string, error) {
   265  		log.Printf("[DEBUG] Attempting to delete OpenStack LBaaSV2 Member %s", memberID)
   266  
   267  		member, err := pools.GetMember(networkingClient, poolID, memberID).Extract()
   268  		if err != nil {
   269  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   270  				log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Member %s", memberID)
   271  				return member, "DELETED", nil
   272  			}
   273  			return member, "ACTIVE", err
   274  		}
   275  
   276  		log.Printf("[DEBUG] Openstack LBaaSV2 Member: %+v", member)
   277  		err = pools.DeleteMember(networkingClient, poolID, memberID).ExtractErr()
   278  		if err != nil {
   279  			switch errCode := err.(type) {
   280  			case gophercloud.ErrDefault404:
   281  				log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Member %s", memberID)
   282  				return member, "DELETED", nil
   283  			case gophercloud.ErrDefault500:
   284  				log.Printf("[DEBUG] OpenStack LBaaSV2 Member (%s) is still in use.", memberID)
   285  				return member, "PENDING_DELETE", nil
   286  			case gophercloud.ErrUnexpectedResponseCode:
   287  				if errCode.Actual == 409 {
   288  					log.Printf("[DEBUG] OpenStack LBaaSV2 Member (%s) is still in use.", memberID)
   289  					return member, "PENDING_DELETE", nil
   290  				}
   291  
   292  			default:
   293  				return member, "ACTIVE", err
   294  			}
   295  		}
   296  
   297  		log.Printf("[DEBUG] OpenStack LBaaSV2 Member %s still active.", memberID)
   298  		return member, "ACTIVE", nil
   299  	}
   300  }