github.com/sixgill/terraform@v0.9.0-beta2.0.20170316214032-033f6226ae50/builtin/providers/openstack/resource_openstack_lb_member_v2.go (about) 1 package openstack 2 3 import ( 4 "fmt" 5 "log" 6 "time" 7 8 "github.com/hashicorp/terraform/helper/resource" 9 "github.com/hashicorp/terraform/helper/schema" 10 11 "github.com/gophercloud/gophercloud" 12 "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/lbaas_v2/pools" 13 ) 14 15 func resourceMemberV2() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceMemberV2Create, 18 Read: resourceMemberV2Read, 19 Update: resourceMemberV2Update, 20 Delete: resourceMemberV2Delete, 21 22 Schema: map[string]*schema.Schema{ 23 "region": &schema.Schema{ 24 Type: schema.TypeString, 25 Required: true, 26 ForceNew: true, 27 DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""), 28 }, 29 30 "name": &schema.Schema{ 31 Type: schema.TypeString, 32 Optional: true, 33 }, 34 35 "tenant_id": &schema.Schema{ 36 Type: schema.TypeString, 37 Optional: true, 38 Computed: true, 39 ForceNew: true, 40 }, 41 42 "address": &schema.Schema{ 43 Type: schema.TypeString, 44 Required: true, 45 ForceNew: true, 46 }, 47 48 "protocol_port": &schema.Schema{ 49 Type: schema.TypeInt, 50 Required: true, 51 ForceNew: true, 52 }, 53 54 "weight": &schema.Schema{ 55 Type: schema.TypeInt, 56 Optional: true, 57 Computed: true, 58 ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) { 59 value := v.(int) 60 if value < 1 { 61 errors = append(errors, fmt.Errorf( 62 "Only numbers greater than 0 are supported values for 'weight'")) 63 } 64 return 65 }, 66 }, 67 68 "subnet_id": &schema.Schema{ 69 Type: schema.TypeString, 70 Required: true, 71 ForceNew: true, 72 }, 73 74 "admin_state_up": &schema.Schema{ 75 Type: schema.TypeBool, 76 Default: true, 77 Optional: true, 78 }, 79 80 "pool_id": &schema.Schema{ 81 Type: schema.TypeString, 82 Required: true, 83 ForceNew: true, 84 }, 85 86 "id": &schema.Schema{ 87 Type: schema.TypeString, 88 Optional: true, 89 Computed: true, 90 }, 91 }, 92 } 93 } 94 95 func resourceMemberV2Create(d *schema.ResourceData, meta interface{}) error { 96 config := meta.(*Config) 97 networkingClient, err := config.networkingV2Client(GetRegion(d)) 98 if err != nil { 99 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 100 } 101 102 adminStateUp := d.Get("admin_state_up").(bool) 103 createOpts := pools.CreateMemberOpts{ 104 Name: d.Get("name").(string), 105 TenantID: d.Get("tenant_id").(string), 106 Address: d.Get("address").(string), 107 ProtocolPort: d.Get("protocol_port").(int), 108 Weight: d.Get("weight").(int), 109 AdminStateUp: &adminStateUp, 110 } 111 112 // Must omit if not set 113 if v, ok := d.GetOk("subnet_id"); ok { 114 createOpts.SubnetID = v.(string) 115 } 116 117 poolID := d.Get("pool_id").(string) 118 119 log.Printf("[DEBUG] Create Options: %#v", createOpts) 120 121 var member *pools.Member 122 err = resource.Retry(10*time.Minute, func() *resource.RetryError { 123 var err error 124 log.Printf("[DEBUG] Attempting to create LBaaSV2 member") 125 member, err = pools.CreateMember(networkingClient, poolID, createOpts).Extract() 126 if err != nil { 127 switch errCode := err.(type) { 128 case gophercloud.ErrDefault500: 129 log.Printf("[DEBUG] OpenStack LBaaSV2 member is still creating.") 130 return resource.RetryableError(err) 131 case gophercloud.ErrUnexpectedResponseCode: 132 if errCode.Actual == 409 { 133 log.Printf("[DEBUG] OpenStack LBaaSV2 member is still creating.") 134 return resource.RetryableError(err) 135 } 136 137 default: 138 return resource.NonRetryableError(err) 139 } 140 } 141 return nil 142 }) 143 144 if err != nil { 145 return fmt.Errorf("Error creating OpenStack LBaaSV2 member: %s", err) 146 } 147 log.Printf("[INFO] member ID: %s", member.ID) 148 149 log.Printf("[DEBUG] Waiting for Openstack LBaaSV2 member (%s) to become available.", member.ID) 150 151 stateConf := &resource.StateChangeConf{ 152 Pending: []string{"PENDING_CREATE"}, 153 Target: []string{"ACTIVE"}, 154 Refresh: waitForMemberActive(networkingClient, poolID, member.ID), 155 Timeout: 2 * time.Minute, 156 Delay: 5 * time.Second, 157 MinTimeout: 3 * time.Second, 158 } 159 160 _, err = stateConf.WaitForState() 161 if err != nil { 162 return err 163 } 164 165 d.SetId(member.ID) 166 167 return resourceMemberV2Read(d, meta) 168 } 169 170 func resourceMemberV2Read(d *schema.ResourceData, meta interface{}) error { 171 config := meta.(*Config) 172 networkingClient, err := config.networkingV2Client(GetRegion(d)) 173 if err != nil { 174 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 175 } 176 177 member, err := pools.GetMember(networkingClient, d.Get("pool_id").(string), d.Id()).Extract() 178 if err != nil { 179 return CheckDeleted(d, err, "LBV2 Member") 180 } 181 182 log.Printf("[DEBUG] Retrieved OpenStack LBaaSV2 Member %s: %+v", d.Id(), member) 183 184 d.Set("name", member.Name) 185 d.Set("weight", member.Weight) 186 d.Set("admin_state_up", member.AdminStateUp) 187 d.Set("tenant_id", member.TenantID) 188 d.Set("subnet_id", member.SubnetID) 189 d.Set("address", member.Address) 190 d.Set("protocol_port", member.ProtocolPort) 191 d.Set("id", member.ID) 192 193 return nil 194 } 195 196 func resourceMemberV2Update(d *schema.ResourceData, meta interface{}) error { 197 config := meta.(*Config) 198 networkingClient, err := config.networkingV2Client(GetRegion(d)) 199 if err != nil { 200 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 201 } 202 203 var updateOpts pools.UpdateMemberOpts 204 if d.HasChange("name") { 205 updateOpts.Name = d.Get("name").(string) 206 } 207 if d.HasChange("weight") { 208 updateOpts.Weight = d.Get("weight").(int) 209 } 210 if d.HasChange("admin_state_up") { 211 asu := d.Get("admin_state_up").(bool) 212 updateOpts.AdminStateUp = &asu 213 } 214 215 log.Printf("[DEBUG] Updating OpenStack LBaaSV2 Member %s with options: %+v", d.Id(), updateOpts) 216 217 _, err = pools.UpdateMember(networkingClient, d.Get("pool_id").(string), d.Id(), updateOpts).Extract() 218 if err != nil { 219 return fmt.Errorf("Error updating OpenStack LBaaSV2 Member: %s", err) 220 } 221 222 return resourceMemberV2Read(d, meta) 223 } 224 225 func resourceMemberV2Delete(d *schema.ResourceData, meta interface{}) error { 226 config := meta.(*Config) 227 networkingClient, err := config.networkingV2Client(GetRegion(d)) 228 if err != nil { 229 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 230 } 231 232 stateConf := &resource.StateChangeConf{ 233 Pending: []string{"ACTIVE", "PENDING_DELETE"}, 234 Target: []string{"DELETED"}, 235 Refresh: waitForMemberDelete(networkingClient, d.Get("pool_id").(string), d.Id()), 236 Timeout: 2 * time.Minute, 237 Delay: 5 * time.Second, 238 MinTimeout: 3 * time.Second, 239 } 240 241 _, err = stateConf.WaitForState() 242 if err != nil { 243 return fmt.Errorf("Error deleting OpenStack LBaaSV2 Member: %s", err) 244 } 245 246 d.SetId("") 247 return nil 248 } 249 250 func waitForMemberActive(networkingClient *gophercloud.ServiceClient, poolID string, memberID string) resource.StateRefreshFunc { 251 return func() (interface{}, string, error) { 252 member, err := pools.GetMember(networkingClient, poolID, memberID).Extract() 253 if err != nil { 254 return nil, "", err 255 } 256 257 // The member resource has no Status attribute, so a successful Get is the best we can do 258 log.Printf("[DEBUG] OpenStack LBaaSV2 Member: %+v", member) 259 return member, "ACTIVE", nil 260 } 261 } 262 263 func waitForMemberDelete(networkingClient *gophercloud.ServiceClient, poolID string, memberID string) resource.StateRefreshFunc { 264 return func() (interface{}, string, error) { 265 log.Printf("[DEBUG] Attempting to delete OpenStack LBaaSV2 Member %s", memberID) 266 267 member, err := pools.GetMember(networkingClient, poolID, memberID).Extract() 268 if err != nil { 269 if _, ok := err.(gophercloud.ErrDefault404); ok { 270 log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Member %s", memberID) 271 return member, "DELETED", nil 272 } 273 return member, "ACTIVE", err 274 } 275 276 log.Printf("[DEBUG] Openstack LBaaSV2 Member: %+v", member) 277 err = pools.DeleteMember(networkingClient, poolID, memberID).ExtractErr() 278 if err != nil { 279 switch errCode := err.(type) { 280 case gophercloud.ErrDefault404: 281 log.Printf("[DEBUG] Successfully deleted OpenStack LBaaSV2 Member %s", memberID) 282 return member, "DELETED", nil 283 case gophercloud.ErrDefault500: 284 log.Printf("[DEBUG] OpenStack LBaaSV2 Member (%s) is still in use.", memberID) 285 return member, "PENDING_DELETE", nil 286 case gophercloud.ErrUnexpectedResponseCode: 287 if errCode.Actual == 409 { 288 log.Printf("[DEBUG] OpenStack LBaaSV2 Member (%s) is still in use.", memberID) 289 return member, "PENDING_DELETE", nil 290 } 291 292 default: 293 return member, "ACTIVE", err 294 } 295 } 296 297 log.Printf("[DEBUG] OpenStack LBaaSV2 Member %s still active.", memberID) 298 return member, "ACTIVE", nil 299 } 300 }