diff --git a/commands/partner_interconnect_attachment.go b/commands/partner_interconnect_attachment.go index 592ba5429..3ed0771df 100644 --- a/commands/partner_interconnect_attachment.go +++ b/commands/partner_interconnect_attachment.go @@ -15,12 +15,16 @@ package commands import ( "fmt" + "os" + "strings" + "time" + + "github.com/digitalocean/godo" + "github.com/spf13/cobra" "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. @@ -67,6 +71,56 @@ With the Partner Interconnect Attachments commands, you can get, list, create, u 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"` + interconnectAttachmentDetails := ` +- The Partner Interconnect Attachment ID +- The Partner Interconnect Attachment Name +- The Partner Interconnect Attachment State +- The Partner Interconnect Attachment Connection Bandwidth in Mbps +- The Partner Interconnect Attachment Region +- The Partner Interconnect Attachment NaaS Provider +- The Partner Interconnect Attachment VPC network IDs +- The Partner Interconnect Attachment creation date, in ISO8601 combined date and time format +- The Partner Interconnect Attachment BGP Local ASN +- The Partner Interconnect Attachment BGP Local Router IP +- The Partner Interconnect Attachment BGP Peer ASN +- The Partner Interconnect Attachment BGP Peer Router IP` + + cmdPartnerIAGet := CmdBuilder(cmd, RunPartnerInterconnectAttachmentGet, "get ", + "Retrieves a Partner Interconnect Attachment", "Retrieves information about a Partner Interconnect Attachment, including:"+interconnectAttachmentDetails, Writer, + aliasOpt("g"), displayerType(&displayers.PartnerInterconnectAttachment{})) + AddStringFlag(cmdPartnerIAGet, doctl.ArgInterconnectAttachmentType, "", "partner", "Specify interconnect attachment type (e.g., partner)") + cmdPartnerIAGet.Example = `The following example retrieves information about a Partner Interconnect Attachment with the ID ` + "`" + `f81d4fae-7dec-11d0-a765-00a0c91e6bf6` + "`" + + `: doctl network --type "partner" interconnect-attachment get f81d4fae-7dec-11d0-a765-00a0c91e6bf6` + + cmdPartnerIAList := CmdBuilder(cmd, RunPartnerInterconnectAttachmentList, "list", "List Network Interconnect Attachments", "Retrieves a list of the Network Interconnect Attachments on your account, including the following information for each:"+interconnectAttachmentDetails, Writer, + aliasOpt("ls"), displayerType(&displayers.PartnerInterconnectAttachment{})) + AddStringFlag(cmdPartnerIAList, doctl.ArgInterconnectAttachmentType, "", "partner", "Specify interconnect attachment type (e.g., partner)") + 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 ", + "Deletes a Partner Interconnect Attachment", "Deletes information about a Partner Interconnect Attachment. This is irreversible ", Writer, + aliasOpt("rm"), displayerType(&displayers.PartnerInterconnectAttachment{})) + AddBoolFlag(cmdPartnerIADelete, doctl.ArgForce, doctl.ArgShortForce, false, + "Delete the VPC Peering without any confirmation prompt") + AddBoolFlag(cmdPartnerIADelete, doctl.ArgCommandWait, "", false, + "Boolean that specifies whether to wait for a VPC Peering deletion to complete before returning control to the terminal") + AddStringFlag(cmdPartnerIADelete, doctl.ArgInterconnectAttachmentType, "", "partner", "Specify interconnect attachment type (e.g., partner)") + 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 ", + "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.ArgInterconnectAttachmentType, "", "partner", "Specify interconnect attachment type (e.g., partner)") + 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" -- +vpc-ids "270a76ed-1bb7-4c5d-a6a5-e863de086940"` + return cmd } @@ -153,3 +207,169 @@ func RunPartnerInterconnectAttachmentCreate(c *CmdConfig) error { item := &displayers.PartnerInterconnectAttachment{PartnerInterconnectAttachments: do.PartnerInterconnectAttachments{*pia}} return c.Display(item) } + +// RunPartnerInterconnectAttachmentGet retrieves an existing Partner Interconnect Attachment by its identifier. +func RunPartnerInterconnectAttachmentGet(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() + interconnectAttachment, err := pias.GetPartnerInterconnectAttachment(iaID) + if err != nil { + return err + } + + item := &displayers.PartnerInterconnectAttachment{ + PartnerInterconnectAttachments: do.PartnerInterconnectAttachments{*interconnectAttachment}, + } + return c.Display(item) +} + +// RunPartnerInterconnectAttachmentList lists Partner Interconnect Attachment +func RunPartnerInterconnectAttachmentList(c *CmdConfig) error { + + if err := ensurePartnerAttachmentType(c); err != nil { + return err + } + + pias := c.PartnerInterconnectAttachments() + list, err := pias.ListPartnerInterconnectAttachments() + if err != nil { + return err + } + + 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.PartnerInterconnectAttachments().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 { + + pias := c.PartnerInterconnectAttachments() + err := pias.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(pias, 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(pias do.PartnerInterconnectAttachmentsService, 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 := pias.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) +} diff --git a/commands/partner_interconnect_attachment_test.go b/commands/partner_interconnect_attachment_test.go index df7048c3b..2f9eda145 100644 --- a/commands/partner_interconnect_attachment_test.go +++ b/commands/partner_interconnect_attachment_test.go @@ -1,6 +1,7 @@ package commands import ( + "strings" "testing" "time" @@ -23,13 +24,17 @@ var ( CreatedAt: time.Date(2025, 1, 30, 0, 0, 0, 0, time.UTC), }, } + + testPartnerIAList = do.PartnerInterconnectAttachments{ + testPartnerAttachment, + } ) func TestPartnerInterconnectAttachmentsCommand(t *testing.T) { cmd := PartnerInterconnectAttachments() assert.NotNil(t, cmd) - assertCommandNames(t, cmd, "create") + assertCommandNames(t, cmd, "create", "get", "list", "delete", "update") } func TestPartnerInterconnectAttachmentCreate(t *testing.T) { @@ -68,3 +73,80 @@ func TestPartnerInterconnectAttachmentCreateUnsupportedType(t *testing.T) { assert.Contains(t, err.Error(), "unsupported attachment type") }) } + +func TestInterconnectAttachmentsGet(t *testing.T) { + withTestClient(t, func(config *CmdConfig, tm *tcMocks) { + config.Doit.Set(config.NS, doctl.ArgInterconnectAttachmentType, "partner") + + iaID := "e819b321-a9a1-4078-b437-8e6b8bf13530" + tm.partnerInterconnectAttachment.EXPECT().GetPartnerInterconnectAttachment(iaID).Return(&testPartnerAttachment, nil) + + config.Args = append(config.Args, iaID) + + err := RunPartnerInterconnectAttachmentGet(config) + assert.NoError(t, err) + }) +} + +func TestInterconnectAttachmentsGetNoID(t *testing.T) { + withTestClient(t, func(config *CmdConfig, tm *tcMocks) { + config.Doit.Set(config.NS, doctl.ArgInterconnectAttachmentType, "partner") + + err := RunPartnerInterconnectAttachmentGet(config) + assert.Error(t, err) + }) +} + +func TestInterconnectAttachmentsList(t *testing.T) { + withTestClient(t, func(config *CmdConfig, tm *tcMocks) { + config.Doit.Set(config.NS, doctl.ArgInterconnectAttachmentType, "partner") + + tm.partnerInterconnectAttachment.EXPECT().ListPartnerInterconnectAttachments().Return(testPartnerIAList, nil) + + err := RunPartnerInterconnectAttachmentList(config) + assert.NoError(t, err) + }) +} + +func TestInterconnectAttachmentsDelete(t *testing.T) { + withTestClient(t, func(config *CmdConfig, tm *tcMocks) { + config.Doit.Set(config.NS, doctl.ArgInterconnectAttachmentType, "partner") + + iaID := "e819b321-a9a1-4078-b437-8e6b8bf13530" + tm.partnerInterconnectAttachment.EXPECT().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(config.NS, 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.partnerInterconnectAttachment.EXPECT().UpdatePartnerInterconnectAttachment(iaID, &r).Return(&testPartnerAttachment, 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(config.NS, doctl.ArgInterconnectAttachmentType, "partner") + + err := RunPartnerInterconnectAttachmentUpdate(config) + assert.Error(t, err) + }) +} diff --git a/do/mocks/PartnerInterconnectAttachmentsService.go b/do/mocks/PartnerInterconnectAttachmentsService.go index 99e0e5b72..3174ca740 100644 --- a/do/mocks/PartnerInterconnectAttachmentsService.go +++ b/do/mocks/PartnerInterconnectAttachmentsService.go @@ -3,7 +3,7 @@ // // Generated by this command: // -// mockgen -source partner_interconnect_attachments.go -package=mocks -destination=mocks/PartnerInterconnectAttachmentsService.go PartnerInterconnectAttachmentsService +// mockgen -source partner_interconnect_attachments.go -package=mocks PartnerInterconnectAttachmentsService // // Package mocks is a generated GoMock package. @@ -55,3 +55,62 @@ func (mr *MockPartnerInterconnectAttachmentsServiceMockRecorder) Create(arg0 any mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockPartnerInterconnectAttachmentsService)(nil).Create), arg0) } + +// DeletePartnerInterconnectAttachment mocks base method. +func (m *MockPartnerInterconnectAttachmentsService) DeletePartnerInterconnectAttachment(iaID string) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DeletePartnerInterconnectAttachment", iaID) + ret0, _ := ret[0].(error) + return ret0 +} + +// DeletePartnerInterconnectAttachment indicates an expected call of DeletePartnerInterconnectAttachment. +func (mr *MockPartnerInterconnectAttachmentsServiceMockRecorder) DeletePartnerInterconnectAttachment(iaID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeletePartnerInterconnectAttachment", reflect.TypeOf((*MockPartnerInterconnectAttachmentsService)(nil).DeletePartnerInterconnectAttachment), iaID) +} + +// GetPartnerInterconnectAttachment mocks base method. +func (m *MockPartnerInterconnectAttachmentsService) GetPartnerInterconnectAttachment(iaID string) (*do.PartnerInterconnectAttachment, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetPartnerInterconnectAttachment", iaID) + ret0, _ := ret[0].(*do.PartnerInterconnectAttachment) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// GetPartnerInterconnectAttachment indicates an expected call of GetPartnerInterconnectAttachment. +func (mr *MockPartnerInterconnectAttachmentsServiceMockRecorder) GetPartnerInterconnectAttachment(iaID any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPartnerInterconnectAttachment", reflect.TypeOf((*MockPartnerInterconnectAttachmentsService)(nil).GetPartnerInterconnectAttachment), iaID) +} + +// ListPartnerInterconnectAttachments mocks base method. +func (m *MockPartnerInterconnectAttachmentsService) ListPartnerInterconnectAttachments() (do.PartnerInterconnectAttachments, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "ListPartnerInterconnectAttachments") + ret0, _ := ret[0].(do.PartnerInterconnectAttachments) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// ListPartnerInterconnectAttachments indicates an expected call of ListPartnerInterconnectAttachments. +func (mr *MockPartnerInterconnectAttachmentsServiceMockRecorder) ListPartnerInterconnectAttachments() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListPartnerInterconnectAttachments", reflect.TypeOf((*MockPartnerInterconnectAttachmentsService)(nil).ListPartnerInterconnectAttachments)) +} + +// UpdatePartnerInterconnectAttachment mocks base method. +func (m *MockPartnerInterconnectAttachmentsService) UpdatePartnerInterconnectAttachment(iaID string, req *godo.PartnerInterconnectAttachmentUpdateRequest) (*do.PartnerInterconnectAttachment, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UpdatePartnerInterconnectAttachment", iaID, req) + ret0, _ := ret[0].(*do.PartnerInterconnectAttachment) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// UpdatePartnerInterconnectAttachment indicates an expected call of UpdatePartnerInterconnectAttachment. +func (mr *MockPartnerInterconnectAttachmentsServiceMockRecorder) UpdatePartnerInterconnectAttachment(iaID, req any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdatePartnerInterconnectAttachment", reflect.TypeOf((*MockPartnerInterconnectAttachmentsService)(nil).UpdatePartnerInterconnectAttachment), iaID, req) +} diff --git a/do/partner_interconnect_attachments.go b/do/partner_interconnect_attachments.go index 8d5ccc39b..f7ea118f3 100644 --- a/do/partner_interconnect_attachments.go +++ b/do/partner_interconnect_attachments.go @@ -31,6 +31,10 @@ type PartnerInterconnectAttachments []PartnerInterconnectAttachment // DigitalOcean's partner interconnect attachments api. type PartnerInterconnectAttachmentsService interface { Create(*godo.PartnerInterconnectAttachmentCreateRequest) (*PartnerInterconnectAttachment, error) + GetPartnerInterconnectAttachment(iaID string) (*PartnerInterconnectAttachment, error) + ListPartnerInterconnectAttachments() (PartnerInterconnectAttachments, error) + DeletePartnerInterconnectAttachment(iaID string) error + UpdatePartnerInterconnectAttachment(iaID string, req *godo.PartnerInterconnectAttachmentUpdateRequest) (*PartnerInterconnectAttachment, error) } var _ PartnerInterconnectAttachmentsService = &partnerInterconnectAttachmentsService{} @@ -54,3 +58,56 @@ func (p *partnerInterconnectAttachmentsService) Create(req *godo.PartnerIntercon } return &PartnerInterconnectAttachment{PartnerInterconnectAttachment: pia}, nil } + +// GetPartnerInterconnectAttachment retrieves a partner interconnect attachment. +func (p *partnerInterconnectAttachmentsService) GetPartnerInterconnectAttachment(iaID string) (*PartnerInterconnectAttachment, error) { + partnerIA, _, err := p.client.PartnerInterconnectAttachments.Get(context.TODO(), iaID) + if err != nil { + return nil, err + } + return &PartnerInterconnectAttachment{PartnerInterconnectAttachment: partnerIA}, nil +} + +// ListPartnerInterconnectAttachments lists all partner interconnect attachments. +func (p *partnerInterconnectAttachmentsService) ListPartnerInterconnectAttachments() (PartnerInterconnectAttachments, error) { + f := func(opt *godo.ListOptions) ([]any, *godo.Response, error) { + list, resp, err := p.client.PartnerInterconnectAttachments.List(context.TODO(), 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([]PartnerInterconnectAttachment, len(si)) + for i := range si { + a := si[i].(*godo.PartnerInterconnectAttachment) + list[i] = PartnerInterconnectAttachment{PartnerInterconnectAttachment: a} + } + + return list, nil +} + +func (p *partnerInterconnectAttachmentsService) DeletePartnerInterconnectAttachment(iaID string) error { + _, err := p.client.PartnerInterconnectAttachments.Delete(context.TODO(), iaID) + return err +} + +func (p *partnerInterconnectAttachmentsService) UpdatePartnerInterconnectAttachment(iaID string, req *godo.PartnerInterconnectAttachmentUpdateRequest) (*PartnerInterconnectAttachment, error) { + partnerIA, _, err := p.client.PartnerInterconnectAttachments.Update(context.TODO(), iaID, req) + if err != nil { + return nil, err + } + + return &PartnerInterconnectAttachment{PartnerInterconnectAttachment: partnerIA}, nil +} diff --git a/scripts/regenmocks.sh b/scripts/regenmocks.sh index 817711a22..1d2866cc4 100755 --- a/scripts/regenmocks.sh +++ b/scripts/regenmocks.sh @@ -46,3 +46,4 @@ mockgen -source monitoring.go -package=mocks MonitoringService > mocks/Monitorin mockgen -source reserved_ip_actions.go -package=mocks ReservedIPActionsService > mocks/ReservedIPActionsService.go mockgen -source reserved_ips.go -package=mocks ReservedIPsService > mocks/ReservedIPsService.go mockgen -source serverless.go -package=mocks ServerlessService > mocks/ServerlessService.go +mockgen -source partner_interconnect_attachments.go -package=mocks PartnerInterconnectAttachmentsService > mocks/PartnerInterconnectAttachmentsService.go