diff --git a/go.mod b/go.mod index 65ba1651..494b1316 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/equinix/metal-cli go 1.19 require ( - github.com/equinix-labs/metal-go v0.22.2 + github.com/equinix-labs/metal-go v0.23.1 github.com/manifoldco/promptui v0.9.0 github.com/olekukonko/tablewriter v0.0.5 github.com/packethost/packngo v0.30.0 diff --git a/go.sum b/go.sum index f65caace..ad899496 100644 --- a/go.sum +++ b/go.sum @@ -62,8 +62,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/equinix-labs/metal-go v0.22.2 h1:3uVx1tMUb+P9MXcQzmoh5sRM40+Efdtc7yWwlKOh/ms= -github.com/equinix-labs/metal-go v0.22.2/go.mod h1:SmxCklxW+KjmBLVMdEXgtFO5gD5/b4N0VxcNgUYbOH4= +github.com/equinix-labs/metal-go v0.23.1 h1:u6rdCKW7ZN/ydxS63+cbPMjHk8wYiVPYJsb8LB1sXZM= +github.com/equinix-labs/metal-go v0.23.1/go.mod h1:SmxCklxW+KjmBLVMdEXgtFO5gD5/b4N0VxcNgUYbOH4= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= diff --git a/internal/capacity/check.go b/internal/capacity/check.go index 847bf85f..0b758fd1 100644 --- a/internal/capacity/check.go +++ b/internal/capacity/check.go @@ -123,7 +123,7 @@ func (c *Client) Check() *cobra.Command { data[i] = []string{ s.GetMetro(), s.GetPlan(), - s.GetQuantity(), + string(s.GetQuantity()), strconv.FormatBool(s.GetAvailable()), } } diff --git a/internal/ips/assign.go b/internal/ips/assign.go index 1f927247..6bb6aa22 100644 --- a/internal/ips/assign.go +++ b/internal/ips/assign.go @@ -21,10 +21,11 @@ package ips import ( + "context" "fmt" "strconv" - "github.com/packethost/packngo" + metal "github.com/equinix-labs/metal-go/metal/v1" "github.com/spf13/cobra" ) @@ -44,14 +45,16 @@ func (c *Client) Assign() *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { cmd.SilenceUsage = true - assignment, _, err := c.DeviceService.Assign(deviceID, &packngo.AddressStruct{Address: address}) + IPAssignmentInput := metal.NewIPAssignmentInput(address) + + assignment, _, err := c.DeviceService.CreateIPAssignment(context.Background(), deviceID).IPAssignmentInput(*IPAssignmentInput).Execute() if err != nil { return fmt.Errorf("Could not assign Device IP address: %w", err) } data := make([][]string, 1) - data[0] = []string{assignment.ID, assignment.Address, strconv.FormatBool(assignment.Public), assignment.Created} + data[0] = []string{assignment.GetId(), assignment.GetAddress(), strconv.FormatBool(assignment.GetPublic()), assignment.CreatedAt.String()} header := []string{"ID", "Address", "Public", "Created"} return c.Out.Output(assignment, header, &data) diff --git a/internal/ips/available.go b/internal/ips/available.go index d406f27b..647997fe 100644 --- a/internal/ips/available.go +++ b/internal/ips/available.go @@ -21,9 +21,11 @@ package ips import ( + "context" "fmt" + "strconv" - "github.com/packethost/packngo" + metal "github.com/equinix-labs/metal-go/metal/v1" "github.com/spf13/cobra" ) @@ -41,10 +43,12 @@ func (c *Client) Available() *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { cmd.SilenceUsage = true - result, _, err := c.ProjectService.AvailableAddresses(reservationID, &packngo.AvailableRequest{CIDR: cidr}) + Cidr := metal.FindIPAvailabilitiesCidrParameter(strconv.Itoa(cidr)) + resultList, _, err := c.IPService.FindIPAvailabilities(context.Background(), reservationID).Cidr(Cidr).Execute() if err != nil { return fmt.Errorf("Could not get available IP addresses: %w", err) } + result := resultList.GetAvailable() data := make([][]string, len(result)) for i, r := range result { data[i] = []string{r} diff --git a/internal/ips/ip.go b/internal/ips/ip.go index 1f4cd220..9b0c5c7e 100644 --- a/internal/ips/ip.go +++ b/internal/ips/ip.go @@ -21,16 +21,16 @@ package ips import ( + metal "github.com/equinix-labs/metal-go/metal/v1" "github.com/equinix/metal-cli/internal/outputs" - "github.com/packethost/packngo" "github.com/spf13/cobra" ) type Client struct { - Servicer Servicer - ProjectService packngo.ProjectIPService - DeviceService packngo.DeviceIPService - Out outputs.Outputer + Servicer Servicer + IPService metal.IPAddressesApiService + DeviceService metal.DevicesApiService + Out outputs.Outputer } func (c *Client) NewCommand() *cobra.Command { @@ -46,8 +46,8 @@ func (c *Client) NewCommand() *cobra.Command { root.PersistentPreRun(cmd, args) } } - c.ProjectService = c.Servicer.API(cmd).ProjectIPs - c.DeviceService = c.Servicer.API(cmd).DeviceIPs + c.IPService = *c.Servicer.MetalAPI(cmd).IPAddressesApi + c.DeviceService = *c.Servicer.MetalAPI(cmd).DevicesApi }, } @@ -63,8 +63,7 @@ func (c *Client) NewCommand() *cobra.Command { } type Servicer interface { - API(*cobra.Command) *packngo.Client - ListOptions(defaultIncludes, defaultExcludes []string) *packngo.ListOptions + MetalAPI(*cobra.Command) *metal.APIClient } func NewClient(s Servicer, out outputs.Outputer) *Client { diff --git a/internal/ips/remove.go b/internal/ips/remove.go index 7afcf3a9..05ff67ed 100644 --- a/internal/ips/remove.go +++ b/internal/ips/remove.go @@ -21,6 +21,7 @@ package ips import ( + "context" "fmt" "github.com/spf13/cobra" @@ -38,7 +39,7 @@ func (c *Client) Remove() *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { cmd.SilenceUsage = true - _, err := c.ProjectService.Remove(reservationID) + _, err := c.IPService.DeleteIPAddress(context.Background(), reservationID).Execute() if err != nil { return fmt.Errorf("Could not remove IP address Reservation: %w", err) } diff --git a/internal/ips/request.go b/internal/ips/request.go index cd2dc075..fd07aafd 100644 --- a/internal/ips/request.go +++ b/internal/ips/request.go @@ -21,10 +21,11 @@ package ips import ( + "context" "fmt" "strconv" - "github.com/packethost/packngo" + metal "github.com/equinix-labs/metal-go/metal/v1" "github.com/spf13/cobra" ) @@ -49,22 +50,30 @@ func (c *Client) Request() *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { cmd.SilenceUsage = true - req := &packngo.IPReservationRequest{ - Type: packngo.IPReservationType(ttype), - Quantity: quantity, - Facility: &facility, + + req := &metal.IPReservationRequestInput{ Metro: &metro, Tags: tags, + Quantity: int32(quantity), + Type: ttype, + Facility: &facility, + } + + requestIPReservationRequest := &metal.RequestIPReservationRequest{ + IPReservationRequestInput: req, } - reservation, _, err := c.ProjectService.Request(projectID, req) + reservation, _, err := c.IPService.RequestIPReservation(context.Background(), projectID).RequestIPReservationRequest(*requestIPReservationRequest).Execute() if err != nil { return fmt.Errorf("Could not request IP addresses: %w", err) } data := make([][]string, 1) - data[0] = []string{reservation.ID, reservation.Address, strconv.FormatBool(reservation.Public), reservation.Created} + data[0] = []string{reservation.IPReservation.GetId(), + reservation.IPReservation.GetAddress(), + strconv.FormatBool(reservation.IPReservation.GetPublic()), + reservation.IPReservation.CreatedAt.String()} header := []string{"ID", "Address", "Public", "Created"} return c.Out.Output(reservation, header, &data) diff --git a/internal/ips/retrieve.go b/internal/ips/retrieve.go index 55948f3a..8986629e 100644 --- a/internal/ips/retrieve.go +++ b/internal/ips/retrieve.go @@ -21,9 +21,12 @@ package ips import ( + "context" "fmt" "strconv" + metal "github.com/equinix-labs/metal-go/metal/v1" + pager "github.com/equinix/metal-cli/internal/pagination" "github.com/spf13/cobra" ) @@ -59,21 +62,24 @@ func (c *Client) Retrieve() *cobra.Command { } cmd.SilenceUsage = true - listOpts := c.Servicer.ListOptions(nil, nil) + inc := []string{} + exc := []string{} + types := []metal.FindIPReservationsTypesParameterInner{} + if assignmentID != "" { - ip, _, err := c.ProjectService.Get(assignmentID, listOpts) + ip, _, err := c.IPService.FindIPAddressById(context.Background(), assignmentID).Include(inc).Exclude(exc).Execute() if err != nil { return fmt.Errorf("Could not get Device IP address: %w", err) } data := make([][]string, 1) - data[0] = []string{ip.ID, ip.Address, strconv.FormatBool(ip.Public), ip.Created} + data[0] = []string{ip.IPAssignment.GetId(), ip.IPAssignment.GetAddress(), strconv.FormatBool(ip.IPAssignment.GetPublic()), ip.IPAssignment.CreatedAt.String()} header := []string{"ID", "Address", "Public", "Created"} return c.Out.Output(ip, header, &data) } else if reservationID != "" { - ip, _, err := c.ProjectService.Get(reservationID, listOpts) + ip, _, err := c.IPService.FindIPAddressById(context.Background(), reservationID).Include(inc).Exclude(exc).Execute() if err != nil { return fmt.Errorf("Could not get Reservation IP address: %w", err) } @@ -81,35 +87,37 @@ func (c *Client) Retrieve() *cobra.Command { data := make([][]string, 1) code := "" metro := "" - if ip.Facility != nil { - code = ip.Facility.Code + if ip.IPReservation.Facility != nil { + code = ip.IPReservation.Facility.GetCode() } - if ip.Metro != nil { - metro = ip.Metro.Code + + if ip.IPReservation.Metro != nil { + metro = ip.IPReservation.Metro.GetCode() } - data[0] = []string{ip.ID, ip.Address, metro, code, strconv.FormatBool(ip.Public), ip.Created} + + data[0] = []string{ip.IPReservation.GetId(), ip.IPReservation.GetAddress(), metro, code, strconv.FormatBool(ip.IPReservation.GetPublic()), ip.IPReservation.CreatedAt.String()} header := []string{"ID", "Address", "Metro", "Facility", "Public", "Created"} return c.Out.Output(ip, header, &data) } - ips, _, err := c.ProjectService.List(projectID, listOpts) + ips, err := pager.GetAllIPReservations(c.IPService, projectID, inc, exc, types) if err != nil { return fmt.Errorf("Could not list Project IP addresses: %w", err) } - + // ips := ipsList.GetIpAddresses() data := make([][]string, len(ips)) for i, ip := range ips { code := "" metro := "" - if ip.Facility != nil { - code = ip.Facility.Code + if ip.IPReservation.Facility != nil { + code = ip.IPReservation.Facility.GetCode() } - if ip.Metro != nil { - metro = ip.Metro.Code + if ip.IPReservation.Metro != nil { + metro = ip.IPReservation.Metro.GetCode() } - data[i] = []string{ip.ID, ip.Address, metro, code, strconv.FormatBool(ip.Public), ip.Created} + data[i] = []string{ip.IPReservation.GetId(), ip.IPReservation.GetAddress(), metro, code, strconv.FormatBool(ip.IPReservation.GetPublic()), ip.IPReservation.CreatedAt.String()} } header := []string{"ID", "Address", "Metro", "Facility", "Public", "Created"} diff --git a/internal/ips/unassign.go b/internal/ips/unassign.go index 99a15456..2783596d 100644 --- a/internal/ips/unassign.go +++ b/internal/ips/unassign.go @@ -21,6 +21,7 @@ package ips import ( + "context" "fmt" "github.com/spf13/cobra" @@ -38,7 +39,7 @@ func (c *Client) Unassign() *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { cmd.SilenceUsage = true - _, err := c.DeviceService.Unassign(assignmentID) + _, err := c.IPService.DeleteIPAddress(context.Background(), assignmentID).Execute() if err != nil { return fmt.Errorf("Could not unassign IP address: %w", err) } diff --git a/internal/pagination/pager.go b/internal/pagination/pager.go index 5d9ece5c..9272cf40 100644 --- a/internal/pagination/pager.go +++ b/internal/pagination/pager.go @@ -145,3 +145,23 @@ func GetProjectDevices(s metal.ApiFindProjectDevicesRequest) ([]metal.Device, er return devices, nil } } + +func GetAllIPReservations(s metal.IPAddressesApiService, projectId string, inc []string, exc []string, types []metal.FindIPReservationsTypesParameterInner) ([]metal.IPReservationListIpAddressesInner, error) { + var ipReservations []metal.IPReservationListIpAddressesInner + page := int32(1) // int32 | Page to return (optional) (default to 1) + perPage := int32(20) // int32 | Items returned per page (optional) (default to 10) + + for { + ipReservationsPage, _, err := s.FindIPReservations(context.Background(), projectId).Types(types).Include(inc).Exclude(exc).PerPage(perPage).Execute() + if err != nil { + return nil, err + } + + ipReservations = append(ipReservations, ipReservationsPage.GetIpAddresses()...) + if ipReservationsPage.Meta.GetLastPage() > ipReservationsPage.Meta.GetCurrentPage() { + page = page + 1 + continue + } + return ipReservations, nil + } +} diff --git a/test/e2e/ipstest/ips_get_test.go b/test/e2e/ipstest/ips_get_test.go new file mode 100644 index 00000000..2e72b21d --- /dev/null +++ b/test/e2e/ipstest/ips_get_test.go @@ -0,0 +1,87 @@ +package ipstest + +import ( + "io" + "os" + "strings" + "testing" + + root "github.com/equinix/metal-cli/internal/cli" + "github.com/equinix/metal-cli/internal/ips" + outputPkg "github.com/equinix/metal-cli/internal/outputs" + "github.com/equinix/metal-cli/test/helper" + "github.com/spf13/cobra" +) + +func TestCli_Ips_Get(t *testing.T) { + var projectId, ipsId string + var err error + subCommand := "ip" + consumerToken := "" + apiURL := "" + Version := "metal" + rootClient := root.NewClient(consumerToken, apiURL, Version) + type fields struct { + MainCmd *cobra.Command + Outputer outputPkg.Outputer + } + tests := []struct { + name string + fields fields + want *cobra.Command + cmdFunc func(*testing.T, *cobra.Command) + }{ + { + name: "get_ip_reservations", + fields: fields{ + MainCmd: ips.NewClient(rootClient, outputPkg.Outputer(&outputPkg.Standard{})).NewCommand(), + Outputer: outputPkg.Outputer(&outputPkg.Standard{}), + }, + want: &cobra.Command{}, + cmdFunc: func(t *testing.T, c *cobra.Command) { + if true { + t.Skip("Skipping this test because someCondition is true") + } + root := c.Root() + projectId, err = helper.CreateTestProject("metal-cli-ips-get-pro") + if err != nil { + t.Error(err) + } + ipsId, err = helper.CreateTestIps(projectId, 1, "public_ipv4") + if len(projectId) != 0 && len(ipsId) != 0 { + root.SetArgs([]string{subCommand, "get", "-p", projectId}) + rescueStdout := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + if err := root.Execute(); err != nil { + t.Error(err) + } + w.Close() + out, _ := io.ReadAll(r) + os.Stdout = rescueStdout + if !strings.Contains(string(out[:]), ipsId) && + !strings.Contains(string(out[:]), "da") { + t.Error("expected output should include " + ipsId + " da strings in the out string") + } + + err = helper.CleanTestIps(ipsId) + if err != nil { + t.Error(err) + } + err = helper.CleanTestProject(projectId) + if err != nil { + t.Error(err) + } + } + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rootCmd := rootClient.NewCommand() + rootCmd.AddCommand(tt.fields.MainCmd) + tt.cmdFunc(t, tt.fields.MainCmd) + }) + } +} diff --git a/test/e2e/ipstest/ips_request_test.go b/test/e2e/ipstest/ips_request_test.go new file mode 100644 index 00000000..fcb01148 --- /dev/null +++ b/test/e2e/ipstest/ips_request_test.go @@ -0,0 +1,84 @@ +package ipstest + +import ( + "io" + "os" + "strings" + "testing" + "time" + + root "github.com/equinix/metal-cli/internal/cli" + "github.com/equinix/metal-cli/internal/ips" + outputPkg "github.com/equinix/metal-cli/internal/outputs" + "github.com/equinix/metal-cli/test/helper" + "github.com/spf13/cobra" +) + +func TestCli_Vlan_Create(t *testing.T) { + var projectId string + var err error + subCommand := "ip" + consumerToken := "" + apiURL := "" + Version := "metal" + rootClient := root.NewClient(consumerToken, apiURL, Version) + type fields struct { + MainCmd *cobra.Command + Outputer outputPkg.Outputer + } + tests := []struct { + name string + fields fields + want *cobra.Command + cmdFunc func(*testing.T, *cobra.Command) + }{ + { + name: "Request_NewIP", + fields: fields{ + MainCmd: ips.NewClient(rootClient, outputPkg.Outputer(&outputPkg.Standard{})).NewCommand(), + Outputer: outputPkg.Outputer(&outputPkg.Standard{}), + }, + want: &cobra.Command{}, + cmdFunc: func(t *testing.T, c *cobra.Command) { + if true { + t.Skip("Skipping temporarily for now") + } + root := c.Root() + projectId, err = helper.CreateTestProject("metal-cli-ips-create-pro") + if err != nil { + t.Error(err) + } + time.Sleep(10 * time.Second) + if len(projectId) != 0 { + root.SetArgs([]string{subCommand, "request", "-p", projectId, "-t", "public_ipv4", "-m", "da", "-q", "4"}) + rescueStdout := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + if err := root.Execute(); err != nil { + t.Error(err) + } + w.Close() + out, _ := io.ReadAll(r) + os.Stdout = rescueStdout + if !strings.Contains(string(out[:]), "ID") && + !strings.Contains(string(out[:]), "PUBLIC") && + !strings.Contains(string(out[:]), "true") { + t.Error("expected output should include ID, PUBLIC and true strings in the out string") + } + } + err = helper.CleanTestProject(projectId) + if err != nil { + t.Error(err) + } + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rootCmd := rootClient.NewCommand() + rootCmd.AddCommand(tt.fields.MainCmd) + tt.cmdFunc(t, tt.fields.MainCmd) + }) + } +} diff --git a/test/helper/helper.go b/test/helper/helper.go index eb1007ef..3bbb2f5f 100644 --- a/test/helper/helper.go +++ b/test/helper/helper.go @@ -107,3 +107,39 @@ func CleanTestProject(projectId string) error { } return nil } + +func CreateTestIps(projectId string, quantity int, ipType string) (string, error) { + TestApiClient := TestClient() + metro := "da" + var tags []string + var facility string + + req := &openapiclient.IPReservationRequestInput{ + Metro: &metro, + Tags: tags, + Quantity: int32(quantity), + Type: ipType, + Facility: &facility, + } + + requestIPReservationRequest := &openapiclient.RequestIPReservationRequest{ + IPReservationRequestInput: req, + } + + ipsresp, _, err := TestApiClient.IPAddressesApi.RequestIPReservation(context.Background(), projectId).RequestIPReservationRequest(*requestIPReservationRequest).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `VLANsApi.CreateVirtualNetwork``: %v\n", err) + return "", err + } + return ipsresp.IPReservation.GetId(), nil +} + +func CleanTestIps(ipsId string) error { + TestApiClient := TestClient() + _, err := TestApiClient.IPAddressesApi.DeleteIPAddress(context.Background(), ipsId).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling `IPAddressesApi.DeleteIPAddress``: %v\n", err) + return err + } + return nil +}