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 }