github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/openstack/resource_openstack_lb_listener_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/listeners"
    13  )
    14  
    15  func resourceListenerV2() *schema.Resource {
    16  	return &schema.Resource{
    17  		Create: resourceListenerV2Create,
    18  		Read:   resourceListenerV2Read,
    19  		Update: resourceListenerV2Update,
    20  		Delete: resourceListenerV2Delete,
    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  			"protocol": &schema.Schema{
    31  				Type:     schema.TypeString,
    32  				Required: true,
    33  				ForceNew: true,
    34  				ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
    35  					value := v.(string)
    36  					if value != "TCP" && value != "HTTP" && value != "HTTPS" {
    37  						errors = append(errors, fmt.Errorf(
    38  							"Only 'TCP', 'HTTP', and 'HTTPS' are supported values for 'protocol'"))
    39  					}
    40  					return
    41  				},
    42  			},
    43  
    44  			"protocol_port": &schema.Schema{
    45  				Type:     schema.TypeInt,
    46  				Required: true,
    47  				ForceNew: true,
    48  			},
    49  
    50  			"tenant_id": &schema.Schema{
    51  				Type:     schema.TypeString,
    52  				Optional: true,
    53  				Computed: true,
    54  				ForceNew: true,
    55  			},
    56  
    57  			"loadbalancer_id": &schema.Schema{
    58  				Type:     schema.TypeString,
    59  				Required: true,
    60  				ForceNew: true,
    61  			},
    62  
    63  			"name": &schema.Schema{
    64  				Type:     schema.TypeString,
    65  				Optional: true,
    66  				Computed: true,
    67  			},
    68  
    69  			"default_pool_id": &schema.Schema{
    70  				Type:     schema.TypeString,
    71  				Optional: true,
    72  				Computed: true,
    73  				ForceNew: true,
    74  			},
    75  
    76  			"description": &schema.Schema{
    77  				Type:     schema.TypeString,
    78  				Optional: true,
    79  			},
    80  
    81  			"connection_limit": &schema.Schema{
    82  				Type:     schema.TypeInt,
    83  				Optional: true,
    84  			},
    85  
    86  			"default_tls_container_ref": &schema.Schema{
    87  				Type:     schema.TypeString,
    88  				Optional: true,
    89  			},
    90  
    91  			"sni_container_refs": &schema.Schema{
    92  				Type:     schema.TypeList,
    93  				Optional: true,
    94  				Elem:     &schema.Schema{Type: schema.TypeString},
    95  			},
    96  
    97  			"admin_state_up": &schema.Schema{
    98  				Type:     schema.TypeBool,
    99  				Default:  true,
   100  				Optional: true,
   101  			},
   102  
   103  			"id": &schema.Schema{
   104  				Type:     schema.TypeString,
   105  				Optional: true,
   106  				Computed: true,
   107  			},
   108  		},
   109  	}
   110  }
   111  
   112  func resourceListenerV2Create(d *schema.ResourceData, meta interface{}) error {
   113  	config := meta.(*Config)
   114  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   115  	if err != nil {
   116  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   117  	}
   118  
   119  	adminStateUp := d.Get("admin_state_up").(bool)
   120  	connLimit := d.Get("connection_limit").(int)
   121  	var sniContainerRefs []string
   122  	if raw, ok := d.GetOk("sni_container_refs"); ok {
   123  		for _, v := range raw.([]interface{}) {
   124  			sniContainerRefs = append(sniContainerRefs, v.(string))
   125  		}
   126  	}
   127  	createOpts := listeners.CreateOpts{
   128  		Protocol:               listeners.Protocol(d.Get("protocol").(string)),
   129  		ProtocolPort:           d.Get("protocol_port").(int),
   130  		TenantID:               d.Get("tenant_id").(string),
   131  		LoadbalancerID:         d.Get("loadbalancer_id").(string),
   132  		Name:                   d.Get("name").(string),
   133  		DefaultPoolID:          d.Get("default_pool_id").(string),
   134  		Description:            d.Get("description").(string),
   135  		ConnLimit:              &connLimit,
   136  		DefaultTlsContainerRef: d.Get("default_tls_container_ref").(string),
   137  		SniContainerRefs:       sniContainerRefs,
   138  		AdminStateUp:           &adminStateUp,
   139  	}
   140  
   141  	log.Printf("[DEBUG] Create Options: %#v", createOpts)
   142  	listener, err := listeners.Create(networkingClient, createOpts).Extract()
   143  	if err != nil {
   144  		return fmt.Errorf("Error creating OpenStack LBaaSV2 listener: %s", err)
   145  	}
   146  	log.Printf("[INFO] Listener ID: %s", listener.ID)
   147  
   148  	log.Printf("[DEBUG] Waiting for Openstack LBaaSV2 listener (%s) to become available.", listener.ID)
   149  
   150  	stateConf := &resource.StateChangeConf{
   151  		Pending:    []string{"PENDING_CREATE"},
   152  		Target:     []string{"ACTIVE"},
   153  		Refresh:    waitForListenerActive(networkingClient, listener.ID),
   154  		Timeout:    2 * time.Minute,
   155  		Delay:      5 * time.Second,
   156  		MinTimeout: 3 * time.Second,
   157  	}
   158  
   159  	_, err = stateConf.WaitForState()
   160  	if err != nil {
   161  		return err
   162  	}
   163  
   164  	d.SetId(listener.ID)
   165  
   166  	return resourceListenerV2Read(d, meta)
   167  }
   168  
   169  func resourceListenerV2Read(d *schema.ResourceData, meta interface{}) error {
   170  	config := meta.(*Config)
   171  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   172  	if err != nil {
   173  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   174  	}
   175  
   176  	listener, err := listeners.Get(networkingClient, d.Id()).Extract()
   177  	if err != nil {
   178  		return CheckDeleted(d, err, "LBV2 listener")
   179  	}
   180  
   181  	log.Printf("[DEBUG] Retrieved OpenStack LBaaSV2 listener %s: %+v", d.Id(), listener)
   182  
   183  	d.Set("id", listener.ID)
   184  	d.Set("name", listener.Name)
   185  	d.Set("protocol", listener.Protocol)
   186  	d.Set("tenant_id", listener.TenantID)
   187  	d.Set("description", listener.Description)
   188  	d.Set("protocol_port", listener.ProtocolPort)
   189  	d.Set("admin_state_up", listener.AdminStateUp)
   190  	d.Set("default_pool_id", listener.DefaultPoolID)
   191  	d.Set("connection_limit", listener.ConnLimit)
   192  	d.Set("sni_container_refs", listener.SniContainerRefs)
   193  	d.Set("default_tls_container_ref", listener.DefaultTlsContainerRef)
   194  
   195  	return nil
   196  }
   197  
   198  func resourceListenerV2Update(d *schema.ResourceData, meta interface{}) error {
   199  	config := meta.(*Config)
   200  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   201  	if err != nil {
   202  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   203  	}
   204  
   205  	var updateOpts listeners.UpdateOpts
   206  	if d.HasChange("name") {
   207  		updateOpts.Name = d.Get("name").(string)
   208  	}
   209  	if d.HasChange("description") {
   210  		updateOpts.Description = d.Get("description").(string)
   211  	}
   212  	if d.HasChange("connection_limit") {
   213  		connLimit := d.Get("connection_limit").(int)
   214  		updateOpts.ConnLimit = &connLimit
   215  	}
   216  	if d.HasChange("default_tls_container_ref") {
   217  		updateOpts.DefaultTlsContainerRef = d.Get("default_tls_container_ref").(string)
   218  	}
   219  	if d.HasChange("sni_container_refs") {
   220  		var sniContainerRefs []string
   221  		if raw, ok := d.GetOk("sni_container_refs"); ok {
   222  			for _, v := range raw.([]interface{}) {
   223  				sniContainerRefs = append(sniContainerRefs, v.(string))
   224  			}
   225  		}
   226  		updateOpts.SniContainerRefs = sniContainerRefs
   227  	}
   228  	if d.HasChange("admin_state_up") {
   229  		asu := d.Get("admin_state_up").(bool)
   230  		updateOpts.AdminStateUp = &asu
   231  	}
   232  
   233  	log.Printf("[DEBUG] Updating OpenStack LBaaSV2 Listener %s with options: %+v", d.Id(), updateOpts)
   234  
   235  	_, err = listeners.Update(networkingClient, d.Id(), updateOpts).Extract()
   236  	if err != nil {
   237  		return fmt.Errorf("Error updating OpenStack LBaaSV2 Listener: %s", err)
   238  	}
   239  
   240  	return resourceListenerV2Read(d, meta)
   241  
   242  }
   243  
   244  func resourceListenerV2Delete(d *schema.ResourceData, meta interface{}) error {
   245  	config := meta.(*Config)
   246  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   247  	if err != nil {
   248  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   249  	}
   250  
   251  	stateConf := &resource.StateChangeConf{
   252  		Pending:    []string{"ACTIVE", "PENDING_DELETE"},
   253  		Target:     []string{"DELETED"},
   254  		Refresh:    waitForListenerDelete(networkingClient, d.Id()),
   255  		Timeout:    2 * time.Minute,
   256  		Delay:      5 * time.Second,
   257  		MinTimeout: 3 * time.Second,
   258  	}
   259  
   260  	_, err = stateConf.WaitForState()
   261  	if err != nil {
   262  		return fmt.Errorf("Error deleting OpenStack LBaaSV2 listener: %s", err)
   263  	}
   264  
   265  	d.SetId("")
   266  	return nil
   267  }
   268  
   269  func waitForListenerActive(networkingClient *gophercloud.ServiceClient, listenerID string) resource.StateRefreshFunc {
   270  	return func() (interface{}, string, error) {
   271  		listener, err := listeners.Get(networkingClient, listenerID).Extract()
   272  		if err != nil {
   273  			return nil, "", err
   274  		}
   275  
   276  		// The listener resource has no Status attribute, so a successful Get is the best we can do
   277  		log.Printf("[DEBUG] OpenStack LBaaSV2 listener: %+v", listener)
   278  		return listener, "ACTIVE", nil
   279  	}
   280  }
   281  
   282  func waitForListenerDelete(networkingClient *gophercloud.ServiceClient, listenerID string) resource.StateRefreshFunc {
   283  	return func() (interface{}, string, error) {
   284  		log.Printf("[DEBUG] Attempting to delete OpenStack LBaaSV2 listener %s", listenerID)
   285  
   286  		listener, err := listeners.Get(networkingClient, listenerID).Extract()
   287  		if err != nil {
   288  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   289  				log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 listener %s", listenerID)
   290  				return listener, "DELETED", nil
   291  			}
   292  			return listener, "ACTIVE", err
   293  		}
   294  
   295  		log.Printf("[DEBUG] Openstack LBaaSV2 listener: %+v", listener)
   296  		err = listeners.Delete(networkingClient, listenerID).ExtractErr()
   297  		if err != nil {
   298  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   299  				log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 listener %s", listenerID)
   300  				return listener, "DELETED", nil
   301  			}
   302  
   303  			if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok {
   304  				if errCode.Actual == 409 {
   305  					log.Printf("[DEBUG] OpenStack LBaaSV2 listener (%s) is still in use.", listenerID)
   306  					return listener, "ACTIVE", nil
   307  				}
   308  			}
   309  
   310  			return listener, "ACTIVE", err
   311  		}
   312  
   313  		log.Printf("[DEBUG] OpenStack LBaaSV2 listener %s still active.", listenerID)
   314  		return listener, "ACTIVE", nil
   315  	}
   316  }