Skip to content

Commit

Permalink
feat: VRF support
Browse files Browse the repository at this point in the history
  • Loading branch information
irusoeqx committed Oct 4, 2023
1 parent ccfb9bd commit c85a559
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 0 deletions.
Binary file added .DS_Store
Binary file not shown.
83 changes: 83 additions & 0 deletions internal/vrf/create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package vrf

Check failure on line 1 in internal/vrf/create.go

View workflow job for this annotation

GitHub Actions / lint

: found packages vrf (create.go) and main (delete.go) in internal/vrf (typecheck)

import (
"context"
"fmt"
"strconv"
"strings"

metal "github.com/equinix-labs/metal-go/metal/v1"
"github.com/spf13/cobra"
)

func (c *Client) Create() *cobra.Command {
var (
projectID string
metro string
name string
description string
ipRanges []string
localASN int32
tags []string
)

// createVRFCmd represents the creatVRF command
createVRFCmd := &cobra.Command{
Use: `create vrf <my_vrf> [-m <metro_code>] [-AS int] [-I str] [-d <description>]`,
Short: "Creates a virtual network.",
Long: "Creates a VRF",
Example: `# Creates a VRF, metal vrf create `,

RunE: func(cmd *cobra.Command, args []string) error {
cmd.SilenceUsage = true

req := metal.VrfCreateInput{
Metro: metro,
Name: name,
IpRanges: ipRanges,
LocalAsn: &localASN,
Tags: tags,
Description: &description,
}
// Retreived these params from packetngo based on the VRF function there, assuming vrfs need these to work
// From Packetngo >>>>
// IPRanges is a list of all IPv4 and IPv6 Ranges that will be available to
// BGP Peers. IPv4 addresses must be /8 or smaller with a minimum size of
// /29. IPv6 must be /56 or smaller with a minimum size of /64. Ranges must
// not overlap other ranges within the VRF.
// >>>>>>>>>>>>>>>>>>>
// Not quite sure if a CIDR can be specified here using a "/" or how it works exactly in tandem with VRFs
// may also need some logic for processing errors on local asn, also not sure about this
request := c.Service.CreateVrf(context.Background(), projectID).VrfCreateInput(req).Exclude(nil).Include(nil)
n, _, err := request.Execute()
if err != nil {
return fmt.Errorf("Could not create VRF: %w", err)
}

data := make([][]string, 1)

// This output block below is probably incorrect but leaving it for now for testing later.
data[0] = []string{n.GetName(), *n.GetMetro().Code, n.GetDescription(), strconv.Itoa(int(n.GetLocalAsn())), strings.Join(n.GetIpRanges(), ","), n.GetCreatedAt().String()}
header := []string{"Name", "Metro", "Description", "LocalASN", "IPrange", "Created"}

return c.Out.Output(n, header, &data)
},
}

createVRFCmd.Flags().StringVarP(&projectID, "project-id", "p", "", "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.")
createVRFCmd.Flags().StringSliceVarP(&tags, "tags", "t", []string{}, `Tag or list of tags for the VRF: --tags="tag1,tag2".`)
createVRFCmd.Flags().StringVarP(&name, "name", "n", "", "Name of the VRF")
createVRFCmd.Flags().StringVarP(&description, "description", "d", "", "Description of the VRF.")

createVRFCmd.Flags().StringVarP(&metro, "metro", "m", "", "Code of the metro where the VRF will be created")
createVRFCmd.Flags().Int32VarP(&localASN, "local-asn", "a", 0, "Local ASN for the VRF")
createVRFCmd.Flags().StringSliceVarP(&ipRanges, "ip-ranges", "r", []string{}, "IP range or list of IP ranges for the VRF")

// making them all required here
_ = createVRFCmd.MarkFlagRequired("name")
_ = createVRFCmd.MarkFlagRequired("metro")
_ = createVRFCmd.MarkFlagRequired("local-asn")
_ = createVRFCmd.MarkFlagRequired("IPrange")

return createVRFCmd
}
63 changes: 63 additions & 0 deletions internal/vrf/delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package main // or your appropriate package name

import (
"fmt"

"github.com/manifoldco/promptui"
"github.com/spf13/cobra"
)

func (c *Client) Delete() *cobra.Command {
var (
vrfID string
force bool
)

deleteVrf := func(id string) error {
_, err := c.Service.Delete(id)
if err != nil {
return err
}
fmt.Println("VRF", id, "successfully deleted.")
return nil // No need to return 'err' here; it's always nil.
}

deleteMetalVrfCmd := &cobra.Command{
Use: "delete vrf -i <metal_vrf_UUID> [-f]",
Short: "Deletes a VRF.",
Long: "Deletes the specified VRF with a confirmation prompt. To skip the confirmation, use --force.",
Example: `# Deletes a VRF, with confirmation.
metal delete vrf -i 77e6d57a-d7a4-4816-b451-cf9b043444e2
# Deletes a VRF, skipping confirmation.
metal delete vrf -f -i 77e6d57a-d7a4-4816-b451-cf9b043444e2`,
RunE: func(cmd *cobra.Command, args []string) error {
cmd.SilenceUsage = true

if !force {
prompt := promptui.Prompt{
Label: fmt.Sprintf("Are you sure you want to delete VRF %s: ", vrfID),
IsConfirm: true,
}

result, err := prompt.Run()
if err != nil || result != "y" {
fmt.Println("VRF deletion aborted.")
return nil
}
}

if err := deleteVrf(vrfID); err != nil {
return fmt.Errorf("Could not delete VRF: %w", err)
}

return nil
},
}

deleteMetalVrfCmd.Flags().StringVarP(&vrfID, "id", "i", "", "UUID of the VRF.")
_ = deleteMetalVrfCmd.MarkFlagRequired("id")
deleteMetalVrfCmd.Flags().BoolVarP(&force, "force", "f", false, "Skips confirmation for the removal of the VRF.")

return deleteMetalVrfCmd
}
52 changes: 52 additions & 0 deletions internal/vrf/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package vrf

import (
metal "github.com/equinix-labs/metal-go/metal/v1"
"github.com/equinix/metal-cli/internal/outputs"
"github.com/spf13/cobra"
"fmt"
)

type Client struct {
Servicer Servicer
Service metal.VRFsApiService
Out outputs.Outputer
}


func (c *Client) Retrieve() *cobra.Command {
var projectID string

// retrieveVrfsCmd represents the retrieveMetalGateways command
retrieveVrfsCmd := &cobra.Command{
Use: `get -p <project_UUID>`,
Aliases: []string{"list"},
Short: "Lists VRFs.",
Long: "Retrieves a list of all VRFs for the specified project.",
Example: `
# Lists VRFs for project 3b0795ba-ec9a-4a9e-83a7-043e7e11407c:
metal vrf list -p 3b0795ba-ec9a-4a9e-83a7-043e7e11407c`,

RunE: func(cmd *cobra.Command, args []string) error {
cmd.SilenceUsage = true
vrfs, _, err := c.Service.Get(ProjectID, c.Servicer.GetOptions(nil, nil)) // <- Not sure about the correct service to call Im assuming its this? https://github.com/packethost/packngo/blob/master/vrf.go
if err != nil {
return fmt.Errorf("Could not list VRFs: %w", err)
}

data := make([][]string, len(vrfs.VirtualRoutingFunctions))

for i, n := range vnets.VirtualRoutingFunctions {
data[i] = []string{n.ID, n.Description, n.IPranges, n.LocalASN n.Metro, n.CreatedAt} //Data should contain all info about VRF not sure where the formatting comes from, going with this.

Check failure on line 40 in internal/vrf/list.go

View workflow job for this annotation

GitHub Actions / lint

missing ',' in composite literal (typecheck)
}
header := []string{"ID", "Description", "VXLAN", "Facility", "Created"}

return c.Out.Output(vrfs, header, &data)

Check failure on line 44 in internal/vrf/list.go

View workflow job for this annotation

GitHub Actions / lint

missing ',' in composite literal (typecheck)
},
}

Check failure on line 46 in internal/vrf/list.go

View workflow job for this annotation

GitHub Actions / lint

expected operand, found '}' (typecheck)
retrieveVrfsCmd.Flags().StringVarP(&projectID, "project-id", "p", "", "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.")
_ = retrieveVrfsCmd.MarkFlagRequired("project-id")

return retrieveVrfsCmd

Check failure on line 50 in internal/vrf/list.go

View workflow job for this annotation

GitHub Actions / lint

expected ';', found 'return' (typecheck)
}

Check failure on line 52 in internal/vrf/list.go

View workflow job for this annotation

GitHub Actions / lint

expected '}', found 'EOF' (typecheck)
47 changes: 47 additions & 0 deletions internal/vrf/vrf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package vrf

import (
metal "github.com/equinix-labs/metal-go/metal/v1"
"github.com/equinix/metal-cli/internal/outputs"
"github.com/spf13/cobra"
)

type Client struct {
Servicer Servicer
Service metal.VRFsApiService
Out outputs.Outputer
}

func (c *Client) NewCommand() *cobra.Command {
cmd := &cobra.Command{
Use: `vrf`,
Aliases: []string{"vrf"},
Short: "VRF operations : create, TODO: make other commands.",
Long: "Experimental VRF function",

PersistentPreRun: func(cmd *cobra.Command, args []string) {
if root := cmd.Root(); root != nil {
if root.PersistentPreRun != nil {
root.PersistentPreRun(cmd, args)
}
}
c.Service = *c.Servicer.MetalAPI(cmd).VRFsApi
},
}

cmd.AddCommand(
c.Create(),
)
return cmd
}

type Servicer interface {
MetalAPI(*cobra.Command) *metal.APIClient
}

func NewClient(s Servicer, out outputs.Outputer) *Client {
return &Client{
Servicer: s,
Out: out,
}
}

0 comments on commit c85a559

Please sign in to comment.