Repository: digitalocean/doctl
Pull request #1509 (2024-02-29 13:56:39)

[NETPROD-3585] Added name filter in doctl for certificates
[NETPROD-3585](https://do-internal.atlassian.net/browse/NETPROD-3585)

[NETPROD-3585]: https://do-internal.atlassian.net/browse/NETPROD-3585?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ

guptado: > Can the changes be tested locally apart from the unit tests?

Yes. We can do go install & try it. I was not able to do it on my machine due to multiple set up issues. But this pattern & code is being used everywhere so it gives confidence 
andrewsomething: @guptado, @apinonformoso - You should be able to build doctl locally by running `make build`. Please let us know what kinds of issues you were running into so we can improve the contributor experience.  

Patch:
diff --git a/commands/certificates.go b/commands/certificates.go
index 154b5ac2..cfb68cdd 100644
--- a/commands/certificates.go
+++ b/commands/certificates.go
@@ -78,6 +78,8 @@ To upload a custom certificate, you need to provide a certificate name, the path
 	cmdCertificateList := CmdBuilder(cmd, RunCertificateList, "list", "Retrieve list of the account's stored certificates", `This command retrieves a list of all certificates associated with the account. The following details are shown for each certificate:`+certDetails, Writer,
 		aliasOpt("ls"), displayerType(&displayers.Certificate{}))
 	cmdCertificateList.Example = `The following example retrieves a list of all certificates associated with your account and uses the ` + "`" + `--format` + "`" + ` flag return only the IDs, names, and the domains associated with each ticket: doctl compute certificate list --format ID,Name,DNSNames`
+	AddStringFlag(cmdCertificateList, doctl.ArgCertificateName, "", "",
+		"Filter certificates by the specified name")
 
 	cmdCertificateDelete := CmdBuilder(cmd, RunCertificateDelete, "delete <id>",
 		"Delete the specified certificate", `Deletes the specified certificate.
@@ -184,8 +186,20 @@ func RunCertificateCreate(c *CmdConfig) error {
 
 // RunCertificateList lists certificates.
 func RunCertificateList(c *CmdConfig) error {
+
+	name, err := c.Doit.GetString(c.NS, doctl.ArgCertificateName)
+	if err != nil {
+		return err
+	}
+
 	cs := c.Certificates()
-	list, err := cs.List()
+	var list do.Certificates
+
+	if name == "" {
+		list, err = cs.List()
+	} else {
+		list, err = cs.ListByName(name)
+	}
 	if err != nil {
 		return err
 	}
diff --git a/do/certificates.go b/do/certificates.go
index b381a2a0..972b9b9b 100644
--- a/do/certificates.go
+++ b/do/certificates.go
@@ -32,6 +32,7 @@ type CertificatesService interface {
 	Get(cID string) (*Certificate, error)
 	Create(cr *godo.CertificateRequest) (*Certificate, error)
 	List() (Certificates, error)
+	ListByName(cName string) (Certificates, error)
 	Delete(cID string) error
 }
 
@@ -95,6 +96,17 @@ func (cs *certificatesService) List() (Certificates, error) {
 	return list, nil
 }
 
+func (cs *certificatesService) ListByName(cName string) (Certificates, error) {
+	c, _, err := cs.client.Certificates.ListByName(context.TODO(), cName, nil)
+
+	list := make([]Certificate, len(c))
+	for i := range c {
+		list[i] = Certificate{Certificate: &c[i]}
+	}
+
+	return list, err
+}
+
 func (cs *certificatesService) Delete(cID string) error {
 	_, err := cs.client.Certificates.Delete(context.TODO(), cID)
 	return err
diff --git a/do/mocks/CDNsService.go b/do/mocks/CDNsService.go
index 99b8951e..d2aaf34c 100644
--- a/do/mocks/CDNsService.go
+++ b/do/mocks/CDNsService.go
@@ -5,6 +5,7 @@
 //
 //	mockgen -source cdns.go -package=mocks CDNsService
 //
+
 // Package mocks is a generated GoMock package.
 package mocks
 
diff --git a/do/mocks/CertificatesService.go b/do/mocks/CertificatesService.go
index b7acaf41..33cecdf0 100644
--- a/do/mocks/CertificatesService.go
+++ b/do/mocks/CertificatesService.go
@@ -5,6 +5,7 @@
 //
 //	mockgen -source certificates.go -package=mocks CertificateSservice
 //
+
 // Package mocks is a generated GoMock package.
 package mocks
 
@@ -97,3 +98,18 @@ func (mr *MockCertificatesServiceMockRecorder) List() *gomock.Call {
 	mr.mock.ctrl.T.Helper()
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockCertificatesService)(nil).List))
 }
+
+// ListByName mocks base method.
+func (m *MockCertificatesService) ListByName(cName string) (do.Certificates, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "ListByName", cName)
+	ret0, _ := ret[0].(do.Certificates)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// ListByName indicates an expected call of ListByName.
+func (mr *MockCertificatesServiceMockRecorder) ListByName(cName any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListByName", reflect.TypeOf((*MockCertificatesService)(nil).ListByName), cName)
+}
diff --git a/go.mod b/go.mod
index 207add7a..abc0d8f0 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.107.0
+	github.com/digitalocean/godo v1.109.1-0.20240228180303-16a4709be517
 	github.com/docker/cli v24.0.5+incompatible
 	github.com/docker/docker v24.0.7+incompatible
 	github.com/docker/docker-credential-helpers v0.7.0 // indirect
diff --git a/go.sum b/go.sum
index 270bc714..feb0d67f 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.107.0 h1:P72IbmGFQvKOvyjVLyT59bmHxilA4E5hWi40rF4zNQc=
-github.com/digitalocean/godo v1.107.0/go.mod h1:R6EmmWI8CT1+fCtjWY9UCB+L5uufuZH13wk3YhxycCs=
+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/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/vendor/github.com/digitalocean/godo/CHANGELOG.md b/vendor/github.com/digitalocean/godo/CHANGELOG.md
index 8177eb19..1d5070fd 100644
--- a/vendor/github.com/digitalocean/godo/CHANGELOG.md
+++ b/vendor/github.com/digitalocean/godo/CHANGELOG.md
@@ -1,5 +1,16 @@
 # Change Log
 
+## [v1.109.0] - 2024-02-09
+
+- #668 - @greeshmapill - APPS-8315: Update app instance size spec
+- #665 - @jcodybaker - APPS-8263: methods for managing App Platform dev DBs
+- #663 - @dwilsondo - Include replica connection info on DBaaS clusters & DBaaS PG pools
+- #662 - @ddatta-do - load balancer : add regional network as new LB type
+
+## [v1.108.0] - 2024-01-17
+
+- #660 - @dweinshenker - Enable CRUD operations for replicas with storage_size_mib
+
 ## [v1.107.0] - 2023-12-07
 
 - #658 - @markusthoemmes - APPS-8033 Add the RUN_RESTARTED log type
diff --git a/vendor/github.com/digitalocean/godo/apps.gen.go b/vendor/github.com/digitalocean/godo/apps.gen.go
index 7ec24c33..c037559d 100644
--- a/vendor/github.com/digitalocean/godo/apps.gen.go
+++ b/vendor/github.com/digitalocean/godo/apps.gen.go
@@ -98,6 +98,8 @@ type App struct {
 	BuildConfig             *AppBuildConfig `json:"build_config,omitempty"`
 	// The id of the project for the app. This will be empty if there is a fleet (project) lookup failure.
 	ProjectID string `json:"project_id,omitempty"`
+	// The dedicated egress ip addresses associated with the app.
+	DedicatedIps []*AppDedicatedIp `json:"dedicated_ips,omitempty"`
 }
 
 // AppAlertSpec Configuration of an alert for the app or a individual component.
@@ -221,6 +223,26 @@ const (
 	AppDatabaseSpecEngine_MongoDB AppDatabaseSpecEngine = "MONGODB"
 )
 
+// AppDedicatedIp Represents a dedicated egress ip.
+type AppDedicatedIp struct {
+	// The ip address of the dedicated egress ip.
+	Ip string `json:"ip,omitempty"`
+	// The id of the dedictated egress ip.
+	ID     string               `json:"id,omitempty"`
+	Status AppDedicatedIpStatus `json:"status,omitempty"`
+}
+
+// AppDedicatedIpStatus the model 'AppDedicatedIpStatus'
+type AppDedicatedIpStatus string
+
+// List of AppDedicatedIPStatus
+const (
+	APPDEDICATEDIPSTATUS_Unknown   AppDedicatedIpStatus = "UNKNOWN"
+	APPDEDICATEDIPSTATUS_Assigning AppDedicatedIpStatus = "ASSIGNING"
+	APPDEDICATEDIPSTATUS_Assigned  AppDedicatedIpStatus = "ASSIGNED"
+	APPDEDICATEDIPSTATUS_Removed   AppDedicatedIpStatus = "REMOVED"
+)
+
 // AppDomainSpec struct for AppDomainSpec
 type AppDomainSpec struct {
 	Domain   string            `json:"domain"`
@@ -997,6 +1019,41 @@ type AppDomainValidation struct {
 	TXTValue string `json:"txt_value,omitempty"`
 }
 
+// GetAppDatabaseConnectionDetailsResponse struct for GetAppDatabaseConnectionDetailsResponse
+type GetAppDatabaseConnectionDetailsResponse struct {
+	ConnectionDetails []*GetDatabaseConnectionDetailsResponse `json:"connection_details,omitempty"`
+}
+
+// GetDatabaseConnectionDetailsResponse struct for GetDatabaseConnectionDetailsResponse
+type GetDatabaseConnectionDetailsResponse struct {
+	Host          string                                      `json:"host,omitempty"`
+	Port          int64                                       `json:"port,omitempty"`
+	Username      string                                      `json:"username,omitempty"`
+	Password      string                                      `json:"password,omitempty"`
+	DatabaseName  string                                      `json:"database_name,omitempty"`
+	SslMode       string                                      `json:"ssl_mode,omitempty"`
+	DatabaseURL   string                                      `json:"database_url,omitempty"`
+	ComponentName string                                      `json:"component_name,omitempty"`
+	Pools         []*GetDatabaseConnectionDetailsResponsePool `json:"pools,omitempty"`
+}
+
+// GetDatabaseConnectionDetailsResponsePool struct for GetDatabaseConnectionDetailsResponsePool
+type GetDatabaseConnectionDetailsResponsePool struct {
+	PoolName     string `json:"pool_name,omitempty"`
+	Host         string `json:"host,omitempty"`
+	Port         int64  `json:"port,omitempty"`
+	Username     string `json:"username,omitempty"`
+	Password     string `json:"password,omitempty"`
+	DatabaseName string `json:"database_name,omitempty"`
+	SslMode      string `json:"ssl_mode,omitempty"`
+	DatabaseURL  string `json:"database_url,omitempty"`
+}
+
+// GetDatabaseTrustedSourceResponse struct for GetDatabaseTrustedSourceResponse
+type GetDatabaseTrustedSourceResponse struct {
+	IsEnabled bool `json:"is_enabled,omitempty"`
+}
+
 // GitHubSourceSpec struct for GitHubSourceSpec
 type GitHubSourceSpec struct {
 	Repo         string `json:"repo,omitempty"`
@@ -1062,6 +1119,15 @@ type AppInstanceSize struct {
 	TierSlug        string                 `json:"tier_slug,omitempty"`
 	TierUpgradeTo   string                 `json:"tier_upgrade_to,omitempty"`
 	TierDowngradeTo string                 `json:"tier_downgrade_to,omitempty"`
+	// Indicates if the tier instance size can enable autoscaling.
+	Scalable       bool `json:"scalable,omitempty"`
+	FeaturePreview bool `json:"feature_preview,omitempty"`
+	// Indicates if the tier instance size allows more than one instance.
+	SingleInstanceOnly bool `json:"single_instance_only,omitempty"`
+	// Indicates if the tier instance size is intended for deprecation.
+	DeprecationIntent bool `json:"deprecation_intent,omitempty"`
+	// The bandwidth allowance in GiB for the tier instance size.
+	BandwidthAllowanceGib string `json:"bandwidth_allowance_gib,omitempty"`
 }
 
 // AppInstanceSizeCPUType the model 'AppInstanceSizeCPUType'
@@ -1127,6 +1193,17 @@ type AppRegion struct {
 	Default bool `json:"default,omitempty"`
 }
 
+// ResetDatabasePasswordRequest struct for ResetDatabasePasswordRequest
+type ResetDatabasePasswordRequest struct {
+	AppID         string `json:"app_id,omitempty"`
+	ComponentName string `json:"component_name,omitempty"`
+}
+
+// ResetDatabasePasswordResponse struct for ResetDatabasePasswordResponse
+type ResetDatabasePasswordResponse struct {
+	Deployment *Deployment `json:"deployment,omitempty"`
+}
+
 // AppStringMatch struct for AppStringMatch
 type AppStringMatch struct {
 	// Exact string match. Only 1 of `exact`, `prefix`, or `regex` must be set.
@@ -1144,6 +1221,18 @@ type AppTier struct {
 	BuildSeconds         string `json:"build_seconds,omitempty"`
 }
 
+// ToggleDatabaseTrustedSourceRequest struct for ToggleDatabaseTrustedSourceRequest
+type ToggleDatabaseTrustedSourceRequest struct {
+	AppID         string `json:"app_id,omitempty"`
+	ComponentName string `json:"component_name,omitempty"`
+	Enable        bool   `json:"enable,omitempty"`
+}
+
+// ToggleDatabaseTrustedSourceResponse struct for ToggleDatabaseTrustedSourceResponse
+type ToggleDatabaseTrustedSourceResponse struct {
+	IsEnabled bool `json:"is_enabled,omitempty"`
+}
+
 // UpgradeBuildpackResponse struct for UpgradeBuildpackResponse
 type UpgradeBuildpackResponse struct {
 	// The components that were affected by the upgrade.
diff --git a/vendor/github.com/digitalocean/godo/apps.go b/vendor/github.com/digitalocean/godo/apps.go
index 880ce092..cd72f740 100644
--- a/vendor/github.com/digitalocean/godo/apps.go
+++ b/vendor/github.com/digitalocean/godo/apps.go
@@ -56,6 +56,19 @@ type AppsService interface {
 
 	ListBuildpacks(ctx context.Context) ([]*Buildpack, *Response, error)
 	UpgradeBuildpack(ctx context.Context, appID string, opts UpgradeBuildpackOptions) (*UpgradeBuildpackResponse, *Response, error)
+
+	GetAppDatabaseConnectionDetails(ctx context.Context, appID string) ([]*GetDatabaseConnectionDetailsResponse, *Response, error)
+	ResetDatabasePassword(ctx context.Context, appID string, component string) (*Deployment, *Response, error)
+	ToggleDatabaseTrustedSource(
+		ctx context.Context,
+		appID string,
+		component string,
+		opts ToggleDatabaseTrustedSourceOptions,
+	) (
+		*ToggleDatabaseTrustedSourceResponse,
+		*Response,
+		error,
+	)
 }
 
 // AppLogs represent app logs.
@@ -90,6 +103,12 @@ type UpgradeBuildpackOptions struct {
 	TriggerDeployment bool `json:"trigger_deployment,omitempty"`
 }
 
+// ToggleDatabaseTrustedSourceOptions provides optional parameters for ToggleDatabaseTrustedSource.
+type ToggleDatabaseTrustedSourceOptions struct {
+	// Enable, if true, indicates the database should enable the trusted sources firewall.
+	Enable bool
+}
+
 type appRoot struct {
 	App *App `json:"app"`
 }
@@ -498,6 +517,60 @@ func (s *AppsServiceOp) UpgradeBuildpack(ctx context.Context, appID string, opts
 	return root, resp, nil
 }
 
+// GetAppDatabaseConnectionDetails retrieves credentials for databases associated with the app.
+func (s *AppsServiceOp) GetAppDatabaseConnectionDetails(ctx context.Context, appID string) ([]*GetDatabaseConnectionDetailsResponse, *Response, error) {
+	path := fmt.Sprintf("%s/%s/database_connection_details", appsBasePath, appID)
+	req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+	root := new(GetAppDatabaseConnectionDetailsResponse)
+	resp, err := s.client.Do(ctx, req, root)
+	if err != nil {
+		return nil, resp, err
+	}
+	return root.ConnectionDetails, resp, nil
+}
+
+// ResetDatabasePassword resets credentials for a database component associated with the app.
+func (s *AppsServiceOp) ResetDatabasePassword(ctx context.Context, appID string, component string) (*Deployment, *Response, error) {
+	path := fmt.Sprintf("%s/%s/components/%s/reset_password", appsBasePath, appID, component)
+	req, err := s.client.NewRequest(ctx, http.MethodPost, path, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+	root := new(deploymentRoot)
+	resp, err := s.client.Do(ctx, req, root)
+	if err != nil {
+		return nil, resp, err
+	}
+	return root.Deployment, resp, nil
+}
+
+// ToggleDatabaseTrustedSource enables/disables trusted sources on the specified dev database component.
+func (s *AppsServiceOp) ToggleDatabaseTrustedSource(
+	ctx context.Context,
+	appID string,
+	component string,
+	opts ToggleDatabaseTrustedSourceOptions,
+) (
+	*ToggleDatabaseTrustedSourceResponse,
+	*Response,
+	error,
+) {
+	path := fmt.Sprintf("%s/%s/components/%s/trusted_sources", appsBasePath, appID, component)
+	req, err := s.client.NewRequest(ctx, http.MethodPost, path, opts)
+	if err != nil {
+		return nil, nil, err
+	}
+	root := new(ToggleDatabaseTrustedSourceResponse)
+	resp, err := s.client.Do(ctx, req, root)
+	if err != nil {
+		return nil, resp, err
+	}
+	return root, resp, nil
+}
+
 // AppComponentType is an app component type.
 type AppComponentType string
 
diff --git a/vendor/github.com/digitalocean/godo/apps_accessors.go b/vendor/github.com/digitalocean/godo/apps_accessors.go
index 059d6473..0d655d84 100644
--- a/vendor/github.com/digitalocean/godo/apps_accessors.go
+++ b/vendor/github.com/digitalocean/godo/apps_accessors.go
@@ -37,6 +37,14 @@ func (a *App) GetCreatedAt() time.Time {
 	return a.CreatedAt
 }
 
+// GetDedicatedIps returns the DedicatedIps field.
+func (a *App) GetDedicatedIps() []*AppDedicatedIp {
+	if a == nil {
+		return nil
+	}
+	return a.DedicatedIps
+}
+
 // GetDefaultIngress returns the DefaultIngress field.
 func (a *App) GetDefaultIngress() string {
 	if a == nil {
@@ -557,6 +565,30 @@ func (a *AppDatabaseSpec) GetVersion() string {
 	return a.Version
 }
 
+// GetID returns the ID field.
+func (a *AppDedicatedIp) GetID() string {
+	if a == nil {
+		return ""
+	}
+	return a.ID
+}
+
+// GetIp returns the Ip field.
+func (a *AppDedicatedIp) GetIp() string {
+	if a == nil {
+		return ""
+	}
+	return a.Ip
+}
+
+// GetStatus returns the Status field.
+func (a *AppDedicatedIp) GetStatus() AppDedicatedIpStatus {
+	if a == nil {
+		return ""
+	}
+	return a.Status
+}
+
 // GetCertificateExpiresAt returns the CertificateExpiresAt field.
 func (a *AppDomain) GetCertificateExpiresAt() time.Time {
 	if a == nil {
@@ -981,6 +1013,14 @@ func (a *AppIngressSpecRuleStringMatch) GetPrefix() string {
 	return a.Prefix
 }
 
+// GetBandwidthAllowanceGib returns the BandwidthAllowanceGib field.
+func (a *AppInstanceSize) GetBandwidthAllowanceGib() string {
+	if a == nil {
+		return ""
+	}
+	return a.BandwidthAllowanceGib
+}
+
 // GetCPUs returns the CPUs field.
 func (a *AppInstanceSize) GetCPUs() string {
 	if a == nil {
@@ -997,6 +1037,22 @@ func (a *AppInstanceSize) GetCPUType() AppInstanceSizeCPUType {
 	return a.CPUType
 }
 
+// GetDeprecationIntent returns the DeprecationIntent field.
+func (a *AppInstanceSize) GetDeprecationIntent() bool {
+	if a == nil {
+		return false
+	}
+	return a.DeprecationIntent
+}
+
+// GetFeaturePreview returns the FeaturePreview field.
+func (a *AppInstanceSize) GetFeaturePreview() bool {
+	if a == nil {
+		return false
+	}
+	return a.FeaturePreview
+}
+
 // GetMemoryBytes returns the MemoryBytes field.
 func (a *AppInstanceSize) GetMemoryBytes() string {
 	if a == nil {
@@ -1013,6 +1069,22 @@ func (a *AppInstanceSize) GetName() string {
 	return a.Name
 }
 
+// GetScalable returns the Scalable field.
+func (a *AppInstanceSize) GetScalable() bool {
+	if a == nil {
+		return false
+	}
+	return a.Scalable
+}
+
+// GetSingleInstanceOnly returns the SingleInstanceOnly field.
+func (a *AppInstanceSize) GetSingleInstanceOnly() bool {
+	if a == nil {
+		return false
+	}
+	return a.SingleInstanceOnly
+}
+
 // GetSlug returns the Slug field.
 func (a *AppInstanceSize) GetSlug() string {
 	if a == nil {
@@ -3117,6 +3189,158 @@ func (d *DetectResponseServerlessPackage) GetName() string {
 	return d.Name
 }
 
+// GetConnectionDetails returns the ConnectionDetails field.
+func (g *GetAppDatabaseConnectionDetailsResponse) GetConnectionDetails() []*GetDatabaseConnectionDetailsResponse {
+	if g == nil {
+		return nil
+	}
+	return g.ConnectionDetails
+}
+
+// GetComponentName returns the ComponentName field.
+func (g *GetDatabaseConnectionDetailsResponse) GetComponentName() string {
+	if g == nil {
+		return ""
+	}
+	return g.ComponentName
+}
+
+// GetDatabaseName returns the DatabaseName field.
+func (g *GetDatabaseConnectionDetailsResponse) GetDatabaseName() string {
+	if g == nil {
+		return ""
+	}
+	return g.DatabaseName
+}
+
+// GetDatabaseURL returns the DatabaseURL field.
+func (g *GetDatabaseConnectionDetailsResponse) GetDatabaseURL() string {
+	if g == nil {
+		return ""
+	}
+	return g.DatabaseURL
+}
+
+// GetHost returns the Host field.
+func (g *GetDatabaseConnectionDetailsResponse) GetHost() string {
+	if g == nil {
+		return ""
+	}
+	return g.Host
+}
+
+// GetPassword returns the Password field.
+func (g *GetDatabaseConnectionDetailsResponse) GetPassword() string {
+	if g == nil {
+		return ""
+	}
+	return g.Password
+}
+
+// GetPools returns the Pools field.
+func (g *GetDatabaseConnectionDetailsResponse) GetPools() []*GetDatabaseConnectionDetailsResponsePool {
+	if g == nil {
+		return nil
+	}
+	return g.Pools
+}
+
+// GetPort returns the Port field.
+func (g *GetDatabaseConnectionDetailsResponse) GetPort() int64 {
+	if g == nil {
+		return 0
+	}
+	return g.Port
+}
+
+// GetSslMode returns the SslMode field.
+func (g *GetDatabaseConnectionDetailsResponse) GetSslMode() string {
+	if g == nil {
+		return ""
+	}
+	return g.SslMode
+}
+
+// GetUsername returns the Username field.
+func (g *GetDatabaseConnectionDetailsResponse) GetUsername() string {
+	if g == nil {
+		return ""
+	}
+	return g.Username
+}
+
+// GetDatabaseName returns the DatabaseName field.
+func (g *GetDatabaseConnectionDetailsResponsePool) GetDatabaseName() string {
+	if g == nil {
+		return ""
+	}
+	return g.DatabaseName
+}
+
+// GetDatabaseURL returns the DatabaseURL field.
+func (g *GetDatabaseConnectionDetailsResponsePool) GetDatabaseURL() string {
+	if g == nil {
+		return ""
+	}
+	return g.DatabaseURL
+}
+
+// GetHost returns the Host field.
+func (g *GetDatabaseConnectionDetailsResponsePool) GetHost() string {
+	if g == nil {
+		return ""
+	}
+	return g.Host
+}
+
+// GetPassword returns the Password field.
+func (g *GetDatabaseConnectionDetailsResponsePool) GetPassword() string {
+	if g == nil {
+		return ""
+	}
+	return g.Password
+}
+
+// GetPoolName returns the PoolName field.
+func (g *GetDatabaseConnectionDetailsResponsePool) GetPoolName() string {
+	if g == nil {
+		return ""
+	}
+	return g.PoolName
+}
+
+// GetPort returns the Port field.
+func (g *GetDatabaseConnectionDetailsResponsePool) GetPort() int64 {
+	if g == nil {
+		return 0
+	}
+	return g.Port
+}
+
+// GetSslMode returns the SslMode field.
+func (g *GetDatabaseConnectionDetailsResponsePool) GetSslMode() string {
+	if g == nil {
+		return ""
+	}
+	return g.SslMode
+}
+
+// GetUsername returns the Username field.
+func (g *GetDatabaseConnectionDetailsResponsePool) GetUsername() string {
+	if g == nil {
+		return ""
+	}
+	return g.Username
+}
+
+// GetIsEnabled returns the IsEnabled field.
+func (g *GetDatabaseTrustedSourceResponse) GetIsEnabled() bool {
+	if g == nil {
+		return false
+	}
+	return g.IsEnabled
+}
+
 // GetBranch returns the Branch field.
 func (g *GitHubSourceSpec) GetBranch() string {
 	if g == nil {
@@ -3253,6 +3477,62 @@ func (l *ListBuildpacksResponse) GetBuildpacks() []*Buildpack {
 	return l.Buildpacks
 }
 
+// GetAppID returns the AppID field.
+func (r *ResetDatabasePasswordRequest) GetAppID() string {
+	if r == nil {
+		return ""
+	}
+	return r.AppID
+}
+
+// GetComponentName returns the ComponentName field.
+func (r *ResetDatabasePasswordRequest) GetComponentName() string {
+	if r == nil {
+		return ""
+	}
+	return r.ComponentName
+}
+
+// GetDeployment returns the Deployment field.
+func (r *ResetDatabasePasswordResponse) GetDeployment() *Deployment {
+	if r == nil {
+		return nil
+	}
+	return r.Deployment
+}
+
+// GetAppID returns the AppID field.
+func (t *ToggleDatabaseTrustedSourceRequest) GetAppID() string {
+	if t == nil {
+		return ""
+	}
+	return t.AppID
+}
+
+// GetComponentName returns the ComponentName field.
+func (t *ToggleDatabaseTrustedSourceRequest) GetComponentName() string {
+	if t == nil {
+		return ""
+	}
+	return t.ComponentName
+}
+
+// GetEnable returns the Enable field.
+func (t *ToggleDatabaseTrustedSourceRequest) GetEnable() bool {
+	if t == nil {
+		return false
+	}
+	return t.Enable
+}
+
+// GetIsEnabled returns the IsEnabled field.
+func (t *ToggleDatabaseTrustedSourceResponse) GetIsEnabled() bool {
+	if t == nil {
+		return false
+	}
+	return t.IsEnabled
+}
+
 // GetAffectedComponents returns the AffectedComponents field.
 func (u *UpgradeBuildpackResponse) GetAffectedComponents() []string {
 	if u == nil {
diff --git a/vendor/github.com/digitalocean/godo/certificates.go b/vendor/github.com/digitalocean/godo/certificates.go
index faf26a3e..7612acf0 100644
--- a/vendor/github.com/digitalocean/godo/certificates.go
+++ b/vendor/github.com/digitalocean/godo/certificates.go
@@ -2,6 +2,7 @@ package godo
 
 import (
 	"context"
+	"fmt"
 	"net/http"
 	"path"
 )
@@ -13,6 +14,7 @@ const certificatesBasePath = "/v2/certificates"
 type CertificatesService interface {
 	Get(context.Context, string) (*Certificate, *Response, error)
 	List(context.Context, *ListOptions) ([]Certificate, *Response, error)
+	ListByName(context.Context, string, *ListOptions) ([]Certificate, *Response, error)
 	Create(context.Context, *CertificateRequest) (*Certificate, *Response, error)
 	Delete(context.Context, string) (*Response, error)
 }
@@ -101,6 +103,39 @@ func (c *CertificatesServiceOp) List(ctx context.Context, opt *ListOptions) ([]C
 	return root.Certificates, resp, nil
 }
 
+func (c *CertificatesServiceOp) ListByName(ctx context.Context, name string, opt *ListOptions) ([]Certificate, *Response, error) {
+
+	if len(name) < 1 {
+		return nil, nil, NewArgError("name", "cannot be an empty string")
+	}
+
+	path := fmt.Sprintf("%s?name=%s", certificatesBasePath, name)
+	urlStr, err := addOptions(path, opt)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	req, err := c.client.NewRequest(ctx, http.MethodGet, urlStr, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	root := new(certificatesRoot)
+	resp, err := c.client.Do(ctx, req, root)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	if l := root.Links; l != nil {
+		resp.Links = l
+	}
+	if m := root.Meta; m != nil {
+		resp.Meta = m
+	}
+
+	return root.Certificates, resp, err
+}
+
 // Create a new certificate with provided configuration.
 func (c *CertificatesServiceOp) Create(ctx context.Context, cr *CertificateRequest) (*Certificate, *Response, error) {
 	req, err := c.client.NewRequest(ctx, http.MethodPost, certificatesBasePath, cr)
diff --git a/vendor/github.com/digitalocean/godo/databases.go b/vendor/github.com/digitalocean/godo/databases.go
index 54d80e99..14d5a04c 100644
--- a/vendor/github.com/digitalocean/godo/databases.go
+++ b/vendor/github.com/digitalocean/godo/databases.go
@@ -170,24 +170,26 @@ var _ DatabasesService = &DatabasesServiceOp{}
 // "pg", "mysql" or "redis". A Database also includes connection information and other
 // properties of the service like region, size and current status.
 type Database struct {
-	ID                 string                     `json:"id,omitempty"`
-	Name               string                     `json:"name,omitempty"`
-	EngineSlug         string                     `json:"engine,omitempty"`
-	VersionSlug        string                     `json:"version,omitempty"`
-	Connection         *DatabaseConnection        `json:"connection,omitempty"`
-	PrivateConnection  *DatabaseConnection        `json:"private_connection,omitempty"`
-	Users              []DatabaseUser             `json:"users,omitempty"`
-	NumNodes           int                        `json:"num_nodes,omitempty"`
-	SizeSlug           string                     `json:"size,omitempty"`
-	DBNames            []string                   `json:"db_names,omitempty"`
-	RegionSlug         string                     `json:"region,omitempty"`
-	Status             string                     `json:"status,omitempty"`
-	MaintenanceWindow  *DatabaseMaintenanceWindow `json:"maintenance_window,omitempty"`
-	CreatedAt          time.Time                  `json:"created_at,omitempty"`
-	PrivateNetworkUUID string                     `json:"private_network_uuid,omitempty"`
-	Tags               []string                   `json:"tags,omitempty"`
-	ProjectID          string                     `json:"project_id,omitempty"`
-	StorageSizeMib     uint64                     `json:"storage_size_mib,omitempty"`
+	ID                       string                     `json:"id,omitempty"`
+	Name                     string                     `json:"name,omitempty"`
+	EngineSlug               string                     `json:"engine,omitempty"`
+	VersionSlug              string                     `json:"version,omitempty"`
+	Connection               *DatabaseConnection        `json:"connection,omitempty"`
+	PrivateConnection        *DatabaseConnection        `json:"private_connection,omitempty"`
+	StandbyConnection        *DatabaseConnection        `json:"standby_connection,omitempty"`
+	StandbyPrivateConnection *DatabaseConnection        `json:"standby_private_connection,omitempty"`
+	Users                    []DatabaseUser             `json:"users,omitempty"`
+	NumNodes                 int                        `json:"num_nodes,omitempty"`
+	SizeSlug                 string                     `json:"size,omitempty"`
+	DBNames                  []string                   `json:"db_names,omitempty"`
+	RegionSlug               string                     `json:"region,omitempty"`
+	Status                   string                     `json:"status,omitempty"`
+	MaintenanceWindow        *DatabaseMaintenanceWindow `json:"maintenance_window,omitempty"`
+	CreatedAt                time.Time                  `json:"created_at,omitempty"`
+	PrivateNetworkUUID       string                     `json:"private_network_uuid,omitempty"`
+	Tags                     []string                   `json:"tags,omitempty"`
+	ProjectID                string                     `json:"project_id,omitempty"`
+	StorageSizeMib           uint64                     `json:"storage_size_mib,omitempty"`
 }
 
 // DatabaseCA represents a database ca.
@@ -376,17 +378,20 @@ type DatabaseReplica struct {
 	CreatedAt          time.Time           `json:"created_at"`
 	PrivateNetworkUUID string              `json:"private_network_uuid,omitempty"`
 	Tags               []string            `json:"tags,omitempty"`
+	StorageSizeMib     uint64              `json:"storage_size_mib,omitempty"`
 }
 
 // DatabasePool represents a database connection pool
 type DatabasePool struct {
-	User              string              `json:"user"`
-	Name              string              `json:"name"`
-	Size              int                 `json:"size"`
-	Database          string              `json:"db"`
-	Mode              string              `json:"mode"`
-	Connection        *DatabaseConnection `json:"connection"`
-	PrivateConnection *DatabaseConnection `json:"private_connection,omitempty"`
+	User                     string              `json:"user"`
+	Name                     string              `json:"name"`
+	Size                     int                 `json:"size"`
+	Database                 string              `json:"db"`
+	Mode                     string              `json:"mode"`
+	Connection               *DatabaseConnection `json:"connection"`
+	PrivateConnection        *DatabaseConnection `json:"private_connection,omitempty"`
+	StandbyConnection        *DatabaseConnection `json:"standby_connection,omitempty"`
+	StandbyPrivateConnection *DatabaseConnection `json:"standby_private_connection,omitempty"`
 }
 
 // DatabaseCreatePoolRequest is used to create a new database connection pool
@@ -436,6 +441,7 @@ type DatabaseCreateReplicaRequest struct {
 	Size               string   `json:"size"`
 	PrivateNetworkUUID string   `json:"private_network_uuid"`
 	Tags               []string `json:"tags,omitempty"`
+	StorageSizeMib     uint64   `json:"storage_size_mib,omitempty"`
 }
 
 // DatabaseUpdateFirewallRulesRequest is used to set the firewall rules for a database
diff --git a/vendor/github.com/digitalocean/godo/godo.go b/vendor/github.com/digitalocean/godo/godo.go
index e7373897..a35c95f3 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.107.0"
+	libraryVersion = "1.109.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 7e1cfc16..a4043a37 100644
--- a/vendor/github.com/digitalocean/godo/load_balancers.go
+++ b/vendor/github.com/digitalocean/godo/load_balancers.go
@@ -14,8 +14,9 @@ const (
 
 // Load Balancer types.
 const (
-	LoadBalancerTypeGlobal   = "GLOBAL"
-	LoadBalancerTypeRegional = "REGIONAL"
+	LoadBalancerTypeGlobal          = "GLOBAL"
+	LoadBalancerTypeRegional        = "REGIONAL"
+	LoadBalancerTypeRegionalNetwork = "REGIONAL_NETWORK"
 )
 
 // LoadBalancersService is an interface for managing load balancers with the DigitalOcean API.
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 291b133a..d1d28724 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.107.0
+# github.com/digitalocean/godo v1.109.1-0.20240228180303-16a4709be517
 ## explicit; go 1.20
 github.com/digitalocean/godo
 github.com/digitalocean/godo/metrics


Test patch:
diff --git a/commands/certificates_test.go b/commands/certificates_test.go
index 4ce254dd..1ea4c5e2 100644
--- a/commands/certificates_test.go
+++ b/commands/certificates_test.go
@@ -165,6 +165,17 @@ func TestCertificateList(t *testing.T) {
 	})
 }
 
+func TestCertificateListByName(t *testing.T) {
+	withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
+		name := "web-cert-01"
+		tm.certificates.EXPECT().ListByName(name).Return(testCertificateList, nil)
+
+		config.Doit.Set(config.NS, doctl.ArgCertificateName, name)
+		err := RunCertificateList(config)
+		assert.NoError(t, err)
+	})
+}
+
 func TestCertificateDelete(t *testing.T) {
 	withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
 		cID := "892071a0-bb95-49bc-8021-3afd67a210bf"