Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add config options for creating global load balancers #1521

Merged
merged 6 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions args.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,14 @@ const (
ArgDenyList = "deny-list"
// ArgLoadBalancerType is the type of the load balancer.
ArgLoadBalancerType = "type"
// ArgLoadBalancerDomains is list of domains supported for global load balancer.
ArgLoadBalancerDomains = "domains"
// ArgGlobalLoadBalancerSettings is global load balancer settings.
ArgGlobalLoadBalancerSettings = "glb-settings"
// ArgGlobalLoadBalancerCDNSettings is global load balancer CDN settings.
ArgGlobalLoadBalancerCDNSettings = "glb-cdn-settings"
// ArgTargetLoadBalancerIDs is a list of target load balancer IDs.
ArgTargetLoadBalancerIDs = "target-lb-ids"

// ArgFirewallName is a name of the firewall.
ArgFirewallName = "name"
Expand Down
119 changes: 119 additions & 0 deletions commands/load_balancers.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,18 @@ With the load-balancer command, you can list, create, or delete load balancers,
"A comma-separated list of ALLOW rules for the load balancer, e.g.: `ip:1.2.3.4,cidr:1.2.0.0/16`")
AddStringSliceFlag(cmdLoadBalancerCreate, doctl.ArgDenyList, "", []string{},
"A comma-separated list of DENY rules for the load balancer, e.g.: `ip:1.2.3.4,cidr:1.2.0.0/16`")
AddStringSliceFlag(cmdLoadBalancerCreate, doctl.ArgLoadBalancerDomains, "", []string{},
"A comma-separated list of domains required to ingress traffic to a global load balancer, e.g.: `name:test-domain,is_managed:true,certificate_id:test-cert-id` "+
"(NOTE: this is a closed beta feature, contact DigitalOcean support to review its public availability.)")
AddStringFlag(cmdLoadBalancerCreate, doctl.ArgGlobalLoadBalancerSettings, "", "",
"Target protocol and port settings for ingressing traffic to a global load balancer, e.g.: `target_protocol:http,target_port:80` "+
"(NOTE: this is a closed beta feature, contact DigitalOcean support to review its public availability.)")
AddStringFlag(cmdLoadBalancerCreate, doctl.ArgGlobalLoadBalancerCDNSettings, "", "",
"CDN cache settings global load balancer, e.g.: `is_enabled:true` "+
andrewsomething marked this conversation as resolved.
Show resolved Hide resolved
"(NOTE: this is a closed beta feature, contact DigitalOcean support to review its public availability.)")
AddStringSliceFlag(cmdLoadBalancerCreate, doctl.ArgTargetLoadBalancerIDs, "", []string{},
"A comma-separated list of Load Balancer IDs to add as target to the global load balancer "+
"(NOTE: this is a closed beta feature, contact DigitalOcean support to review its public availability.)")
cmdLoadBalancerCreate.Flags().MarkHidden(doctl.ArgLoadBalancerType)

cmdRecordUpdate := CmdBuilder(cmd, RunLoadBalancerUpdate, "update <id>",
Expand Down Expand Up @@ -129,6 +141,18 @@ With the load-balancer command, you can list, create, or delete load balancers,
"A comma-separated list of ALLOW rules for the load balancer, e.g.: `ip:1.2.3.4,cidr:1.2.0.0/16`")
AddStringSliceFlag(cmdRecordUpdate, doctl.ArgDenyList, "", nil,
"A comma-separated list of DENY rules for the load balancer, e.g.: `ip:1.2.3.4,cidr:1.2.0.0/16`")
AddStringSliceFlag(cmdRecordUpdate, doctl.ArgLoadBalancerDomains, "", []string{},
"A comma-separated list of domains required to ingress traffic to a global load balancer, e.g.: `name:test-domain,is_managed:true,certificate_id:test-cert-id` "+
"(NOTE: this is a closed beta feature, contact DigitalOcean support to review its public availability.)")
AddStringFlag(cmdRecordUpdate, doctl.ArgGlobalLoadBalancerSettings, "", "",
"Target protocol and port settings for ingressing traffic to a global load balancer, e.g.: `target_protocol:http,target_port:80` "+
"(NOTE: this is a closed beta feature, contact DigitalOcean support to review its public availability.)")
AddStringFlag(cmdRecordUpdate, doctl.ArgGlobalLoadBalancerCDNSettings, "", "",
"CDN cache settings global load balancer, e.g.: `is_enabled:true` "+
"(NOTE: this is a closed beta feature, contact DigitalOcean support to review its public availability.)")
AddStringSliceFlag(cmdRecordUpdate, doctl.ArgTargetLoadBalancerIDs, "", []string{},
"A comma-separated list of Load Balancer IDs to add as target to the global load balancer "+
"(NOTE: this is a closed beta feature, contact DigitalOcean support to review its public availability.)")

CmdBuilder(cmd, RunLoadBalancerList, "list", "List load balancers", "Use this command to get a list of the load balancers on your account, including the following information for each:\n\n"+lbDetail, Writer,
aliasOpt("ls"), displayerType(&displayers.LoadBalancer{}))
Expand Down Expand Up @@ -156,6 +180,12 @@ With the load-balancer command, you can list, create, or delete load balancers,
"remove-forwarding-rules <id>", "Remove forwarding rules from a load balancer", "Use this command to remove forwarding rules from a load balancer, specified with the `--forwarding-rules` flag. Valid rules include:\n"+forwardingDetail, Writer)
AddStringFlag(cmdRemoveForwardingRules, doctl.ArgForwardingRules, "", "", forwardingRulesTxt)

cmdRunCachePurge := CmdBuilder(cmd, RunLoadBalancerPurgeCache, "purge-cache <id>",
"Purges CDN cache for a global load balancer", `Use this command to purge the CDN cache for specified global load balancer.`, Writer)
AddBoolFlag(cmdRunCachePurge, doctl.ArgForce, doctl.ArgShortForce, false,
"Purge the global load balancer CDN cache without a confirmation prompt "+
"(NOTE: this is a closed beta feature, contact DigitalOcean support to review its public availability.)")

return cmd
}

Expand Down Expand Up @@ -359,6 +389,31 @@ func RunLoadBalancerRemoveForwardingRules(c *CmdConfig) error {
return c.LoadBalancers().RemoveForwardingRules(lbID, forwardingRules...)
}

// RunLoadBalancerPurgeCache purges cache for a global load balancer by its identifier.
func RunLoadBalancerPurgeCache(c *CmdConfig) error {
err := ensureOneArg(c)
if err != nil {
return err
}
lbID := c.Args[0]

force, err := c.Doit.GetBool(c.NS, doctl.ArgForce)
if err != nil {
return err
}

if force || AskForConfirmDelete("load balancer", 1) == nil {
asaha2 marked this conversation as resolved.
Show resolved Hide resolved
lbs := c.LoadBalancers()
if err := lbs.PurgeCache(lbID); err != nil {
return err
}
} else {
return errOperationAborted
}

return nil
}

func extractForwardingRules(s string) (forwardingRules []godo.ForwardingRule, err error) {
if len(s) == 0 {
return forwardingRules, err
Expand All @@ -378,6 +433,23 @@ func extractForwardingRules(s string) (forwardingRules []godo.ForwardingRule, er
return forwardingRules, err
}

func extractDomains(s []string) (domains []*godo.LBDomain, err error) {
if len(s) == 0 {
return domains, err
}

for _, v := range s {
domain := new(godo.LBDomain)
if err := fillStructFromStringSliceArgs(domain, v); err != nil {
return nil, err
}

domains = append(domains, domain)
}

return domains, err
}

func fillStructFromStringSliceArgs(obj any, s string) error {
if len(s) == 0 {
return nil
Expand Down Expand Up @@ -412,6 +484,10 @@ func fillStructFromStringSliceArgs(obj any, s string) error {
if v, err := strconv.Atoi(val); err == nil {
f.Set(reflect.ValueOf(v))
}
case reflect.Uint32:
if v64, err := strconv.ParseUint(val, 10, 32); err == nil {
f.Set(reflect.ValueOf(uint32(v64)))
}
case reflect.String:
f.Set(reflect.ValueOf(val))
default:
Expand Down Expand Up @@ -574,6 +650,49 @@ func buildRequestFromArgs(c *CmdConfig, r *godo.LoadBalancerRequest) error {
r.Firewall = firewall
}

dms, err := c.Doit.GetStringSlice(c.NS, doctl.ArgLoadBalancerDomains)
if err != nil {
return err
}

domains, err := extractDomains(dms)
if err != nil {
return err
}
r.Domains = domains

glbs, err := c.Doit.GetString(c.NS, doctl.ArgGlobalLoadBalancerSettings)
if err != nil {
return err
}

glbSettings := new(godo.GLBSettings)
if err := fillStructFromStringSliceArgs(glbSettings, glbs); err != nil {
return err
}
if glbSettings.TargetProtocol != "" && glbSettings.TargetPort != 0 {
r.GLBSettings = glbSettings
}

cdns, err := c.Doit.GetString(c.NS, doctl.ArgGlobalLoadBalancerCDNSettings)
if err != nil {
return err
}

cdnSettings := new(godo.CDNSettings)
if err := fillStructFromStringSliceArgs(cdnSettings, cdns); err != nil {
return err
}
if r.GLBSettings != nil {
r.GLBSettings.CDN = cdnSettings
}

lbIDsList, err := c.Doit.GetStringSlice(c.NS, doctl.ArgTargetLoadBalancerIDs)
if err != nil {
return err
}
r.TargetLoadBalancerIDs = lbIDsList

return nil
}

Expand Down
148 changes: 147 additions & 1 deletion commands/load_balancers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ var (
func TestLoadBalancerCommand(t *testing.T) {
cmd := LoadBalancer()
assert.NotNil(t, cmd)
assertCommandNames(t, cmd, "get", "list", "create", "update", "delete", "add-droplets", "remove-droplets", "add-forwarding-rules", "remove-forwarding-rules")
assertCommandNames(t, cmd, "get", "list", "create", "update", "delete", "add-droplets", "remove-droplets", "add-forwarding-rules", "remove-forwarding-rules", "purge-cache")
}

func TestLoadBalancerGet(t *testing.T) {
Expand Down Expand Up @@ -141,6 +141,72 @@ func TestLoadBalancerCreate(t *testing.T) {
})
}

func TestLoadBalancerCreateGLB(t *testing.T) {
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
r := godo.LoadBalancerRequest{
Name: "lb-name",
Type: "GLOBAL",
StickySessions: &godo.StickySessions{
Type: "none",
},
HealthCheck: &godo.HealthCheck{
Protocol: "http",
Port: 80,
Path: "/",
CheckIntervalSeconds: 10,
ResponseTimeoutSeconds: 5,
HealthyThreshold: 5,
UnhealthyThreshold: 3,
},
GLBSettings: &godo.GLBSettings{
TargetProtocol: "http",
TargetPort: 80,
CDN: &godo.CDNSettings{IsEnabled: true},
},
DropletIDs: []int{1, 2},
Domains: []*godo.LBDomain{
{
Name: "test-domain-1",
IsManaged: true,
CertificateID: "test-cert-id-1",
},
{
Name: "test-domain-2",
IsManaged: false,
CertificateID: "test-cert-id-2",
},
},
TargetLoadBalancerIDs: []string{
"019cb059-603f-4828-8be4-641a20f25006",
"023da268-bc81-468f-aa4d-9abdc4f69935",
},
}
disableLetsEncryptDNSRecords := true
r.DisableLetsEncryptDNSRecords = &disableLetsEncryptDNSRecords
tm.loadBalancers.EXPECT().Create(&r).Return(&testLoadBalancer, nil)

config.Doit.Set(config.NS, doctl.ArgLoadBalancerName, "lb-name")
config.Doit.Set(config.NS, doctl.ArgLoadBalancerType, "GLOBAL")
config.Doit.Set(config.NS, doctl.ArgStickySessions, "type:none")
config.Doit.Set(config.NS, doctl.ArgHealthCheck, "protocol:http,port:80,path:/,check_interval_seconds:10,response_timeout_seconds:5,healthy_threshold:5,unhealthy_threshold:3")
config.Doit.Set(config.NS, doctl.ArgGlobalLoadBalancerSettings, "target_protocol:http,target_port:80")
config.Doit.Set(config.NS, doctl.ArgGlobalLoadBalancerCDNSettings, "is_enabled:true")
config.Doit.Set(config.NS, doctl.ArgDropletIDs, []string{"1", "2"})
config.Doit.Set(config.NS, doctl.ArgLoadBalancerDomains, []string{
"name:test-domain-1,is_managed:true,certificate_id:test-cert-id-1",
"name:test-domain-2,is_managed:false,certificate_id:test-cert-id-2",
})
config.Doit.Set(config.NS, doctl.ArgDisableLetsEncryptDNSRecords, true)
config.Doit.Set(config.NS, doctl.ArgTargetLoadBalancerIDs, []string{
"019cb059-603f-4828-8be4-641a20f25006",
"023da268-bc81-468f-aa4d-9abdc4f69935",
})

err := RunLoadBalancerCreate(config)
assert.NoError(t, err)
})
}

func TestLoadBalancerUpdate(t *testing.T) {
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
lbID := "cde2c0d6-41e3-479e-ba60-ad971227232c"
Expand Down Expand Up @@ -201,6 +267,73 @@ func TestLoadBalancerUpdate(t *testing.T) {
assert.NoError(t, err)
})
}
func TestLoadBalancerUpdateGLB(t *testing.T) {
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
lbID := "cde2c0d6-41e3-479e-ba60-ad971227232c"
r := godo.LoadBalancerRequest{
Name: "lb-name",
Type: "GLOBAL",
StickySessions: &godo.StickySessions{
Type: "none",
},
HealthCheck: &godo.HealthCheck{
Protocol: "http",
Port: 80,
Path: "/",
CheckIntervalSeconds: 10,
ResponseTimeoutSeconds: 5,
HealthyThreshold: 5,
UnhealthyThreshold: 3,
},
GLBSettings: &godo.GLBSettings{
TargetProtocol: "http",
TargetPort: 80,
CDN: &godo.CDNSettings{IsEnabled: true},
},
DropletIDs: []int{1, 2},
Domains: []*godo.LBDomain{
{
Name: "test-domain-1",
IsManaged: true,
CertificateID: "test-cert-id-1",
},
{
Name: "test-domain-2",
IsManaged: false,
CertificateID: "test-cert-id-2",
},
},
TargetLoadBalancerIDs: []string{
"019cb059-603f-4828-8be4-641a20f25006",
"023da268-bc81-468f-aa4d-9abdc4f69935",
},
}
disableLetsEncryptDNSRecords := true
r.DisableLetsEncryptDNSRecords = &disableLetsEncryptDNSRecords
tm.loadBalancers.EXPECT().Update(lbID, &r).Return(&testLoadBalancer, nil)

config.Args = append(config.Args, lbID)
config.Doit.Set(config.NS, doctl.ArgLoadBalancerName, "lb-name")
config.Doit.Set(config.NS, doctl.ArgLoadBalancerType, "GLOBAL")
config.Doit.Set(config.NS, doctl.ArgStickySessions, "type:none")
config.Doit.Set(config.NS, doctl.ArgHealthCheck, "protocol:http,port:80,path:/,check_interval_seconds:10,response_timeout_seconds:5,healthy_threshold:5,unhealthy_threshold:3")
config.Doit.Set(config.NS, doctl.ArgGlobalLoadBalancerSettings, "target_protocol:http,target_port:80")
config.Doit.Set(config.NS, doctl.ArgGlobalLoadBalancerCDNSettings, "is_enabled:true")
config.Doit.Set(config.NS, doctl.ArgDropletIDs, []string{"1", "2"})
config.Doit.Set(config.NS, doctl.ArgLoadBalancerDomains, []string{
"name:test-domain-1,is_managed:true,certificate_id:test-cert-id-1",
"name:test-domain-2,is_managed:false,certificate_id:test-cert-id-2",
})
config.Doit.Set(config.NS, doctl.ArgDisableLetsEncryptDNSRecords, true)
config.Doit.Set(config.NS, doctl.ArgTargetLoadBalancerIDs, []string{
"019cb059-603f-4828-8be4-641a20f25006",
"023da268-bc81-468f-aa4d-9abdc4f69935",
})

err := RunLoadBalancerUpdate(config)
assert.NoError(t, err)
})
}

func TestLoadBalancerUpdateNoID(t *testing.T) {
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
Expand Down Expand Up @@ -339,6 +472,19 @@ func TestLoadBalancerDelete(t *testing.T) {
})
}

func TestLoadBalancerPurgeCache(t *testing.T) {
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
lbID := "cde2c0d6-41e3-479e-ba60-ad971227232c"
tm.loadBalancers.EXPECT().PurgeCache(lbID).Return(nil)

config.Args = append(config.Args, lbID)
config.Doit.Set(config.NS, doctl.ArgForce, true)

err := RunLoadBalancerPurgeCache(config)
assert.NoError(t, err)
})
}

func TestLoadBalancerDeleteNoID(t *testing.T) {
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
err := RunLoadBalancerDelete(config)
Expand Down
6 changes: 6 additions & 0 deletions do/load_balancers.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ type LoadBalancersService interface {
RemoveDroplets(lbID string, dIDs ...int) error
AddForwardingRules(lbID string, rules ...godo.ForwardingRule) error
RemoveForwardingRules(lbID string, rules ...godo.ForwardingRule) error
PurgeCache(lbID string) error
}

var _ LoadBalancersService = &loadBalancersService{}
Expand Down Expand Up @@ -133,3 +134,8 @@ func (lbs *loadBalancersService) RemoveForwardingRules(lbID string, rules ...god
_, err := lbs.client.LoadBalancers.RemoveForwardingRules(context.TODO(), lbID, rules...)
return err
}

func (lbs *loadBalancersService) PurgeCache(lbID string) error {
_, err := lbs.client.LoadBalancers.PurgeCache(context.TODO(), lbID)
return err
}
1 change: 1 addition & 0 deletions do/mocks/AccountService.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions do/mocks/ActionService.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions do/mocks/AppsService.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading