github.com/willrstern/terraform@v0.6.7-0.20151106173844-fa471ddbb53a/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go (about)

     1  package openstack
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"time"
     8  
     9  	"github.com/hashicorp/terraform/helper/hashcode"
    10  	"github.com/hashicorp/terraform/helper/resource"
    11  	"github.com/hashicorp/terraform/helper/schema"
    12  	"github.com/rackspace/gophercloud"
    13  	"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
    14  )
    15  
    16  func resourceComputeSecGroupV2() *schema.Resource {
    17  	return &schema.Resource{
    18  		Create: resourceComputeSecGroupV2Create,
    19  		Read:   resourceComputeSecGroupV2Read,
    20  		Update: resourceComputeSecGroupV2Update,
    21  		Delete: resourceComputeSecGroupV2Delete,
    22  
    23  		Schema: map[string]*schema.Schema{
    24  			"region": &schema.Schema{
    25  				Type:        schema.TypeString,
    26  				Required:    true,
    27  				ForceNew:    true,
    28  				DefaultFunc: envDefaultFuncAllowMissing("OS_REGION_NAME"),
    29  			},
    30  			"name": &schema.Schema{
    31  				Type:     schema.TypeString,
    32  				Required: true,
    33  				ForceNew: false,
    34  			},
    35  			"description": &schema.Schema{
    36  				Type:     schema.TypeString,
    37  				Required: true,
    38  				ForceNew: false,
    39  			},
    40  			"rule": &schema.Schema{
    41  				Type:     schema.TypeList,
    42  				Optional: true,
    43  				Elem: &schema.Resource{
    44  					Schema: map[string]*schema.Schema{
    45  						"id": &schema.Schema{
    46  							Type:     schema.TypeString,
    47  							Computed: true,
    48  						},
    49  						"from_port": &schema.Schema{
    50  							Type:     schema.TypeInt,
    51  							Required: true,
    52  							ForceNew: false,
    53  						},
    54  						"to_port": &schema.Schema{
    55  							Type:     schema.TypeInt,
    56  							Required: true,
    57  							ForceNew: false,
    58  						},
    59  						"ip_protocol": &schema.Schema{
    60  							Type:     schema.TypeString,
    61  							Required: true,
    62  							ForceNew: false,
    63  						},
    64  						"cidr": &schema.Schema{
    65  							Type:     schema.TypeString,
    66  							Optional: true,
    67  							ForceNew: false,
    68  						},
    69  						"from_group_id": &schema.Schema{
    70  							Type:     schema.TypeString,
    71  							Optional: true,
    72  							ForceNew: false,
    73  						},
    74  						"self": &schema.Schema{
    75  							Type:     schema.TypeBool,
    76  							Optional: true,
    77  							Default:  false,
    78  							ForceNew: false,
    79  						},
    80  					},
    81  				},
    82  			},
    83  		},
    84  	}
    85  }
    86  
    87  func resourceComputeSecGroupV2Create(d *schema.ResourceData, meta interface{}) error {
    88  	config := meta.(*Config)
    89  	computeClient, err := config.computeV2Client(d.Get("region").(string))
    90  	if err != nil {
    91  		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
    92  	}
    93  
    94  	createOpts := secgroups.CreateOpts{
    95  		Name:        d.Get("name").(string),
    96  		Description: d.Get("description").(string),
    97  	}
    98  
    99  	log.Printf("[DEBUG] Create Options: %#v", createOpts)
   100  	sg, err := secgroups.Create(computeClient, createOpts).Extract()
   101  	if err != nil {
   102  		return fmt.Errorf("Error creating OpenStack security group: %s", err)
   103  	}
   104  
   105  	d.SetId(sg.ID)
   106  
   107  	createRuleOptsList := resourceSecGroupRulesV2(d)
   108  	for _, createRuleOpts := range createRuleOptsList {
   109  		_, err := secgroups.CreateRule(computeClient, createRuleOpts).Extract()
   110  		if err != nil {
   111  			return fmt.Errorf("Error creating OpenStack security group rule: %s", err)
   112  		}
   113  	}
   114  
   115  	return resourceComputeSecGroupV2Read(d, meta)
   116  }
   117  
   118  func resourceComputeSecGroupV2Read(d *schema.ResourceData, meta interface{}) error {
   119  	config := meta.(*Config)
   120  	computeClient, err := config.computeV2Client(d.Get("region").(string))
   121  	if err != nil {
   122  		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
   123  	}
   124  
   125  	sg, err := secgroups.Get(computeClient, d.Id()).Extract()
   126  	if err != nil {
   127  		return CheckDeleted(d, err, "security group")
   128  	}
   129  
   130  	d.Set("name", sg.Name)
   131  	d.Set("description", sg.Description)
   132  	rtm := rulesToMap(sg.Rules)
   133  	for _, v := range rtm {
   134  		if v["group"] == d.Get("name") {
   135  			v["self"] = "1"
   136  		} else {
   137  			v["self"] = "0"
   138  		}
   139  	}
   140  	log.Printf("[DEBUG] rulesToMap(sg.Rules): %+v", rtm)
   141  	d.Set("rule", rtm)
   142  
   143  	return nil
   144  }
   145  
   146  func resourceComputeSecGroupV2Update(d *schema.ResourceData, meta interface{}) error {
   147  	config := meta.(*Config)
   148  	computeClient, err := config.computeV2Client(d.Get("region").(string))
   149  	if err != nil {
   150  		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
   151  	}
   152  
   153  	updateOpts := secgroups.UpdateOpts{
   154  		Name:        d.Get("name").(string),
   155  		Description: d.Get("description").(string),
   156  	}
   157  
   158  	log.Printf("[DEBUG] Updating Security Group (%s) with options: %+v", d.Id(), updateOpts)
   159  
   160  	_, err = secgroups.Update(computeClient, d.Id(), updateOpts).Extract()
   161  	if err != nil {
   162  		return fmt.Errorf("Error updating OpenStack security group (%s): %s", d.Id(), err)
   163  	}
   164  
   165  	if d.HasChange("rule") {
   166  		oldSGRaw, newSGRaw := d.GetChange("rule")
   167  		oldSGRSlice, newSGRSlice := oldSGRaw.([]interface{}), newSGRaw.([]interface{})
   168  		oldSGRSet := schema.NewSet(secgroupRuleV2Hash, oldSGRSlice)
   169  		newSGRSet := schema.NewSet(secgroupRuleV2Hash, newSGRSlice)
   170  		secgrouprulesToAdd := newSGRSet.Difference(oldSGRSet)
   171  		secgrouprulesToRemove := oldSGRSet.Difference(newSGRSet)
   172  
   173  		log.Printf("[DEBUG] Security group rules to add: %v", secgrouprulesToAdd)
   174  
   175  		log.Printf("[DEBUG] Security groups rules to remove: %v", secgrouprulesToRemove)
   176  
   177  		for _, rawRule := range secgrouprulesToAdd.List() {
   178  			createRuleOpts := resourceSecGroupRuleCreateOptsV2(d, rawRule)
   179  			rule, err := secgroups.CreateRule(computeClient, createRuleOpts).Extract()
   180  			if err != nil {
   181  				return fmt.Errorf("Error adding rule to OpenStack security group (%s): %s", d.Id(), err)
   182  			}
   183  			log.Printf("[DEBUG] Added rule (%s) to OpenStack security group (%s) ", rule.ID, d.Id())
   184  		}
   185  
   186  		for _, r := range secgrouprulesToRemove.List() {
   187  			rule := resourceSecGroupRuleV2(d, r)
   188  			err := secgroups.DeleteRule(computeClient, rule.ID).ExtractErr()
   189  			if err != nil {
   190  				errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
   191  				if !ok {
   192  					return fmt.Errorf("Error removing rule (%s) from OpenStack security group (%s): %s", rule.ID, d.Id(), err)
   193  				}
   194  				if errCode.Actual == 404 {
   195  					continue
   196  				} else {
   197  					return fmt.Errorf("Error removing rule (%s) from OpenStack security group (%s)", rule.ID, d.Id())
   198  				}
   199  			} else {
   200  				log.Printf("[DEBUG] Removed rule (%s) from OpenStack security group (%s): %s", rule.ID, d.Id(), err)
   201  			}
   202  		}
   203  	}
   204  
   205  	return resourceComputeSecGroupV2Read(d, meta)
   206  }
   207  
   208  func resourceComputeSecGroupV2Delete(d *schema.ResourceData, meta interface{}) error {
   209  	config := meta.(*Config)
   210  	computeClient, err := config.computeV2Client(d.Get("region").(string))
   211  	if err != nil {
   212  		return fmt.Errorf("Error creating OpenStack compute client: %s", err)
   213  	}
   214  
   215  	stateConf := &resource.StateChangeConf{
   216  		Pending:    []string{"ACTIVE"},
   217  		Target:     "DELETED",
   218  		Refresh:    SecGroupV2StateRefreshFunc(computeClient, d),
   219  		Timeout:    10 * time.Minute,
   220  		Delay:      10 * time.Second,
   221  		MinTimeout: 3 * time.Second,
   222  	}
   223  
   224  	_, err = stateConf.WaitForState()
   225  	if err != nil {
   226  		return fmt.Errorf("Error deleting OpenStack security group: %s", err)
   227  	}
   228  
   229  	d.SetId("")
   230  	return nil
   231  }
   232  
   233  func resourceSecGroupRulesV2(d *schema.ResourceData) []secgroups.CreateRuleOpts {
   234  	rawRules := d.Get("rule").([]interface{})
   235  	createRuleOptsList := make([]secgroups.CreateRuleOpts, len(rawRules))
   236  	for i, raw := range rawRules {
   237  		rawMap := raw.(map[string]interface{})
   238  		groupId := rawMap["from_group_id"].(string)
   239  		if rawMap["self"].(bool) {
   240  			groupId = d.Id()
   241  		}
   242  		createRuleOptsList[i] = secgroups.CreateRuleOpts{
   243  			ParentGroupID: d.Id(),
   244  			FromPort:      rawMap["from_port"].(int),
   245  			ToPort:        rawMap["to_port"].(int),
   246  			IPProtocol:    rawMap["ip_protocol"].(string),
   247  			CIDR:          rawMap["cidr"].(string),
   248  			FromGroupID:   groupId,
   249  		}
   250  	}
   251  	return createRuleOptsList
   252  }
   253  
   254  func resourceSecGroupRuleCreateOptsV2(d *schema.ResourceData, raw interface{}) secgroups.CreateRuleOpts {
   255  	rawMap := raw.(map[string]interface{})
   256  	groupId := rawMap["from_group_id"].(string)
   257  	if rawMap["self"].(bool) {
   258  		groupId = d.Id()
   259  	}
   260  	return secgroups.CreateRuleOpts{
   261  		ParentGroupID: d.Id(),
   262  		FromPort:      rawMap["from_port"].(int),
   263  		ToPort:        rawMap["to_port"].(int),
   264  		IPProtocol:    rawMap["ip_protocol"].(string),
   265  		CIDR:          rawMap["cidr"].(string),
   266  		FromGroupID:   groupId,
   267  	}
   268  }
   269  
   270  func resourceSecGroupRuleV2(d *schema.ResourceData, raw interface{}) secgroups.Rule {
   271  	rawMap := raw.(map[string]interface{})
   272  	return secgroups.Rule{
   273  		ID:            rawMap["id"].(string),
   274  		ParentGroupID: d.Id(),
   275  		FromPort:      rawMap["from_port"].(int),
   276  		ToPort:        rawMap["to_port"].(int),
   277  		IPProtocol:    rawMap["ip_protocol"].(string),
   278  		IPRange:       secgroups.IPRange{CIDR: rawMap["cidr"].(string)},
   279  	}
   280  }
   281  
   282  func rulesToMap(sgrs []secgroups.Rule) []map[string]interface{} {
   283  	sgrMap := make([]map[string]interface{}, len(sgrs))
   284  	for i, sgr := range sgrs {
   285  		sgrMap[i] = map[string]interface{}{
   286  			"id":          sgr.ID,
   287  			"from_port":   sgr.FromPort,
   288  			"to_port":     sgr.ToPort,
   289  			"ip_protocol": sgr.IPProtocol,
   290  			"cidr":        sgr.IPRange.CIDR,
   291  			"group":       sgr.Group.Name,
   292  		}
   293  	}
   294  	return sgrMap
   295  }
   296  
   297  func secgroupRuleV2Hash(v interface{}) int {
   298  	var buf bytes.Buffer
   299  	m := v.(map[string]interface{})
   300  	buf.WriteString(fmt.Sprintf("%d-", m["from_port"].(int)))
   301  	buf.WriteString(fmt.Sprintf("%d-", m["to_port"].(int)))
   302  	buf.WriteString(fmt.Sprintf("%s-", m["ip_protocol"].(string)))
   303  	buf.WriteString(fmt.Sprintf("%s-", m["cidr"].(string)))
   304  
   305  	return hashcode.String(buf.String())
   306  }
   307  
   308  func SecGroupV2StateRefreshFunc(computeClient *gophercloud.ServiceClient, d *schema.ResourceData) resource.StateRefreshFunc {
   309  	return func() (interface{}, string, error) {
   310  		log.Printf("[DEBUG] Attempting to delete Security Group %s.\n", d.Id())
   311  
   312  		err := secgroups.Delete(computeClient, d.Id()).ExtractErr()
   313  		if err != nil {
   314  			return nil, "", err
   315  		}
   316  
   317  		s, err := secgroups.Get(computeClient, d.Id()).Extract()
   318  		if err != nil {
   319  			err = CheckDeleted(d, err, "Security Group")
   320  			if err != nil {
   321  				return s, "", err
   322  			} else {
   323  				log.Printf("[DEBUG] Successfully deleted Security Group %s", d.Id())
   324  				return s, "DELETED", nil
   325  			}
   326  		}
   327  
   328  		log.Printf("[DEBUG] Security Group %s still active.\n", d.Id())
   329  		return s, "ACTIVE", nil
   330  	}
   331  }