diff --git a/digitalocean/loadbalancer/resource_loadbalancer.go b/digitalocean/loadbalancer/resource_loadbalancer.go index 53585245f..446b2bc4e 100644 --- a/digitalocean/loadbalancer/resource_loadbalancer.go +++ b/digitalocean/loadbalancer/resource_loadbalancer.go @@ -81,6 +81,10 @@ func ResourceDigitalOceanLoadbalancer() *schema.Resource { } } + if err := loadbalancerDiffCheck(ctx, diff, v); err != nil { + return err + } + return nil }, } @@ -113,12 +117,35 @@ func resourceDigitalOceanLoadBalancerV1() map[string]*schema.Schema { return loadBalancerV1Schema } +func loadbalancerDiffCheck(ctx context.Context, d *schema.ResourceDiff, v interface{}) error { + typ, typSet := d.GetOk("type") + region, regionSet := d.GetOk("region") + + if !typSet && !regionSet { + return fmt.Errorf("missing 'region' value") + } + + typStr := typ.(string) + switch strings.ToUpper(typStr) { + case "GLOBAL": + if regionSet && region.(string) != "" { + return fmt.Errorf("'region' must be empty or not set when 'type' is '%s'", typStr) + } + case "REGIONAL": + if !regionSet || region.(string) == "" { + return fmt.Errorf("'region' must be set and not be empty when 'type' is '%s'", typStr) + } + } + + return nil +} + func resourceDigitalOceanLoadBalancerV0() *schema.Resource { return &schema.Resource{ Schema: map[string]*schema.Schema{ "region": { Type: schema.TypeString, - Required: true, + Optional: true, ForceNew: true, StateFunc: func(val interface{}) string { // DO API V2 region slug is always lowercase @@ -404,10 +431,11 @@ func resourceDigitalOceanLoadBalancerV0() *schema.Resource { }, }, "type": { - Type: schema.TypeString, - Optional: true, - ForceNew: true, - Description: "the type of the load balancer (GLOBAL or REGIONAL)", + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validation.StringInSlice([]string{"REGIONAL", "GLOBAL"}, true), + Description: "the type of the load balancer (GLOBAL or REGIONAL)", }, }, } diff --git a/digitalocean/loadbalancer/resource_loadbalancer_test.go b/digitalocean/loadbalancer/resource_loadbalancer_test.go index b6609c9bf..d6b956be4 100644 --- a/digitalocean/loadbalancer/resource_loadbalancer_test.go +++ b/digitalocean/loadbalancer/resource_loadbalancer_test.go @@ -11,6 +11,7 @@ import ( "github.com/digitalocean/godo" "github.com/digitalocean/terraform-provider-digitalocean/digitalocean/acceptance" "github.com/digitalocean/terraform-provider-digitalocean/digitalocean/config" + "github.com/digitalocean/terraform-provider-digitalocean/digitalocean/loadbalancer" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" ) @@ -737,6 +738,101 @@ func TestAccDigitalOceanLoadbalancer_Firewall(t *testing.T) { }) } +func TestLoadbalancerDiffCheck(t *testing.T) { + cases := []struct { + name string + attrs map[string]interface{} + expectError bool + errorMessage string + }{ + { + name: "Missing type and region", + expectError: true, + errorMessage: "missing 'region' value", + }, + { + name: "Missing type", + expectError: false, + attrs: map[string]interface{}{ + "region": "nyc3", + }, + }, + { + name: "Empty region", + expectError: true, + errorMessage: "missing 'region' value", + attrs: map[string]interface{}{ + "region": "", + }, + }, + { + name: "Regional type without region", + expectError: true, + errorMessage: "'region' must be set and not be empty when 'type' is 'REGIONAL'", + attrs: map[string]interface{}{ + "type": "REGIONAL", + }, + }, + { + name: "Regional type with empty region", + expectError: true, + errorMessage: "'region' must be set and not be empty when 'type' is 'REGIONAL'", + attrs: map[string]interface{}{ + "type": "REGIONAL", + "region": "", + }, + }, + { + name: "Regional type with region", + expectError: false, + attrs: map[string]interface{}{ + "type": "REGIONAL", + "region": "nyc3", + }, + }, + { + name: "Global type without region", + expectError: false, + attrs: map[string]interface{}{ + "type": "GLOBAL", + }, + }, + { + name: "Global type with empty region", + expectError: false, + attrs: map[string]interface{}{ + "type": "GLOBAL", + "region": "", + }, + }, + { + name: "Global type with region", + expectError: true, + errorMessage: "'region' must be empty or not set when 'type' is 'GLOBAL'", + attrs: map[string]interface{}{ + "type": "GLOBAL", + "region": "nyc3", + }, + }, + } + + for _, c := range cases { + t.Run(c.name, func(t *testing.T) { + var s *terraform.InstanceState + conf := terraform.NewResourceConfigRaw(c.attrs) + + r := loadbalancer.ResourceDigitalOceanLoadbalancer() + _, err := r.Diff(context.Background(), s, conf, nil) + + if c.expectError { + if err.Error() != c.errorMessage { + t.Fatalf("Expected %s, got %s", c.errorMessage, err) + } + } + }) + } +} + func testAccCheckDigitalOceanLoadbalancerDestroy(s *terraform.State) error { client := acceptance.TestAccProvider.Meta().(*config.CombinedConfig).GodoClient()