Repository: digitalocean/doctl
Pull request #1646 (2025-01-27 13:49:40)

create partner interconnect attachment support
#### Verification

```sh
doctl network --type "partner" interconnect-attachment create --name "doctl-pia" --connection-bandwidth-in-mbps 50 --naas-provider "MEGAPORT" --region "nyc" --vpc-ids "d35e5cb7-7957-xxxx-xxxx-xxxx"
```

![image](https://github.com/user-attachments/assets/b4f56f0f-7d8e-49a9-bf85-eb059304f8a7)





loosla: Notes: 
[Related godo PR](https://github.com/digitalocean/godo/pull/769/files)
[PR that makes BGP field a pointer](https://github.com/digitalocean/godo/pull/777)

Patch:
diff --git a/args.go b/args.go
index 219cf5d4..a66aa8df 100644
--- a/args.go
+++ b/args.go
@@ -523,6 +523,27 @@ const (
 	// ArgVPCPeeringVPCID is id of the VPC.
 	ArgVPCPeeringVPCID = "vpc-id"
 
+	// ArgInterconnectAttachmentType is the type of the Interconnect Attachment e.g. "partner".
+	ArgInterconnectAttachmentType = "type"
+	// ArgPartnerInterconnectAttachmentName is a name of the Partner Interconnect Attachment.
+	ArgPartnerInterconnectAttachmentName = "name"
+	// ArgPartnerInterconnectAttachmentConnectionBandwidthInMbps is the connection bandwidth in megabits per second.
+	ArgPartnerInterconnectAttachmentConnectionBandwidthInMbps = "connection-bandwidth-in-mbps"
+	// ArgPartnerInterconnectAttachmentRegion is the region slug.
+	ArgPartnerInterconnectAttachmentRegion = "region"
+	// ArgPartnerInterconnectAttachmentNaaSProvider is the name of the Network as a Service provider
+	ArgPartnerInterconnectAttachmentNaaSProvider = "naas-provider"
+	// ArgPartnerInterconnectAttachmentVPCIDs are the IDs of the VPCs which the Partner Interconnect Attachment is connected
+	ArgPartnerInterconnectAttachmentVPCIDs = "vpc-ids"
+	// ArgPartnerInterconnectAttachmentBGPLocalASN is the BGP Autonomous System Number (ASN) of the local device
+	ArgPartnerInterconnectAttachmentBGPLocalASN = "bgp-local-asn"
+	// ArgPartnerInterconnectAttachmentBGPLocalRouterIP is the BGP IP address of the local device
+	ArgPartnerInterconnectAttachmentBGPLocalRouterIP = "bgp-local-router-ip"
+	// ArgPartnerInterconnectAttachmentBGPPeerASN is the BGP Autonomous System Number (ASN) of the peer device
+	ArgPartnerInterconnectAttachmentBGPPeerASN = "bgp-peer-asn"
+	// ArgPartnerInterconnectAttachmentBGPPeerIPAddress is the BGP IP address of the peer device
+	ArgPartnerInterconnectAttachmentBGPPeerRouterIP = "bgp-peer-router-ip"
+
 	// ArgReadWrite indicates a generated token should be read/write.
 	ArgReadWrite = "read-write"
 	// ArgRegistry indicates the name of the registry.
diff --git a/commands/command_config.go b/commands/command_config.go
index c0e6bfdc..14bd8e98 100644
--- a/commands/command_config.go
+++ b/commands/command_config.go
@@ -38,42 +38,43 @@ type CmdConfig struct {
 	componentBuilderFactory builder.ComponentBuilderFactory
 
 	// services
-	Keys              func() do.KeysService
-	Sizes             func() do.SizesService
-	Regions           func() do.RegionsService
-	Images            func() do.ImagesService
-	ImageActions      func() do.ImageActionsService
-	LoadBalancers     func() do.LoadBalancersService
-	ReservedIPs       func() do.ReservedIPsService
-	ReservedIPActions func() do.ReservedIPActionsService
-	ReservedIPv6s     func() do.ReservedIPv6sService
-	Droplets          func() do.DropletsService
-	DropletActions    func() do.DropletActionsService
-	DropletAutoscale  func() do.DropletAutoscaleService
-	Domains           func() do.DomainsService
-	Actions           func() do.ActionsService
-	Account           func() do.AccountService
-	Balance           func() do.BalanceService
-	BillingHistory    func() do.BillingHistoryService
-	Invoices          func() do.InvoicesService
-	Tags              func() do.TagsService
-	UptimeChecks      func() do.UptimeChecksService
-	Volumes           func() do.VolumesService
-	VolumeActions     func() do.VolumeActionsService
-	Snapshots         func() do.SnapshotsService
-	Certificates      func() do.CertificatesService
-	Firewalls         func() do.FirewallsService
-	CDNs              func() do.CDNsService
-	Projects          func() do.ProjectsService
-	Kubernetes        func() do.KubernetesService
-	Databases         func() do.DatabasesService
-	Registry          func() do.RegistryService
-	VPCs              func() do.VPCsService
-	OneClicks         func() do.OneClickService
-	Apps              func() do.AppsService
-	Monitoring        func() do.MonitoringService
-	Serverless        func() do.ServerlessService
-	OAuth             func() do.OAuthService
+	Keys                           func() do.KeysService
+	Sizes                          func() do.SizesService
+	Regions                        func() do.RegionsService
+	Images                         func() do.ImagesService
+	ImageActions                   func() do.ImageActionsService
+	LoadBalancers                  func() do.LoadBalancersService
+	ReservedIPs                    func() do.ReservedIPsService
+	ReservedIPActions              func() do.ReservedIPActionsService
+	ReservedIPv6s                  func() do.ReservedIPv6sService
+	Droplets                       func() do.DropletsService
+	DropletActions                 func() do.DropletActionsService
+	DropletAutoscale               func() do.DropletAutoscaleService
+	Domains                        func() do.DomainsService
+	Actions                        func() do.ActionsService
+	Account                        func() do.AccountService
+	Balance                        func() do.BalanceService
+	BillingHistory                 func() do.BillingHistoryService
+	Invoices                       func() do.InvoicesService
+	Tags                           func() do.TagsService
+	UptimeChecks                   func() do.UptimeChecksService
+	Volumes                        func() do.VolumesService
+	VolumeActions                  func() do.VolumeActionsService
+	Snapshots                      func() do.SnapshotsService
+	Certificates                   func() do.CertificatesService
+	Firewalls                      func() do.FirewallsService
+	CDNs                           func() do.CDNsService
+	Projects                       func() do.ProjectsService
+	Kubernetes                     func() do.KubernetesService
+	Databases                      func() do.DatabasesService
+	Registry                       func() do.RegistryService
+	VPCs                           func() do.VPCsService
+	OneClicks                      func() do.OneClickService
+	Apps                           func() do.AppsService
+	Monitoring                     func() do.MonitoringService
+	Serverless                     func() do.ServerlessService
+	OAuth                          func() do.OAuthService
+	PartnerInterconnectAttachments func() do.PartnerInterconnectAttachmentsService
 }
 
 // NewCmdConfig creates an instance of a CmdConfig.
@@ -130,6 +131,9 @@ func NewCmdConfig(ns string, dc doctl.Config, out io.Writer, args []string, init
 				return do.NewServerlessService(godoClient, getServerlessDirectory(), accessToken)
 			}
 			c.OAuth = func() do.OAuthService { return do.NewOAuthService(godoClient) }
+			c.PartnerInterconnectAttachments = func() do.PartnerInterconnectAttachmentsService {
+				return do.NewPartnerInterconnectAttachmentsService(godoClient)
+			}
 
 			return nil
 		},
diff --git a/commands/displayers/partner_interconnect_attachment.go b/commands/displayers/partner_interconnect_attachment.go
new file mode 100644
index 00000000..30178277
--- /dev/null
+++ b/commands/displayers/partner_interconnect_attachment.go
@@ -0,0 +1,76 @@
+package displayers
+
+import (
+	"io"
+	"strings"
+
+	"github.com/digitalocean/doctl/do"
+)
+
+type PartnerInterconnectAttachment struct {
+	PartnerInterconnectAttachments do.PartnerInterconnectAttachments
+}
+
+var _ Displayable = &PartnerInterconnectAttachment{}
+
+func (v *PartnerInterconnectAttachment) JSON(out io.Writer) error {
+	return writeJSON(v.PartnerInterconnectAttachments, out)
+}
+
+func (v *PartnerInterconnectAttachment) Cols() []string {
+	return []string{
+		"ID",
+		"Name",
+		"State",
+		"ConnectionBandwidthInMbps",
+		"Region",
+		"NaaSProvider",
+		"VPCIDs",
+		"CreatedAt",
+		"BGPLocalASN",
+		"BGPLocalRouterIP",
+		"BGPPeerASN",
+		"BGPPeerRouterIP",
+	}
+}
+
+func (v *PartnerInterconnectAttachment) ColMap() map[string]string {
+	return map[string]string{
+		"ID":                        "ID",
+		"Name":                      "Name",
+		"State":                     "State",
+		"ConnectionBandwidthInMbps": "Connection Bandwidth (MBPS)",
+		"Region":                    "Region",
+		"NaaSProvider":              "NaaS Provider",
+		"VPCIDs":                    "VPC IDs",
+		"CreatedAt":                 "Created At",
+		"BGPLocalASN":               "BGP Local ASN",
+		"BGPLocalRouterIP":          "BGP Local Router IP",
+		"BGPPeerASN":                "BGP Peer ASN",
+		"BGPPeerRouterIP":           "BGP Peer Router IP",
+	}
+}
+
+func (v *PartnerInterconnectAttachment) KV() []map[string]any {
+	out := make([]map[string]any, 0, len(v.PartnerInterconnectAttachments))
+
+	for _, ia := range v.PartnerInterconnectAttachments {
+		o := map[string]any{
+			"ID":                        ia.ID,
+			"Name":                      ia.Name,
+			"State":                     ia.State,
+			"ConnectionBandwidthInMbps": ia.ConnectionBandwidthInMbps,
+			"Region":                    ia.Region,
+			"NaaSProvider":              ia.NaaSProvider,
+			"VPCIDs":                    strings.Join(ia.VPCIDs, ","),
+			"CreatedAt":                 ia.CreatedAt,
+			"BGPLocalASN":               ia.BGP.LocalASN,
+			"BGPLocalRouterIP":          ia.BGP.LocalRouterIP,
+			"BGPPeerASN":                ia.BGP.PeerASN,
+			"BGPPeerRouterIP":           ia.BGP.PeerRouterIP,
+		}
+		out = append(out, o)
+	}
+
+	return out
+}
diff --git a/commands/doit.go b/commands/doit.go
index f4f42e23..7a68715a 100644
--- a/commands/doit.go
+++ b/commands/doit.go
@@ -184,6 +184,7 @@ func addCommands() {
 	DoitCmd.AddCommand(Version())
 	DoitCmd.AddCommand(Registry())
 	DoitCmd.AddCommand(VPCs())
+	DoitCmd.AddCommand(Network())
 	DoitCmd.AddCommand(OneClicks())
 	DoitCmd.AddCommand(Monitoring())
 	DoitCmd.AddCommand(Serverless())
diff --git a/commands/partner_interconnect_attachment.go b/commands/partner_interconnect_attachment.go
new file mode 100644
index 00000000..592ba542
--- /dev/null
+++ b/commands/partner_interconnect_attachment.go
@@ -0,0 +1,155 @@
+/*
+Copyright 2018 The Doctl Authors All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+	http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package commands
+
+import (
+	"fmt"
+
+	"github.com/digitalocean/doctl"
+	"github.com/digitalocean/doctl/commands/displayers"
+	"github.com/digitalocean/doctl/do"
+	"github.com/digitalocean/godo"
+	"github.com/spf13/cobra"
+)
+
+// Network creates the network command.
+func Network() *Command {
+	cmd := &Command{
+		Command: &cobra.Command{
+			Use:     "network",
+			Short:   "Display commands that manage network products",
+			Long:    `The commands under ` + "`" + `doctl network` + "`" + ` are for managing network products`,
+			GroupID: manageResourcesGroup,
+			Hidden:  true,
+		},
+	}
+
+	cmd.AddCommand(PartnerInterconnectAttachments())
+
+	return cmd
+}
+
+// PartnerInterconnectAttachments creates the interconnect attachments command.
+func PartnerInterconnectAttachments() *Command {
+	cmd := &Command{
+		Command: &cobra.Command{
+			Use:   "interconnect-attachment",
+			Short: "Display commands that manage Partner Interconnect Attachments",
+			Long: `The commands under ` + "`" + `doctl network interconnect-attachment` + "`" + ` are for managing your Partner Interconnect Attachments.
+
+With the Partner Interconnect Attachments commands, you can get, list, create, update, or delete Partner Interconnect Attachments, and manage their configuration details.`,
+		},
+	}
+
+	cmdPartnerIACreate := CmdBuilder(cmd, RunPartnerInterconnectAttachmentCreate, "create",
+		"Create a Partner Interconnect Attachment", "Use this command to create a new Partner Interconnect Attachment on your account.", Writer, aliasOpt("c"), displayerType(&displayers.PartnerInterconnectAttachment{}))
+	AddStringFlag(cmdPartnerIACreate, doctl.ArgInterconnectAttachmentType, "", "partner", "Specify interconnect attachment type (e.g., partner)")
+
+	AddStringFlag(cmdPartnerIACreate, doctl.ArgPartnerInterconnectAttachmentName, "", "", "Name of the Partner Interconnect Attachment", requiredOpt())
+	AddIntFlag(cmdPartnerIACreate, doctl.ArgPartnerInterconnectAttachmentConnectionBandwidthInMbps, "", 0, "Connection Bandwidth in Mbps", requiredOpt())
+	AddStringFlag(cmdPartnerIACreate, doctl.ArgPartnerInterconnectAttachmentRegion, "", "", "Region", requiredOpt())
+	AddStringFlag(cmdPartnerIACreate, doctl.ArgPartnerInterconnectAttachmentNaaSProvider, "", "", "NaaS Provider", requiredOpt())
+	AddStringSliceFlag(cmdPartnerIACreate, doctl.ArgPartnerInterconnectAttachmentVPCIDs, "", []string{}, "VPC network IDs", requiredOpt())
+	AddIntFlag(cmdPartnerIACreate, doctl.ArgPartnerInterconnectAttachmentBGPLocalASN, "", 0, "BGP Local ASN")
+	AddStringFlag(cmdPartnerIACreate, doctl.ArgPartnerInterconnectAttachmentBGPLocalRouterIP, "", "", "BGP Local Router IP")
+	AddIntFlag(cmdPartnerIACreate, doctl.ArgPartnerInterconnectAttachmentBGPPeerASN, "", 0, "BGP Peer ASN")
+	AddStringFlag(cmdPartnerIACreate, doctl.ArgPartnerInterconnectAttachmentBGPPeerRouterIP, "", "", "BGP Peer Router IP")
+	cmdPartnerIACreate.Example = `The following example creates a Partner Interconnect Attachment: doctl network interconnect-attachment create --name "example-pia" --connection-bandwidth-in-mbps 50 --naas-provider "MEGAPORT" --region "nyc" --vpc-ids "c5537207-ebf0-47cb-bc10-6fac717cd672"`
+
+	return cmd
+}
+
+func ensurePartnerAttachmentType(c *CmdConfig) error {
+	attachmentType, err := c.Doit.GetString(c.NS, doctl.ArgInterconnectAttachmentType)
+	if err != nil {
+		return err
+	}
+	if attachmentType != "partner" {
+		return fmt.Errorf("unsupported attachment type: %s", attachmentType)
+	}
+	return nil
+}
+
+// RunPartnerInterconnectAttachmentCreate creates a new Partner Interconnect Attachment with a given configuration.
+func RunPartnerInterconnectAttachmentCreate(c *CmdConfig) error {
+	if err := ensurePartnerAttachmentType(c); err != nil {
+		return err
+	}
+
+	r := new(godo.PartnerInterconnectAttachmentCreateRequest)
+	name, err := c.Doit.GetString(c.NS, doctl.ArgPartnerInterconnectAttachmentName)
+	if err != nil {
+		return err
+	}
+	r.Name = name
+
+	connBandwidth, err := c.Doit.GetInt(c.NS, doctl.ArgPartnerInterconnectAttachmentConnectionBandwidthInMbps)
+	if err != nil {
+		return err
+	}
+	r.ConnectionBandwidthInMbps = connBandwidth
+
+	region, err := c.Doit.GetString(c.NS, doctl.ArgPartnerInterconnectAttachmentRegion)
+	if err != nil {
+		return err
+	}
+	r.Region = region
+
+	naasProvider, err := c.Doit.GetString(c.NS, doctl.ArgPartnerInterconnectAttachmentNaaSProvider)
+	if err != nil {
+		return err
+	}
+	r.NaaSProvider = naasProvider
+
+	vpcIDs, err := c.Doit.GetStringSlice(c.NS, doctl.ArgPartnerInterconnectAttachmentVPCIDs)
+	if err != nil {
+		return err
+	}
+	r.VPCIDs = vpcIDs
+
+	bgpConfig := new(godo.BGP)
+
+	bgpLocalASN, err := c.Doit.GetInt(c.NS, doctl.ArgPartnerInterconnectAttachmentBGPLocalASN)
+	if err != nil {
+		return err
+	}
+	bgpConfig.LocalASN = bgpLocalASN
+
+	bgpLocalRouterIP, err := c.Doit.GetString(c.NS, doctl.ArgPartnerInterconnectAttachmentBGPLocalRouterIP)
+	if err != nil {
+		return err
+	}
+	bgpConfig.LocalRouterIP = bgpLocalRouterIP
+
+	bgpPeerASN, err := c.Doit.GetInt(c.NS, doctl.ArgPartnerInterconnectAttachmentBGPPeerASN)
+	if err != nil {
+		return err
+	}
+	bgpConfig.PeerASN = bgpPeerASN
+
+	bgpPeerRouterIP, err := c.Doit.GetString(c.NS, doctl.ArgPartnerInterconnectAttachmentBGPPeerRouterIP)
+	if err != nil {
+		return err
+	}
+	bgpConfig.PeerRouterIP = bgpPeerRouterIP
+
+	pias := c.PartnerInterconnectAttachments()
+	pia, err := pias.Create(r)
+	if err != nil {
+		return err
+	}
+
+	item := &displayers.PartnerInterconnectAttachment{PartnerInterconnectAttachments: do.PartnerInterconnectAttachments{*pia}}
+	return c.Display(item)
+}
diff --git a/do/mocks/PartnerInterconnectAttachmentsService.go b/do/mocks/PartnerInterconnectAttachmentsService.go
new file mode 100644
index 00000000..99e0e5b7
--- /dev/null
+++ b/do/mocks/PartnerInterconnectAttachmentsService.go
@@ -0,0 +1,57 @@
+// Code generated by MockGen. DO NOT EDIT.
+// Source: partner_interconnect_attachments.go
+//
+// Generated by this command:
+//
+//	mockgen -source partner_interconnect_attachments.go -package=mocks -destination=mocks/PartnerInterconnectAttachmentsService.go PartnerInterconnectAttachmentsService
+//
+
+// Package mocks is a generated GoMock package.
+package mocks
+
+import (
+	reflect "reflect"
+
+	do "github.com/digitalocean/doctl/do"
+	godo "github.com/digitalocean/godo"
+	gomock "go.uber.org/mock/gomock"
+)
+
+// MockPartnerInterconnectAttachmentsService is a mock of PartnerInterconnectAttachmentsService interface.
+type MockPartnerInterconnectAttachmentsService struct {
+	ctrl     *gomock.Controller
+	recorder *MockPartnerInterconnectAttachmentsServiceMockRecorder
+	isgomock struct{}
+}
+
+// MockPartnerInterconnectAttachmentsServiceMockRecorder is the mock recorder for MockPartnerInterconnectAttachmentsService.
+type MockPartnerInterconnectAttachmentsServiceMockRecorder struct {
+	mock *MockPartnerInterconnectAttachmentsService
+}
+
+// NewMockPartnerInterconnectAttachmentsService creates a new mock instance.
+func NewMockPartnerInterconnectAttachmentsService(ctrl *gomock.Controller) *MockPartnerInterconnectAttachmentsService {
+	mock := &MockPartnerInterconnectAttachmentsService{ctrl: ctrl}
+	mock.recorder = &MockPartnerInterconnectAttachmentsServiceMockRecorder{mock}
+	return mock
+}
+
+// EXPECT returns an object that allows the caller to indicate expected use.
+func (m *MockPartnerInterconnectAttachmentsService) EXPECT() *MockPartnerInterconnectAttachmentsServiceMockRecorder {
+	return m.recorder
+}
+
+// Create mocks base method.
+func (m *MockPartnerInterconnectAttachmentsService) Create(arg0 *godo.PartnerInterconnectAttachmentCreateRequest) (*do.PartnerInterconnectAttachment, error) {
+	m.ctrl.T.Helper()
+	ret := m.ctrl.Call(m, "Create", arg0)
+	ret0, _ := ret[0].(*do.PartnerInterconnectAttachment)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
+}
+
+// Create indicates an expected call of Create.
+func (mr *MockPartnerInterconnectAttachmentsServiceMockRecorder) Create(arg0 any) *gomock.Call {
+	mr.mock.ctrl.T.Helper()
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockPartnerInterconnectAttachmentsService)(nil).Create), arg0)
+}
diff --git a/do/partner_interconnect_attachments.go b/do/partner_interconnect_attachments.go
new file mode 100644
index 00000000..8d5ccc39
--- /dev/null
+++ b/do/partner_interconnect_attachments.go
@@ -0,0 +1,56 @@
+/*
+Copyright 2018 The Doctl Authors All rights reserved.
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+	http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package do
+
+import (
+	"context"
+
+	"github.com/digitalocean/godo"
+)
+
+// PartnerInterconnectAttachment wraps a godo PartnerInterconnectAttachment.
+type PartnerInterconnectAttachment struct {
+	*godo.PartnerInterconnectAttachment
+}
+
+// PartnerInterconnectAttachments is a slice of PartnerInterconnectAttachment.
+type PartnerInterconnectAttachments []PartnerInterconnectAttachment
+
+// PartnerInterconnectAttachmentsService is an interface for interacting with
+// DigitalOcean's partner interconnect attachments api.
+type PartnerInterconnectAttachmentsService interface {
+	Create(*godo.PartnerInterconnectAttachmentCreateRequest) (*PartnerInterconnectAttachment, error)
+}
+
+var _ PartnerInterconnectAttachmentsService = &partnerInterconnectAttachmentsService{}
+
+type partnerInterconnectAttachmentsService struct {
+	client *godo.Client
+}
+
+// NewPartnerInterconnectAttachmentsService builds an instance of PartnerInterconnectAttachmentsService.
+func NewPartnerInterconnectAttachmentsService(client *godo.Client) PartnerInterconnectAttachmentsService {
+	return &partnerInterconnectAttachmentsService{
+		client: client,
+	}
+}
+
+// Create creates a partner interconnect attachment.
+func (p *partnerInterconnectAttachmentsService) Create(req *godo.PartnerInterconnectAttachmentCreateRequest) (*PartnerInterconnectAttachment, error) {
+	pia, _, err := p.client.PartnerInterconnectAttachments.Create(context.TODO(), req)
+	if err != nil {
+		return nil, err
+	}
+	return &PartnerInterconnectAttachment{PartnerInterconnectAttachment: pia}, nil
+}
diff --git a/go.mod b/go.mod
index bd597bb0..695e7f93 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.135.0
+	github.com/digitalocean/godo v1.136.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 98415bb4..298c4ec2 100644
--- a/go.sum
+++ b/go.sum
@@ -91,10 +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.134.0 h1:dT7aQR9jxNOQEZwzP+tAYcxlj5szFZScC33+PAYGQVM=
-github.com/digitalocean/godo v1.134.0/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc=
-github.com/digitalocean/godo v1.135.0 h1:H/A2wFBLo3hUDAw0xjHayHaNxY2hRvM3xxa+lTpriT8=
-github.com/digitalocean/godo v1.135.0/go.mod h1:PU8JB6I1XYkQIdHFop8lLAY9ojp6M0XcU0TWaQSxbrc=
+github.com/digitalocean/godo v1.136.0 h1:DTxugljFJSMBPfEGq4KeXpnKeAHicggNqogcrw/YdZw=
+github.com/digitalocean/godo v1.136.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 ce1110c8..8f926be6 100644
--- a/vendor/github.com/digitalocean/godo/CHANGELOG.md
+++ b/vendor/github.com/digitalocean/godo/CHANGELOG.md
@@ -1,5 +1,10 @@
 # Change Log
 
+## [v1.136.0] - 2025-01-28
+
+- #776 - @danaelhe - Databases: Support online-migrations
+- #777 - @apinonformoso - update bgp to be a pointer
+
 ##  [v1.135.0] - 2025-01-27
 - #766 - @dhij - kubernetes: add cluster autoscaler config
 - #775 - @jvasilevsky - LBASA-3620: add network_stack field to load balancers model
diff --git a/vendor/github.com/digitalocean/godo/databases.go b/vendor/github.com/digitalocean/godo/databases.go
index 1217ef05..ed4b4f8b 100644
--- a/vendor/github.com/digitalocean/godo/databases.go
+++ b/vendor/github.com/digitalocean/godo/databases.go
@@ -42,6 +42,8 @@ const (
 	databaseIndexPath                   = databaseBasePath + "/%s/indexes/%s"
 	databaseLogsinkPath                 = databaseBasePath + "/%s/logsink/%s"
 	databaseLogsinksPath                = databaseBasePath + "/%s/logsink"
+	databaseOnlineMigrationsPath        = databaseBasePath + "/%s/online-migration"
+	databaseOnlineMigrationPath         = databaseBasePath + "/%s/online-migration/%s"
 )
 
 // SQL Mode constants allow for MySQL-specific SQL flavor configuration.
@@ -179,6 +181,9 @@ type DatabasesService interface {
 	ListLogsinks(ctx context.Context, databaseID string, opts *ListOptions) ([]DatabaseLogsink, *Response, error)
 	UpdateLogsink(ctx context.Context, databaseID string, logsinkID string, updateLogsink *DatabaseUpdateLogsinkRequest) (*Response, error)
 	DeleteLogsink(ctx context.Context, databaseID, logsinkID string) (*Response, error)
+	StartOnlineMigration(ctx context.Context, databaseID string, onlineMigrationRequest *DatabaseStartOnlineMigrationRequest) (*DatabaseOnlineMigrationStatus, *Response, error)
+	StopOnlineMigration(ctx context.Context, databaseID, migrationID string) (*Response, error)
+	GetOnlineMigrationStatus(ctx context.Context, databaseID string) (*DatabaseOnlineMigrationStatus, *Response, error)
 }
 
 // DatabasesServiceOp handles communication with the Databases related methods
@@ -366,6 +371,13 @@ type DatabaseLogsink struct {
 	Config *DatabaseLogsinkConfig `json:"config,omitempty"`
 }
 
+// DatabaseOnlineMigrationStatus represents an online migration status
+type DatabaseOnlineMigrationStatus struct {
+	ID        string `json:"id"`
+	Status    string `json:"status"`
+	CreatedAt string `json:"created_at"`
+}
+
 // TopicPartition represents the state of a Kafka topic partition
 type TopicPartition struct {
 	EarliestOffset uint64                `json:"earliest_offset,omitempty"`
@@ -515,6 +527,13 @@ type DatabaseFirewallRule struct {
 	CreatedAt   time.Time `json:"created_at"`
 }
 
+// DatabaseStartOnlineMigrationRequest is used to start an online migration for a database cluster
+type DatabaseStartOnlineMigrationRequest struct {
+	Source     *DatabaseOnlineMigrationConfig `json:"source"`
+	DisableSSL bool                           `json:"disable_ssl,omitempty"`
+	IgnoreDBs  []string                       `json:"ignore_dbs,omitempty"`
+}
+
 // DatabaseCreateLogsinkRequest is used to create logsink for a database cluster
 type DatabaseCreateLogsinkRequest struct {
 	Name   string                 `json:"sink_name"`
@@ -544,6 +563,15 @@ type DatabaseLogsinkConfig struct {
 	Cert         string  `json:"cert,omitempty"`
 }
 
+// DatabaseOnlineMigrationConfig represents the configuration options for database online migrations.
+type DatabaseOnlineMigrationConfig struct {
+	Host         string `json:"host,omitempty"`
+	Port         int    `json:"port,omitempty"`
+	DatabaseName string `json:"dbname,omitempty"`
+	Username     string `json:"username,omitempty"`
+	Password     string `json:"password,omitempty"`
+}
+
 // PostgreSQLConfig holds advanced configurations for PostgreSQL database clusters.
 type PostgreSQLConfig struct {
 	AutovacuumFreezeMaxAge          *int                         `json:"autovacuum_freeze_max_age,omitempty"`
@@ -1975,3 +2003,50 @@ func (svc *DatabasesServiceOp) DeleteLogsink(ctx context.Context, databaseID, lo
 	}
 	return resp, nil
 }
+
+// StartOnlineMigration starts an online migration for a database. Migrating a cluster establishes a connection with an existing cluster
+// and replicates its contents to the target cluster. Online migration is only available for MySQL, PostgreSQL, and Redis clusters.
+func (svc *DatabasesServiceOp) StartOnlineMigration(ctx context.Context, databaseID string, onlineMigration *DatabaseStartOnlineMigrationRequest) (*DatabaseOnlineMigrationStatus, *Response, error) {
+	path := fmt.Sprintf(databaseOnlineMigrationsPath, databaseID)
+	req, err := svc.client.NewRequest(ctx, http.MethodPut, path, onlineMigration)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	root := new(DatabaseOnlineMigrationStatus)
+	resp, err := svc.client.Do(ctx, req, root)
+	if err != nil {
+		return nil, resp, err
+	}
+	return root, resp, nil
+}
+
+// GetOnlineMigrationStatus retrieves the status of the most recent online migration
+func (svc *DatabasesServiceOp) GetOnlineMigrationStatus(ctx context.Context, databaseID string) (*DatabaseOnlineMigrationStatus, *Response, error) {
+	path := fmt.Sprintf(databaseOnlineMigrationsPath, databaseID)
+	req, err := svc.client.NewRequest(ctx, http.MethodGet, path, nil)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	root := new(DatabaseOnlineMigrationStatus)
+	resp, err := svc.client.Do(ctx, req, root)
+	if err != nil {
+		return nil, resp, err
+	}
+	return root, resp, nil
+}
+
+// StopOnlineMigration stops an online migration
+func (svc *DatabasesServiceOp) StopOnlineMigration(ctx context.Context, databaseID, migrationID string) (*Response, error) {
+	path := fmt.Sprintf(databaseOnlineMigrationPath, databaseID, migrationID)
+	req, err := svc.client.NewRequest(ctx, http.MethodDelete, path, nil)
+	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 de918ba2..ff4c04e1 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.135.0"
+	libraryVersion = "1.136.0"
 	defaultBaseURL = "https://api.digitalocean.com/"
 	userAgent      = "godo/" + libraryVersion
 	mediaType      = "application/json"
diff --git a/vendor/github.com/digitalocean/godo/partner_interconnect_attachments.go b/vendor/github.com/digitalocean/godo/partner_interconnect_attachments.go
index 87a0e9ed..df21c929 100644
--- a/vendor/github.com/digitalocean/godo/partner_interconnect_attachments.go
+++ b/vendor/github.com/digitalocean/godo/partner_interconnect_attachments.go
@@ -46,6 +46,37 @@ type PartnerInterconnectAttachmentCreateRequest struct {
 	BGP BGP `json:"bgp,omitempty"`
 }
 
+type partnerInterconnectAttachmentRequestBody struct {
+	// Name is the name of the Partner Interconnect Attachment
+	Name string `json:"name,omitempty"`
+	// ConnectionBandwidthInMbps is the bandwidth of the connection in Mbps
+	ConnectionBandwidthInMbps int `json:"connection_bandwidth_in_mbps,omitempty"`
+	// Region is the region where the Partner Interconnect Attachment is created
+	Region string `json:"region,omitempty"`
+	// NaaSProvider is the name of the Network as a Service provider
+	NaaSProvider string `json:"naas_provider,omitempty"`
+	// VPCIDs is the IDs of the VPCs to which the Partner Interconnect Attachment is connected
+	VPCIDs []string `json:"vpc_ids,omitempty"`
+	// BGP is the BGP configuration of the Partner Interconnect Attachment
+	BGP *BGP `json:"bgp,omitempty"`
+}
+
+func (req *PartnerInterconnectAttachmentCreateRequest) buildReq() *partnerInterconnectAttachmentRequestBody {
+	request := &partnerInterconnectAttachmentRequestBody{
+		Name:                      req.Name,
+		ConnectionBandwidthInMbps: req.ConnectionBandwidthInMbps,
+		Region:                    req.Region,
+		NaaSProvider:              req.NaaSProvider,
+		VPCIDs:                    req.VPCIDs,
+	}
+
+	if req.BGP != (BGP{}) {
+		request.BGP = &req.BGP
+	}
+
+	return request
+}
+
 // PartnerInterconnectAttachmentUpdateRequest represents a request to update a Partner Interconnect Attachment.
 type PartnerInterconnectAttachmentUpdateRequest struct {
 	// Name is the name of the Partner Interconnect Attachment
@@ -154,7 +185,8 @@ func (s *PartnerInterconnectAttachmentsServiceOp) List(ctx context.Context, opt
 // Create creates a new Partner Interconnect Attachment.
 func (s *PartnerInterconnectAttachmentsServiceOp) Create(ctx context.Context, create *PartnerInterconnectAttachmentCreateRequest) (*PartnerInterconnectAttachment, *Response, error) {
 	path := partnerInterconnectAttachmentsBasePath
-	req, err := s.client.NewRequest(ctx, http.MethodPost, path, create)
+
+	req, err := s.client.NewRequest(ctx, http.MethodPost, path, create.buildReq())
 	if err != nil {
 		return nil, nil, err
 	}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 447dcc02..4f4af180 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.135.0
+# github.com/digitalocean/godo v1.136.0
 ## explicit; go 1.22
 github.com/digitalocean/godo
 github.com/digitalocean/godo/metrics


Test patch:
diff --git a/commands/commands_test.go b/commands/commands_test.go
index 160f65a0..b3cd1ae0 100644
--- a/commands/commands_test.go
+++ b/commands/commands_test.go
@@ -212,48 +212,49 @@ func assertCommandNames(t *testing.T, cmd *Command, expected ...string) {
 type testFn func(c *CmdConfig, tm *tcMocks)
 
 type tcMocks struct {
-	account               *domocks.MockAccountService
-	actions               *domocks.MockActionsService
-	apps                  *domocks.MockAppsService
-	balance               *domocks.MockBalanceService
-	billingHistory        *domocks.MockBillingHistoryService
-	databases             *domocks.MockDatabasesService
-	dropletActions        *domocks.MockDropletActionsService
-	dropletAutoscale      *domocks.MockDropletAutoscaleService
-	droplets              *domocks.MockDropletsService
-	keys                  *domocks.MockKeysService
-	sizes                 *domocks.MockSizesService
-	regions               *domocks.MockRegionsService
-	images                *domocks.MockImagesService
-	imageActions          *domocks.MockImageActionsService
-	invoices              *domocks.MockInvoicesService
-	reservedIPs           *domocks.MockReservedIPsService
-	reservedIPActions     *domocks.MockReservedIPActionsService
-	reservedIPv6s         *domocks.MockReservedIPv6sService
-	domains               *domocks.MockDomainsService
-	uptimeChecks          *domocks.MockUptimeChecksService
-	volumes               *domocks.MockVolumesService
-	volumeActions         *domocks.MockVolumeActionsService
-	tags                  *domocks.MockTagsService
-	snapshots             *domocks.MockSnapshotsService
-	certificates          *domocks.MockCertificatesService
-	loadBalancers         *domocks.MockLoadBalancersService
-	firewalls             *domocks.MockFirewallsService
-	cdns                  *domocks.MockCDNsService
-	projects              *domocks.MockProjectsService
-	kubernetes            *domocks.MockKubernetesService
-	registry              *domocks.MockRegistryService
-	sshRunner             *domocks.MockRunner
-	vpcs                  *domocks.MockVPCsService
-	oneClick              *domocks.MockOneClickService
-	listen                *domocks.MockListenerService
-	terminal              *domocks.MockTerminal
-	monitoring            *domocks.MockMonitoringService
-	serverless            *domocks.MockServerlessService
-	appBuilderFactory     *builder.MockComponentBuilderFactory
-	appBuilder            *builder.MockComponentBuilder
-	appDockerEngineClient *builder.MockDockerEngineClient
-	oauth                 *domocks.MockOAuthService
+	account                       *domocks.MockAccountService
+	actions                       *domocks.MockActionsService
+	apps                          *domocks.MockAppsService
+	balance                       *domocks.MockBalanceService
+	billingHistory                *domocks.MockBillingHistoryService
+	databases                     *domocks.MockDatabasesService
+	dropletActions                *domocks.MockDropletActionsService
+	dropletAutoscale              *domocks.MockDropletAutoscaleService
+	droplets                      *domocks.MockDropletsService
+	keys                          *domocks.MockKeysService
+	sizes                         *domocks.MockSizesService
+	regions                       *domocks.MockRegionsService
+	images                        *domocks.MockImagesService
+	imageActions                  *domocks.MockImageActionsService
+	invoices                      *domocks.MockInvoicesService
+	reservedIPs                   *domocks.MockReservedIPsService
+	reservedIPActions             *domocks.MockReservedIPActionsService
+	reservedIPv6s                 *domocks.MockReservedIPv6sService
+	domains                       *domocks.MockDomainsService
+	uptimeChecks                  *domocks.MockUptimeChecksService
+	volumes                       *domocks.MockVolumesService
+	volumeActions                 *domocks.MockVolumeActionsService
+	tags                          *domocks.MockTagsService
+	snapshots                     *domocks.MockSnapshotsService
+	certificates                  *domocks.MockCertificatesService
+	loadBalancers                 *domocks.MockLoadBalancersService
+	firewalls                     *domocks.MockFirewallsService
+	cdns                          *domocks.MockCDNsService
+	projects                      *domocks.MockProjectsService
+	kubernetes                    *domocks.MockKubernetesService
+	registry                      *domocks.MockRegistryService
+	sshRunner                     *domocks.MockRunner
+	vpcs                          *domocks.MockVPCsService
+	oneClick                      *domocks.MockOneClickService
+	listen                        *domocks.MockListenerService
+	terminal                      *domocks.MockTerminal
+	monitoring                    *domocks.MockMonitoringService
+	serverless                    *domocks.MockServerlessService
+	appBuilderFactory             *builder.MockComponentBuilderFactory
+	appBuilder                    *builder.MockComponentBuilder
+	appDockerEngineClient         *builder.MockDockerEngineClient
+	oauth                         *domocks.MockOAuthService
+	partnerInterconnectAttachment *domocks.MockPartnerInterconnectAttachmentsService
 }
 
 func withTestClient(t *testing.T, tFn testFn) {
@@ -261,48 +262,49 @@ func withTestClient(t *testing.T, tFn testFn) {
 	defer ctrl.Finish()
 
 	tm := &tcMocks{
-		account:               domocks.NewMockAccountService(ctrl),
-		actions:               domocks.NewMockActionsService(ctrl),
-		apps:                  domocks.NewMockAppsService(ctrl),
-		balance:               domocks.NewMockBalanceService(ctrl),
-		billingHistory:        domocks.NewMockBillingHistoryService(ctrl),
-		keys:                  domocks.NewMockKeysService(ctrl),
-		sizes:                 domocks.NewMockSizesService(ctrl),
-		regions:               domocks.NewMockRegionsService(ctrl),
-		images:                domocks.NewMockImagesService(ctrl),
-		imageActions:          domocks.NewMockImageActionsService(ctrl),
-		invoices:              domocks.NewMockInvoicesService(ctrl),
-		reservedIPs:           domocks.NewMockReservedIPsService(ctrl),
-		reservedIPActions:     domocks.NewMockReservedIPActionsService(ctrl),
-		reservedIPv6s:         domocks.NewMockReservedIPv6sService(ctrl),
-		droplets:              domocks.NewMockDropletsService(ctrl),
-		dropletActions:        domocks.NewMockDropletActionsService(ctrl),
-		dropletAutoscale:      domocks.NewMockDropletAutoscaleService(ctrl),
-		domains:               domocks.NewMockDomainsService(ctrl),
-		tags:                  domocks.NewMockTagsService(ctrl),
-		uptimeChecks:          domocks.NewMockUptimeChecksService(ctrl),
-		volumes:               domocks.NewMockVolumesService(ctrl),
-		volumeActions:         domocks.NewMockVolumeActionsService(ctrl),
-		snapshots:             domocks.NewMockSnapshotsService(ctrl),
-		certificates:          domocks.NewMockCertificatesService(ctrl),
-		loadBalancers:         domocks.NewMockLoadBalancersService(ctrl),
-		firewalls:             domocks.NewMockFirewallsService(ctrl),
-		cdns:                  domocks.NewMockCDNsService(ctrl),
-		projects:              domocks.NewMockProjectsService(ctrl),
-		kubernetes:            domocks.NewMockKubernetesService(ctrl),
-		databases:             domocks.NewMockDatabasesService(ctrl),
-		registry:              domocks.NewMockRegistryService(ctrl),
-		sshRunner:             domocks.NewMockRunner(ctrl),
-		vpcs:                  domocks.NewMockVPCsService(ctrl),
-		oneClick:              domocks.NewMockOneClickService(ctrl),
-		listen:                domocks.NewMockListenerService(ctrl),
-		terminal:              domocks.NewMockTerminal(ctrl),
-		monitoring:            domocks.NewMockMonitoringService(ctrl),
-		serverless:            domocks.NewMockServerlessService(ctrl),
-		appBuilderFactory:     builder.NewMockComponentBuilderFactory(ctrl),
-		appBuilder:            builder.NewMockComponentBuilder(ctrl),
-		appDockerEngineClient: builder.NewMockDockerEngineClient(ctrl),
-		oauth:                 domocks.NewMockOAuthService(ctrl),
+		account:                       domocks.NewMockAccountService(ctrl),
+		actions:                       domocks.NewMockActionsService(ctrl),
+		apps:                          domocks.NewMockAppsService(ctrl),
+		balance:                       domocks.NewMockBalanceService(ctrl),
+		billingHistory:                domocks.NewMockBillingHistoryService(ctrl),
+		keys:                          domocks.NewMockKeysService(ctrl),
+		sizes:                         domocks.NewMockSizesService(ctrl),
+		regions:                       domocks.NewMockRegionsService(ctrl),
+		images:                        domocks.NewMockImagesService(ctrl),
+		imageActions:                  domocks.NewMockImageActionsService(ctrl),
+		invoices:                      domocks.NewMockInvoicesService(ctrl),
+		reservedIPs:                   domocks.NewMockReservedIPsService(ctrl),
+		reservedIPActions:             domocks.NewMockReservedIPActionsService(ctrl),
+		reservedIPv6s:                 domocks.NewMockReservedIPv6sService(ctrl),
+		droplets:                      domocks.NewMockDropletsService(ctrl),
+		dropletActions:                domocks.NewMockDropletActionsService(ctrl),
+		dropletAutoscale:              domocks.NewMockDropletAutoscaleService(ctrl),
+		domains:                       domocks.NewMockDomainsService(ctrl),
+		tags:                          domocks.NewMockTagsService(ctrl),
+		uptimeChecks:                  domocks.NewMockUptimeChecksService(ctrl),
+		volumes:                       domocks.NewMockVolumesService(ctrl),
+		volumeActions:                 domocks.NewMockVolumeActionsService(ctrl),
+		snapshots:                     domocks.NewMockSnapshotsService(ctrl),
+		certificates:                  domocks.NewMockCertificatesService(ctrl),
+		loadBalancers:                 domocks.NewMockLoadBalancersService(ctrl),
+		firewalls:                     domocks.NewMockFirewallsService(ctrl),
+		cdns:                          domocks.NewMockCDNsService(ctrl),
+		projects:                      domocks.NewMockProjectsService(ctrl),
+		kubernetes:                    domocks.NewMockKubernetesService(ctrl),
+		databases:                     domocks.NewMockDatabasesService(ctrl),
+		registry:                      domocks.NewMockRegistryService(ctrl),
+		sshRunner:                     domocks.NewMockRunner(ctrl),
+		vpcs:                          domocks.NewMockVPCsService(ctrl),
+		oneClick:                      domocks.NewMockOneClickService(ctrl),
+		listen:                        domocks.NewMockListenerService(ctrl),
+		terminal:                      domocks.NewMockTerminal(ctrl),
+		monitoring:                    domocks.NewMockMonitoringService(ctrl),
+		serverless:                    domocks.NewMockServerlessService(ctrl),
+		appBuilderFactory:             builder.NewMockComponentBuilderFactory(ctrl),
+		appBuilder:                    builder.NewMockComponentBuilder(ctrl),
+		appDockerEngineClient:         builder.NewMockDockerEngineClient(ctrl),
+		oauth:                         domocks.NewMockOAuthService(ctrl),
+		partnerInterconnectAttachment: domocks.NewMockPartnerInterconnectAttachmentsService(ctrl),
 	}
 
 	testConfig := doctl.NewTestConfig()
@@ -324,42 +326,43 @@ func withTestClient(t *testing.T, tFn testFn) {
 
 		componentBuilderFactory: tm.appBuilderFactory,
 
-		Keys:              func() do.KeysService { return tm.keys },
-		Sizes:             func() do.SizesService { return tm.sizes },
-		Regions:           func() do.RegionsService { return tm.regions },
-		Images:            func() do.ImagesService { return tm.images },
-		ImageActions:      func() do.ImageActionsService { return tm.imageActions },
-		ReservedIPs:       func() do.ReservedIPsService { return tm.reservedIPs },
-		ReservedIPActions: func() do.ReservedIPActionsService { return tm.reservedIPActions },
-		ReservedIPv6s:     func() do.ReservedIPv6sService { return tm.reservedIPv6s },
-		Droplets:          func() do.DropletsService { return tm.droplets },
-		DropletActions:    func() do.DropletActionsService { return tm.dropletActions },
-		DropletAutoscale:  func() do.DropletAutoscaleService { return tm.dropletAutoscale },
-		Domains:           func() do.DomainsService { return tm.domains },
-		Actions:           func() do.ActionsService { return tm.actions },
-		Account:           func() do.AccountService { return tm.account },
-		Balance:           func() do.BalanceService { return tm.balance },
-		BillingHistory:    func() do.BillingHistoryService { return tm.billingHistory },
-		Invoices:          func() do.InvoicesService { return tm.invoices },
-		Tags:              func() do.TagsService { return tm.tags },
-		UptimeChecks:      func() do.UptimeChecksService { return tm.uptimeChecks },
-		Volumes:           func() do.VolumesService { return tm.volumes },
-		VolumeActions:     func() do.VolumeActionsService { return tm.volumeActions },
-		Snapshots:         func() do.SnapshotsService { return tm.snapshots },
-		Certificates:      func() do.CertificatesService { return tm.certificates },
-		LoadBalancers:     func() do.LoadBalancersService { return tm.loadBalancers },
-		Firewalls:         func() do.FirewallsService { return tm.firewalls },
-		CDNs:              func() do.CDNsService { return tm.cdns },
-		Projects:          func() do.ProjectsService { return tm.projects },
-		Kubernetes:        func() do.KubernetesService { return tm.kubernetes },
-		Databases:         func() do.DatabasesService { return tm.databases },
-		Registry:          func() do.RegistryService { return tm.registry },
-		VPCs:              func() do.VPCsService { return tm.vpcs },
-		OneClicks:         func() do.OneClickService { return tm.oneClick },
-		Apps:              func() do.AppsService { return tm.apps },
-		Monitoring:        func() do.MonitoringService { return tm.monitoring },
-		Serverless:        func() do.ServerlessService { return tm.serverless },
-		OAuth:             func() do.OAuthService { return tm.oauth },
+		Keys:                           func() do.KeysService { return tm.keys },
+		Sizes:                          func() do.SizesService { return tm.sizes },
+		Regions:                        func() do.RegionsService { return tm.regions },
+		Images:                         func() do.ImagesService { return tm.images },
+		ImageActions:                   func() do.ImageActionsService { return tm.imageActions },
+		ReservedIPs:                    func() do.ReservedIPsService { return tm.reservedIPs },
+		ReservedIPActions:              func() do.ReservedIPActionsService { return tm.reservedIPActions },
+		ReservedIPv6s:                  func() do.ReservedIPv6sService { return tm.reservedIPv6s },
+		Droplets:                       func() do.DropletsService { return tm.droplets },
+		DropletActions:                 func() do.DropletActionsService { return tm.dropletActions },
+		DropletAutoscale:               func() do.DropletAutoscaleService { return tm.dropletAutoscale },
+		Domains:                        func() do.DomainsService { return tm.domains },
+		Actions:                        func() do.ActionsService { return tm.actions },
+		Account:                        func() do.AccountService { return tm.account },
+		Balance:                        func() do.BalanceService { return tm.balance },
+		BillingHistory:                 func() do.BillingHistoryService { return tm.billingHistory },
+		Invoices:                       func() do.InvoicesService { return tm.invoices },
+		Tags:                           func() do.TagsService { return tm.tags },
+		UptimeChecks:                   func() do.UptimeChecksService { return tm.uptimeChecks },
+		Volumes:                        func() do.VolumesService { return tm.volumes },
+		VolumeActions:                  func() do.VolumeActionsService { return tm.volumeActions },
+		Snapshots:                      func() do.SnapshotsService { return tm.snapshots },
+		Certificates:                   func() do.CertificatesService { return tm.certificates },
+		LoadBalancers:                  func() do.LoadBalancersService { return tm.loadBalancers },
+		Firewalls:                      func() do.FirewallsService { return tm.firewalls },
+		CDNs:                           func() do.CDNsService { return tm.cdns },
+		Projects:                       func() do.ProjectsService { return tm.projects },
+		Kubernetes:                     func() do.KubernetesService { return tm.kubernetes },
+		Databases:                      func() do.DatabasesService { return tm.databases },
+		Registry:                       func() do.RegistryService { return tm.registry },
+		VPCs:                           func() do.VPCsService { return tm.vpcs },
+		OneClicks:                      func() do.OneClickService { return tm.oneClick },
+		Apps:                           func() do.AppsService { return tm.apps },
+		Monitoring:                     func() do.MonitoringService { return tm.monitoring },
+		Serverless:                     func() do.ServerlessService { return tm.serverless },
+		OAuth:                          func() do.OAuthService { return tm.oauth },
+		PartnerInterconnectAttachments: func() do.PartnerInterconnectAttachmentsService { return tm.partnerInterconnectAttachment },
 	}
 
 	tFn(config, tm)
diff --git a/commands/partner_interconnect_attachment_test.go b/commands/partner_interconnect_attachment_test.go
new file mode 100644
index 00000000..df7048c3
--- /dev/null
+++ b/commands/partner_interconnect_attachment_test.go
@@ -0,0 +1,70 @@
+package commands
+
+import (
+	"testing"
+	"time"
+
+	"github.com/digitalocean/doctl"
+	"github.com/digitalocean/doctl/do"
+	"github.com/digitalocean/godo"
+	"github.com/stretchr/testify/assert"
+)
+
+var (
+	testPartnerAttachment = do.PartnerInterconnectAttachment{
+		PartnerInterconnectAttachment: &godo.PartnerInterconnectAttachment{
+			ID:                        "test-id",
+			Name:                      "doctl-pia",
+			State:                     "active",
+			ConnectionBandwidthInMbps: 50,
+			Region:                    "stage2",
+			NaaSProvider:              "MEGAPORT",
+			VPCIDs:                    []string{"d35e5cb7-7957-4643-8e3a-1ab4eb3a494c"},
+			CreatedAt:                 time.Date(2025, 1, 30, 0, 0, 0, 0, time.UTC),
+		},
+	}
+)
+
+func TestPartnerInterconnectAttachmentsCommand(t *testing.T) {
+	cmd := PartnerInterconnectAttachments()
+	assert.NotNil(t, cmd)
+
+	assertCommandNames(t, cmd, "create")
+}
+
+func TestPartnerInterconnectAttachmentCreate(t *testing.T) {
+	withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
+		config.Doit.Set(config.NS, doctl.ArgInterconnectAttachmentType, "partner")
+		config.Doit.Set(config.NS, doctl.ArgPartnerInterconnectAttachmentName, "doctl-pia")
+		config.Doit.Set(config.NS, doctl.ArgPartnerInterconnectAttachmentConnectionBandwidthInMbps, 50)
+		config.Doit.Set(config.NS, doctl.ArgPartnerInterconnectAttachmentRegion, "stage2")
+		config.Doit.Set(config.NS, doctl.ArgPartnerInterconnectAttachmentNaaSProvider, "MEGAPORT")
+		config.Doit.Set(config.NS, doctl.ArgPartnerInterconnectAttachmentVPCIDs, []string{"d35e5cb7-7957-4643-8e3a-1ab4eb3a494c"})
+		config.Doit.Set(config.NS, doctl.ArgPartnerInterconnectAttachmentBGPLocalASN, 65001)
+		config.Doit.Set(config.NS, doctl.ArgPartnerInterconnectAttachmentBGPLocalRouterIP, "192.168.1.1")
+		config.Doit.Set(config.NS, doctl.ArgPartnerInterconnectAttachmentBGPPeerASN, 65002)
+		config.Doit.Set(config.NS, doctl.ArgPartnerInterconnectAttachmentBGPPeerRouterIP, "192.168.1.2")
+
+		expectedRequest := &godo.PartnerInterconnectAttachmentCreateRequest{
+			Name:                      "doctl-pia",
+			ConnectionBandwidthInMbps: 50,
+			Region:                    "stage2",
+			NaaSProvider:              "MEGAPORT",
+			VPCIDs:                    []string{"d35e5cb7-7957-4643-8e3a-1ab4eb3a494c"},
+		}
+
+		tm.partnerInterconnectAttachment.EXPECT().Create(expectedRequest).Return(&testPartnerAttachment, nil)
+
+		err := RunPartnerInterconnectAttachmentCreate(config)
+		assert.NoError(t, err)
+	})
+}
+
+func TestPartnerInterconnectAttachmentCreateUnsupportedType(t *testing.T) {
+	withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
+		config.Doit.Set(config.NS, doctl.ArgInterconnectAttachmentType, "unsupported")
+		err := RunPartnerInterconnectAttachmentCreate(config)
+		assert.Error(t, err)
+		assert.Contains(t, err.Error(), "unsupported attachment type")
+	})
+}
diff --git a/integration/partner_interconnect_attachment_test.go b/integration/partner_interconnect_attachment_test.go
new file mode 100644
index 00000000..cf6267d8
--- /dev/null
+++ b/integration/partner_interconnect_attachment_test.go
@@ -0,0 +1,119 @@
+package integration
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"html/template"
+	"io"
+	"net/http"
+	"net/http/httptest"
+	"net/http/httputil"
+	"os/exec"
+	"strings"
+	"testing"
+
+	"github.com/sclevine/spec"
+	"github.com/stretchr/testify/require"
+)
+
+var partnerAttachmentCreateResponse = `
+{
+  "partner_interconnect_attachment": {
+    "id": "12345",
+    "name": "{{.Name}}",
+    "state": "active",
+    "connection_bandwidth_in_mbps": {{.ConnectionBandwidthInMbps}},
+    "region": "{{.Region}}",
+    "naas_provider": "{{.NaaSProvider}}",
+    "vpc_ids": ["{{index .VPCIDs 0}}"],
+    "created_at": "2025-01-30T12:00:00Z",
+    "bgp": {
+      "local_asn": 0,
+      "local_router_ip": "",
+      "peer_asn": 0,
+      "peer_router_ip": ""
+    }
+  }
+}
+`
+
+var partnerAttachmentCreateOutput = `
+ID       Name         State     Connection Bandwidth (MBPS)    Region    NaaS Provider    VPC IDs                                 Created At                       BGP Local ASN    BGP Local Router IP    BGP Peer ASN    BGP Peer Router IP
+12345    doctl-pia    active    50                             stage2    MEGAPORT         d35e5cb7-7957-4643-8e3a-1ab4eb3a494c    2025-01-30 12:00:00 +0000 UTC    0                                       0
+`
+
+var _ = suite("partner_interconnect_attachments/create", 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":
+				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)
+
+				var request struct {
+					Name                      string   `json:"name"`
+					ConnectionBandwidthInMbps int      `json:"connection_bandwidth_in_mbps"`
+					Region                    string   `json:"region"`
+					NaaSProvider              string   `json:"naas_provider"`
+					VPCIDs                    []string `json:"vpc_ids"`
+				}
+				err = json.Unmarshal(reqBody, &request)
+				expect.NoError(err)
+
+				t, err := template.New("response").Parse(partnerAttachmentCreateResponse)
+				expect.NoError(err)
+
+				var b []byte
+				buffer := bytes.NewBuffer(b)
+				err = t.Execute(buffer, request)
+				expect.NoError(err)
+
+				w.Write(buffer.Bytes())
+			default:
+				dump, err := httputil.DumpRequest(req, true)
+				if err != nil {
+					t.Fatal("failed to dump request")
+				}
+				t.Fatalf("received unknown request: %s", dump)
+			}
+		}))
+	})
+
+	when("all required flags are passed", func() {
+		it("creates new Partner Interconnect Attachment", func() {
+			cmd := exec.Command(builtBinaryPath,
+				"-t", "some-magic-token",
+				"-u", server.URL,
+				"network",
+				"interconnect-attachment",
+				"create",
+				"--name", "doctl-pia",
+				"--connection-bandwidth-in-mbps", "50",
+				"--naas-provider", "MEGAPORT",
+				"--region", "stage2",
+				"--vpc-ids", "d35e5cb7-7957-4643-8e3a-1ab4eb3a494c",
+			)
+
+			output, err := cmd.CombinedOutput()
+			expect.NoError(err, fmt.Sprintf("received error output: %s", output))
+			expect.Equal(strings.TrimSpace(partnerAttachmentCreateOutput), strings.TrimSpace(string(output)))
+		})
+	})
+})