From 9fd3c2a90cddfa31ef9976ae8bfa46e27fd85f0e Mon Sep 17 00:00:00 2001 From: kbabu <3358152+codinja1188@users.noreply.github.com> Date: Thu, 25 Jan 2024 01:00:00 +0530 Subject: [PATCH] feat: Enabled VRF IP reservations (#418) Co-authored-by: codinja1188 <3358152+vasubabu@users.noreply.github.com> --- docs/metal_ip_get.md | 2 +- docs/metal_ip_request.md | 11 ++- docs/metal_vrf.md | 1 + docs/metal_vrf_ips.md | 50 ++++++++++ internal/ips/request.go | 133 +++++++++++++++++---------- internal/ips/retrieve.go | 52 +++++++---- internal/vrf/ips.go | 66 +++++++++++++ internal/vrf/vrf.go | 1 + test/e2e/ipstest/ips_request_test.go | 30 ++++++ test/e2e/vrfstest/vrf_test.go | 30 ++++++ test/helper/helper.go | 34 +++++++ 11 files changed, 339 insertions(+), 71 deletions(-) create mode 100644 docs/metal_vrf_ips.md create mode 100644 internal/vrf/ips.go diff --git a/docs/metal_ip_get.md b/docs/metal_ip_get.md index 386975ca..4169373c 100644 --- a/docs/metal_ip_get.md +++ b/docs/metal_ip_get.md @@ -7,7 +7,7 @@ Retrieves information about IP addresses, IP address reservations, and IP addres Retrieves information about the IP addresses in a project, the IP addresses that are in a specified assignment, or the IP addresses that are in a specified reservation. ``` -metal ip get -p | -a | -r [flags] +metal ip get -p | -a | -r [flags] ``` ### Examples diff --git a/docs/metal_ip_request.md b/docs/metal_ip_request.md index 64d24582..65fc903f 100644 --- a/docs/metal_ip_request.md +++ b/docs/metal_ip_request.md @@ -7,7 +7,7 @@ Request a block of IP addresses. Requests either a block of public IPv4 addresses or global IPv4 addresses for your project in a specific metro or facility. ``` -metal ip request -p -t -q (-m | -f ) [-f ] [-c ] [flags] +metal ip request -p -t -q (-m | -f ) [-f ] [-c ] [flags] ``` ### Examples @@ -15,19 +15,26 @@ metal ip request -p -t -q (-m ``` # Requests a block of 4 public IPv4 addresses in Dallas: metal ip request -p $METAL_PROJECT_ID -t public_ipv4 -q 4 -m da + + metal ip request -v df18fbd8-2919-4104-a042-5d42a05b8eed -t vrf --cidr 24 -n 172.89.1.0 --tags foo --tags bar --customdata '{"my":"goodness"}' --details "i don't think VRF users need this or will see it after submitting the request" ``` ### Options ``` + --cidr int The size of the desired subnet in bits. -c, --comments string General comments or description. + --customdata string customdata is to add to the reservation, in a comma-separated list. + --details string VRF IP Reservation's details -f, --facility string Code of the facility where the IP Reservation will be created -h, --help help for request -m, --metro string Code of the metro where the IP Reservation will be created + -n, --network string The starting address for this VRF IP Reservation's subnet -p, --project-id string The project's UUID. This flag is required, unless specified in the config created by metal init or set as METAL_PROJECT_ID environment variable. -q, --quantity int Number of IP addresses to reserve. - --tags strings Tag or Tags to add to the reservation, in a comma-separated list. + --tags strings Adds the tags for the IP Reservations --tags "tag1,tag2" OR --tags "tag1" --tags "tag2" -t, --type string The type of IP Address, either public_ipv4 or global_ipv4. + -v, --vrf-id string Specify the VRF UUID. ``` ### Options inherited from parent commands diff --git a/docs/metal_vrf.md b/docs/metal_vrf.md index 345f44b6..22bb9c33 100644 --- a/docs/metal_vrf.md +++ b/docs/metal_vrf.md @@ -33,4 +33,5 @@ VRF operations : It defines a collection of customer-managed IP blocks that can * [metal vrf create](metal_vrf_create.md) - Creates a Virtual Routing and Forwarding(VRF) for a specified project. * [metal vrf delete](metal_vrf_delete.md) - Deletes a VRF. * [metal vrf get](metal_vrf_get.md) - Lists VRFs. +* [metal vrf ips](metal_vrf_ips.md) - Retrieves the list of VRF IP Reservations for the VRF. diff --git a/docs/metal_vrf_ips.md b/docs/metal_vrf_ips.md new file mode 100644 index 00000000..d81e6e0e --- /dev/null +++ b/docs/metal_vrf_ips.md @@ -0,0 +1,50 @@ +## metal vrf ips + +Retrieves the list of VRF IP Reservations for the VRF. + +### Synopsis + +Retrieves the list of VRF IP Reservations for the VRF. + +``` +metal vrf ips [-v ] [flags] +``` + +### Examples + +``` + # Retrieves the list of VRF IP Reservations for the VRF. + + metal vrf ips [-v ] +``` + +### Options + +``` + -h, --help help for ips + -i, --id string Specify the IP UUID to retrieve the details of a VRF IP reservation. + -v, --vrf-id string Specify the VRF UUID to list its associated IP reservations. +``` + +### Options inherited from parent commands + +``` + --config string Path to JSON or YAML configuration file (METAL_CONFIG) + --exclude strings Comma separated Href references to collapse in results, may be dotted three levels deep + --filter stringArray Filter 'get' actions with name value pairs. Filter is not supported by all resources and is implemented as request query parameters. + --http-header strings Headers to add to requests (in format key=value) + --include strings Comma separated Href references to expand in results, may be dotted three levels deep + -o, --output string Output format (*table, json, yaml). env output formats are (*sh, terraform, capp). + --search string Search keyword for use in 'get' actions. Search is not supported by all resources. + --sort-by string Sort fields for use in 'get' actions. Sort is not supported by all resources. + --sort-dir string Sort field direction for use in 'get' actions. Sort is not supported by all resources. + --token string Metal API Token (METAL_AUTH_TOKEN) +``` + +### SEE ALSO + +* [metal vrf](metal_vrf.md) - VRF operations : create, get, delete + diff --git a/internal/ips/request.go b/internal/ips/request.go index f360a68c..9f5d2a90 100644 --- a/internal/ips/request.go +++ b/internal/ips/request.go @@ -1,28 +1,11 @@ -// Copyright © 2018 Jasmin Gacic -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -// THE SOFTWARE. - package ips import ( "context" + "encoding/json" + "errors" "fmt" + "log" "strconv" metal "github.com/equinix/equinix-sdk-go/services/metalv1" @@ -31,50 +14,100 @@ import ( func (c *Client) Request() *cobra.Command { var ( - ttype string - quantity int - comments string - facility string - metro string - projectID string - tags []string + ttype string + quantity int + comments string + facility string + metro string + projectID string + cidr int + network string + vrfID string + details string + tags []string + customdata string ) // requestIPCmd represents the requestIp command requestIPCmd := &cobra.Command{ - Use: `request -p -t -q (-m | -f ) [-f ] [-c ]`, + Use: `request -p -t -q (-m | -f ) [-f ] [-c ]`, Short: "Request a block of IP addresses.", Long: "Requests either a block of public IPv4 addresses or global IPv4 addresses for your project in a specific metro or facility.", Example: ` # Requests a block of 4 public IPv4 addresses in Dallas: - metal ip request -p $METAL_PROJECT_ID -t public_ipv4 -q 4 -m da`, + metal ip request -p $METAL_PROJECT_ID -t public_ipv4 -q 4 -m da + + metal ip request -v df18fbd8-2919-4104-a042-5d42a05b8eed -t vrf --cidr 24 -n 172.89.1.0 --tags foo --tags bar --customdata '{"my":"goodness"}' --details "i don't think VRF users need this or will see it after submitting the request"`, RunE: func(cmd *cobra.Command, args []string) error { + var ( + req *metal.IPReservationRequestInput + vrfReq *metal.VrfIpReservationCreateInput + requestIPReservationRequest *metal.RequestIPReservationRequest + ) cmd.SilenceUsage = true + // It's a required flag in case of VRFIPReservations and we conduct thorough validation to ensure its inclusion. + // By detecting its presence, we can identify whether it pertains to a standard IP Reservation Request or a VRF IP Reservation Request. - req := &metal.IPReservationRequestInput{ - Metro: &metro, - Tags: tags, - Quantity: int32(quantity), - Type: ttype, - Facility: &facility, - } + if ttype != "vrf" { - requestIPReservationRequest := &metal.RequestIPReservationRequest{ - IPReservationRequestInput: req, - } + req = &metal.IPReservationRequestInput{ + Metro: &metro, + Tags: tags, + Quantity: int32(quantity), + Type: ttype, + Facility: &facility, + } + requestIPReservationRequest = &metal.RequestIPReservationRequest{ + IPReservationRequestInput: req, + } + } else { + // Below are required Flags in VRF IP Reservation Request. + if cidr == 0 || network == "" || vrfID == "" { + return errors.New(" cidr, network and ID of the VRF are required to create VFR IP Reservations") + } + // This is an optinal Flag in VRF IP Reservation Request. + var data map[string]interface{} + if customdata != "" { + err := json.Unmarshal([]byte(customdata), &data) + if err != nil { + log.Fatalf("Error parsing custom data: %v", err) + } + } + + vrfReq = &metal.VrfIpReservationCreateInput{ + Type: ttype, + Cidr: int32(cidr), + Network: network, + VrfId: vrfID, + Details: &details, + Customdata: data, + Tags: tags, + } + requestIPReservationRequest = &metal.RequestIPReservationRequest{ + VrfIpReservationCreateInput: vrfReq, + } + } reservation, _, err := c.IPService.RequestIPReservation(context.Background(), projectID).RequestIPReservationRequest(*requestIPReservationRequest).Execute() if err != nil { - return fmt.Errorf("Could not request IP addresses: %w", err) + return fmt.Errorf("could not request IP addresses: %w", err) } data := make([][]string, 1) - - data[0] = []string{reservation.IPReservation.GetId(), - reservation.IPReservation.GetAddress(), - strconv.FormatBool(reservation.IPReservation.GetPublic()), - reservation.IPReservation.CreatedAt.String()} - header := []string{"ID", "Address", "Public", "Created"} + if ttype != "vrf" { + data[0] = []string{reservation.IPReservation.GetId(), + string(reservation.IPReservation.GetType()), + reservation.IPReservation.GetAddress(), + strconv.FormatBool(reservation.IPReservation.GetPublic()), + reservation.IPReservation.CreatedAt.String()} + } else { + data[0] = []string{reservation.VrfIpReservation.GetId(), + string(reservation.VrfIpReservation.GetType()), + reservation.VrfIpReservation.GetAddress(), + strconv.FormatBool(reservation.VrfIpReservation.GetPublic()), + reservation.VrfIpReservation.CreatedAt.String()} + } + header := []string{"ID", "Type", "Address", "Public", "Created"} return c.Out.Output(reservation, header, &data) }, @@ -85,11 +118,15 @@ func (c *Client) Request() *cobra.Command { requestIPCmd.Flags().StringVarP(&facility, "facility", "f", "", "Code of the facility where the IP Reservation will be created") requestIPCmd.Flags().StringVarP(&metro, "metro", "m", "", "Code of the metro where the IP Reservation will be created") requestIPCmd.Flags().IntVarP(&quantity, "quantity", "q", 0, "Number of IP addresses to reserve.") - requestIPCmd.Flags().StringSliceVar(&tags, "tags", nil, "Tag or Tags to add to the reservation, in a comma-separated list.") + requestIPCmd.Flags().StringSliceVarP(&tags, "tags", "", []string{}, `Adds the tags for the IP Reservations --tags "tag1,tag2" OR --tags "tag1" --tags "tag2"`) + requestIPCmd.Flags().IntVar(&cidr, "cidr", 0, "The size of the desired subnet in bits.") + requestIPCmd.Flags().StringVarP(&network, "network", "n", "", "The starting address for this VRF IP Reservation's subnet") + requestIPCmd.Flags().StringVarP(&vrfID, "vrf-id", "v", "", "Specify the VRF UUID.") + requestIPCmd.Flags().StringVarP(&details, "details", "", "", "VRF IP Reservation's details") + requestIPCmd.Flags().StringVarP(&customdata, "customdata", "", "", "customdata is to add to the reservation, in a comma-separated list.") _ = requestIPCmd.MarkFlagRequired("project-id") _ = requestIPCmd.MarkFlagRequired("type") - _ = requestIPCmd.MarkFlagRequired("quantity") requestIPCmd.Flags().StringVarP(&comments, "comments", "c", "", "General comments or description.") return requestIPCmd diff --git a/internal/ips/retrieve.go b/internal/ips/retrieve.go index 5b636aa6..5ecd81a7 100644 --- a/internal/ips/retrieve.go +++ b/internal/ips/retrieve.go @@ -38,7 +38,7 @@ func (c *Client) Retrieve() *cobra.Command { // ipCmd represents the ip command retrieveIPCmd := &cobra.Command{ - Use: `get -p | -a | -r `, + Use: `get -p | -a | -r `, Aliases: []string{"list"}, Short: "Retrieves information about IP addresses, IP address reservations, and IP address assignments.", Long: "Retrieves information about the IP addresses in a project, the IP addresses that are in a specified assignment, or the IP addresses that are in a specified reservation.", @@ -68,57 +68,69 @@ func (c *Client) Retrieve() *cobra.Command { if assignmentID != "" { 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) + return fmt.Errorf("could not get Device IP address: %w", err) } data := make([][]string, 1) - data[0] = []string{ip.IPAssignment.GetId(), ip.IPAssignment.GetAddress(), strconv.FormatBool(ip.IPAssignment.GetPublic()), ip.IPAssignment.CreatedAt.String()} - header := []string{"ID", "Address", "Public", "Created"} + data[0] = []string{ip.IPAssignment.GetId(), string(ip.IPAssignment.GetType()), ip.IPAssignment.GetAddress(), strconv.FormatBool(ip.IPAssignment.GetPublic()), ip.IPAssignment.CreatedAt.String()} + header := []string{"ID", "Type", "Address", "Public", "Created"} return c.Out.Output(ip, header, &data) } else if reservationID != "" { 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) + return fmt.Errorf("could not get Reservation IP address: %w", err) } data := make([][]string, 1) - code := "" metro := "" - if ip.IPReservation.Facility != nil { - code = ip.IPReservation.Facility.GetCode() + + if ip.IPReservation != nil { + + if ip.IPReservation.Metro != nil { + metro = ip.IPReservation.Metro.GetCode() + } + data[0] = []string{ip.IPReservation.GetId(), string(ip.IPReservation.GetType()), ip.IPReservation.GetAddress(), metro, strconv.FormatBool(ip.IPReservation.GetPublic()), ip.IPReservation.CreatedAt.String()} } - if ip.IPReservation.Metro != nil { - metro = ip.IPReservation.Metro.GetCode() + if ip.VrfIpReservation != nil { + + if ip.VrfIpReservation.Metro != nil { + metro = ip.VrfIpReservation.Metro.GetCode() + } + data[0] = []string{ip.VrfIpReservation.GetId(), string(ip.VrfIpReservation.GetType()), ip.VrfIpReservation.GetAddress(), metro, strconv.FormatBool(ip.VrfIpReservation.GetPublic()), ip.VrfIpReservation.CreatedAt.String()} } - 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"} + header := []string{"ID", "Type", "Address", "Metro", "Public", "Created"} return c.Out.Output(ip, header, &data) } resp, err := c.IPService.FindIPReservations(context.Background(), projectID).Include(inc).Exclude(exc).Types(types).ExecuteWithPagination() if err != nil { - return fmt.Errorf("Could not list Project IP addresses: %w", err) + return fmt.Errorf("could not list Project IP addresses: %w", err) } ips := resp.IpAddresses data := make([][]string, len(ips)) for i, ip := range ips { - code := "" metro := "" - if ip.IPReservation.Facility != nil { - code = ip.IPReservation.Facility.GetCode() + if ip.IPReservation != nil { + + if ip.IPReservation.Metro != nil { + metro = ip.IPReservation.Metro.GetCode() + } + data[i] = []string{ip.IPReservation.GetId(), string(ip.IPReservation.GetType()), ip.IPReservation.GetAddress(), metro, strconv.FormatBool(ip.IPReservation.GetPublic()), ip.IPReservation.CreatedAt.String()} } - if ip.IPReservation.Metro != nil { - metro = ip.IPReservation.Metro.GetCode() + if ip.VrfIpReservation != nil { + if ip.VrfIpReservation.Metro != nil { + metro = ip.IPReservation.Metro.GetCode() + } + data[i] = []string{ip.VrfIpReservation.GetId(), string(ip.VrfIpReservation.GetType()), ip.VrfIpReservation.GetAddress(), metro, strconv.FormatBool(ip.VrfIpReservation.GetPublic()), ip.VrfIpReservation.CreatedAt.String()} } - 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"} + header := []string{"ID", "Type", "Address", "Metro", "Public", "Created"} return c.Out.Output(ips, header, &data) }, diff --git a/internal/vrf/ips.go b/internal/vrf/ips.go new file mode 100644 index 00000000..0ec20e8f --- /dev/null +++ b/internal/vrf/ips.go @@ -0,0 +1,66 @@ +package vrf + +import ( + "context" + "fmt" + "strconv" + + "github.com/spf13/cobra" +) + +func (c *Client) Ips() *cobra.Command { + var ( + vrfID string + ipID string + ) + + // createVRFCmd represents the creatVRF command + ipsVRFCmd := &cobra.Command{ + Use: "ips [-v ]", + Short: "Retrieves the list of VRF IP Reservations for the VRF.", + Long: "Retrieves the list of VRF IP Reservations for the VRF.", + Example: ` # Retrieves the list of VRF IP Reservations for the VRF. + + metal vrf ips [-v ]`, + RunE: func(cmd *cobra.Command, args []string) error { + cmd.SilenceUsage = true + + inc := []string{} + exc := []string{} + + var data [][]string + var header = []string{"ID", "Type", "ADDRESS", "PUBLIC", "METRO", "Created"} + + if ipID == "" { + vrfIpReservationList, _, err := c.Service.FindVrfIpReservations(context.Background(), vrfID).Include(c.Servicer.Includes(inc)).Exclude(c.Servicer.Excludes(exc)).Execute() + if err != nil { + return fmt.Errorf("could not find VrfIpReservations: %w", err) + } + ipReservations := vrfIpReservationList.GetIpAddresses() + data = make([][]string, len(ipReservations)) + for i, vrf := range ipReservations { + data[i] = []string{vrf.GetId(), string(vrf.GetType()), vrf.GetAddress(), strconv.FormatBool(vrf.GetPublic()), vrf.Metro.GetCode(), vrf.CreatedAt.String()} + } + } else { + ipReservation, _, err := c.Service.FindVrfIpReservation(context.Background(), vrfID, ipID).Include(c.Servicer.Includes(inc)).Exclude(c.Servicer.Excludes(exc)).Execute() + if err != nil { + return fmt.Errorf("could not find VrfIpReservation: %w", err) + } + data = [][]string{{ipReservation.GetId(), string(ipReservation.GetType()), ipReservation.GetAddress(), strconv.FormatBool(ipReservation.GetPublic()), ipReservation.Metro.GetCode(), ipReservation.CreatedAt.String()}} + } + + return c.Out.Output(nil, header, &data) + }, + } + + ipsVRFCmd.Flags().StringVarP(&vrfID, "vrf-id", "v", "", "Specify the VRF UUID to list its associated IP reservations.") + ipsVRFCmd.Flags().StringVarP(&ipID, "id", "i", "", "Specify the IP UUID to retrieve the details of a VRF IP reservation.") + + // making them all required here + _ = ipsVRFCmd.MarkFlagRequired("vrf-id") + + return ipsVRFCmd +} diff --git a/internal/vrf/vrf.go b/internal/vrf/vrf.go index 790512ce..a4123276 100644 --- a/internal/vrf/vrf.go +++ b/internal/vrf/vrf.go @@ -33,6 +33,7 @@ func (c *Client) NewCommand() *cobra.Command { c.Create(), c.Retrieve(), c.Delete(), + c.Ips(), ) return cmd } diff --git a/test/e2e/ipstest/ips_request_test.go b/test/e2e/ipstest/ips_request_test.go index 644022fc..a8f7facc 100644 --- a/test/e2e/ipstest/ips_request_test.go +++ b/test/e2e/ipstest/ips_request_test.go @@ -50,6 +50,36 @@ func TestCli_Vlan_Create(t *testing.T) { } }, }, + + { + name: "Request_NewVRFIP", + 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() + projectName := "metal-cli-" + helper.GenerateRandomString(5) + "-ips-request-vrf" + project := helper.CreateTestProject(t, projectName) + _ = helper.CreateTestVlanWithVxLan(t, project.GetId(), 5678, projectName) + vrf := helper.CreateTestVrfs(t, project.GetId(), projectName) + + root.SetArgs([]string{subCommand, "request", "-v", vrf.GetId(), "-t", "vrf", "--cidr", "24", "-n", "10.10.1.0", "--tags", "foobar", "--tags", "barfoo"}) + + out := helper.ExecuteAndCaptureOutput(t, root) + + if !strings.Contains(string(out[:]), "TYPE") && + !strings.Contains(string(out[:]), "vrf") && + !strings.Contains(string(out[:]), "PUBLIC") && + !strings.Contains(string(out[:]), "false") { + t.Error("expected output should include TYPE, PUBLIC and false strings in the out string") + } + }, + }, } for _, tt := range tests { diff --git a/test/e2e/vrfstest/vrf_test.go b/test/e2e/vrfstest/vrf_test.go index 26f10c59..eba53d1e 100644 --- a/test/e2e/vrfstest/vrf_test.go +++ b/test/e2e/vrfstest/vrf_test.go @@ -156,6 +156,36 @@ func TestCli_Vrf_Create(t *testing.T) { } }, }, + { + name: "vrf-ips-getById-test", + fields: fields{ + MainCmd: vrf.NewClient(rootClient, outputPkg.Outputer(&outputPkg.Standard{})).NewCommand(), + Outputer: outputPkg.Outputer(&outputPkg.Standard{}), + }, + want: &cobra.Command{}, + cmdFunc: func(t *testing.T, c *cobra.Command) { + root := c.Root() + + projName := "metal-cli-" + randName + "-vrf-ips-get-test" + project := helper.CreateTestProject(t, projName) + + if project.GetId() != "" { + + vrf := helper.CreateTestVrfs(t, project.GetId(), projName) + if vrf.GetId() != "" { + + ipRequest := helper.CreateTestVrfIpRequest(t, project.GetId(), vrf.GetId()) + root.SetArgs([]string{subCommand, "ips", "-v", vrf.GetId(), "-i", ipRequest.IPReservation.GetId()}) + out := helper.ExecuteAndCaptureOutput(t, root) + + if !strings.Contains(string(out[:]), ipRequest.IPReservation.GetId()) && + !strings.Contains(string(out[:]), projName) { + t.Error("expected output should include " + ipRequest.IPReservation.GetId() + ", in the out string ") + } + } + } + }, + }, } for _, tt := range tests { diff --git a/test/helper/helper.go b/test/helper/helper.go index 2888e8a5..021d8631 100644 --- a/test/helper/helper.go +++ b/test/helper/helper.go @@ -675,3 +675,37 @@ func CleanTestVrfs(t *testing.T, vrfId string) { t.Fatalf("Error when calling `VRFsApi.DeleteVrf`` for %v: %v\n", vrfId, err) } } + +func CreateTestVrfIpRequest(t *testing.T, projectId, vrfId string) *metalv1.RequestIPReservation201Response { + t.Helper() + TestApiClient := TestClient() + + vrfReq := &metalv1.VrfIpReservationCreateInput{ + Type: "vrf", + Cidr: int32(24), + Network: "10.10.1.0", + VrfId: vrfId, + } + requestIPReservationRequest := &metalv1.RequestIPReservationRequest{ + VrfIpReservationCreateInput: vrfReq, + } + reservation, _, err := TestApiClient.IPAddressesApi.RequestIPReservation(context.Background(), projectId).RequestIPReservationRequest(*requestIPReservationRequest).Execute() + if err != nil { + t.Fatalf("Error when calling `IPAddressesApi.RequestIPReservation` for %v: %v\n", vrfId, err) + } + + t.Cleanup(func() { + CleanTestVrfIpRequest(t, reservation.IPReservation.GetId()) + }) + + return reservation +} + +func CleanTestVrfIpRequest(t *testing.T, IPReservationId string) { + t.Helper() + TestApiClient := TestClient() + resp, err := TestApiClient.IPAddressesApi.DeleteIPAddress(context.Background(), IPReservationId).Execute() + if err != nil && resp.StatusCode != http.StatusNotFound { + t.Fatalf("Error when calling `IPAddressesApi.DeleteIPAddress``for %v: %v\n", IPReservationId, err) + } +}