Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions args.go
Original file line number Diff line number Diff line change
Expand Up @@ -525,6 +525,10 @@ const (

// ArgInterconnectAttachmentType is the type of the Interconnect Attachment e.g. "partner".
ArgInterconnectAttachmentType = "type"
// ArgPartnerInterconnectAttachmentName is a name of the Partner Interconnect Attachment.
ArgPartnerInterconnectAttachmentName = "name"
// ArgPartnerInterconnectAttachmentVPCIDs are the IDs of the VPCs which the Partner Interconnect Attachment is connected
ArgPartnerInterconnectAttachmentVPCIDs = "vpc-ids"

// ArgReadWrite indicates a generated token should be read/write.
ArgReadWrite = "read-write"
Expand Down
143 changes: 143 additions & 0 deletions commands/partner_interconnect_attachments.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ package commands

import (
"fmt"
"os"
"strings"
"time"

"github.com/spf13/cobra"
"github.com/spf13/viper"

"github.com/digitalocean/doctl"
"github.com/digitalocean/doctl/commands/displayers"
"github.com/digitalocean/doctl/do"
"github.com/digitalocean/godo"
)

// Network creates the partner commands
Expand Down Expand Up @@ -68,6 +71,22 @@ With the Partner Interconnect Attachments commands, you can get or list, create,
cmdPartnerIAList.Example = `The following example lists the Network Interconnect Attachments on your account :" +
" doctl network --type "partner" interconnect-attachment list --format Name,VPCIDs`

cmdPartnerIADelete := CmdBuilder(cmd, RunPartnerInterconnectAttachmentDelete, "delete <interconnect-attachment-id>",
"Deletes a Partner Interconnect Attachment", "Deletes information about a Partner Interconnect Attachment. This is irreversible ", Writer,
aliasOpt("rm"), displayerType(&displayers.PartnerInterconnectAttachment{}))
cmdPartnerIADelete.Example = `The following example deletes a Partner Interconnect Attachment with the ID ` + "`" + `f81d4fae-7dec-11d0-a765-00a0c91e6bf6` + "`" +
`: doctl network --type "partner" interconnect-attachment delete f81d4fae-7dec-11d0-a765-00a0c91e6bf6`

cmdPartnerIAUpdate := CmdBuilder(cmd, RunPartnerInterconnectAttachmentUpdate, "update <interconnect-attachment-id>",
"Update a Partner Interconnect Attachment's name and configuration", `Use this command to update the name and and configuration of a Partner Interconnect Attachment`, Writer, aliasOpt("u"))
AddStringFlag(cmdPartnerIAUpdate, doctl.ArgPartnerInterconnectAttachmentName, "", "",
"The Partner Interconnect Attachment's name", requiredOpt())
AddStringFlag(cmdPartnerIAUpdate, doctl.ArgPartnerInterconnectAttachmentVPCIDs, "", "",
"The Partner Interconnect Attachment's vpc ids", requiredOpt())
cmdPartnerIAUpdate.Example = `The following example updates the name of a Partner Interconnect Attachment with the ID ` +
"`" + `f81d4fae-7dec-11d0-a765-00a0c91e6bf6` + "`" + ` to ` + "`" + `new-name` + "`" +
`: doctl network --type "partner" interconnect-attachment update f81d4fae-7dec-11d0-a765-00a0c91e6bf6 --name new-name`

return cmd
}

Expand Down Expand Up @@ -121,3 +140,127 @@ func RunPartnerInterconnectAttachmentList(c *CmdConfig) error {
item := &displayers.PartnerInterconnectAttachment{PartnerInterconnectAttachments: list}
return c.Display(item)
}

// RunPartnerInterconnectAttachmentUpdate updates an existing Partner Interconnect Attachment with new configuration.
func RunPartnerInterconnectAttachmentUpdate(c *CmdConfig) error {
if err := ensurePartnerAttachmentType(c); err != nil {
return err
}

err := ensureOneArg(c)
if err != nil {
return err
}
peeringID := c.Args[0]

r := new(godo.PartnerInterconnectAttachmentUpdateRequest)
name, err := c.Doit.GetString(c.NS, doctl.ArgPartnerInterconnectAttachmentName)
if err != nil {
return err
}
r.Name = name

vpcIDs, err := c.Doit.GetString(c.NS, doctl.ArgPartnerInterconnectAttachmentVPCIDs)
if err != nil {
return err
}
r.VPCIDs = strings.Split(vpcIDs, ",")

interconnectAttachment, err := c.VPCs().UpdatePartnerInterconnectAttachment(peeringID, r)
if err != nil {
return err
}

item := &displayers.PartnerInterconnectAttachment{
PartnerInterconnectAttachments: do.PartnerInterconnectAttachments{*interconnectAttachment},
}
return c.Display(item)
}

// RunPartnerInterconnectAttachmentDelete deletes an existing Partner Interconnect Attachment by its identifier.
func RunPartnerInterconnectAttachmentDelete(c *CmdConfig) error {

if err := ensurePartnerAttachmentType(c); err != nil {
return err
}

err := ensureOneArg(c)
if err != nil {
return err
}
iaID := c.Args[0]

force, err := c.Doit.GetBool(c.NS, doctl.ArgForce)
if err != nil {
return err
}

if force || AskForConfirmDelete("Partner Interconnect Attachment", 1) == nil {

vpcs := c.VPCs()
err := vpcs.DeletePartnerInterconnectAttachment(iaID)
if err != nil {
return err
}

wait, err := c.Doit.GetBool(c.NS, doctl.ArgCommandWait)
if err != nil {
return err
}

if wait {
notice("Partner Interconnect Attachment is in progress, waiting for Partner Interconnect Attachment to be deleted")

err := waitForPIA(vpcs, iaID, "DELETED", true)
if err != nil {
return fmt.Errorf("Partner Interconnect Attachment couldn't be deleted : %v", err)
}
notice("Partner Interconnect Attachment is successfully deleted")
} else {
notice("Partner Interconnect Attachment deletion request accepted")
}

} else {
return fmt.Errorf("operation aborted")
}

return nil
}

func waitForPIA(vpcService do.VPCsService, iaID string, wantStatus string, terminateOnNotFound bool) error {
const maxAttempts = 360
const errStatus = "ERROR"
attempts := 0
printNewLineSet := false

for i := 0; i < maxAttempts; i++ {
if attempts != 0 {
fmt.Fprint(os.Stderr, ".")
if !printNewLineSet {
printNewLineSet = true
defer fmt.Fprintln(os.Stderr)
}
}

interconnectAttachment, err := vpcService.GetPartnerInterconnectAttachment(iaID)
if err != nil {
if terminateOnNotFound && strings.Contains(err.Error(), "not found") {
return nil
}
return err
}

if interconnectAttachment.State == errStatus {
return fmt.Errorf("Partner Interconnect Attachment (%s) entered status `%s`", iaID, errStatus)
}

if interconnectAttachment.State == wantStatus {
return nil
}

attempts++
time.Sleep(5 * time.Second)
}

return fmt.Errorf("timeout waiting for Partner Interconnect Attachment (%s) to become %s", iaID, wantStatus)
}
46 changes: 45 additions & 1 deletion commands/partner_interconnect_attachments_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package commands

import (
"strings"
"testing"

"github.com/digitalocean/godo"
Expand All @@ -26,7 +27,7 @@ var (
func TestInterconnectAttachmentsCommand(t *testing.T) {
cmd := PartnerInterconnectAttachments()
assert.NotNil(t, cmd)
assertCommandNames(t, cmd, "get", "list")
assertCommandNames(t, cmd, "get", "list", "delete", "update")
}

func TestInterconnectAttachmentsGet(t *testing.T) {
Expand Down Expand Up @@ -59,3 +60,46 @@ func TestInterconnectAttachmentsList(t *testing.T) {
assert.NoError(t, err)
})
}

func TestInterconnectAttachmentsDelete(t *testing.T) {
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
config.Doit.Set("network", doctl.ArgInterconnectAttachmentType, "partner")

iaID := "e819b321-a9a1-4078-b437-8e6b8bf13530"
tm.vpcs.EXPECT().DeletePartnerInterconnectAttachment(iaID).Return(nil)

config.Args = append(config.Args, iaID)
config.Doit.Set(config.NS, doctl.ArgForce, true)

err := RunPartnerInterconnectAttachmentDelete(config)
assert.NoError(t, err)
})
}

func TestInterconnectAttachmentsUpdate(t *testing.T) {
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
config.Doit.Set("network", doctl.ArgInterconnectAttachmentType, "partner")

iaID := "ia-uuid1"
iaName := "ia-name"
vpcIDs := "f81d4fae-7dec-11d0-a765-00a0c91e6bf6,3f900b61-30d7-40d8-9711-8c5d6264b268"
r := godo.PartnerInterconnectAttachmentUpdateRequest{Name: iaName, VPCIDs: strings.Split(vpcIDs, ",")}
tm.vpcs.EXPECT().UpdatePartnerInterconnectAttachment(iaID, &r).Return(&testIA, nil)

config.Args = append(config.Args, iaID)
config.Doit.Set(config.NS, doctl.ArgPartnerInterconnectAttachmentName, iaName)
config.Doit.Set(config.NS, doctl.ArgPartnerInterconnectAttachmentVPCIDs, vpcIDs)

err := RunPartnerInterconnectAttachmentUpdate(config)
assert.NoError(t, err)
})
}

func TestInterconnectAttachmentsUpdateNoID(t *testing.T) {
withTestClient(t, func(config *CmdConfig, tm *tcMocks) {
config.Doit.Set("network", doctl.ArgInterconnectAttachmentType, "partner")

err := RunPartnerInterconnectAttachmentUpdate(config)
assert.Error(t, err)
})
}
29 changes: 29 additions & 0 deletions do/mocks/VPCsService.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions do/vpcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ type VPCsService interface {
ListVPCPeeringsByVPCID(vpcID string) (VPCPeerings, error)
GetPartnerInterconnectAttachment(iaID string) (*PartnerInterconnectAttachment, error)
ListPartnerInterconnectAttachments() (PartnerInterconnectAttachments, error)
DeletePartnerInterconnectAttachment(iaID string) error
UpdatePartnerInterconnectAttachment(iaID string, req *godo.PartnerInterconnectAttachmentUpdateRequest) (*PartnerInterconnectAttachment, error)
}

var _ VPCsService = &vpcsService{}
Expand Down Expand Up @@ -268,3 +270,17 @@ func (v *vpcsService) ListPartnerInterconnectAttachments() (PartnerInterconnectA

return list, nil
}

func (v *vpcsService) DeletePartnerInterconnectAttachment(iaID string) error {
_, err := v.client.PartnerInterconnectAttachments.Delete(context.TODO(), iaID)
return err
}

func (v *vpcsService) UpdatePartnerInterconnectAttachment(iaID string, req *godo.PartnerInterconnectAttachmentUpdateRequest) (*PartnerInterconnectAttachment, error) {
partnerIA, _, err := v.client.PartnerInterconnectAttachments.Update(context.TODO(), iaID, req)
if err != nil {
return nil, err
}

return &PartnerInterconnectAttachment{PartnerInterconnectAttachment: partnerIA}, nil
}