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