Repository: digitalocean/doctl
Pull request #1662 (2025-02-19 08:01:29)

[VPC-4215] Support PIA Get BGP auth key and regenerate service key
Testing:

Get bgp auth key:
```
➜  builds git:(VPC-4215/bgp_key) ✗ ./doctl --context new_s2 network --type "partner" interconnect-attachment get-bgp-auth-key 7b48cbc3-cacb-418e-908c-a9a86189a642
Value
0xXXXXXXXXXXXXXX
```

Regenerate service key:
```
➜  builds git:(VPC-4215/bgp_key) ✗ ./doctl --context new_s2 network --type "partner" interconnect-attachment regenerate-service-key 7b48cbc3-cacb-418e-908c-a9a86189a642



```


Patch:
diff --git a/commands/displayers/partner_interconnect_attachment.go b/commands/displayers/partner_interconnect_attachment.go
index ecdec432..ccd9b9cc 100644
--- a/commands/displayers/partner_interconnect_attachment.go
+++ b/commands/displayers/partner_interconnect_attachment.go
@@ -112,3 +112,57 @@ func (v *PartnerInterconnectAttachmentRoute) KV() []map[string]any {
 
 	return out
 }
+
+type PartnerInterconnectAttachmentRegenerateServiceKey struct {
+	RegenerateKey do.PartnerInterconnectAttachmentRegenerateServiceKey
+}
+
+var _ Displayable = &PartnerInterconnectAttachmentRegenerateServiceKey{}
+
+func (v *PartnerInterconnectAttachmentRegenerateServiceKey) JSON(out io.Writer) error {
+	return writeJSON(v.RegenerateKey, out)
+}
+
+func (v *PartnerInterconnectAttachmentRegenerateServiceKey) Cols() []string {
+	return []string{}
+}
+
+func (v *PartnerInterconnectAttachmentRegenerateServiceKey) ColMap() map[string]string {
+	return map[string]string{}
+}
+
+func (v *PartnerInterconnectAttachmentRegenerateServiceKey) KV() []map[string]any {
+	out := make([]map[string]any, 0, 1)
+
+	o := map[string]any{}
+	out = append(out, o)
+	return out
+}
+
+type PartnerInterconnectAttachmentBgpAuthKey struct {
+	Key do.PartnerInterconnectAttachmentBGPAuthKey
+}
+
+var _ Displayable = &PartnerInterconnectAttachmentBgpAuthKey{}
+
+func (v *PartnerInterconnectAttachmentBgpAuthKey) JSON(out io.Writer) error {
+	return writeJSON(v.Key, out)
+}
+
+func (v *PartnerInterconnectAttachmentBgpAuthKey) Cols() []string {
+	return []string{"Value"}
+}
+
+func (v *PartnerInterconnectAttachmentBgpAuthKey) ColMap() map[string]string {
+	return map[string]string{"Value": "Value"}
+}
+
+func (v *PartnerInterconnectAttachmentBgpAuthKey) KV() []map[string]any {
+	out := make([]map[string]any, 0, 1)
+
+	o := map[string]any{
+		"Value": v.Key.BgpAuthKey.Value,
+	}
+	out = append(out, o)
+	return out
+}
diff --git a/commands/partner_interconnect_attachment.go b/commands/partner_interconnect_attachment.go
index ac88f48e..2ce2a2fa 100644
--- a/commands/partner_interconnect_attachment.go
+++ b/commands/partner_interconnect_attachment.go
@@ -131,6 +131,20 @@ vpc-ids "270a76ed-1bb7-4c5d-a6a5-e863de086940"`
 	cmdPartnerIARouteList.Example = `The following example lists the Network Interconnect Attachments on your account :` +
 		` doctl network --type "partner" interconnect-attachment list-routes --format ID,Cidr `
 
+	cmdGetPartnerIARegenerateServiceKey := CmdBuilder(cmd, RunPartnerInterconnectAttachmentRegenerateServiceKey, "regenerate-service-key <interconnect-attachment-id>",
+		"Regenerates a Service key of Partner Interconnect Attachment", "Regenerates information about a Service key of Partner Interconnect Attachment", Writer,
+		aliasOpt("regen-service-key"), displayerType(&displayers.PartnerInterconnectAttachmentRegenerateServiceKey{}))
+	AddStringFlag(cmdGetPartnerIARegenerateServiceKey, doctl.ArgInterconnectAttachmentType, "", "partner", "Specify interconnect attachment type (e.g., partner)")
+	cmdGetPartnerIARegenerateServiceKey.Example = `The following example retrieves information about a Service key of Partner Interconnect Attachment with the ID ` + "`" + `f81d4fae-7dec-11d0-a765-00a0c91e6bf6` + "`" +
+		`: doctl network --type "partner" interconnect-attachment regenerate-service-key f81d4fae-7dec-11d0-a765-00a0c91e6bf6`
+
+	cmdGetPartnerIAGetBGPAuthKey := CmdBuilder(cmd, RunGetPartnerInterconnectAttachmentBGPAuthKey, "get-bgp-auth-key <interconnect-attachment-id>",
+		"Retrieves a BGP Auth key of Partner Interconnect Attachment", "Retrieves information about a BGP Auth key of Partner Interconnect Attachment", Writer,
+		aliasOpt("g-bgp-auth-key"), displayerType(&displayers.PartnerInterconnectAttachmentBgpAuthKey{}))
+	AddStringFlag(cmdGetPartnerIAGetBGPAuthKey, doctl.ArgInterconnectAttachmentType, "", "partner", "Specify interconnect attachment type (e.g., partner)")
+	cmdGetPartnerIAGetBGPAuthKey.Example = `The following example retrieves information about a Service key of Partner Interconnect Attachment with the ID ` + "`" + `f81d4fae-7dec-11d0-a765-00a0c91e6bf6` + "`" +
+		`: doctl network --type "partner" interconnect-attachment get-bgp-auth-key f81d4fae-7dec-11d0-a765-00a0c91e6bf6`
+
 	return cmd
 }
 
@@ -296,6 +310,56 @@ func RunPartnerInterconnectAttachmentUpdate(c *CmdConfig) error {
 	return c.Display(item)
 }
 
+// RunPartnerInterconnectAttachmentRegenerateServiceKey regenerates a service key of existing Partner Interconnect Attachment
+func RunPartnerInterconnectAttachmentRegenerateServiceKey(c *CmdConfig) error {
+
+	if err := ensurePartnerAttachmentType(c); err != nil {
+		return err
+	}
+
+	err := ensureOneArg(c)
+	if err != nil {
+		return err
+	}
+	iaID := c.Args[0]
+
+	pias := c.PartnerInterconnectAttachments()
+	regenerateServiceKey, err := pias.RegenerateServiceKey(iaID)
+	if err != nil {
+		return err
+	}
+
+	item := &displayers.PartnerInterconnectAttachmentRegenerateServiceKey{
+		RegenerateKey: *regenerateServiceKey,
+	}
+	return c.Display(item)
+}
+
+// RunGetPartnerInterconnectAttachmentBGPAuthKey get a bgp auth key of existing Partner Interconnect Attachment
+func RunGetPartnerInterconnectAttachmentBGPAuthKey(c *CmdConfig) error {
+
+	if err := ensurePartnerAttachmentType(c); err != nil {
+		return err
+	}
+
+	err := ensureOneArg(c)
+	if err != nil {
+		return err
+	}
+	iaID := c.Args[0]
+
+	pias := c.PartnerInterconnectAttachments()
+	bgpAuthKey, err := pias.GetBGPAuthKey(iaID)
+	if err != nil {
+		return err
+	}
+
+	item := &displayers.PartnerInterconnectAttachmentBgpAuthKey{
+		Key: *bgpAuthKey,
+	}
+	return c.Display(item)
+}
+
 // RunPartnerInterconnectAttachmentDelete deletes an existing Partner Interconnect Attachment by its identifier.
 func RunPartnerInterconnectAttachmentDelete(c *CmdConfig) error {
 
diff --git a/do/mocks/PartnerInterconnectAttachmentsService.go b/do/mocks/PartnerInterconnectAttachmentsService.go
index 404c5d33..ee717d86 100644
--- a/do/mocks/PartnerInterconnectAttachmentsService.go
+++ b/do/mocks/PartnerInterconnectAttachmentsService.go
@@ -70,6 +70,21 @@ func (mr *MockPartnerInterconnectAttachmentsServiceMockRecorder) DeletePartnerIn
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePartnerInterconnectAttachment", reflect.TypeOf((*MockPartnerInterconnectAttachmentsService)(nil).DeletePartnerInterconnectAttachment), iaID)
 }
 
+// GetBGPAuthKey mocks base method.
+func (m *MockPartnerInterconnectAttachmentsService) GetBGPAuthKey(iaID string) (*do.PartnerInterconnectAttachmentBGPAuthKey, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "GetBGPAuthKey", iaID)
+	ret0, _ := ret[0].(*do.PartnerInterconnectAttachmentBGPAuthKey)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// GetBGPAuthKey indicates an expected call of GetBGPAuthKey.
+func (mr *MockPartnerInterconnectAttachmentsServiceMockRecorder) GetBGPAuthKey(iaID any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetBGPAuthKey", reflect.TypeOf((*MockPartnerInterconnectAttachmentsService)(nil).GetBGPAuthKey), iaID)
+}
+
 // GetPartnerInterconnectAttachment mocks base method.
 func (m *MockPartnerInterconnectAttachmentsService) GetPartnerInterconnectAttachment(iaID string) (*do.PartnerInterconnectAttachment, error) {
 	m.ctrl.T.Helper()
@@ -115,6 +130,21 @@ func (mr *MockPartnerInterconnectAttachmentsServiceMockRecorder) ListPartnerInte
 	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListPartnerInterconnectAttachments", reflect.TypeOf((*MockPartnerInterconnectAttachmentsService)(nil).ListPartnerInterconnectAttachments))
 }
 
+// RegenerateServiceKey mocks base method.
+func (m *MockPartnerInterconnectAttachmentsService) RegenerateServiceKey(iaID string) (*do.PartnerInterconnectAttachmentRegenerateServiceKey, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "RegenerateServiceKey", iaID)
+	ret0, _ := ret[0].(*do.PartnerInterconnectAttachmentRegenerateServiceKey)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// RegenerateServiceKey indicates an expected call of RegenerateServiceKey.
+func (mr *MockPartnerInterconnectAttachmentsServiceMockRecorder) RegenerateServiceKey(iaID any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegenerateServiceKey", reflect.TypeOf((*MockPartnerInterconnectAttachmentsService)(nil).RegenerateServiceKey), iaID)
+}
+
 // UpdatePartnerInterconnectAttachment mocks base method.
 func (m *MockPartnerInterconnectAttachmentsService) UpdatePartnerInterconnectAttachment(iaID string, req *godo.PartnerInterconnectAttachmentUpdateRequest) (*do.PartnerInterconnectAttachment, error) {
 	m.ctrl.T.Helper()
diff --git a/do/partner_interconnect_attachments.go b/do/partner_interconnect_attachments.go
index 2972205a..76fc8e0e 100644
--- a/do/partner_interconnect_attachments.go
+++ b/do/partner_interconnect_attachments.go
@@ -35,6 +35,16 @@ type PartnerInterconnectAttachmentRoute struct {
 // PartnerInterconnectAttachmentRoutes is a slice of PartnerInterconnectAttachmentRoute.
 type PartnerInterconnectAttachmentRoutes []PartnerInterconnectAttachmentRoute
 
+// PartnerInterconnectAttachmentBGPAuthKey wrap a godo BgpAuthKey.
+type PartnerInterconnectAttachmentBGPAuthKey struct {
+	*godo.BgpAuthKey
+}
+
+// PartnerInterconnectAttachmentRegenerateServiceKey wraps a godo ServiceKey.
+type PartnerInterconnectAttachmentRegenerateServiceKey struct {
+	*godo.RegenerateServiceKey
+}
+
 // PartnerInterconnectAttachmentsService is an interface for interacting with
 // DigitalOcean's partner interconnect attachments api.
 type PartnerInterconnectAttachmentsService interface {
@@ -44,6 +54,8 @@ type PartnerInterconnectAttachmentsService interface {
 	DeletePartnerInterconnectAttachment(iaID string) error
 	UpdatePartnerInterconnectAttachment(iaID string, req *godo.PartnerInterconnectAttachmentUpdateRequest) (*PartnerInterconnectAttachment, error)
 	ListPartnerInterconnectAttachmentRoutes(iaID string) (PartnerInterconnectAttachmentRoutes, error)
+	GetBGPAuthKey(iaID string) (*PartnerInterconnectAttachmentBGPAuthKey, error)
+	RegenerateServiceKey(iaID string) (*PartnerInterconnectAttachmentRegenerateServiceKey, error)
 }
 
 var _ PartnerInterconnectAttachmentsService = &partnerInterconnectAttachmentsService{}
@@ -150,3 +162,19 @@ func (p *partnerInterconnectAttachmentsService) ListPartnerInterconnectAttachmen
 
 	return list, nil
 }
+
+func (p *partnerInterconnectAttachmentsService) GetBGPAuthKey(iaID string) (*PartnerInterconnectAttachmentBGPAuthKey, error) {
+	bgpAuthKey, _, err := p.client.PartnerInterconnectAttachments.GetBGPAuthKey(context.TODO(), iaID)
+	if err != nil {
+		return nil, err
+	}
+	return &PartnerInterconnectAttachmentBGPAuthKey{BgpAuthKey: bgpAuthKey}, nil
+}
+
+func (p *partnerInterconnectAttachmentsService) RegenerateServiceKey(iaID string) (*PartnerInterconnectAttachmentRegenerateServiceKey, error) {
+	regenerateServiceKey, _, err := p.client.PartnerInterconnectAttachments.RegenerateServiceKey(context.TODO(), iaID)
+	if err != nil {
+		return nil, err
+	}
+	return &PartnerInterconnectAttachmentRegenerateServiceKey{RegenerateServiceKey: regenerateServiceKey}, nil
+}
diff --git a/go.mod b/go.mod
index 695e7f93..5609caab 100644
--- a/go.mod
+++ b/go.mod
@@ -5,7 +5,7 @@ go 1.22
 require (
 	github.com/blang/semver v3.5.1+incompatible
 	github.com/creack/pty v1.1.21
-	github.com/digitalocean/godo v1.136.0
+	github.com/digitalocean/godo v1.138.0
 	github.com/docker/cli v24.0.5+incompatible
 	github.com/docker/docker v25.0.6+incompatible
 	github.com/docker/docker-credential-helpers v0.7.0 // indirect
diff --git a/go.sum b/go.sum
index 298c4ec2..9dc765c7 100644
--- a/go.sum
+++ b/go.sum
@@ -91,8 +91,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.136.0 h1:DTxugljFJSMBPfEGq4KeXpnKeAHicggNqogcrw/YdZw=
-github.com/digitalocean/godo v1.136.0/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc=
+github.com/digitalocean/godo v1.138.0 h1:0l0UEaVyNKvEmrn4r5NkZCpDysimJjVhLj30Gl6HgiQ=
+github.com/digitalocean/godo v1.138.0/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc=
 github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
 github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
 github.com/docker/cli v24.0.5+incompatible h1:WeBimjvS0eKdH4Ygx+ihVq1Q++xg36M/rMi4aXAvodc=
diff --git a/vendor/github.com/digitalocean/godo/CHANGELOG.md b/vendor/github.com/digitalocean/godo/CHANGELOG.md
index 8f926be6..6d689226 100644
--- a/vendor/github.com/digitalocean/godo/CHANGELOG.md
+++ b/vendor/github.com/digitalocean/godo/CHANGELOG.md
@@ -1,5 +1,17 @@
 # Change Log
 
+## [v1.138.0] - 2025-03-18
+
+- #785 - @guptado - Support partner interconnect GetBgpAuthKey and RegenerateServiceKey operations
+- #787 - @andrewsomething - ci: upgrade to actions/cache@v4
+- #786 - @m3co-code - add flags for doks routing-agent plugin
+- #784 - @asaha2 - Support name and id filters for list op
+
+## [v1.137.0] - 2025-02-12
+
+- #782 - @apinonformoso - fix partner interconnect json tag
+- #781 - @dylanrhysscott - CON-11810 Implement GetNodePoolTemplate endpoint for DOKS godo client
+
 ## [v1.136.0] - 2025-01-28
 
 - #776 - @danaelhe - Databases: Support online-migrations
diff --git a/vendor/github.com/digitalocean/godo/godo.go b/vendor/github.com/digitalocean/godo/godo.go
index ff4c04e1..db841d5c 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.136.0"
+	libraryVersion = "1.138.0"
 	defaultBaseURL = "https://api.digitalocean.com/"
 	userAgent      = "godo/" + libraryVersion
 	mediaType      = "application/json"
diff --git a/vendor/github.com/digitalocean/godo/kubernetes.go b/vendor/github.com/digitalocean/godo/kubernetes.go
index 8353e78c..4a1530aa 100644
--- a/vendor/github.com/digitalocean/godo/kubernetes.go
+++ b/vendor/github.com/digitalocean/godo/kubernetes.go
@@ -40,6 +40,7 @@ type KubernetesService interface {
 
 	CreateNodePool(ctx context.Context, clusterID string, req *KubernetesNodePoolCreateRequest) (*KubernetesNodePool, *Response, error)
 	GetNodePool(ctx context.Context, clusterID, poolID string) (*KubernetesNodePool, *Response, error)
+	GetNodePoolTemplate(ctx context.Context, clusterID string, nodePoolName string) (*KubernetesNodePoolTemplateResponse, *Response, error)
 	ListNodePools(ctx context.Context, clusterID string, opts *ListOptions) ([]*KubernetesNodePool, *Response, error)
 	UpdateNodePool(ctx context.Context, clusterID, poolID string, req *KubernetesNodePoolUpdateRequest) (*KubernetesNodePool, *Response, error)
 	// RecycleNodePoolNodes is DEPRECATED please use DeleteNode
@@ -83,6 +84,7 @@ type KubernetesClusterCreateRequest struct {
 	SurgeUpgrade                   bool                                      `json:"surge_upgrade"`
 	ControlPlaneFirewall           *KubernetesControlPlaneFirewall           `json:"control_plane_firewall,omitempty"`
 	ClusterAutoscalerConfiguration *KubernetesClusterAutoscalerConfiguration `json:"cluster_autoscaler_configuration,omitempty"`
+	RoutingAgent                   *KubernetesRoutingAgent                   `json:"routing_agent,omitempty"`
 }
 
 // KubernetesClusterUpdateRequest represents a request to update a Kubernetes cluster.
@@ -94,6 +96,7 @@ type KubernetesClusterUpdateRequest struct {
 	SurgeUpgrade                   bool                                      `json:"surge_upgrade,omitempty"`
 	ControlPlaneFirewall           *KubernetesControlPlaneFirewall           `json:"control_plane_firewall,omitempty"`
 	ClusterAutoscalerConfiguration *KubernetesClusterAutoscalerConfiguration `json:"cluster_autoscaler_configuration,omitempty"`
+	RoutingAgent                   *KubernetesRoutingAgent                   `json:"routing_agent,omitempty"`
 
 	// Convert cluster to run highly available control plane
 	HA *bool `json:"ha,omitempty"`
@@ -213,6 +216,7 @@ type KubernetesCluster struct {
 	RegistryEnabled                bool                                      `json:"registry_enabled,omitempty"`
 	ControlPlaneFirewall           *KubernetesControlPlaneFirewall           `json:"control_plane_firewall,omitempty"`
 	ClusterAutoscalerConfiguration *KubernetesClusterAutoscalerConfiguration `json:"cluster_autoscaler_configuration,omitempty"`
+	RoutingAgent                   *KubernetesRoutingAgent                   `json:"routing_agent,omitempty"`
 
 	Status    *KubernetesClusterStatus `json:"status,omitempty"`
 	CreatedAt time.Time                `json:"created_at,omitempty"`
@@ -255,6 +259,11 @@ type KubernetesControlPlaneFirewall struct {
 	AllowedAddresses []string `json:"allowed_addresses"`
 }
 
+// KubernetesRoutingAgent represents information about the routing-agent cluster plugin.
+type KubernetesRoutingAgent struct {
+	Enabled *bool `json:"enabled"`
+}
+
 // KubernetesClusterAutoscalerConfiguration represents Kubernetes cluster autoscaler configuration.
 type KubernetesClusterAutoscalerConfiguration struct {
 	ScaleDownUtilizationThreshold *float64 `json:"scale_down_utilization_threshold"`
@@ -426,6 +435,30 @@ type KubernetesNodePool struct {
 	Nodes []*KubernetesNode `json:"nodes,omitempty"`
 }
 
+// KubernetesNodePool represents a node pool template response from the node template endpoint
+type KubernetesNodePoolTemplateResponse struct {
+	ClusterUUID string                      `json:"cluster_uuid,omitempty"`
+	Name        string                      `json:"name,omitempty"`
+	Slug        string                      `json:"slug,omitempty"`
+	Template    *KubernetesNodePoolTemplate `json:"template,omitempty"`
+}
+
+// KubernetesNodePool represents the node pool template data for a given pool.
+type KubernetesNodePoolTemplate struct {
+	Labels      map[string]string            `json:"labels,omitempty"`
+	Capacity    *KubernetesNodePoolResources `json:"capacity,omitempty"`
+	Allocatable *KubernetesNodePoolResources `json:"allocatable,omitempty"`
+}
+
+// KubernetesNodePoolResources represents the resources within a given template for a node pool
+// This follows https://pkg.go.dev/k8s.io/kubernetes@v1.32.1/pkg/scheduler/framework#Resource to represent
+// node resources within the node object.
+type KubernetesNodePoolResources struct {
+	CPU    int64  `json:"cpu,omitempty"`
+	Memory string `json:"memory,omitempty"`
+	Pods   int64  `json:"pods,omitempty"`
+}
+
 // KubernetesNode represents a Node in a node pool in a Kubernetes cluster.
 type KubernetesNode struct {
 	ID        string                `json:"id,omitempty"`
@@ -804,6 +837,24 @@ func (svc *KubernetesServiceOp) GetNodePool(ctx context.Context, clusterID, pool
 	return root.NodePool, resp, nil
 }
 
+// GetNodePoolTemplate retrieves the template used for a given node pool to scale up from zero.
+func (svc *KubernetesServiceOp) GetNodePoolTemplate(ctx context.Context, clusterID string, nodePoolName string) (*KubernetesNodePoolTemplateResponse, *Response, error) {
+	path, err := url.JoinPath(kubernetesClustersPath, clusterID, "node_pools_template", nodePoolName)
+	if err != nil {
+		return nil, nil, err
+	}
+	req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+	root := new(KubernetesNodePoolTemplateResponse)
+	resp, err := svc.client.Do(ctx, req, root)
+	if err != nil {
+		return nil, resp, err
+	}
+	return root, resp, nil
+}
+
 // ListNodePools lists all the node pools found in a Kubernetes cluster.
 func (svc *KubernetesServiceOp) ListNodePools(ctx context.Context, clusterID string, opts *ListOptions) ([]*KubernetesNodePool, *Response, error) {
 	path := fmt.Sprintf("%s/%s/node_pools", kubernetesClustersPath, clusterID)
diff --git a/vendor/github.com/digitalocean/godo/load_balancers.go b/vendor/github.com/digitalocean/godo/load_balancers.go
index 54bec0c4..a1b833b6 100644
--- a/vendor/github.com/digitalocean/godo/load_balancers.go
+++ b/vendor/github.com/digitalocean/godo/load_balancers.go
@@ -33,6 +33,8 @@ const (
 type LoadBalancersService interface {
 	Get(context.Context, string) (*LoadBalancer, *Response, error)
 	List(context.Context, *ListOptions) ([]LoadBalancer, *Response, error)
+	ListByNames(context.Context, []string, *ListOptions) ([]LoadBalancer, *Response, error)
+	ListByUUIDs(context.Context, []string, *ListOptions) ([]LoadBalancer, *Response, error)
 	Create(context.Context, *LoadBalancerRequest) (*LoadBalancer, *Response, error)
 	Update(ctx context.Context, lbID string, lbr *LoadBalancerRequest) (*LoadBalancer, *Response, error)
 	Delete(ctx context.Context, lbID string) (*Response, error)
@@ -403,6 +405,72 @@ func (l *LoadBalancersServiceOp) List(ctx context.Context, opt *ListOptions) ([]
 	return root.LoadBalancers, resp, err
 }
 
+// ListByNames lists load balancers filtered by resource names, with optional pagination.
+func (l *LoadBalancersServiceOp) ListByNames(ctx context.Context, names []string, opt *ListOptions) ([]LoadBalancer, *Response, error) {
+	path, err := addOptions(loadBalancersBasePath, opt)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	req, err := l.client.NewRequest(ctx, http.MethodGet, path, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	q := req.URL.Query()
+	for _, name := range names {
+		q.Add("names", name)
+	}
+	req.URL.RawQuery = q.Encode()
+
+	root := new(loadBalancersRoot)
+	resp, err := l.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.LoadBalancers, resp, err
+}
+
+// ListByUUIDs lists load balancers filtered by resource UUIDs, with optional pagination.
+func (l *LoadBalancersServiceOp) ListByUUIDs(ctx context.Context, uuids []string, opt *ListOptions) ([]LoadBalancer, *Response, error) {
+	path, err := addOptions(loadBalancersBasePath, opt)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	req, err := l.client.NewRequest(ctx, http.MethodGet, path, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	q := req.URL.Query()
+	for _, uuid := range uuids {
+		q.Add("uuids", uuid)
+	}
+	req.URL.RawQuery = q.Encode()
+
+	root := new(loadBalancersRoot)
+	resp, err := l.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.LoadBalancers, resp, err
+}
+
 // Create a new load balancer with a given configuration.
 func (l *LoadBalancersServiceOp) Create(ctx context.Context, lbr *LoadBalancerRequest) (*LoadBalancer, *Response, error) {
 	req, err := l.client.NewRequest(ctx, http.MethodPost, loadBalancersBasePath, lbr)
diff --git a/vendor/github.com/digitalocean/godo/partner_interconnect_attachments.go b/vendor/github.com/digitalocean/godo/partner_interconnect_attachments.go
index df21c929..e2707dca 100644
--- a/vendor/github.com/digitalocean/godo/partner_interconnect_attachments.go
+++ b/vendor/github.com/digitalocean/godo/partner_interconnect_attachments.go
@@ -21,6 +21,8 @@ type PartnerInterconnectAttachmentsService interface {
 	GetServiceKey(context.Context, string) (*ServiceKey, *Response, error)
 	SetRoutes(context.Context, string, *PartnerInterconnectAttachmentSetRoutesRequest) (*PartnerInterconnectAttachment, *Response, error)
 	ListRoutes(context.Context, string, *ListOptions) ([]*RemoteRoute, *Response, error)
+	GetBGPAuthKey(ctx context.Context, iaID string) (*BgpAuthKey, *Response, error)
+	RegenerateServiceKey(ctx context.Context, iaID string) (*RegenerateServiceKey, *Response, error)
 }
 
 var _ PartnerInterconnectAttachmentsService = &PartnerInterconnectAttachmentsServiceOp{}
@@ -97,7 +99,7 @@ type BGP struct {
 	// LocalRouterIP is the local router IP
 	LocalRouterIP string `json:"local_router_ip,omitempty"`
 	// PeerASN is the peer ASN
-	PeerASN int `json:"peer_asn,omitempty"`
+	PeerASN int `json:"peer_router_asn,omitempty"`
 	// PeerRouterIP is the peer router IP
 	PeerRouterIP string `json:"peer_router_ip,omitempty"`
 }
@@ -105,6 +107,7 @@ type BGP struct {
 // ServiceKey represents the service key of a Partner Interconnect Attachment.
 type ServiceKey struct {
 	ServiceKey string `json:"service_key,omitempty"`
+	State      string `json:"state,omitempty"`
 }
 
 // RemoteRoute represents a route for a Partner Interconnect Attachment.
@@ -157,6 +160,21 @@ type remoteRoutesRoot struct {
 	Meta         *Meta          `json:"meta"`
 }
 
+type BgpAuthKey struct {
+	Value string `json:"value"`
+}
+
+type bgpAuthKeyRoot struct {
+	BgpAuthKey *BgpAuthKey `json:"bgp_auth_key"`
+}
+
+type RegenerateServiceKey struct {
+}
+
+type regenerateServiceKeyRoot struct {
+	RegenerateServiceKey *RegenerateServiceKey `json:"-"`
+}
+
 // List returns a list of all Partner Interconnect Attachments, with optional pagination.
 func (s *PartnerInterconnectAttachmentsServiceOp) List(ctx context.Context, opt *ListOptions) ([]*PartnerInterconnectAttachment, *Response, error) {
 	path, err := addOptions(partnerInterconnectAttachmentsBasePath, opt)
@@ -308,3 +326,37 @@ func (s *PartnerInterconnectAttachmentsServiceOp) SetRoutes(ctx context.Context,
 
 	return root.PartnerInterconnectAttachment, resp, nil
 }
+
+// GetBGPAuthKey returns Partner Interconnect Attachment bgp auth key
+func (s *PartnerInterconnectAttachmentsServiceOp) GetBGPAuthKey(ctx context.Context, iaID string) (*BgpAuthKey, *Response, error) {
+	path := fmt.Sprintf("%s/%s/bgp_auth_key", partnerInterconnectAttachmentsBasePath, iaID)
+	req, err := s.client.NewRequest(ctx, http.MethodGet, path, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	root := new(bgpAuthKeyRoot)
+	resp, err := s.client.Do(ctx, req, root)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return root.BgpAuthKey, resp, nil
+}
+
+// RegenerateServiceKey regenerates the service key of a Partner Interconnect Attachment.
+func (s *PartnerInterconnectAttachmentsServiceOp) RegenerateServiceKey(ctx context.Context, iaID string) (*RegenerateServiceKey, *Response, error) {
+	path := fmt.Sprintf("%s/%s/service_key", partnerInterconnectAttachmentsBasePath, iaID)
+	req, err := s.client.NewRequest(ctx, http.MethodPost, path, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	root := new(regenerateServiceKeyRoot)
+	resp, err := s.client.Do(ctx, req, root)
+	if err != nil {
+		return nil, resp, err
+	}
+
+	return root.RegenerateServiceKey, resp, nil
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 4f4af180..0147b4cd 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -61,7 +61,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.136.0
+# github.com/digitalocean/godo v1.138.0
 ## explicit; go 1.22
 github.com/digitalocean/godo
 github.com/digitalocean/godo/metrics


Test patch:
diff --git a/commands/partner_interconnect_attachment_test.go b/commands/partner_interconnect_attachment_test.go
index 97de2c9a..5d8e4a1f 100644
--- a/commands/partner_interconnect_attachment_test.go
+++ b/commands/partner_interconnect_attachment_test.go
@@ -5,10 +5,11 @@ import (
 	"testing"
 	"time"
 
-	"github.com/digitalocean/doctl"
-	"github.com/digitalocean/doctl/do"
 	"github.com/digitalocean/godo"
 	"github.com/stretchr/testify/assert"
+
+	"github.com/digitalocean/doctl"
+	"github.com/digitalocean/doctl/do"
 )
 
 var (
@@ -39,13 +40,23 @@ var (
 	testPartnerIARouteList = do.PartnerInterconnectAttachmentRoutes{
 		testPartnerAttachmentRoute,
 	}
+
+	testRegenerateServiceKey = do.PartnerInterconnectAttachmentRegenerateServiceKey{
+		RegenerateServiceKey: &godo.RegenerateServiceKey{},
+	}
+
+	testBGPAuthKey = do.PartnerInterconnectAttachmentBGPAuthKey{
+		BgpAuthKey: &godo.BgpAuthKey{
+			Value: "test-bgp-auth-key",
+		},
+	}
 )
 
 func TestPartnerInterconnectAttachmentsCommand(t *testing.T) {
 	cmd := PartnerInterconnectAttachments()
 	assert.NotNil(t, cmd)
 
-	assertCommandNames(t, cmd, "create", "get", "list", "delete", "update", "list-routes")
+	assertCommandNames(t, cmd, "create", "get", "list", "delete", "update", "list-routes", "regenerate-service-key", "get-bgp-auth-key")
 }
 
 func TestPartnerInterconnectAttachmentCreate(t *testing.T) {
@@ -174,3 +185,31 @@ func TestInterconnectAttachmentRoutesList(t *testing.T) {
 		assert.NoError(t, err)
 	})
 }
+
+func TestInterconnectAttachmentsRegenerateServiceKey(t *testing.T) {
+	withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
+		config.Doit.Set(config.NS, doctl.ArgInterconnectAttachmentType, "partner")
+
+		iaID := "e819b321-a9a1-4078-b437-8e6b8bf13530"
+		tm.partnerInterconnectAttachment.EXPECT().RegenerateServiceKey(iaID).Return(&testRegenerateServiceKey, nil)
+
+		config.Args = append(config.Args, iaID)
+
+		err := RunPartnerInterconnectAttachmentRegenerateServiceKey(config)
+		assert.NoError(t, err)
+	})
+}
+
+func TestInterconnectAttachmentsBgpAuthKey(t *testing.T) {
+	withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
+		config.Doit.Set(config.NS, doctl.ArgInterconnectAttachmentType, "partner")
+
+		iaID := "e819b321-a9a1-4078-b437-8e6b8bf13530"
+		tm.partnerInterconnectAttachment.EXPECT().GetBGPAuthKey(iaID).Return(&testBGPAuthKey, nil)
+
+		config.Args = append(config.Args, iaID)
+
+		err := RunGetPartnerInterconnectAttachmentBGPAuthKey(config)
+		assert.NoError(t, err)
+	})
+}
diff --git a/integration/partner_interconnect_attachment_test.go b/integration/partner_interconnect_attachment_test.go
index ba63e8bd..50e74440 100644
--- a/integration/partner_interconnect_attachment_test.go
+++ b/integration/partner_interconnect_attachment_test.go
@@ -68,6 +68,20 @@ a0eb6eb0-fa38-41a8-a5de-1a75524667fe    169.250.0.0/29
   }
 }
 `
+
+	interconnectRegenerateServiceKeyOutput   = ``
+	interconnectRegenerateServiceKeyResponse = `{}`
+
+	interconnectGetBgpAuthKeyOutput = `
+Value
+test-bgp-auth-key
+	`
+	interconnectGetBgpAuthKeyResponse = `
+{
+	"bgp_auth_key": {
+		"value": "test-bgp-auth-key"
+	}
+}`
 )
 
 var _ = suite("partner_interconnect_attachments/create", func(t *testing.T, when spec.G, it spec.S) {
@@ -216,3 +230,128 @@ var _ = suite("partner_interconnect_attachments/list-routes", func(t *testing.T,
 		})
 	})
 })
+
+var _ = suite("partner_interconnect_attachments/regenerate-service-key", func(t *testing.T, when spec.G, it spec.S) {
+	var (
+		expect *require.Assertions
+		server *httptest.Server
+	)
+
+	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/partner_interconnect/attachments/c5537207-ebf0-47cb-bc10-6fac717cd672/service_key":
+				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
+				}
+
+				w.Write([]byte(interconnectRegenerateServiceKeyResponse))
+			default:
+				dump, err := httputil.DumpRequest(req, true)
+				if err != nil {
+					t.Fatal("failed to dump request")
+				}
+
+				t.Fatalf("received unknown request: %s", dump)
+			}
+		}))
+	})
+
+	when("no flags are passed", func() {
+		it("gets the specified service key", func() {
+			cmd := exec.Command(builtBinaryPath,
+				"-t", "some-magic-token",
+				"-u", server.URL,
+				"network",
+				"interconnect-attachment",
+				"regenerate-service-key",
+				"c5537207-ebf0-47cb-bc10-6fac717cd672",
+			)
+
+			output, err := cmd.CombinedOutput()
+			expect.NoError(err, fmt.Sprintf("received error output: %s", output))
+			expect.Equal(strings.TrimSpace(interconnectRegenerateServiceKeyOutput), strings.TrimSpace(string(output)))
+		})
+	})
+})
+
+var _ = suite("partner_interconnect_attachments/get-bgp-auth-key", func(t *testing.T, when spec.G, it spec.S) {
+	var (
+		expect *require.Assertions
+		server *httptest.Server
+	)
+
+	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/partner_interconnect/attachments/c5537207-ebf0-47cb-bc10-6fac717cd672/bgp_auth_key":
+				auth := req.Header.Get("Authorization")
+				if auth != "Bearer some-magic-token" {
+					w.WriteHeader(http.StatusUnauthorized)
+					return
+				}
+
+				if req.Method != http.MethodGet {
+					w.WriteHeader(http.StatusMethodNotAllowed)
+					return
+				}
+
+				w.Write([]byte(interconnectGetBgpAuthKeyResponse))
+			default:
+				dump, err := httputil.DumpRequest(req, true)
+				if err != nil {
+					t.Fatal("failed to dump request")
+				}
+
+				t.Fatalf("received unknown request: %s", dump)
+			}
+		}))
+	})
+
+	when("no flags are passed", func() {
+		it("gets the specified bgp auth key", func() {
+			cmd := exec.Command(builtBinaryPath,
+				"-t", "some-magic-token",
+				"-u", server.URL,
+				"network",
+				"interconnect-attachment",
+				"get-bgp-auth-key",
+				"c5537207-ebf0-47cb-bc10-6fac717cd672",
+			)
+
+			output, err := cmd.CombinedOutput()
+			expect.NoError(err, fmt.Sprintf("received error output: %s", output))
+			expect.Equal(strings.TrimSpace(interconnectGetBgpAuthKeyOutput), strings.TrimSpace(string(output)))
+		})
+	})
+
+	when("format and no-header flags are passed", func() {
+		it("gets the specified bgp auth key", func() {
+			cmd := exec.Command(builtBinaryPath,
+				"-t", "some-magic-token",
+				"-u", server.URL,
+				"network",
+				"interconnect-attachment",
+				"get-bgp-auth-key",
+				"--format", "Value",
+				"--no-header",
+				"c5537207-ebf0-47cb-bc10-6fac717cd672",
+			)
+
+			output, err := cmd.CombinedOutput()
+			expect.NoError(err, fmt.Sprintf("received error output: %s", output))
+			expect.Equal("test-bgp-auth-key", strings.TrimSpace(string(output)))
+		})
+	})
+})