diff --git a/args.go b/args.go index 219cf5d44..a66aa8df1 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 c0e6bfdc9..14bd8e98b 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/commands_test.go b/commands/commands_test.go index 160f65a0c..b3cd1ae0c 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/displayers/partner_interconnect_attachment.go b/commands/displayers/partner_interconnect_attachment.go new file mode 100644 index 000000000..301782771 --- /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 f4f42e23f..7a68715a7 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 000000000..592ba5429 --- /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/commands/partner_interconnect_attachment_test.go b/commands/partner_interconnect_attachment_test.go new file mode 100644 index 000000000..df7048c3b --- /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/do/mocks/PartnerInterconnectAttachmentsService.go b/do/mocks/PartnerInterconnectAttachmentsService.go new file mode 100644 index 000000000..99e0e5b72 --- /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 000000000..8d5ccc39b --- /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 bd597bb09..695e7f930 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 98415bb4a..298c4ec24 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/integration/partner_interconnect_attachment_test.go b/integration/partner_interconnect_attachment_test.go new file mode 100644 index 000000000..cf6267d81 --- /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))) + }) + }) +}) diff --git a/vendor/github.com/digitalocean/godo/CHANGELOG.md b/vendor/github.com/digitalocean/godo/CHANGELOG.md index ce1110c80..8f926be62 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 1217ef05e..ed4b4f8ba 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 de918ba2b..ff4c04e1e 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 87a0e9ed6..df21c9292 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 447dcc026..4f4af1808 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