github.com/jsoriano/terraform@v0.6.7-0.20151026070445-8b70867fdd95/builtin/providers/openstack/resource_openstack_compute_secgroup_v2.go (about)

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