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