Skip to content

Commit

Permalink
Support global lb configurations
Browse files Browse the repository at this point in the history
  • Loading branch information
asaha2 committed Apr 2, 2024
1 parent e03a24f commit be101e7
Show file tree
Hide file tree
Showing 38 changed files with 286 additions and 1 deletion.
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"
// ArgLoadBalancerIDs is a list of load balancer IDs.
ArgLoadBalancerIDs = "lb-ids"

// ArgFirewallName is a name of the firewall.
ArgFirewallName = "name"
Expand Down
113 changes: 113 additions & 0 deletions commands/load_balancers.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@ 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`")
AddStringFlag(cmdLoadBalancerCreate, doctl.ArgLoadBalancerDomains, "", "",
"A comma-separated list of domains required to ingress traffic to a global load balancer")
AddStringFlag(cmdLoadBalancerCreate, doctl.ArgGlobalLoadBalancerSettings, "", "",
"Global load balancer settings")
AddStringFlag(cmdLoadBalancerCreate, doctl.ArgGlobalLoadBalancerCDNSettings, "", "",
"Global load balancer CDN settings")
AddStringFlag(cmdLoadBalancerCreate, doctl.ArgLoadBalancerIDs, "", "",
"A comma-separated list of Load Balancer IDs to add to the global load balancer")
cmdLoadBalancerCreate.Flags().MarkHidden(doctl.ArgLoadBalancerType)

cmdRecordUpdate := CmdBuilder(cmd, RunLoadBalancerUpdate, "update <id>",
Expand Down Expand Up @@ -129,6 +137,14 @@ 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`")
AddStringFlag(cmdRecordUpdate, doctl.ArgLoadBalancerDomains, "", "",
"A comma-separated list of domains required to ingress traffic to a global load balancer")
AddStringFlag(cmdRecordUpdate, doctl.ArgGlobalLoadBalancerSettings, "", "",
"Global load balancer settings")
AddStringFlag(cmdRecordUpdate, doctl.ArgGlobalLoadBalancerCDNSettings, "", "",
"Global load balancer CDN settings")
AddStringFlag(cmdRecordUpdate, doctl.ArgLoadBalancerIDs, "", "",
"A comma-separated list of Load Balancer IDs to add to the global load balancer")

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 +172,11 @@ 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")

return cmd
}

Expand Down Expand Up @@ -359,6 +380,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 {
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 +424,25 @@ 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
}

list := strings.Split(s, " ")

for _, v := range list {
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,9 +477,14 @@ 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:
fmt.Println("debug kv pairs", jv, val)
return fmt.Errorf("Unexpected type for struct field %v", val)
}
}
Expand Down Expand Up @@ -574,6 +644,49 @@ func buildRequestFromArgs(c *CmdConfig, r *godo.LoadBalancerRequest) error {
r.Firewall = firewall
}

dms, err := c.Doit.GetString(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.ArgLoadBalancerIDs)
if err != nil {
return err
}
r.TargetLoadBalancerIDs = lbIDsList

return nil
}

Expand Down
112 changes: 111 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,54 @@ 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",
}},
}
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, "name:test-domain-1,is_managed:true,certificate_id:test-cert-id-1")
config.Doit.Set(config.NS, doctl.ArgDisableLetsEncryptDNSRecords, true)

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 +249,55 @@ 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",
}},
}
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, "name:test-domain-1,is_managed:true,certificate_id:test-cert-id-1")
config.Doit.Set(config.NS, doctl.ArgDisableLetsEncryptDNSRecords, true)

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 +436,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.

1 change: 1 addition & 0 deletions do/mocks/BalanceService.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/BillingHistoryService.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/DatabasesService.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/DomainService.go

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

Loading

0 comments on commit be101e7

Please sign in to comment.