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