diff --git a/args.go b/args.go index d34c71ca9..d6d4ab43b 100644 --- a/args.go +++ b/args.go @@ -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" diff --git a/commands/displayers/load_balancer.go b/commands/displayers/load_balancer.go index 11dd53d17..37524762d 100644 --- a/commands/displayers/load_balancer.go +++ b/commands/displayers/load_balancer.go @@ -89,7 +89,6 @@ func (lb *LoadBalancer) KV() []map[string]any { "Name": l.Name, "Status": l.Status, "Created": l.Created, - "Region": l.Region.Slug, "VPCUUID": l.VPCUUID, "Tag": l.Tag, "DropletIDs": strings.Trim(strings.Replace(fmt.Sprint(l.DropletIDs), " ", ",", -1), "[]"), @@ -99,6 +98,9 @@ func (lb *LoadBalancer) KV() []map[string]any { "ForwardingRules": strings.Join(forwardingRules, " "), "DisableLetsEncryptDNSRecords": toBool(l.DisableLetsEncryptDNSRecords), } + if l.Region != nil { + o["Region"] = l.Region.Slug + } if l.SizeSlug != "" { o["Size"] = l.SizeSlug } diff --git a/commands/load_balancers.go b/commands/load_balancers.go index 845ab546b..089ef59ad 100644 --- a/commands/load_balancers.go +++ b/commands/load_balancers.go @@ -59,7 +59,7 @@ With the load-balancer command, you can list, create, or delete load balancers, AddStringFlag(cmdLoadBalancerCreate, doctl.ArgLoadBalancerName, "", "", "The load balancer's name", requiredOpt()) AddStringFlag(cmdLoadBalancerCreate, doctl.ArgRegionSlug, "", "", - "The load balancer's region, e.g.: `nyc1`", requiredOpt()) + "The load balancer's region, e.g.: `nyc1`") AddStringFlag(cmdLoadBalancerCreate, doctl.ArgSizeSlug, "", "", fmt.Sprintf("The load balancer's size, e.g.: `lb-small`. Only one of %s and %s should be used", doctl.ArgSizeSlug, doctl.ArgSizeUnit)) AddIntFlag(cmdLoadBalancerCreate, doctl.ArgSizeUnit, "", 0, @@ -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-1 is_managed:true certificate_id:test-cert-id-1` "+ + "(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` "+ + "(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 ", @@ -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-1 is_managed:true certificate_id:test-cert-id-1` "+ + "(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{})) @@ -156,6 +180,12 @@ With the load-balancer command, you can list, create, or delete load balancers, "remove-forwarding-rules ", "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 ", + "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 } @@ -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 || AskForConfirm("purge CDN cache for global load balancer") == 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 @@ -368,7 +423,7 @@ func extractForwardingRules(s string) (forwardingRules []godo.ForwardingRule, er for _, v := range list { forwardingRule := new(godo.ForwardingRule) - if err := fillStructFromStringSliceArgs(forwardingRule, v); err != nil { + if err := fillStructFromStringSliceArgs(forwardingRule, v, ","); err != nil { return nil, err } @@ -378,12 +433,29 @@ func extractForwardingRules(s string) (forwardingRules []godo.ForwardingRule, er return forwardingRules, err } -func fillStructFromStringSliceArgs(obj any, s string) error { +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, delimiter string) error { if len(s) == 0 { return nil } - kvs := strings.Split(s, ",") + kvs := strings.Split(s, delimiter) m := map[string]string{} for _, v := range kvs { @@ -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: @@ -513,7 +589,7 @@ func buildRequestFromArgs(c *CmdConfig, r *godo.LoadBalancerRequest) error { } stickySession := new(godo.StickySessions) - if err := fillStructFromStringSliceArgs(stickySession, ssa); err != nil { + if err := fillStructFromStringSliceArgs(stickySession, ssa, ","); err != nil { return err } r.StickySessions = stickySession @@ -524,7 +600,7 @@ func buildRequestFromArgs(c *CmdConfig, r *godo.LoadBalancerRequest) error { } healthCheck := new(godo.HealthCheck) - if err := fillStructFromStringSliceArgs(healthCheck, hca); err != nil { + if err := fillStructFromStringSliceArgs(healthCheck, hca, ","); err != nil { return err } r.HealthCheck = healthCheck @@ -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 } diff --git a/commands/load_balancers_test.go b/commands/load_balancers_test.go index 9d7632747..20fe19e5d 100644 --- a/commands/load_balancers_test.go +++ b/commands/load_balancers_test.go @@ -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) { @@ -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" @@ -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) { @@ -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) diff --git a/do/load_balancers.go b/do/load_balancers.go index 1118f67ed..c4161f1a2 100644 --- a/do/load_balancers.go +++ b/do/load_balancers.go @@ -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{} @@ -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 +} diff --git a/do/mocks/AccountService.go b/do/mocks/AccountService.go index 5c3a17e1b..2a98fcd08 100644 --- a/do/mocks/AccountService.go +++ b/do/mocks/AccountService.go @@ -5,6 +5,7 @@ // // mockgen -source account.go -package=mocks AccountService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/ActionService.go b/do/mocks/ActionService.go index 2748248f8..8dbf872ff 100644 --- a/do/mocks/ActionService.go +++ b/do/mocks/ActionService.go @@ -5,6 +5,7 @@ // // mockgen -source actions.go -package=mocks ActionService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/AppsService.go b/do/mocks/AppsService.go index bbf27feb4..c1f9421f6 100644 --- a/do/mocks/AppsService.go +++ b/do/mocks/AppsService.go @@ -5,6 +5,7 @@ // // mockgen -source apps.go -package=mocks AppsService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/BalanceService.go b/do/mocks/BalanceService.go index e33b43066..ea9a08ff4 100644 --- a/do/mocks/BalanceService.go +++ b/do/mocks/BalanceService.go @@ -5,6 +5,7 @@ // // mockgen -source balance.go -package=mocks BalanceService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/BillingHistoryService.go b/do/mocks/BillingHistoryService.go index bd1da6ee3..79203d17b 100644 --- a/do/mocks/BillingHistoryService.go +++ b/do/mocks/BillingHistoryService.go @@ -5,6 +5,7 @@ // // mockgen -source billing_history.go -package=mocks BillingHistoryService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/DatabasesService.go b/do/mocks/DatabasesService.go index 452c28a20..f083415a0 100644 --- a/do/mocks/DatabasesService.go +++ b/do/mocks/DatabasesService.go @@ -5,6 +5,7 @@ // // mockgen -source databases.go -package=mocks DatabasesService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/DomainService.go b/do/mocks/DomainService.go index 207d0ad62..ae1c24f49 100644 --- a/do/mocks/DomainService.go +++ b/do/mocks/DomainService.go @@ -5,6 +5,7 @@ // // mockgen -source domains.go -package=mocks DomainService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/DropletActionService.go b/do/mocks/DropletActionService.go index f21f51d2e..0aacf090d 100644 --- a/do/mocks/DropletActionService.go +++ b/do/mocks/DropletActionService.go @@ -5,6 +5,7 @@ // // mockgen -source droplet_actions.go -package=mocks DropletActionsService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/DropletsService.go b/do/mocks/DropletsService.go index 8ab49a95d..8b36751fc 100644 --- a/do/mocks/DropletsService.go +++ b/do/mocks/DropletsService.go @@ -5,6 +5,7 @@ // // mockgen -source droplets.go -package=mocks DropletsService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/FirewallsService.go b/do/mocks/FirewallsService.go index 84c1dca1e..8a318691c 100644 --- a/do/mocks/FirewallsService.go +++ b/do/mocks/FirewallsService.go @@ -5,6 +5,7 @@ // // mockgen -source firewalls.go -package=mocks FirewallsService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/ImageActionsService.go b/do/mocks/ImageActionsService.go index 3ec020670..0ca645aa0 100644 --- a/do/mocks/ImageActionsService.go +++ b/do/mocks/ImageActionsService.go @@ -5,6 +5,7 @@ // // mockgen -source image_actions.go -package=mocks ImageActionsService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/ImageService.go b/do/mocks/ImageService.go index aa81ebb1d..4dacd4e2d 100644 --- a/do/mocks/ImageService.go +++ b/do/mocks/ImageService.go @@ -5,6 +5,7 @@ // // mockgen -source images.go -package=mocks ImageService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/InvoicesService.go b/do/mocks/InvoicesService.go index ae900fd72..f7e2625f8 100644 --- a/do/mocks/InvoicesService.go +++ b/do/mocks/InvoicesService.go @@ -5,6 +5,7 @@ // // mockgen -source invoices.go -package=mocks InvoicesService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/KeysService.go b/do/mocks/KeysService.go index b308cd5b6..a4fd9002a 100644 --- a/do/mocks/KeysService.go +++ b/do/mocks/KeysService.go @@ -5,6 +5,7 @@ // // mockgen -source sshkeys.go -package=mocks KeysService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/KubernetesService.go b/do/mocks/KubernetesService.go index 97eaa9989..5ec1063c7 100644 --- a/do/mocks/KubernetesService.go +++ b/do/mocks/KubernetesService.go @@ -5,6 +5,7 @@ // // mockgen -source kubernetes.go -package=mocks KubernetesService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/Listen.go b/do/mocks/Listen.go index c627bc062..49e600c1c 100644 --- a/do/mocks/Listen.go +++ b/do/mocks/Listen.go @@ -5,6 +5,7 @@ // // mockgen -source ../pkg/listen/listen.go -package=mocks Listen // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/LoadBalancersService.go b/do/mocks/LoadBalancersService.go index 069e23991..ac9644046 100644 --- a/do/mocks/LoadBalancersService.go +++ b/do/mocks/LoadBalancersService.go @@ -5,6 +5,7 @@ // // mockgen -source load_balancers.go -package=mocks LoadBalancersService // + // Package mocks is a generated GoMock package. package mocks @@ -136,6 +137,20 @@ func (mr *MockLoadBalancersServiceMockRecorder) List() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockLoadBalancersService)(nil).List)) } +// PurgeCache mocks base method. +func (m *MockLoadBalancersService) PurgeCache(lbID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PurgeCache", lbID) + ret0, _ := ret[0].(error) + return ret0 +} + +// PurgeCache indicates an expected call of PurgeCache. +func (mr *MockLoadBalancersServiceMockRecorder) PurgeCache(lbID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PurgeCache", reflect.TypeOf((*MockLoadBalancersService)(nil).PurgeCache), lbID) +} + // RemoveDroplets mocks base method. func (m *MockLoadBalancersService) RemoveDroplets(lbID string, dIDs ...int) error { m.ctrl.T.Helper() diff --git a/do/mocks/MonitoringService.go b/do/mocks/MonitoringService.go index 5313378de..42d33f7e4 100644 --- a/do/mocks/MonitoringService.go +++ b/do/mocks/MonitoringService.go @@ -5,6 +5,7 @@ // // mockgen -source monitoring.go -package=mocks MonitoringService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/OAuthService.go b/do/mocks/OAuthService.go index a38d5a6e9..eba9c703a 100644 --- a/do/mocks/OAuthService.go +++ b/do/mocks/OAuthService.go @@ -5,6 +5,7 @@ // // mockgen -source oauth.go -package=mocks OAuthService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/OneClickService.go b/do/mocks/OneClickService.go index 9aebeb0b6..825b19ea6 100644 --- a/do/mocks/OneClickService.go +++ b/do/mocks/OneClickService.go @@ -5,6 +5,7 @@ // // mockgen -source 1_clicks.go -package=mocks OneClickService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/ProjectsService.go b/do/mocks/ProjectsService.go index 05be220b6..f50b07e76 100644 --- a/do/mocks/ProjectsService.go +++ b/do/mocks/ProjectsService.go @@ -5,6 +5,7 @@ // // mockgen -source projects.go -package=mocks ProjectsService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/RegionsService.go b/do/mocks/RegionsService.go index f2c0c7720..8c6d2165b 100644 --- a/do/mocks/RegionsService.go +++ b/do/mocks/RegionsService.go @@ -5,6 +5,7 @@ // // mockgen -source regions.go -package=mocks RegionsService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/RegistryService.go b/do/mocks/RegistryService.go index 6071046a4..74d41b9b4 100644 --- a/do/mocks/RegistryService.go +++ b/do/mocks/RegistryService.go @@ -5,6 +5,7 @@ // // mockgen -source registry.go -package=mocks RegistryService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/ReservedIPActionsService.go b/do/mocks/ReservedIPActionsService.go index 578490cb3..5725b1ca0 100644 --- a/do/mocks/ReservedIPActionsService.go +++ b/do/mocks/ReservedIPActionsService.go @@ -5,6 +5,7 @@ // // mockgen -source reserved_ip_actions.go -package=mocks ReservedIPActionsService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/ReservedIPsService.go b/do/mocks/ReservedIPsService.go index 1ef849079..1a16d115c 100644 --- a/do/mocks/ReservedIPsService.go +++ b/do/mocks/ReservedIPsService.go @@ -5,6 +5,7 @@ // // mockgen -source reserved_ips.go -package=mocks ReservedIPsService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/Runner.go b/do/mocks/Runner.go index 04cdce594..55a32a8e1 100644 --- a/do/mocks/Runner.go +++ b/do/mocks/Runner.go @@ -5,6 +5,7 @@ // // mockgen -source ../pkg/runner/runner.go -package=mocks Runner // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/ServerlessService.go b/do/mocks/ServerlessService.go index be67deefb..9a14a899b 100644 --- a/do/mocks/ServerlessService.go +++ b/do/mocks/ServerlessService.go @@ -5,6 +5,7 @@ // // mockgen -source serverless.go -package=mocks ServerlessService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/SizesService.go b/do/mocks/SizesService.go index 68fe58ea5..c9ffe8057 100644 --- a/do/mocks/SizesService.go +++ b/do/mocks/SizesService.go @@ -5,6 +5,7 @@ // // mockgen -source sizes.go -package=mocks SizesService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/SnapshotsService.go b/do/mocks/SnapshotsService.go index 4b762893b..09086ae82 100644 --- a/do/mocks/SnapshotsService.go +++ b/do/mocks/SnapshotsService.go @@ -5,6 +5,7 @@ // // mockgen -source snapshots.go -package=mocks SnapshotsService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/TagsService.go b/do/mocks/TagsService.go index 82c4034c5..5a6ed17a8 100644 --- a/do/mocks/TagsService.go +++ b/do/mocks/TagsService.go @@ -5,6 +5,7 @@ // // mockgen -source tags.go -package=mocks TagsService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/UptimeChecksService.go b/do/mocks/UptimeChecksService.go index ca38f9e18..926c34e4d 100644 --- a/do/mocks/UptimeChecksService.go +++ b/do/mocks/UptimeChecksService.go @@ -5,6 +5,7 @@ // // mockgen -source uptime_checks.go -package=mocks UptimeChecksService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/VPCsService.go b/do/mocks/VPCsService.go index a33bfc592..6074c3db2 100644 --- a/do/mocks/VPCsService.go +++ b/do/mocks/VPCsService.go @@ -5,6 +5,7 @@ // // mockgen -source vpcs.go -package=mocks VPCsService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/VolumeActionsService.go b/do/mocks/VolumeActionsService.go index 754473583..60a22b67f 100644 --- a/do/mocks/VolumeActionsService.go +++ b/do/mocks/VolumeActionsService.go @@ -5,6 +5,7 @@ // // mockgen -source volume_actions.go -package=mocks VolumeActionsService // + // Package mocks is a generated GoMock package. package mocks diff --git a/do/mocks/VolumesService.go b/do/mocks/VolumesService.go index 8575a369d..48920d665 100644 --- a/do/mocks/VolumesService.go +++ b/do/mocks/VolumesService.go @@ -5,6 +5,7 @@ // // mockgen -source volumes.go -package=mocks VolumesService // + // Package mocks is a generated GoMock package. package mocks diff --git a/go.mod b/go.mod index 11a4cbc40..ae5ff3612 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21 require ( github.com/blang/semver v3.5.1+incompatible github.com/creack/pty v1.1.11 - github.com/digitalocean/godo v1.109.1-0.20240228180303-16a4709be517 + github.com/digitalocean/godo v1.111.0 github.com/docker/cli v24.0.5+incompatible github.com/docker/docker v24.0.9+incompatible github.com/docker/docker-credential-helpers v0.7.0 // indirect diff --git a/go.sum b/go.sum index 6dde13124..51de8f5b7 100644 --- a/go.sum +++ b/go.sum @@ -86,8 +86,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/digitalocean/godo v1.109.1-0.20240228180303-16a4709be517 h1:vj0Sh/Ghq4MCkE+IM84lsA+9vMfnPpsacX7PRUJ0oh4= -github.com/digitalocean/godo v1.109.1-0.20240228180303-16a4709be517/go.mod h1:R6EmmWI8CT1+fCtjWY9UCB+L5uufuZH13wk3YhxycCs= +github.com/digitalocean/godo v1.111.0 h1:nBXi9LtykvQiwZjMbljrwr17HwABSBY8ZE872dm2DzI= +github.com/digitalocean/godo v1.111.0/go.mod h1:R6EmmWI8CT1+fCtjWY9UCB+L5uufuZH13wk3YhxycCs= github.com/docker/cli v24.0.5+incompatible h1:WeBimjvS0eKdH4Ygx+ihVq1Q++xg36M/rMi4aXAvodc= github.com/docker/cli v24.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= diff --git a/integration/glb_create_test.go b/integration/glb_create_test.go new file mode 100644 index 000000000..ffad32197 --- /dev/null +++ b/integration/glb_create_test.go @@ -0,0 +1,184 @@ +package integration + +import ( + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/http/httputil" + "os/exec" + "strings" + "testing" + + "github.com/sclevine/spec" + "github.com/stretchr/testify/require" +) + +var _ = suite("compute/load-balancer/create", func(t *testing.T, when spec.G, it spec.S) { + var ( + expect *require.Assertions + server *httptest.Server + cmd *exec.Cmd + ) + + it.Before(func() { + expect = require.New(t) + + server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + switch req.URL.Path { + case "/v2/load_balancers": + auth := req.Header.Get("Authorization") + if auth != "Bearer some-magic-token" { + w.WriteHeader(http.StatusUnauthorized) + return + } + + if req.Method != http.MethodPost { + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + + reqBody, err := io.ReadAll(req.Body) + expect.NoError(err) + + expect.JSONEq(glbCreateRequest, string(reqBody)) + + w.Write([]byte(glbCreateResponse)) + default: + dump, err := httputil.DumpRequest(req, true) + if err != nil { + t.Fatal("failed to dump request") + } + + t.Fatalf("received unknown request: %s", dump) + } + })) + + cmd = exec.Command(builtBinaryPath, + "-t", "some-magic-token", + "-u", server.URL, + "compute", + "load-balancer", + ) + }) + + when("command is create with global config", func() { + it("creates a global load balancer", func() { + args := append([]string{"create"}, []string{ + "--name", "my-glb-name", + "--type", "GLOBAL", + "--domains", "name:test-domain-1 is_managed:true certificate_id:test-cert-id-1", + "--domains", "name:test-domain-2 is_managed:false certificate_id:test-cert-id-2", + "--glb-settings", "target_protocol:http,target_port:80", + "--glb-cdn-settings", "is_enabled:true", + "--target-lb-ids", "target-lb-id-1", + "--target-lb-ids", "target-lb-id-2", + }...) + cmd.Args = append(cmd.Args, args...) + + output, err := cmd.CombinedOutput() + expect.NoError(err, fmt.Sprintf("received error output: %s", output)) + expect.Equal(strings.TrimSpace(glbCreateOutput), strings.TrimSpace(string(output))) + }) + }) +}) + +const ( + glbCreateRequest = ` +{ + "name": "my-glb-name", + "algorithm": "round_robin", + "type": "GLOBAL", + "health_check": {}, + "sticky_sessions": {}, + "disable_lets_encrypt_dns_records": false, + "domains": [ + { + "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" + } + ], + "glb_settings": { + "target_protocol": "http", + "target_port": 80, + "cdn": { + "is_enabled": true + } + }, + "target_load_balancer_ids": [ + "target-lb-id-1", + "target-lb-id-2" + ] +}` + glbCreateResponse = ` +{ + "load_balancer": { + "id": "cf9f1aa1-e1f8-4f3a-ad71-124c45e204b8", + "name": "my-glb-name", + "ip": "", + "size": "lb-small", + "size_unit": 1, + "type": "GLOBAL", + "algorithm": "round_robin", + "status": "new", + "created_at": "2024-04-09T16:10:11Z", + "forwarding_rules": [], + "health_check": { + "protocol": "http", + "port": 80, + "path": "/", + "check_interval_seconds": 10, + "response_timeout_seconds": 5, + "healthy_threshold": 5, + "unhealthy_threshold": 3 + }, + "sticky_sessions": { + "type": "none" + }, + "tag": "", + "droplet_ids": [], + "redirect_http_to_https": false, + "enable_proxy_protocol": false, + "enable_backend_keepalive": false, + "project_id": "1e02c6d8-aa24-477e-bc50-837b44e26cb3", + "disable_lets_encrypt_dns_records": false, + "http_idle_timeout_seconds": 60, + "domains": [ + { + "name": "test-domain-1", + "is_managed": true, + "certificate_id": "test-cert-id-1", + "status": "CREATING" + }, + { + "name": "test-domain-1-2", + "is_managed": false, + "certificate_id": "test-cert-id-2", + "status": "CREATING" + } + ], + "glb_settings": { + "target_protocol": "HTTP", + "target_port": 80, + "cdn": { + "is_enabled": true + } + }, + "target_load_balancer_ids": [ + "target-lb-id-1", + "target-lb-id-2" + ] + } +}` + glbCreateOutput = ` +Notice: Load balancer created +ID IP Name Status Created At Region Size Size Unit VPC UUID Tag Droplet IDs SSL Sticky Sessions Health Check Forwarding Rules Disable Lets Encrypt DNS Records +cf9f1aa1-e1f8-4f3a-ad71-124c45e204b8 my-glb-name new 2024-04-09T16:10:11Z lb-small 1 false type:none,cookie_name:,cookie_ttl_seconds:0 protocol:http,port:80,path:/,check_interval_seconds:10,response_timeout_seconds:5,healthy_threshold:5,unhealthy_threshold:3 false +` +) diff --git a/integration/glb_update_test.go b/integration/glb_update_test.go new file mode 100644 index 000000000..19cf60cac --- /dev/null +++ b/integration/glb_update_test.go @@ -0,0 +1,179 @@ +package integration + +import ( + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/http/httputil" + "os/exec" + "strings" + "testing" + + "github.com/sclevine/spec" + "github.com/stretchr/testify/require" +) + +var _ = suite("compute/load-balancer/update", func(t *testing.T, when spec.G, it spec.S) { + var ( + expect *require.Assertions + server *httptest.Server + cmd *exec.Cmd + ) + + it.Before(func() { + expect = require.New(t) + + server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + switch req.URL.Path { + case "/v2/load_balancers/updated-lb-id": + auth := req.Header.Get("Authorization") + if auth != "Bearer some-magic-token" { + w.WriteHeader(http.StatusUnauthorized) + return + } + + if req.Method != http.MethodPut { + w.WriteHeader(http.StatusMethodNotAllowed) + return + } + + reqBody, err := io.ReadAll(req.Body) + expect.NoError(err) + + expect.JSONEq(glbUpdateRequest, string(reqBody)) + + w.Write([]byte(glbUpdateResponse)) + default: + dump, err := httputil.DumpRequest(req, true) + if err != nil { + t.Fatal("failed to dump request") + } + + t.Fatalf("received unknown request: %s", dump) + } + })) + + cmd = exec.Command(builtBinaryPath, + "-t", "some-magic-token", + "-u", server.URL, + "compute", + "load-balancer", + ) + }) + + when("command is update with global config", func() { + it("updates the global load balancer", func() { + args := append([]string{"update"}, []string{ + "updated-lb-id", + "--domains", "name:test-domain-1 is_managed:true certificate_id:test-cert-id-1", + "--domains", "name:test-domain-2 is_managed:false certificate_id:test-cert-id-2", + "--glb-settings", "target_protocol:http,target_port:80", + "--glb-cdn-settings", "is_enabled:true", + "--target-lb-ids", "target-lb-id-1", + "--target-lb-ids", "target-lb-id-2", + }...) + cmd.Args = append(cmd.Args, args...) + + output, err := cmd.CombinedOutput() + expect.NoError(err, fmt.Sprintf("received error output: %s", output)) + expect.Equal(strings.TrimSpace(glbUpdateOutput), strings.TrimSpace(string(output))) + }) + }) +}) + +const ( + glbUpdateRequest = ` +{ + "algorithm": "round_robin", + "health_check": {}, + "sticky_sessions": {}, + "disable_lets_encrypt_dns_records": false, + "domains": [ + { + "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" + } + ], + "glb_settings": { + "target_protocol": "http", + "target_port": 80, + "cdn": { + "is_enabled": true + } + }, + "target_load_balancer_ids": [ + "target-lb-id-1", + "target-lb-id-2" + ] +}` + glbUpdateResponse = ` +{ + "load_balancer": { + "id": "updated-lb-id", + "name": "my-glb-name", + "ip": "", + "size": "lb-small", + "size_unit": 1, + "type": "GLOBAL", + "algorithm": "round_robin", + "status": "new", + "created_at": "2024-04-09T16:10:11Z", + "forwarding_rules": [], + "health_check": { + "protocol": "http", + "port": 80, + "path": "/", + "check_interval_seconds": 10, + "response_timeout_seconds": 5, + "healthy_threshold": 5, + "unhealthy_threshold": 3 + }, + "sticky_sessions": { + "type": "none" + }, + "tag": "", + "droplet_ids": [], + "redirect_http_to_https": false, + "enable_proxy_protocol": false, + "enable_backend_keepalive": false, + "project_id": "1e02c6d8-aa24-477e-bc50-837b44e26cb3", + "disable_lets_encrypt_dns_records": false, + "http_idle_timeout_seconds": 60, + "domains": [ + { + "name": "test-domain-1", + "is_managed": true, + "certificate_id": "test-cert-id-1", + "status": "CREATING" + }, + { + "name": "test-domain-1-2", + "is_managed": false, + "certificate_id": "test-cert-id-2", + "status": "CREATING" + } + ], + "glb_settings": { + "target_protocol": "HTTP", + "target_port": 80, + "cdn": { + "is_enabled": true + } + }, + "target_load_balancer_ids": [ + "target-lb-id-1", + "target-lb-id-2" + ] + } +}` + glbUpdateOutput = ` +ID IP Name Status Created At Region Size Size Unit VPC UUID Tag Droplet IDs SSL Sticky Sessions Health Check Forwarding Rules Disable Lets Encrypt DNS Records +updated-lb-id my-glb-name new 2024-04-09T16:10:11Z lb-small 1 false type:none,cookie_name:,cookie_ttl_seconds:0 protocol:http,port:80,path:/,check_interval_seconds:10,response_timeout_seconds:5,healthy_threshold:5,unhealthy_threshold:3 false` +) diff --git a/vendor/github.com/digitalocean/godo/CHANGELOG.md b/vendor/github.com/digitalocean/godo/CHANGELOG.md index 1d5070fd0..ff52aa311 100644 --- a/vendor/github.com/digitalocean/godo/CHANGELOG.md +++ b/vendor/github.com/digitalocean/godo/CHANGELOG.md @@ -1,5 +1,15 @@ # Change Log +## [v1.111.0] - 2024-04-02 + +- #674 - @asaha2 - load balancers: introduce glb settings in godo, currently in closed beta + +## [v1.110.0] - 2024-03-14 + +- #667 - @dwilsondo - Include DBaaS metrics credential endpoint operations +- #670 - @guptado - [NETPROD-3583] Added name param in ListOption to get resource by name +- #671 - @greeshmapill - APPS-8383: Add deprecation intent and bandwidth allowance to app instance size spec + ## [v1.109.0] - 2024-02-09 - #668 - @greeshmapill - APPS-8315: Update app instance size spec diff --git a/vendor/github.com/digitalocean/godo/databases.go b/vendor/github.com/digitalocean/godo/databases.go index 14d5a04c9..952663e46 100644 --- a/vendor/github.com/digitalocean/godo/databases.go +++ b/vendor/github.com/digitalocean/godo/databases.go @@ -34,6 +34,7 @@ const ( databasePromoteReplicaToPrimaryPath = databaseReplicaPath + "/promote" databaseTopicPath = databaseBasePath + "/%s/topics/%s" databaseTopicsPath = databaseBasePath + "/%s/topics" + databaseMetricsCredentialsPath = databaseBasePath + "/metrics/credentials" ) // SQL Mode constants allow for MySQL-specific SQL flavor configuration. @@ -154,6 +155,8 @@ type DatabasesService interface { GetTopic(context.Context, string, string) (*DatabaseTopic, *Response, error) DeleteTopic(context.Context, string, string) (*Response, error) UpdateTopic(context.Context, string, string, *DatabaseUpdateTopicRequest) (*Response, error) + GetMetricsCredentials(context.Context) (*DatabaseMetricsCredentials, *Response, error) + UpdateMetricsCredentials(context.Context, *DatabaseUpdateMetricsCredentialsRequest) (*Response, error) } // DatabasesServiceOp handles communication with the Databases related methods @@ -190,6 +193,7 @@ type Database struct { Tags []string `json:"tags,omitempty"` ProjectID string `json:"project_id,omitempty"` StorageSizeMib uint64 `json:"storage_size_mib,omitempty"` + MetricsEndpoints []*ServiceAddress `json:"metrics_endpoints,omitempty"` } // DatabaseCA represents a database ca. @@ -210,6 +214,12 @@ type DatabaseConnection struct { ApplicationPorts map[string]uint32 `json:"application_ports,omitempty"` } +// ServiceAddress represents a host:port for a generic service (e.g. metrics endpoint) +type ServiceAddress struct { + Host string `json:"host"` + Port int `json:"port"` +} + // DatabaseUser represents a user in the database type DatabaseUser struct { Name string `json:"name,omitempty"` @@ -666,6 +676,19 @@ type databaseTopicsRoot struct { Topics []DatabaseTopic `json:"topics"` } +type databaseMetricsCredentialsRoot struct { + Credentials *DatabaseMetricsCredentials `json:"credentials"` +} + +type DatabaseMetricsCredentials struct { + BasicAuthUsername string `json:"basic_auth_username"` + BasicAuthPassword string `json:"basic_auth_password"` +} + +type DatabaseUpdateMetricsCredentialsRequest struct { + Credentials *DatabaseMetricsCredentials `json:"credentials"` +} + // DatabaseOptions represents the available database engines type DatabaseOptions struct { MongoDBOptions DatabaseEngineOptions `json:"mongodb"` @@ -1466,3 +1489,31 @@ func (svc *DatabasesServiceOp) DeleteTopic(ctx context.Context, databaseID, name } return resp, nil } + +// GetMetricsCredentials gets the credentials required to access a user's metrics endpoints +func (svc *DatabasesServiceOp) GetMetricsCredentials(ctx context.Context) (*DatabaseMetricsCredentials, *Response, error) { + req, err := svc.client.NewRequest(ctx, http.MethodGet, databaseMetricsCredentialsPath, nil) + if err != nil { + return nil, nil, err + } + + root := new(databaseMetricsCredentialsRoot) + resp, err := svc.client.Do(ctx, req, root) + if err != nil { + return nil, resp, err + } + return root.Credentials, resp, nil +} + +// UpdateMetricsAuth updates the credentials required to access a user's metrics endpoints +func (svc *DatabasesServiceOp) UpdateMetricsCredentials(ctx context.Context, updateCreds *DatabaseUpdateMetricsCredentialsRequest) (*Response, error) { + req, err := svc.client.NewRequest(ctx, http.MethodPut, databaseMetricsCredentialsPath, updateCreds) + if err != nil { + return nil, err + } + resp, err := svc.client.Do(ctx, req, nil) + if err != nil { + return resp, err + } + return resp, nil +} diff --git a/vendor/github.com/digitalocean/godo/godo.go b/vendor/github.com/digitalocean/godo/godo.go index a35c95f3b..eff1b0dae 100644 --- a/vendor/github.com/digitalocean/godo/godo.go +++ b/vendor/github.com/digitalocean/godo/godo.go @@ -21,7 +21,7 @@ import ( ) const ( - libraryVersion = "1.109.0" + libraryVersion = "1.111.0" defaultBaseURL = "https://api.digitalocean.com/" userAgent = "godo/" + libraryVersion mediaType = "application/json" diff --git a/vendor/github.com/digitalocean/godo/load_balancers.go b/vendor/github.com/digitalocean/godo/load_balancers.go index a4043a376..a044adaeb 100644 --- a/vendor/github.com/digitalocean/godo/load_balancers.go +++ b/vendor/github.com/digitalocean/godo/load_balancers.go @@ -7,6 +7,7 @@ import ( ) const ( + cachePath = "cache" dropletsPath = "droplets" forwardingRulesPath = "forwarding_rules" loadBalancersBasePath = "/v2/load_balancers" @@ -31,6 +32,7 @@ type LoadBalancersService interface { RemoveDroplets(ctx context.Context, lbID string, dropletIDs ...int) (*Response, error) AddForwardingRules(ctx context.Context, lbID string, rules ...ForwardingRule) (*Response, error) RemoveForwardingRules(ctx context.Context, lbID string, rules ...ForwardingRule) (*Response, error) + PurgeCache(ctx context.Context, lbID string) (*Response, error) } // LoadBalancer represents a DigitalOcean load balancer configuration. @@ -63,6 +65,9 @@ type LoadBalancer struct { ProjectID string `json:"project_id,omitempty"` HTTPIdleTimeoutSeconds *uint64 `json:"http_idle_timeout_seconds,omitempty"` Firewall *LBFirewall `json:"firewall,omitempty"` + Domains []*LBDomain `json:"domains,omitempty"` + GLBSettings *GLBSettings `json:"glb_settings,omitempty"` + TargetLoadBalancerIDs []string `json:"target_load_balancer_ids,omitempty"` } // String creates a human-readable description of a LoadBalancer. @@ -90,12 +95,12 @@ func (l LoadBalancer) AsRequest() *LoadBalancerRequest { RedirectHttpToHttps: l.RedirectHttpToHttps, EnableProxyProtocol: l.EnableProxyProtocol, EnableBackendKeepalive: l.EnableBackendKeepalive, - HealthCheck: l.HealthCheck, VPCUUID: l.VPCUUID, DisableLetsEncryptDNSRecords: l.DisableLetsEncryptDNSRecords, ValidateOnly: l.ValidateOnly, ProjectID: l.ProjectID, HTTPIdleTimeoutSeconds: l.HTTPIdleTimeoutSeconds, + TargetLoadBalancerIDs: append([]string(nil), l.TargetLoadBalancerIDs...), } if l.DisableLetsEncryptDNSRecords != nil { @@ -106,10 +111,12 @@ func (l LoadBalancer) AsRequest() *LoadBalancerRequest { r.HealthCheck = &HealthCheck{} *r.HealthCheck = *l.HealthCheck } + if l.StickySessions != nil { r.StickySessions = &StickySessions{} *r.StickySessions = *l.StickySessions } + if l.Region != nil { r.Region = l.Region.Slug } @@ -118,6 +125,18 @@ func (l LoadBalancer) AsRequest() *LoadBalancerRequest { r.Firewall = l.Firewall.deepCopy() } + for _, domain := range l.Domains { + lbDomain := &LBDomain{} + *lbDomain = *domain + lbDomain.VerificationErrorReasons = append([]string(nil), domain.VerificationErrorReasons...) + lbDomain.SSLValidationErrorReasons = append([]string(nil), domain.SSLValidationErrorReasons...) + r.Domains = append(r.Domains, lbDomain) + } + + if l.GLBSettings != nil { + r.GLBSettings = l.GLBSettings.deepCopy() + } + return &r } @@ -216,6 +235,9 @@ type LoadBalancerRequest struct { ProjectID string `json:"project_id,omitempty"` HTTPIdleTimeoutSeconds *uint64 `json:"http_idle_timeout_seconds,omitempty"` Firewall *LBFirewall `json:"firewall,omitempty"` + Domains []*LBDomain `json:"domains,omitempty"` + GLBSettings *GLBSettings `json:"glb_settings,omitempty"` + TargetLoadBalancerIDs []string `json:"target_load_balancer_ids,omitempty"` } // String creates a human-readable description of a LoadBalancerRequest. @@ -239,6 +261,64 @@ func (l dropletIDsRequest) String() string { return Stringify(l) } +// LBDomain defines domain names required to ingress traffic to a Global LB +type LBDomain struct { + // Name defines the domain fqdn + Name string `json:"name"` + // IsManaged indicates if the domain is DO-managed + IsManaged bool `json:"is_managed"` + // CertificateID indicates ID of a TLS certificate + CertificateID string `json:"certificate_id,omitempty"` + // Status indicates the domain validation status + Status string `json:"status,omitempty"` + // VerificationErrorReasons indicates any domain verification errors + VerificationErrorReasons []string `json:"verification_error_reasons,omitempty"` + // SSLValidationErrorReasons indicates any domain SSL validation errors + SSLValidationErrorReasons []string `json:"ssl_validation_error_reasons,omitempty"` +} + +// String creates a human-readable description of a LBDomain +func (d LBDomain) String() string { + return Stringify(d) +} + +// GLBSettings define settings for configuring a Global LB +type GLBSettings struct { + // TargetProtocol is the outgoing traffic protocol. + TargetProtocol string `json:"target_protocol"` + // EntryPort is the outgoing traffic port. + TargetPort uint32 `json:"target_port"` + // CDNSettings is the CDN configurations + CDN *CDNSettings `json:"cdn"` +} + +// String creates a human-readable description of a GLBSettings +func (s GLBSettings) String() string { + return Stringify(s) +} + +func (s GLBSettings) deepCopy() *GLBSettings { + settings := &GLBSettings{ + TargetProtocol: s.TargetProtocol, + TargetPort: s.TargetPort, + } + if s.CDN != nil { + settings.CDN = &CDNSettings{IsEnabled: s.CDN.IsEnabled} + } + return settings +} + +// CDNSettings define CDN settings for a Global LB +type CDNSettings struct { + // IsEnabled is the caching enabled flag + IsEnabled bool `json:"is_enabled"` +} + +// String creates a human-readable description of a CDNSettings +func (c CDNSettings) String() string { + return Stringify(c) +} + type loadBalancersRoot struct { LoadBalancers []LoadBalancer `json:"load_balancers"` Links *Links `json:"links"` @@ -394,3 +474,15 @@ func (l *LoadBalancersServiceOp) RemoveForwardingRules(ctx context.Context, lbID return l.client.Do(ctx, req, nil) } + +// PurgeCache purges the CDN cache of a global load balancer by its identifier. +func (l *LoadBalancersServiceOp) PurgeCache(ctx context.Context, ldID string) (*Response, error) { + path := fmt.Sprintf("%s/%s/%s", loadBalancersBasePath, ldID, cachePath) + + req, err := l.client.NewRequest(ctx, http.MethodDelete, path, nil) + if err != nil { + return nil, err + } + + return l.client.Do(ctx, req, nil) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 61c424dcc..3b965fab1 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -58,7 +58,7 @@ github.com/creack/pty # github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc ## explicit github.com/davecgh/go-spew/spew -# github.com/digitalocean/godo v1.109.1-0.20240228180303-16a4709be517 +# github.com/digitalocean/godo v1.111.0 ## explicit; go 1.20 github.com/digitalocean/godo github.com/digitalocean/godo/metrics