github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/builtin/providers/azurerm/resource_arm_loadbalancer_rule.go (about) 1 package azurerm 2 3 import ( 4 "fmt" 5 "log" 6 "regexp" 7 "time" 8 9 "github.com/Azure/azure-sdk-for-go/arm/network" 10 "github.com/hashicorp/errwrap" 11 "github.com/hashicorp/terraform/helper/resource" 12 "github.com/hashicorp/terraform/helper/schema" 13 "github.com/jen20/riviera/azure" 14 ) 15 16 func resourceArmLoadBalancerRule() *schema.Resource { 17 return &schema.Resource{ 18 Create: resourceArmLoadBalancerRuleCreate, 19 Read: resourceArmLoadBalancerRuleRead, 20 Update: resourceArmLoadBalancerRuleCreate, 21 Delete: resourceArmLoadBalancerRuleDelete, 22 Importer: &schema.ResourceImporter{ 23 State: loadBalancerSubResourceStateImporter, 24 }, 25 26 Schema: map[string]*schema.Schema{ 27 "name": { 28 Type: schema.TypeString, 29 Required: true, 30 ForceNew: true, 31 ValidateFunc: validateArmLoadBalancerRuleName, 32 }, 33 34 "location": deprecatedLocationSchema(), 35 36 "resource_group_name": { 37 Type: schema.TypeString, 38 Required: true, 39 ForceNew: true, 40 }, 41 42 "loadbalancer_id": { 43 Type: schema.TypeString, 44 Required: true, 45 ForceNew: true, 46 }, 47 48 "frontend_ip_configuration_name": { 49 Type: schema.TypeString, 50 Required: true, 51 }, 52 53 "frontend_ip_configuration_id": { 54 Type: schema.TypeString, 55 Computed: true, 56 }, 57 58 "backend_address_pool_id": { 59 Type: schema.TypeString, 60 Optional: true, 61 Computed: true, 62 }, 63 64 "protocol": { 65 Type: schema.TypeString, 66 Required: true, 67 StateFunc: ignoreCaseStateFunc, 68 DiffSuppressFunc: ignoreCaseDiffSuppressFunc, 69 }, 70 71 "frontend_port": { 72 Type: schema.TypeInt, 73 Required: true, 74 }, 75 76 "backend_port": { 77 Type: schema.TypeInt, 78 Required: true, 79 }, 80 81 "probe_id": { 82 Type: schema.TypeString, 83 Optional: true, 84 Computed: true, 85 }, 86 87 "enable_floating_ip": { 88 Type: schema.TypeBool, 89 Optional: true, 90 Default: false, 91 }, 92 93 "idle_timeout_in_minutes": { 94 Type: schema.TypeInt, 95 Optional: true, 96 Computed: true, 97 }, 98 99 "load_distribution": { 100 Type: schema.TypeString, 101 Optional: true, 102 Computed: true, 103 }, 104 }, 105 } 106 } 107 108 func resourceArmLoadBalancerRuleCreate(d *schema.ResourceData, meta interface{}) error { 109 client := meta.(*ArmClient) 110 lbClient := client.loadBalancerClient 111 112 loadBalancerID := d.Get("loadbalancer_id").(string) 113 armMutexKV.Lock(loadBalancerID) 114 defer armMutexKV.Unlock(loadBalancerID) 115 116 loadBalancer, exists, err := retrieveLoadBalancerById(loadBalancerID, meta) 117 if err != nil { 118 return errwrap.Wrapf("Error Getting LoadBalancer By ID {{err}}", err) 119 } 120 if !exists { 121 d.SetId("") 122 log.Printf("[INFO] LoadBalancer %q not found. Removing from state", d.Get("name").(string)) 123 return nil 124 } 125 126 newLbRule, err := expandAzureRmLoadBalancerRule(d, loadBalancer) 127 if err != nil { 128 return errwrap.Wrapf("Error Exanding LoadBalancer Rule {{err}}", err) 129 } 130 131 lbRules := append(*loadBalancer.LoadBalancerPropertiesFormat.LoadBalancingRules, *newLbRule) 132 133 existingRule, existingRuleIndex, exists := findLoadBalancerRuleByName(loadBalancer, d.Get("name").(string)) 134 if exists { 135 if d.Get("name").(string) == *existingRule.Name { 136 // this rule is being updated/reapplied remove old copy from the slice 137 lbRules = append(lbRules[:existingRuleIndex], lbRules[existingRuleIndex+1:]...) 138 } 139 } 140 141 loadBalancer.LoadBalancerPropertiesFormat.LoadBalancingRules = &lbRules 142 resGroup, loadBalancerName, err := resourceGroupAndLBNameFromId(d.Get("loadbalancer_id").(string)) 143 if err != nil { 144 return errwrap.Wrapf("Error Getting LoadBalancer Name and Group: {{err}}", err) 145 } 146 147 _, error := lbClient.CreateOrUpdate(resGroup, loadBalancerName, *loadBalancer, make(chan struct{})) 148 err = <-error 149 if err != nil { 150 return errwrap.Wrapf("Error Creating/Updating LoadBalancer {{err}}", err) 151 } 152 153 read, err := lbClient.Get(resGroup, loadBalancerName, "") 154 if err != nil { 155 return errwrap.Wrapf("Error Getting LoadBalancer {{err}}", err) 156 } 157 if read.ID == nil { 158 return fmt.Errorf("Cannot read LoadBalancer %s (resource group %s) ID", loadBalancerName, resGroup) 159 } 160 161 var rule_id string 162 for _, LoadBalancingRule := range *(*read.LoadBalancerPropertiesFormat).LoadBalancingRules { 163 if *LoadBalancingRule.Name == d.Get("name").(string) { 164 rule_id = *LoadBalancingRule.ID 165 } 166 } 167 168 if rule_id != "" { 169 d.SetId(rule_id) 170 } else { 171 return fmt.Errorf("Cannot find created LoadBalancer Rule ID %q", rule_id) 172 } 173 174 log.Printf("[DEBUG] Waiting for LoadBalancer (%s) to become available", loadBalancerName) 175 stateConf := &resource.StateChangeConf{ 176 Pending: []string{"Accepted", "Updating"}, 177 Target: []string{"Succeeded"}, 178 Refresh: loadbalancerStateRefreshFunc(client, resGroup, loadBalancerName), 179 Timeout: 10 * time.Minute, 180 } 181 if _, err := stateConf.WaitForState(); err != nil { 182 return fmt.Errorf("Error waiting for LoadBalancer (%s) to become available: %s", loadBalancerName, err) 183 } 184 185 return resourceArmLoadBalancerRuleRead(d, meta) 186 } 187 188 func resourceArmLoadBalancerRuleRead(d *schema.ResourceData, meta interface{}) error { 189 id, err := parseAzureResourceID(d.Id()) 190 if err != nil { 191 return err 192 } 193 name := id.Path["loadBalancingRules"] 194 195 loadBalancer, exists, err := retrieveLoadBalancerById(d.Get("loadbalancer_id").(string), meta) 196 if err != nil { 197 return errwrap.Wrapf("Error Getting LoadBalancer By ID {{err}}", err) 198 } 199 if !exists { 200 d.SetId("") 201 log.Printf("[INFO] LoadBalancer %q not found. Removing from state", name) 202 return nil 203 } 204 205 config, _, exists := findLoadBalancerRuleByName(loadBalancer, name) 206 if !exists { 207 d.SetId("") 208 log.Printf("[INFO] LoadBalancer Rule %q not found. Removing from state", name) 209 return nil 210 } 211 212 d.Set("name", config.Name) 213 d.Set("resource_group_name", id.ResourceGroup) 214 215 d.Set("protocol", config.LoadBalancingRulePropertiesFormat.Protocol) 216 d.Set("frontend_port", config.LoadBalancingRulePropertiesFormat.FrontendPort) 217 d.Set("backend_port", config.LoadBalancingRulePropertiesFormat.BackendPort) 218 219 if config.LoadBalancingRulePropertiesFormat.EnableFloatingIP != nil { 220 d.Set("enable_floating_ip", config.LoadBalancingRulePropertiesFormat.EnableFloatingIP) 221 } 222 223 if config.LoadBalancingRulePropertiesFormat.IdleTimeoutInMinutes != nil { 224 d.Set("idle_timeout_in_minutes", config.LoadBalancingRulePropertiesFormat.IdleTimeoutInMinutes) 225 } 226 227 if config.LoadBalancingRulePropertiesFormat.FrontendIPConfiguration != nil { 228 fipID, err := parseAzureResourceID(*config.LoadBalancingRulePropertiesFormat.FrontendIPConfiguration.ID) 229 if err != nil { 230 return err 231 } 232 233 d.Set("frontend_ip_configuration_name", fipID.Path["frontendIPConfigurations"]) 234 d.Set("frontend_ip_configuration_id", config.LoadBalancingRulePropertiesFormat.FrontendIPConfiguration.ID) 235 } 236 237 if config.LoadBalancingRulePropertiesFormat.BackendAddressPool != nil { 238 d.Set("backend_address_pool_id", config.LoadBalancingRulePropertiesFormat.BackendAddressPool.ID) 239 } 240 241 if config.LoadBalancingRulePropertiesFormat.Probe != nil { 242 d.Set("probe_id", config.LoadBalancingRulePropertiesFormat.Probe.ID) 243 } 244 245 if config.LoadBalancingRulePropertiesFormat.LoadDistribution != "" { 246 d.Set("load_distribution", config.LoadBalancingRulePropertiesFormat.LoadDistribution) 247 } 248 249 return nil 250 } 251 252 func resourceArmLoadBalancerRuleDelete(d *schema.ResourceData, meta interface{}) error { 253 client := meta.(*ArmClient) 254 lbClient := client.loadBalancerClient 255 256 loadBalancerID := d.Get("loadbalancer_id").(string) 257 armMutexKV.Lock(loadBalancerID) 258 defer armMutexKV.Unlock(loadBalancerID) 259 260 loadBalancer, exists, err := retrieveLoadBalancerById(loadBalancerID, meta) 261 if err != nil { 262 return errwrap.Wrapf("Error Getting LoadBalancer By ID {{err}}", err) 263 } 264 if !exists { 265 d.SetId("") 266 return nil 267 } 268 269 _, index, exists := findLoadBalancerRuleByName(loadBalancer, d.Get("name").(string)) 270 if !exists { 271 return nil 272 } 273 274 oldLbRules := *loadBalancer.LoadBalancerPropertiesFormat.LoadBalancingRules 275 newLbRules := append(oldLbRules[:index], oldLbRules[index+1:]...) 276 loadBalancer.LoadBalancerPropertiesFormat.LoadBalancingRules = &newLbRules 277 278 resGroup, loadBalancerName, err := resourceGroupAndLBNameFromId(d.Get("loadbalancer_id").(string)) 279 if err != nil { 280 return errwrap.Wrapf("Error Getting LoadBalancer Name and Group: {{err}}", err) 281 } 282 283 _, error := lbClient.CreateOrUpdate(resGroup, loadBalancerName, *loadBalancer, make(chan struct{})) 284 err = <-error 285 if err != nil { 286 return errwrap.Wrapf("Error Creating/Updating LoadBalancer {{err}}", err) 287 } 288 289 read, err := lbClient.Get(resGroup, loadBalancerName, "") 290 if err != nil { 291 return errwrap.Wrapf("Error Getting LoadBalancer {{err}}", err) 292 } 293 if read.ID == nil { 294 return fmt.Errorf("Cannot read LoadBalancer %s (resource group %s) ID", loadBalancerName, resGroup) 295 } 296 297 return nil 298 } 299 300 func expandAzureRmLoadBalancerRule(d *schema.ResourceData, lb *network.LoadBalancer) (*network.LoadBalancingRule, error) { 301 302 properties := network.LoadBalancingRulePropertiesFormat{ 303 Protocol: network.TransportProtocol(d.Get("protocol").(string)), 304 FrontendPort: azure.Int32(int32(d.Get("frontend_port").(int))), 305 BackendPort: azure.Int32(int32(d.Get("backend_port").(int))), 306 EnableFloatingIP: azure.Bool(d.Get("enable_floating_ip").(bool)), 307 } 308 309 if v, ok := d.GetOk("idle_timeout_in_minutes"); ok { 310 properties.IdleTimeoutInMinutes = azure.Int32(int32(v.(int))) 311 } 312 313 if v := d.Get("load_distribution").(string); v != "" { 314 properties.LoadDistribution = network.LoadDistribution(v) 315 } 316 317 if v := d.Get("frontend_ip_configuration_name").(string); v != "" { 318 rule, _, exists := findLoadBalancerFrontEndIpConfigurationByName(lb, v) 319 if !exists { 320 return nil, fmt.Errorf("[ERROR] Cannot find FrontEnd IP Configuration with the name %s", v) 321 } 322 323 feip := network.SubResource{ 324 ID: rule.ID, 325 } 326 327 properties.FrontendIPConfiguration = &feip 328 } 329 330 if v := d.Get("backend_address_pool_id").(string); v != "" { 331 beAP := network.SubResource{ 332 ID: &v, 333 } 334 335 properties.BackendAddressPool = &beAP 336 } 337 338 if v := d.Get("probe_id").(string); v != "" { 339 pid := network.SubResource{ 340 ID: &v, 341 } 342 343 properties.Probe = &pid 344 } 345 346 lbRule := network.LoadBalancingRule{ 347 Name: azure.String(d.Get("name").(string)), 348 LoadBalancingRulePropertiesFormat: &properties, 349 } 350 351 return &lbRule, nil 352 } 353 354 func validateArmLoadBalancerRuleName(v interface{}, k string) (ws []string, errors []error) { 355 value := v.(string) 356 if !regexp.MustCompile(`^[a-zA-Z_0-9.-]+$`).MatchString(value) { 357 errors = append(errors, fmt.Errorf( 358 "only word characters, numbers, underscores, periods, and hyphens allowed in %q: %q", 359 k, value)) 360 } 361 362 if len(value) > 80 { 363 errors = append(errors, fmt.Errorf( 364 "%q cannot be longer than 80 characters: %q", k, value)) 365 } 366 367 if len(value) == 0 { 368 errors = append(errors, fmt.Errorf( 369 "%q cannot be an empty string: %q", k, value)) 370 } 371 if !regexp.MustCompile(`[a-zA-Z0-9_]$`).MatchString(value) { 372 errors = append(errors, fmt.Errorf( 373 "%q must end with a word character, number, or underscore: %q", k, value)) 374 } 375 376 if !regexp.MustCompile(`^[a-zA-Z0-9]`).MatchString(value) { 377 errors = append(errors, fmt.Errorf( 378 "%q must start with a word character or number: %q", k, value)) 379 } 380 381 return 382 }