github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/builtin/providers/openstack/resource_openstack_fw_firewall_v1.go (about)

     1  package openstack
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"time"
     7  
     8  	"github.com/gophercloud/gophercloud"
     9  	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/firewalls"
    10  	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/fwaas/routerinsertion"
    11  	"github.com/hashicorp/terraform/helper/resource"
    12  	"github.com/hashicorp/terraform/helper/schema"
    13  )
    14  
    15  func resourceFWFirewallV1() *schema.Resource {
    16  	return &schema.Resource{
    17  		Create: resourceFWFirewallV1Create,
    18  		Read:   resourceFWFirewallV1Read,
    19  		Update: resourceFWFirewallV1Update,
    20  		Delete: resourceFWFirewallV1Delete,
    21  		Importer: &schema.ResourceImporter{
    22  			State: schema.ImportStatePassthrough,
    23  		},
    24  
    25  		Timeouts: &schema.ResourceTimeout{
    26  			Create: schema.DefaultTimeout(10 * time.Minute),
    27  			Update: schema.DefaultTimeout(10 * time.Minute),
    28  			Delete: schema.DefaultTimeout(10 * time.Minute),
    29  		},
    30  
    31  		Schema: map[string]*schema.Schema{
    32  			"region": &schema.Schema{
    33  				Type:        schema.TypeString,
    34  				Required:    true,
    35  				ForceNew:    true,
    36  				DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
    37  			},
    38  			"name": &schema.Schema{
    39  				Type:     schema.TypeString,
    40  				Optional: true,
    41  			},
    42  			"description": &schema.Schema{
    43  				Type:     schema.TypeString,
    44  				Optional: true,
    45  			},
    46  			"policy_id": &schema.Schema{
    47  				Type:     schema.TypeString,
    48  				Required: true,
    49  			},
    50  			"admin_state_up": &schema.Schema{
    51  				Type:     schema.TypeBool,
    52  				Optional: true,
    53  				Default:  true,
    54  			},
    55  			"tenant_id": &schema.Schema{
    56  				Type:     schema.TypeString,
    57  				Optional: true,
    58  				ForceNew: true,
    59  				Computed: true,
    60  			},
    61  			"associated_routers": &schema.Schema{
    62  				Type:          schema.TypeSet,
    63  				Optional:      true,
    64  				Elem:          &schema.Schema{Type: schema.TypeString},
    65  				Set:           schema.HashString,
    66  				ConflictsWith: []string{"no_routers"},
    67  			},
    68  			"no_routers": &schema.Schema{
    69  				Type:          schema.TypeBool,
    70  				Optional:      true,
    71  				ConflictsWith: []string{"associated_routers"},
    72  			},
    73  			"value_specs": &schema.Schema{
    74  				Type:     schema.TypeMap,
    75  				Optional: true,
    76  				ForceNew: true,
    77  			},
    78  		},
    79  	}
    80  }
    81  
    82  func resourceFWFirewallV1Create(d *schema.ResourceData, meta interface{}) error {
    83  
    84  	config := meta.(*Config)
    85  	networkingClient, err := config.networkingV2Client(GetRegion(d))
    86  	if err != nil {
    87  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
    88  	}
    89  
    90  	var createOpts firewalls.CreateOptsBuilder
    91  
    92  	adminStateUp := d.Get("admin_state_up").(bool)
    93  	createOpts = &firewalls.CreateOpts{
    94  		Name:         d.Get("name").(string),
    95  		Description:  d.Get("description").(string),
    96  		PolicyID:     d.Get("policy_id").(string),
    97  		AdminStateUp: &adminStateUp,
    98  		TenantID:     d.Get("tenant_id").(string),
    99  	}
   100  
   101  	associatedRoutersRaw := d.Get("associated_routers").(*schema.Set).List()
   102  	if len(associatedRoutersRaw) > 0 {
   103  		log.Printf("[DEBUG] Will attempt to associate Firewall with router(s): %+v", associatedRoutersRaw)
   104  
   105  		var routerIds []string
   106  		for _, v := range associatedRoutersRaw {
   107  			routerIds = append(routerIds, v.(string))
   108  		}
   109  
   110  		createOpts = &routerinsertion.CreateOptsExt{
   111  			CreateOptsBuilder: createOpts,
   112  			RouterIDs:         routerIds,
   113  		}
   114  	}
   115  
   116  	if d.Get("no_routers").(bool) {
   117  		routerIds := make([]string, 0)
   118  		log.Println("[DEBUG] No routers specified. Setting to empty slice")
   119  		createOpts = &routerinsertion.CreateOptsExt{
   120  			CreateOptsBuilder: createOpts,
   121  			RouterIDs:         routerIds,
   122  		}
   123  	}
   124  
   125  	createOpts = &FirewallCreateOpts{
   126  		createOpts,
   127  		MapValueSpecs(d),
   128  	}
   129  
   130  	log.Printf("[DEBUG] Create firewall: %#v", createOpts)
   131  
   132  	firewall, err := firewalls.Create(networkingClient, createOpts).Extract()
   133  	if err != nil {
   134  		return err
   135  	}
   136  
   137  	log.Printf("[DEBUG] Firewall created: %#v", firewall)
   138  
   139  	stateConf := &resource.StateChangeConf{
   140  		Pending:    []string{"PENDING_CREATE"},
   141  		Target:     []string{"ACTIVE"},
   142  		Refresh:    waitForFirewallActive(networkingClient, firewall.ID),
   143  		Timeout:    d.Timeout(schema.TimeoutCreate),
   144  		Delay:      0,
   145  		MinTimeout: 2 * time.Second,
   146  	}
   147  
   148  	_, err = stateConf.WaitForState()
   149  	log.Printf("[DEBUG] Firewall (%s) is active.", firewall.ID)
   150  
   151  	d.SetId(firewall.ID)
   152  
   153  	return resourceFWFirewallV1Read(d, meta)
   154  }
   155  
   156  func resourceFWFirewallV1Read(d *schema.ResourceData, meta interface{}) error {
   157  	log.Printf("[DEBUG] Retrieve information about firewall: %s", d.Id())
   158  
   159  	config := meta.(*Config)
   160  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   161  	if err != nil {
   162  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   163  	}
   164  
   165  	var firewall Firewall
   166  	err = firewalls.Get(networkingClient, d.Id()).ExtractInto(&firewall)
   167  	if err != nil {
   168  		return CheckDeleted(d, err, "firewall")
   169  	}
   170  
   171  	log.Printf("[DEBUG] Read OpenStack Firewall %s: %#v", d.Id(), firewall)
   172  
   173  	d.Set("name", firewall.Name)
   174  	d.Set("description", firewall.Description)
   175  	d.Set("policy_id", firewall.PolicyID)
   176  	d.Set("admin_state_up", firewall.AdminStateUp)
   177  	d.Set("tenant_id", firewall.TenantID)
   178  	d.Set("region", GetRegion(d))
   179  	d.Set("associated_routers", firewall.RouterIDs)
   180  
   181  	return nil
   182  }
   183  
   184  func resourceFWFirewallV1Update(d *schema.ResourceData, meta interface{}) error {
   185  
   186  	config := meta.(*Config)
   187  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   188  	if err != nil {
   189  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   190  	}
   191  
   192  	// PolicyID is required
   193  	opts := firewalls.UpdateOpts{
   194  		PolicyID: d.Get("policy_id").(string),
   195  	}
   196  
   197  	if d.HasChange("name") {
   198  		opts.Name = d.Get("name").(string)
   199  	}
   200  
   201  	if d.HasChange("description") {
   202  		opts.Description = d.Get("description").(string)
   203  	}
   204  
   205  	if d.HasChange("admin_state_up") {
   206  		adminStateUp := d.Get("admin_state_up").(bool)
   207  		opts.AdminStateUp = &adminStateUp
   208  	}
   209  
   210  	var updateOpts firewalls.UpdateOptsBuilder
   211  	var routerIds []string
   212  	if d.HasChange("associated_routers") || d.HasChange("no_routers") {
   213  		// 'no_routers' = true means 'associated_routers' will be empty...
   214  		if d.Get("no_routers").(bool) {
   215  			log.Printf("[DEBUG] 'no_routers' is true.")
   216  			routerIds = make([]string, 0)
   217  		} else {
   218  			associatedRoutersRaw := d.Get("associated_routers").(*schema.Set).List()
   219  			for _, v := range associatedRoutersRaw {
   220  				routerIds = append(routerIds, v.(string))
   221  			}
   222  		}
   223  
   224  		updateOpts = routerinsertion.UpdateOptsExt{
   225  			UpdateOptsBuilder: opts,
   226  			RouterIDs:         routerIds,
   227  		}
   228  	} else {
   229  		updateOpts = opts
   230  	}
   231  
   232  	log.Printf("[DEBUG] Updating firewall with id %s: %#v", d.Id(), updateOpts)
   233  
   234  	err = firewalls.Update(networkingClient, d.Id(), updateOpts).Err
   235  	if err != nil {
   236  		return err
   237  	}
   238  
   239  	stateConf := &resource.StateChangeConf{
   240  		Pending:    []string{"PENDING_CREATE", "PENDING_UPDATE"},
   241  		Target:     []string{"ACTIVE"},
   242  		Refresh:    waitForFirewallActive(networkingClient, d.Id()),
   243  		Timeout:    d.Timeout(schema.TimeoutUpdate),
   244  		Delay:      0,
   245  		MinTimeout: 2 * time.Second,
   246  	}
   247  
   248  	_, err = stateConf.WaitForState()
   249  
   250  	return resourceFWFirewallV1Read(d, meta)
   251  }
   252  
   253  func resourceFWFirewallV1Delete(d *schema.ResourceData, meta interface{}) error {
   254  	log.Printf("[DEBUG] Destroy firewall: %s", d.Id())
   255  
   256  	config := meta.(*Config)
   257  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   258  	if err != nil {
   259  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   260  	}
   261  
   262  	// Ensure the firewall was fully created/updated before being deleted.
   263  	stateConf := &resource.StateChangeConf{
   264  		Pending:    []string{"PENDING_CREATE", "PENDING_UPDATE"},
   265  		Target:     []string{"ACTIVE"},
   266  		Refresh:    waitForFirewallActive(networkingClient, d.Id()),
   267  		Timeout:    d.Timeout(schema.TimeoutUpdate),
   268  		Delay:      0,
   269  		MinTimeout: 2 * time.Second,
   270  	}
   271  
   272  	_, err = stateConf.WaitForState()
   273  
   274  	err = firewalls.Delete(networkingClient, d.Id()).Err
   275  
   276  	if err != nil {
   277  		return err
   278  	}
   279  
   280  	stateConf = &resource.StateChangeConf{
   281  		Pending:    []string{"DELETING"},
   282  		Target:     []string{"DELETED"},
   283  		Refresh:    waitForFirewallDeletion(networkingClient, d.Id()),
   284  		Timeout:    d.Timeout(schema.TimeoutDelete),
   285  		Delay:      0,
   286  		MinTimeout: 2 * time.Second,
   287  	}
   288  
   289  	_, err = stateConf.WaitForState()
   290  
   291  	return err
   292  }
   293  
   294  func waitForFirewallActive(networkingClient *gophercloud.ServiceClient, id string) resource.StateRefreshFunc {
   295  
   296  	return func() (interface{}, string, error) {
   297  		var fw Firewall
   298  
   299  		err := firewalls.Get(networkingClient, id).ExtractInto(&fw)
   300  		if err != nil {
   301  			return nil, "", err
   302  		}
   303  		return fw, fw.Status, nil
   304  	}
   305  }
   306  
   307  func waitForFirewallDeletion(networkingClient *gophercloud.ServiceClient, id string) resource.StateRefreshFunc {
   308  
   309  	return func() (interface{}, string, error) {
   310  		fw, err := firewalls.Get(networkingClient, id).Extract()
   311  		log.Printf("[DEBUG] Got firewall %s => %#v", id, fw)
   312  
   313  		if err != nil {
   314  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   315  				log.Printf("[DEBUG] Firewall %s is actually deleted", id)
   316  				return "", "DELETED", nil
   317  			}
   318  			return nil, "", fmt.Errorf("Unexpected error: %s", err)
   319  		}
   320  
   321  		log.Printf("[DEBUG] Firewall %s deletion is pending", id)
   322  		return fw, "DELETING", nil
   323  	}
   324  }