terraform/vendor/github.com/terraform-providers/terraform-provider-openstack/openstack/resource_openstack_lb_l7pol...

428 lines
12 KiB
Go

package openstack
import (
"fmt"
"log"
"net/url"
"time"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/helper/schema"
"github.com/hashicorp/terraform/helper/validation"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/l7policies"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/listeners"
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools"
)
func resourceL7PolicyV2() *schema.Resource {
return &schema.Resource{
Create: resourceL7PolicyV2Create,
Read: resourceL7PolicyV2Read,
Update: resourceL7PolicyV2Update,
Delete: resourceL7PolicyV2Delete,
Importer: &schema.ResourceImporter{
State: resourceL7PolicyV2Import,
},
Timeouts: &schema.ResourceTimeout{
Create: schema.DefaultTimeout(10 * time.Minute),
Update: schema.DefaultTimeout(10 * time.Minute),
Delete: schema.DefaultTimeout(10 * time.Minute),
},
Schema: map[string]*schema.Schema{
"region": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"tenant_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},
"name": {
Type: schema.TypeString,
Optional: true,
},
"description": {
Type: schema.TypeString,
Optional: true,
},
"action": {
Type: schema.TypeString,
Required: true,
ValidateFunc: validation.StringInSlice([]string{
"REDIRECT_TO_POOL", "REDIRECT_TO_URL", "REJECT",
}, true),
},
"listener_id": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"position": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
},
"redirect_pool_id": {
Type: schema.TypeString,
ConflictsWith: []string{"redirect_url"},
Optional: true,
},
"redirect_url": {
Type: schema.TypeString,
ConflictsWith: []string{"redirect_pool_id"},
Optional: true,
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
_, err := url.ParseRequestURI(value)
if err != nil {
errors = append(errors, fmt.Errorf("URL is not valid: %s", err))
}
return
},
},
"admin_state_up": {
Type: schema.TypeBool,
Default: true,
Optional: true,
},
},
}
}
func resourceL7PolicyV2Create(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
lbClient, err := chooseLBV2Client(d, config)
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
// Assign some required variables for use in creation.
listenerID := d.Get("listener_id").(string)
action := d.Get("action").(string)
redirectPoolID := d.Get("redirect_pool_id").(string)
redirectURL := d.Get("redirect_url").(string)
// Ensure the right combination of options have been specified.
err = checkL7PolicyAction(action, redirectURL, redirectPoolID)
if err != nil {
return fmt.Errorf("Unable to create L7 Policy: %s", err)
}
adminStateUp := d.Get("admin_state_up").(bool)
createOpts := l7policies.CreateOpts{
TenantID: d.Get("tenant_id").(string),
Name: d.Get("name").(string),
Description: d.Get("description").(string),
Action: l7policies.Action(action),
ListenerID: listenerID,
RedirectPoolID: redirectPoolID,
RedirectURL: redirectURL,
AdminStateUp: &adminStateUp,
}
if v, ok := d.GetOk("position"); ok {
createOpts.Position = int32(v.(int))
}
log.Printf("[DEBUG] Create Options: %#v", createOpts)
timeout := d.Timeout(schema.TimeoutCreate)
// Make sure the associated pool is active before proceeding.
if redirectPoolID != "" {
pool, err := pools.Get(lbClient, redirectPoolID).Extract()
if err != nil {
return fmt.Errorf("Unable to retrieve %s: %s", redirectPoolID, err)
}
err = waitForLBV2Pool(lbClient, pool, "ACTIVE", lbPendingStatuses, timeout)
if err != nil {
return err
}
}
// Get a clean copy of the parent listener.
parentListener, err := listeners.Get(lbClient, listenerID).Extract()
if err != nil {
return fmt.Errorf("Unable to retrieve listener %s: %s", listenerID, err)
}
// Wait for parent Listener to become active before continuing.
err = waitForLBV2Listener(lbClient, parentListener, "ACTIVE", lbPendingStatuses, timeout)
if err != nil {
return err
}
log.Printf("[DEBUG] Attempting to create L7 Policy")
var l7Policy *l7policies.L7Policy
err = resource.Retry(timeout, func() *resource.RetryError {
l7Policy, err = l7policies.Create(lbClient, createOpts).Extract()
if err != nil {
return checkForRetryableError(err)
}
return nil
})
if err != nil {
return fmt.Errorf("Error creating L7 Policy: %s", err)
}
// Wait for L7 Policy to become active before continuing
err = waitForLBV2L7Policy(lbClient, parentListener, l7Policy, "ACTIVE", lbPendingStatuses, timeout)
if err != nil {
return err
}
d.SetId(l7Policy.ID)
return resourceL7PolicyV2Read(d, meta)
}
func resourceL7PolicyV2Read(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
lbClient, err := chooseLBV2Client(d, config)
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
l7Policy, err := l7policies.Get(lbClient, d.Id()).Extract()
if err != nil {
return CheckDeleted(d, err, "L7 Policy")
}
log.Printf("[DEBUG] Retrieved L7 Policy %s: %#v", d.Id(), l7Policy)
d.Set("action", l7Policy.Action)
d.Set("description", l7Policy.Description)
d.Set("tenant_id", l7Policy.TenantID)
d.Set("name", l7Policy.Name)
d.Set("position", int(l7Policy.Position))
d.Set("redirect_url", l7Policy.RedirectURL)
d.Set("redirect_pool_id", l7Policy.RedirectPoolID)
d.Set("region", GetRegion(d, config))
d.Set("admin_state_up", l7Policy.AdminStateUp)
return nil
}
func resourceL7PolicyV2Update(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
lbClient, err := chooseLBV2Client(d, config)
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
// Assign some required variables for use in updating.
listenerID := d.Get("listener_id").(string)
action := d.Get("action").(string)
redirectPoolID := d.Get("redirect_pool_id").(string)
redirectURL := d.Get("redirect_url").(string)
var updateOpts l7policies.UpdateOpts
if d.HasChange("action") {
updateOpts.Action = l7policies.Action(action)
}
if d.HasChange("name") {
name := d.Get("name").(string)
updateOpts.Name = &name
}
if d.HasChange("description") {
description := d.Get("description").(string)
updateOpts.Description = &description
}
if d.HasChange("redirect_pool_id") {
redirectPoolID = d.Get("redirect_pool_id").(string)
updateOpts.RedirectPoolID = &redirectPoolID
}
if d.HasChange("redirect_url") {
redirectURL = d.Get("redirect_url").(string)
updateOpts.RedirectURL = &redirectURL
}
if d.HasChange("position") {
updateOpts.Position = int32(d.Get("position").(int))
}
if d.HasChange("admin_state_up") {
adminStateUp := d.Get("admin_state_up").(bool)
updateOpts.AdminStateUp = &adminStateUp
}
// Ensure the right combination of options have been specified.
err = checkL7PolicyAction(action, redirectURL, redirectPoolID)
if err != nil {
return err
}
// Make sure the pool is active before continuing.
timeout := d.Timeout(schema.TimeoutUpdate)
if redirectPoolID != "" {
pool, err := pools.Get(lbClient, redirectPoolID).Extract()
if err != nil {
return fmt.Errorf("Unable to retrieve %s: %s", redirectPoolID, err)
}
err = waitForLBV2Pool(lbClient, pool, "ACTIVE", lbPendingStatuses, timeout)
if err != nil {
return err
}
}
// Get a clean copy of the parent listener.
parentListener, err := listeners.Get(lbClient, listenerID).Extract()
if err != nil {
return fmt.Errorf("Unable to retrieve parent listener %s: %s", listenerID, err)
}
// Get a clean copy of the L7 Policy.
l7Policy, err := l7policies.Get(lbClient, d.Id()).Extract()
if err != nil {
return fmt.Errorf("Unable to retrieve L7 Policy: %s: %s", d.Id(), err)
}
// Wait for parent Listener to become active before continuing.
err = waitForLBV2Listener(lbClient, parentListener, "ACTIVE", lbPendingStatuses, timeout)
if err != nil {
return err
}
// Wait for L7 Policy to become active before continuing
err = waitForLBV2L7Policy(lbClient, parentListener, l7Policy, "ACTIVE", lbPendingStatuses, timeout)
if err != nil {
return err
}
log.Printf("[DEBUG] Updating L7 Policy %s with options: %#v", d.Id(), updateOpts)
err = resource.Retry(timeout, func() *resource.RetryError {
_, err = l7policies.Update(lbClient, d.Id(), updateOpts).Extract()
if err != nil {
return checkForRetryableError(err)
}
return nil
})
if err != nil {
return fmt.Errorf("Unable to update L7 Policy %s: %s", d.Id(), err)
}
// Wait for L7 Policy to become active before continuing
err = waitForLBV2L7Policy(lbClient, parentListener, l7Policy, "ACTIVE", lbPendingStatuses, timeout)
if err != nil {
return err
}
return resourceL7PolicyV2Read(d, meta)
}
func resourceL7PolicyV2Delete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
lbClient, err := chooseLBV2Client(d, config)
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
timeout := d.Timeout(schema.TimeoutDelete)
listenerID := d.Get("listener_id").(string)
// Get a clean copy of the listener.
listener, err := listeners.Get(lbClient, listenerID).Extract()
if err != nil {
return fmt.Errorf("Unable to retrieve parent listener (%s) for the L7 Policy: %s", listenerID, err)
}
// Get a clean copy of the L7 Policy.
l7Policy, err := l7policies.Get(lbClient, d.Id()).Extract()
if err != nil {
return CheckDeleted(d, err, "Unable to retrieve L7 Policy")
}
// Wait for Listener to become active before continuing.
err = waitForLBV2Listener(lbClient, listener, "ACTIVE", lbPendingStatuses, timeout)
if err != nil {
return err
}
log.Printf("[DEBUG] Attempting to delete L7 Policy %s", d.Id())
err = resource.Retry(timeout, func() *resource.RetryError {
err = l7policies.Delete(lbClient, d.Id()).ExtractErr()
if err != nil {
return checkForRetryableError(err)
}
return nil
})
if err != nil {
return CheckDeleted(d, err, "Error deleting L7 Policy")
}
err = waitForLBV2L7Policy(lbClient, listener, l7Policy, "DELETED", lbPendingDeleteStatuses, timeout)
if err != nil {
return err
}
return nil
}
func resourceL7PolicyV2Import(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
config := meta.(*Config)
lbClient, err := chooseLBV2Client(d, config)
if err != nil {
return nil, fmt.Errorf("Error creating OpenStack networking client: %s", err)
}
l7Policy, err := l7policies.Get(lbClient, d.Id()).Extract()
if err != nil {
return nil, CheckDeleted(d, err, "L7 Policy")
}
log.Printf("[DEBUG] Retrieved L7 Policy %s during the import: %#v", d.Id(), l7Policy)
if l7Policy.ListenerID != "" {
d.Set("listener_id", l7Policy.ListenerID)
} else {
// Fallback for the Neutron LBaaSv2 extension
listenerID, err := getListenerIDForL7Policy(lbClient, d.Id())
if err != nil {
return nil, err
}
d.Set("listener_id", listenerID)
}
return []*schema.ResourceData{d}, nil
}
func checkL7PolicyAction(action, redirectURL, redirectPoolID string) error {
if action == "REJECT" {
if redirectURL != "" || redirectPoolID != "" {
return fmt.Errorf(
"redirect_url and redirect_pool_id must be empty when action is set to %s", action)
}
}
if action == "REDIRECT_TO_POOL" && redirectURL != "" {
return fmt.Errorf("redirect_url must be empty when action is set to %s", action)
}
if action == "REDIRECT_TO_URL" && redirectPoolID != "" {
return fmt.Errorf("redirect_pool_id must be empty when action is set to %s", action)
}
return nil
}