Skip to content

Commit

Permalink
Add config options for global load balancers (#1133)
Browse files Browse the repository at this point in the history
* Vendor godo v1.111.0

* Update schema and update/reader methods

* Fix lint errors

* Update tests

* Fix format errors

* Add beta release note in the helper text

* Update schema

Also update cert handling.
  • Loading branch information
asaha2 authored Apr 16, 2024
1 parent 007c65e commit 6e1475f
Show file tree
Hide file tree
Showing 13 changed files with 791 additions and 11 deletions.
102 changes: 100 additions & 2 deletions digitalocean/loadbalancer/datasource_loadbalancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,10 +250,89 @@ func DataSourceDigitalOceanLoadbalancer() *schema.Resource {
},
"type": {
Type: schema.TypeString,
Optional: true,
Computed: true,
Description: "the type of the load balancer (GLOBAL or REGIONAL)",
},
"domains": {
Type: schema.TypeSet,
Computed: true,
Description: "the list of domains required to ingress traffic to global load balancer",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Computed: true,
Description: "domain name",
},
"is_managed": {
Type: schema.TypeBool,
Computed: true,
Description: "flag indicating if domain is managed by DigitalOcean",
},
"certificate_id": {
Type: schema.TypeString,
Computed: true,
Description: "certificate ID for TLS handshaking",
},
"certificate_name": {
Type: schema.TypeString,
Computed: true,
Description: "name of certificate required for TLS handshaking",
},
"verification_error_reasons": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
Description: "list of domain verification errors",
},
"ssl_validation_error_reasons": {
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
Description: "list of domain SSL validation errors",
},
},
},
},
"glb_settings": {
Type: schema.TypeList,
Computed: true,
Description: "configuration options for global load balancer",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"target_protocol": {
Type: schema.TypeString,
Computed: true,
Description: "target protocol rules",
},
"target_port": {
Type: schema.TypeInt,
Computed: true,
Description: "target port rules",
},
"cdn": {
Type: schema.TypeList,
Computed: true,
Description: "CDN specific configurations",
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"is_enabled": {
Type: schema.TypeBool,
Computed: true,
Description: "cache enable flag",
},
},
},
},
},
},
},
"target_load_balancer_ids": {
Type: schema.TypeSet,
Elem: &schema.Schema{Type: schema.TypeString},
Computed: true,
Description: "list of load balancer IDs to put behind a global load balancer",
},
},
}
}
Expand Down Expand Up @@ -312,7 +391,9 @@ func dataSourceDigitalOceanLoadbalancerRead(ctx context.Context, d *schema.Resou
d.SetId(foundLoadbalancer.ID)
d.Set("name", foundLoadbalancer.Name)
d.Set("urn", foundLoadbalancer.URN())
d.Set("region", foundLoadbalancer.Region.Slug)
if foundLoadbalancer.Region != nil {
d.Set("region", foundLoadbalancer.Region.Slug)
}
if foundLoadbalancer.SizeUnit > 0 {
d.Set("size_unit", foundLoadbalancer.SizeUnit)
} else if foundLoadbalancer.SizeSlug != "" {
Expand Down Expand Up @@ -357,6 +438,23 @@ func dataSourceDigitalOceanLoadbalancerRead(ctx context.Context, d *schema.Resou
return diag.Errorf("[DEBUG] Error setting Load Balancer firewall - error: %#v", err)
}

domains, err := flattenDomains(client, foundLoadbalancer.Domains)
if err != nil {
return diag.Errorf("[DEBUG] Error building Load Balancer domains - error: %#v", err)
}

if err := d.Set("domains", domains); err != nil {
return diag.Errorf("[DEBUG] Error setting Load Balancer domains - error: %#v", err)
}

if err := d.Set("glb_settings", flattenGLBSettings(foundLoadbalancer.GLBSettings)); err != nil {
return diag.Errorf("[DEBUG] Error setting Load Balancer glb settings - error: %#v", err)
}

if err := d.Set("target_load_balancer_ids", flattenLoadBalancerIds(foundLoadbalancer.TargetLoadBalancerIDs)); err != nil {
return diag.Errorf("[DEBUG] Error setting target Load Balancer ids - error: %#v", err)
}

return nil
}

Expand Down
84 changes: 84 additions & 0 deletions digitalocean/loadbalancer/datasource_loadbalancer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,49 @@ data "digitalocean_loadbalancer" "foobar" {
},
})
}
func TestAccDataSourceDigitalOceanGlobalLoadBalancer(t *testing.T) {
var loadbalancer godo.LoadBalancer
testName := acceptance.RandomTestName()
resourceConfig := testAccCheckDataSourceDigitalOceanGlobalLoadBalancerConfig(testName)
dataSourceConfig := `
data "digitalocean_loadbalancer" "foobar" {
name = digitalocean_loadbalancer.lorem.name
}`

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acceptance.TestAccPreCheck(t) },
ProviderFactories: acceptance.TestAccProviderFactories,
Steps: []resource.TestStep{
{
Config: resourceConfig,
},
{
Config: resourceConfig + dataSourceConfig,
Check: resource.ComposeTestCheckFunc(
testAccCheckDataSourceDigitalOceanLoadBalancerExists("data.digitalocean_loadbalancer.foobar", &loadbalancer),
resource.TestCheckResourceAttr(
"data.digitalocean_loadbalancer.foobar", "name", testName),
resource.TestCheckResourceAttr(
"data.digitalocean_loadbalancer.foobar", "type", "GLOBAL"),
resource.TestCheckResourceAttr(
"data.digitalocean_loadbalancer.foobar", "glb_settings.0.target_protocol", "HTTP"),
resource.TestCheckResourceAttr(
"data.digitalocean_loadbalancer.foobar", "glb_settings.0.target_port", "80"),
resource.TestCheckResourceAttr(
"data.digitalocean_loadbalancer.foobar", "glb_settings.0.cdn.0.is_enabled", "true"),
resource.TestCheckResourceAttr(
"data.digitalocean_loadbalancer.foobar", "domains.#", "2"),
resource.TestCheckResourceAttr(
"data.digitalocean_loadbalancer.foobar", "domains.1.name", "test.github.io"),
resource.TestCheckResourceAttr(
"data.digitalocean_loadbalancer.foobar", "domains.0.name", "test-2.github.io"),
resource.TestCheckResourceAttr(
"data.digitalocean_loadbalancer.foobar", "droplet_ids.#", "1"),
),
},
},
})
}

func testAccCheckDataSourceDigitalOceanLoadBalancerExists(n string, loadbalancer *godo.LoadBalancer) resource.TestCheckFunc {
return func(s *terraform.State) error {
Expand Down Expand Up @@ -729,3 +772,44 @@ resource "digitalocean_loadbalancer" "foo" {
depends_on = ["digitalocean_droplet.foo"]
}`, testName, testName, testName, sizeUnit)
}

func testAccCheckDataSourceDigitalOceanGlobalLoadBalancerConfig(name string) string {
return fmt.Sprintf(`
resource "digitalocean_droplet" "foobar" {
name = "%s"
size = "s-1vcpu-1gb"
image = "ubuntu-22-04-x64"
region = "blr1"
}
resource "digitalocean_loadbalancer" "lorem" {
name = "%s"
type = "GLOBAL"
healthcheck {
port = 80
protocol = "http"
path = "/"
}
glb_settings {
target_protocol = "http"
target_port = "80"
cdn {
is_enabled = true
}
}
domains {
name = "test.github.io"
is_managed = false
}
domains {
name = "test-2.github.io"
is_managed = false
}
droplet_ids = [digitalocean_droplet.foobar.id]
}`, name, name)
}
108 changes: 108 additions & 0 deletions digitalocean/loadbalancer/loadbalancer.go
Original file line number Diff line number Diff line change
Expand Up @@ -256,3 +256,111 @@ func flattenForwardingRules(client *godo.Client, rules []godo.ForwardingRule) ([

return result, nil
}

func expandDomains(client *godo.Client, config []interface{}) ([]*godo.LBDomain, error) {
domains := make([]*godo.LBDomain, 0, len(config))

for _, rawDomain := range config {
domain := rawDomain.(map[string]interface{})
r := &godo.LBDomain{Name: domain["name"].(string)}

if v, ok := domain["is_managed"]; ok {
r.IsManaged = v.(bool)
}

if v, ok := domain["certificate_name"]; ok {
certName := v.(string)
if certName != "" {
cert, err := certificate.FindCertificateByName(client, certName)
if err != nil {
return nil, err
}
r.CertificateID = cert.ID
}
}
domains = append(domains, r)
}

return domains, nil
}

func expandGLBSettings(config []interface{}) *godo.GLBSettings {
glbConfig := config[0].(map[string]interface{})

glbSettings := &godo.GLBSettings{
TargetProtocol: glbConfig["target_protocol"].(string),
TargetPort: uint32(glbConfig["target_port"].(int)),
}

if v, ok := glbConfig["cdn"]; ok {
if raw := v.([]interface{}); len(raw) > 0 {
glbSettings.CDN = &godo.CDNSettings{
IsEnabled: raw[0].(map[string]interface{})["is_enabled"].(bool),
}
}
}

return glbSettings
}

func flattenDomains(client *godo.Client, domains []*godo.LBDomain) ([]map[string]interface{}, error) {
if len(domains) == 0 {
return nil, nil
}

result := make([]map[string]interface{}, 0, 1)
for _, domain := range domains {
r := make(map[string]interface{})

r["name"] = (*domain).Name
r["is_managed"] = (*domain).IsManaged
r["certificate_id"] = (*domain).CertificateID
r["verification_error_reasons"] = (*domain).VerificationErrorReasons
r["ssl_validation_error_reasons"] = (*domain).SSLValidationErrorReasons

if domain.CertificateID != "" {
// When the certificate type is lets_encrypt, the certificate
// ID will change when it's renewed, so we have to rely on the
// certificate name as the primary identifier instead.
cert, _, err := client.Certificates.Get(context.Background(), domain.CertificateID)
if err != nil {
return nil, err
}
r["certificate_id"] = cert.Name
r["certificate_name"] = cert.Name
}
result = append(result, r)
}
return result, nil
}

func flattenGLBSettings(settings *godo.GLBSettings) []map[string]interface{} {
result := make([]map[string]interface{}, 0, 1)

if settings != nil {
r := make(map[string]interface{})

r["target_protocol"] = (*settings).TargetProtocol
r["target_port"] = (*settings).TargetPort

if settings.CDN != nil {
r["cdn"] = []interface{}{
map[string]interface{}{
"is_enabled": (*settings).CDN.IsEnabled,
},
}
}

result = append(result, r)
}

return result
}

func flattenLoadBalancerIds(list []string) *schema.Set {
flatSet := schema.NewSet(schema.HashString, []interface{}{})
for _, v := range list {
flatSet.Add(v)
}
return flatSet
}
Loading

0 comments on commit 6e1475f

Please sign in to comment.