Skip to content

Commit

Permalink
[VPC-3918] List PIA remote routes (#1658)
Browse files Browse the repository at this point in the history
* List PIA remote routes

* address pr feedback

* address pr feedback

* Add integration test.

---------

Co-authored-by: Andrew Starr-Bochicchio <a.starr.b@gmail.com>
Co-authored-by: Andrew Starr-Bochicchio <andrewsomething@users.noreply.github.com>
  • Loading branch information
3 people authored Feb 18, 2025
1 parent 0dd92c6 commit 7a3b683
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 5 deletions.
38 changes: 38 additions & 0 deletions commands/displayers/partner_interconnect_attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,41 @@ func (v *PartnerInterconnectAttachment) KV() []map[string]any {

return out
}

type PartnerInterconnectAttachmentRoute struct {
PartnerInterconnectAttachmentRoutes do.PartnerInterconnectAttachmentRoutes
}

var _ Displayable = &PartnerInterconnectAttachmentRoute{}

func (v *PartnerInterconnectAttachmentRoute) JSON(out io.Writer) error {
return writeJSON(v.PartnerInterconnectAttachmentRoutes, out)
}

func (v *PartnerInterconnectAttachmentRoute) Cols() []string {
return []string{
"ID",
"Cidr",
}
}

func (v *PartnerInterconnectAttachmentRoute) ColMap() map[string]string {
return map[string]string{
"ID": "ID",
"Cidr": "Cidr",
}
}

func (v *PartnerInterconnectAttachmentRoute) KV() []map[string]any {
out := make([]map[string]any, 0, len(v.PartnerInterconnectAttachmentRoutes))

for _, ia := range v.PartnerInterconnectAttachmentRoutes {
o := map[string]any{
"ID": ia.ID,
"Cidr": ia.Cidr,
}
out = append(out, o)
}

return out
}
36 changes: 34 additions & 2 deletions commands/partner_interconnect_attachment.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,16 @@ With the Partner Interconnect Attachments commands, you can get, list, create, u
`: doctl network --type "partner" interconnect-attachment update f81d4fae-7dec-11d0-a765-00a0c91e6bf6 --name "new-name" --
vpc-ids "270a76ed-1bb7-4c5d-a6a5-e863de086940"`

interconnectAttachmentRouteDetails := `
- The Partner Interconnect Attachment ID
- The Partner Interconnect Attachment Cidr`

cmdPartnerIARouteList := CmdBuilder(cmd, RunPartnerInterconnectAttachmentRouteList, "list-routes", "List Network Interconnect Attachment Routes", "Retrieves a list of the Network Interconnect Attachment Routes on your account, including the following information for each:"+interconnectAttachmentRouteDetails, Writer,
aliasOpt("ls-routes"), displayerType(&displayers.PartnerInterconnectAttachment{}))
AddStringFlag(cmdPartnerIARouteList, doctl.ArgInterconnectAttachmentType, "", "partner", "Specify interconnect attachment type (e.g., partner)")
cmdPartnerIARouteList.Example = `The following example lists the Network Interconnect Attachments on your account :` +
` doctl network --type "partner" interconnect-attachment list-routes --format ID,Cidr `

return cmd
}

Expand Down Expand Up @@ -260,7 +270,7 @@ func RunPartnerInterconnectAttachmentUpdate(c *CmdConfig) error {
if err != nil {
return err
}
peeringID := c.Args[0]
iaID := c.Args[0]

r := new(godo.PartnerInterconnectAttachmentUpdateRequest)
name, err := c.Doit.GetString(c.NS, doctl.ArgPartnerInterconnectAttachmentName)
Expand All @@ -275,7 +285,7 @@ func RunPartnerInterconnectAttachmentUpdate(c *CmdConfig) error {
}
r.VPCIDs = strings.Split(vpcIDs, ",")

interconnectAttachment, err := c.PartnerInterconnectAttachments().UpdatePartnerInterconnectAttachment(peeringID, r)
interconnectAttachment, err := c.PartnerInterconnectAttachments().UpdatePartnerInterconnectAttachment(iaID, r)
if err != nil {
return err
}
Expand Down Expand Up @@ -336,6 +346,28 @@ func RunPartnerInterconnectAttachmentDelete(c *CmdConfig) error {
return nil
}

// RunPartnerInterconnectAttachmentRouteList lists Partner Interconnect Attachment routes
func RunPartnerInterconnectAttachmentRouteList(c *CmdConfig) error {
if err := ensurePartnerAttachmentType(c); err != nil {
return err
}

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

pias := c.PartnerInterconnectAttachments()
routeList, err := pias.ListPartnerInterconnectAttachmentRoutes(iaID)
if err != nil {
return err
}

item := &displayers.PartnerInterconnectAttachmentRoute{PartnerInterconnectAttachmentRoutes: routeList}
return c.Display(item)
}

func waitForPIA(pias do.PartnerInterconnectAttachmentsService, iaID string, wantStatus string, terminateOnNotFound bool) error {
const maxAttempts = 360
const errStatus = "ERROR"
Expand Down
26 changes: 25 additions & 1 deletion commands/partner_interconnect_attachment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,24 @@ var (
testPartnerIAList = do.PartnerInterconnectAttachments{
testPartnerAttachment,
}

testPartnerAttachmentRoute = do.PartnerInterconnectAttachmentRoute{
RemoteRoute: &godo.RemoteRoute{
ID: "test-route-id",
Cidr: "10.10.0.0/24",
},
}

testPartnerIARouteList = do.PartnerInterconnectAttachmentRoutes{
testPartnerAttachmentRoute,
}
)

func TestPartnerInterconnectAttachmentsCommand(t *testing.T) {
cmd := PartnerInterconnectAttachments()
assert.NotNil(t, cmd)

assertCommandNames(t, cmd, "create", "get", "list", "delete", "update")
assertCommandNames(t, cmd, "create", "get", "list", "delete", "update", "list-routes")
}

func TestPartnerInterconnectAttachmentCreate(t *testing.T) {
Expand Down Expand Up @@ -150,3 +161,16 @@ func TestInterconnectAttachmentsUpdateNoID(t *testing.T) {
assert.Error(t, err)
})
}

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

iaID := "ia-uuid1"
config.Args = append(config.Args, iaID)
tm.partnerInterconnectAttachment.EXPECT().ListPartnerInterconnectAttachmentRoutes(iaID).Return(testPartnerIARouteList, nil)

err := RunPartnerInterconnectAttachmentRouteList(config)
assert.NoError(t, err)
})
}
15 changes: 15 additions & 0 deletions do/mocks/PartnerInterconnectAttachmentsService.go

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

39 changes: 39 additions & 0 deletions do/partner_interconnect_attachments.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ type PartnerInterconnectAttachment struct {
// PartnerInterconnectAttachments is a slice of PartnerInterconnectAttachment.
type PartnerInterconnectAttachments []PartnerInterconnectAttachment

// PartnerInterconnectAttachmentRoute wraps a godo RemoteRoute.
type PartnerInterconnectAttachmentRoute struct {
*godo.RemoteRoute
}

// PartnerInterconnectAttachmentRoutes is a slice of PartnerInterconnectAttachmentRoute.
type PartnerInterconnectAttachmentRoutes []PartnerInterconnectAttachmentRoute

// PartnerInterconnectAttachmentsService is an interface for interacting with
// DigitalOcean's partner interconnect attachments api.
type PartnerInterconnectAttachmentsService interface {
Expand All @@ -35,6 +43,7 @@ type PartnerInterconnectAttachmentsService interface {
ListPartnerInterconnectAttachments() (PartnerInterconnectAttachments, error)
DeletePartnerInterconnectAttachment(iaID string) error
UpdatePartnerInterconnectAttachment(iaID string, req *godo.PartnerInterconnectAttachmentUpdateRequest) (*PartnerInterconnectAttachment, error)
ListPartnerInterconnectAttachmentRoutes(iaID string) (PartnerInterconnectAttachmentRoutes, error)
}

var _ PartnerInterconnectAttachmentsService = &partnerInterconnectAttachmentsService{}
Expand Down Expand Up @@ -111,3 +120,33 @@ func (p *partnerInterconnectAttachmentsService) UpdatePartnerInterconnectAttachm

return &PartnerInterconnectAttachment{PartnerInterconnectAttachment: partnerIA}, nil
}

// ListPartnerInterconnectAttachmentRoutes lists all partner interconnect attachment routes.
func (p *partnerInterconnectAttachmentsService) ListPartnerInterconnectAttachmentRoutes(iaID string) (PartnerInterconnectAttachmentRoutes, error) {
f := func(opt *godo.ListOptions) ([]any, *godo.Response, error) {
list, resp, err := p.client.PartnerInterconnectAttachments.ListRoutes(context.TODO(), iaID, opt)
if err != nil {
return nil, nil, err
}

si := make([]any, len(list))
for i := range list {
si[i] = list[i]
}

return si, resp, err
}

si, err := PaginateResp(f)
if err != nil {
return nil, err
}

list := make([]PartnerInterconnectAttachmentRoute, len(si))
for i := range si {
a := si[i].(*godo.RemoteRoute)
list[i] = PartnerInterconnectAttachmentRoute{RemoteRoute: a}
}

return list, nil
}
103 changes: 101 additions & 2 deletions integration/partner_interconnect_attachment_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import (
"github.com/stretchr/testify/require"
)

var partnerAttachmentCreateResponse = `
var (
partnerAttachmentCreateResponse = `
{
"partner_interconnect_attachment": {
"id": "12345",
Expand All @@ -38,11 +39,37 @@ var partnerAttachmentCreateResponse = `
}
`

var partnerAttachmentCreateOutput = `
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
`

interconnectListRoutesOutput = `
ID Cidr
a0eb6eb0-fa38-41a8-a5de-1a75524667fe 169.250.0.0/29
`

interconnectListRoutesResponse = `
{
"remote_routes": [
{"id": "a0eb6eb0-fa38-41a8-a5de-1a75524667fe", "cidr": "169.250.0.0/29"}
],
"links": {
"pages": {
"last": "http://localhost/v2/partner_interconnect/attachments?page=1&per_page=1",
"next": "http://localhost/v2/partner_interconnect/attachments?page=2&per_page=1"
}
},
"links": {
"pages": {}
},
"meta": {
"total": 1
}
}
`
)

var _ = suite("partner_interconnect_attachments/create", func(t *testing.T, when spec.G, it spec.S) {
var (
expect *require.Assertions
Expand Down Expand Up @@ -117,3 +144,75 @@ var _ = suite("partner_interconnect_attachments/create", func(t *testing.T, when
})
})
})

var _ = suite("partner_interconnect_attachments/list-routes", func(t *testing.T, when spec.G, it spec.S) {
var (
expect *require.Assertions
server *httptest.Server
)

it.Before(func() {
expect = require.New(t)

server = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
switch req.URL.Path {
case "/v2/partner_interconnect/attachments/c5537207-ebf0-47cb-bc10-6fac717cd672/remote_routes":
auth := req.Header.Get("Authorization")
if auth != "Bearer some-magic-token" {
w.WriteHeader(http.StatusUnauthorized)
return
}

if req.Method != http.MethodGet {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}

w.Write([]byte(interconnectListRoutesResponse))
default:
dump, err := httputil.DumpRequest(req, true)
if err != nil {
t.Fatal("failed to dump request")
}

t.Fatalf("received unknown request: %s", dump)
}
}))
})

when("no flags are passed", func() {
it("gets the specified VPC", func() {
cmd := exec.Command(builtBinaryPath,
"-t", "some-magic-token",
"-u", server.URL,
"network",
"interconnect-attachment",
"list-routes",
"c5537207-ebf0-47cb-bc10-6fac717cd672",
)

output, err := cmd.CombinedOutput()
expect.NoError(err, fmt.Sprintf("received error output: %s", output))
expect.Equal(strings.TrimSpace(interconnectListRoutesOutput), strings.TrimSpace(string(output)))
})
})

when("format and no-header flags are passed", func() {
it("gets the specified VPC", func() {
cmd := exec.Command(builtBinaryPath,
"-t", "some-magic-token",
"-u", server.URL,
"network",
"interconnect-attachment",
"list-routes",
"--format", "Cidr",
"--no-header",
"c5537207-ebf0-47cb-bc10-6fac717cd672",
)

output, err := cmd.CombinedOutput()
expect.NoError(err, fmt.Sprintf("received error output: %s", output))
expect.Equal("169.250.0.0/29", strings.TrimSpace(string(output)))
})
})
})

0 comments on commit 7a3b683

Please sign in to comment.