Skip to content

Commit

Permalink
[VPC-3359] Implemented vpc peering methods (#1532)
Browse files Browse the repository at this point in the history
* Implemented vpc peering methods

* removed peering id

* added error check

* addressed pr feedback

* Update commands/vpc_peerings.go

Co-authored-by: ddatta-do <[email protected]>

* Update commands/vpc_peerings.go

Co-authored-by: ddatta-do <[email protected]>

* Update commands/vpc_peerings.go

Co-authored-by: ddatta-do <[email protected]>

* Update commands/vpc_peerings.go

Co-authored-by: ddatta-do <[email protected]>

* Update commands/vpc_peerings.go

Co-authored-by: ddatta-do <[email protected]>

* address pr feedback

* added wait flag for status completion

* addressed pr feedback

* addressed pr feedback

* updated wait to 5s

* added requiredopts

* resolved pr feedbacks

---------

Co-authored-by: ddatta-do <[email protected]>
  • Loading branch information
guptado and ddatta-do authored May 30, 2024
1 parent 50f53f8 commit 42fc8b6
Show file tree
Hide file tree
Showing 15 changed files with 885 additions and 6 deletions.
7 changes: 7 additions & 0 deletions args.go
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,13 @@ const (
// ArgVPCIPRange is a VPC range of IP addresses in CIDR notation.
ArgVPCIPRange = "ip-range"

// ArgVPCPeeringName is a name of the VPC Peering.
ArgVPCPeeringName = "name"
// ArgVPCPeeringVPCIDs is the vpc ids of the peering
ArgVPCPeeringVPCIDs = "vpc-ids"
// ArgVPCPeeringVPCID is id of the VPC.
ArgVPCPeeringVPCID = "vpc-id"

// ArgReadWrite indicates a generated token should be read/write.
ArgReadWrite = "read-write"
// ArgRegistry indicates the name of the registry.
Expand Down
54 changes: 54 additions & 0 deletions commands/displayers/vpc_peering.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package displayers

import (
"github.com/digitalocean/doctl/do"
"io"
"strings"
)

type VPCPeering struct {
VPCPeerings do.VPCPeerings
}

var _ Displayable = &VPCPeering{}

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

func (v *VPCPeering) Cols() []string {
return []string{
"ID",
"Name",
"VPCIDs",
"Status",
"Created",
}
}

func (v *VPCPeering) ColMap() map[string]string {
return map[string]string{
"ID": "ID",
"Name": "Name",
"VPCIDs": "VPCIDs",
"Status": "Status",
"Created": "Created At",
}
}

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

for _, v := range v.VPCPeerings {
o := map[string]any{
"ID": v.ID,
"Name": v.Name,
"VPCIDs": strings.Join(v.VPCIDs, ","),
"Status": v.Status,
"Created": v.CreatedAt,
}
out = append(out, o)
}

return out
}
270 changes: 270 additions & 0 deletions commands/vpc_peerings.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
package commands

import (
"errors"
"fmt"
"os"
"strings"
"time"

"github.com/digitalocean/doctl"
"github.com/digitalocean/doctl/commands/displayers"
"github.com/digitalocean/doctl/do"
"github.com/digitalocean/godo"
"github.com/spf13/cobra"
)

// VPCPeerings creates the vpc peerings command.
func VPCPeerings() *Command {
cmd := &Command{
Command: &cobra.Command{
Use: "peerings",
Short: "Display commands that manage VPC Peerings",
Long: `The commands under ` + "`" + `doctl vpcs peerings` + "`" + ` are for managing your VPC Peerings.
With the VPC Peerings commands, you can get, list, create, update, or delete VPC Peerings, and manage their configuration details.`,
Hidden: true,
},
}

peeringDetails := `
- The VPC Peering ID
- The VPC Peering Name
- The Peered VPC network IDs
- The VPC Peering Status
- The VPC Peering creation date, in ISO8601 combined date and time format
`
cmdPeeringGet := CmdBuilder(cmd, RunVPCPeeringGet, "get <id>",
"Retrieves a VPC Peering", "Retrieves information about a VPC Peering, including:"+peeringDetails, Writer,
aliasOpt("g"), displayerType(&displayers.VPCPeering{}))
cmdPeeringGet.Example = `The following example retrieves information about a VPC Peering with the ID ` + "`" + `f81d4fae-7dec-11d0-a765-00a0c91e6bf6` + "`" + `: doctl vpcs peerings get f81d4fae-7dec-11d0-a765-00a0c91e6bf6`

cmdPeeringList := CmdBuilder(cmd, RunVPCPeeringList, "list", "List VPC Peerings", "Retrieves a list of the VPC Peerings on your account, including the following information for each:"+peeringDetails, Writer,
aliasOpt("ls"), displayerType(&displayers.VPCPeering{}))
AddStringFlag(cmdPeeringList, doctl.ArgVPCPeeringVPCID, "", "",
"VPC ID")
cmdPeeringList.Example = `The following example lists the VPC Peerings on your account : doctl vpcs peerings list --format Name,VPCIDs`

cmdPeeringCreate := CmdBuilder(cmd, RunVPCPeeringCreate, "create",
"Create a new VPC Peering", "Use this command to create a new VPC Peering on your account.", Writer, aliasOpt("c"))
AddStringFlag(cmdPeeringCreate, doctl.ArgVPCPeeringVPCIDs, "", "",
"Peering VPC IDs should be comma separated", requiredOpt())
AddBoolFlag(cmdPeeringCreate, doctl.ArgCommandWait, "", false, "Boolean that specifies whether to wait for a VPC Peering creation to complete before returning control to the terminal")
cmdPeeringCreate.Example = `The following example creates a VPC Peering named ` +
"`" + `example-peering-name` + "`" +
` : doctl vpcs peerings create example-peering-name --vpc-ids f81d4fae-7dec-11d0-a765-00a0c91e6bf6,3f900b61-30d7-40d8-9711-8c5d6264b268`

cmdPeeringUpdate := CmdBuilder(cmd, RunVPCPeeringUpdate, "update <id>",
"Update a VPC Peering's name", `Use this command to update the name of a VPC Peering`, Writer, aliasOpt("u"))
AddStringFlag(cmdPeeringUpdate, doctl.ArgVPCPeeringName, "", "",
"The VPC Peering's name", requiredOpt())
cmdPeeringUpdate.Example = `The following example updates the name of a VPC Peering with the ID ` +
"`" + `f81d4fae-7dec-11d0-a765-00a0c91e6bf6` + "`" + ` to ` + "`" + `new-name` + "`" +
`: doctl vpcs peerings update f81d4fae-7dec-11d0-a765-00a0c91e6bf6 --name new-name`

cmdPeeringDelete := CmdBuilder(cmd, RunVPCPeeringDelete, "delete <id>",
"Permanently delete a VPC Peering", `Permanently deletes the specified VPC Peering. This is irreversible.`, Writer, aliasOpt("d", "rm"))
AddBoolFlag(cmdPeeringDelete, doctl.ArgForce, doctl.ArgShortForce, false,
"Delete the VPC Peering without any confirmation prompt")
AddBoolFlag(cmdPeeringDelete, doctl.ArgCommandWait, "", false,
"Boolean that specifies whether to wait for a VPC Peering deletion to complete before returning control to the terminal")
cmdPeeringDelete.Example = `The following example deletes the VPC Peering with the ID ` + "`" + `f81d4fae-7dec-11d0-a765-00a0c91e6bf6` + "`" +
`: doctl vpcs peerings delete f81d4fae-7dec-11d0-a765-00a0c91e6bf6`

return cmd
}

// RunVPCPeeringGet retrieves an existing VPC Peering by its identifier.
func RunVPCPeeringGet(c *CmdConfig) error {
err := ensureOneArg(c)
if err != nil {
return err
}
peeringID := c.Args[0]

peering, err := c.VPCs().GetPeering(peeringID)
if err != nil {
return err
}

item := &displayers.VPCPeering{VPCPeerings: do.VPCPeerings{*peering}}
return c.Display(item)
}

// RunVPCPeeringCreate creates a new VPC Peering with a given configuration.
func RunVPCPeeringCreate(c *CmdConfig) error {
if len(c.Args) == 0 {
return doctl.NewMissingArgsErr(c.NS)
}
peeringName := c.Args[0]

r := new(godo.VPCPeeringCreateRequest)
r.Name = peeringName

vpcIDs, err := c.Doit.GetString(c.NS, doctl.ArgVPCPeeringVPCIDs)
if err != nil {
return err
}

for _, v := range strings.Split(vpcIDs, ",") {
if v == "" {
return errors.New("VPC ID is empty")
}

r.VPCIDs = append(r.VPCIDs, strings.TrimSpace(v))
}

if len(r.VPCIDs) != 2 {
return errors.New("VPC IDs length should be 2")
}

vpcService := c.VPCs()
peering, err := vpcService.CreateVPCPeering(r)
if err != nil {
return err
}

wait, err := c.Doit.GetBool(c.NS, doctl.ArgCommandWait)
if err != nil {
return err
}

if wait {
notice("VPC Peering creation is in progress, waiting for VPC Peering to become active")

err := waitForVPCPeering(vpcService, peering.ID, "ACTIVE", false)
if err != nil {
return fmt.Errorf("VPC Peering couldn't enter `active` state: %v", err)
}

peering, _ = vpcService.GetPeering(peering.ID)
}

item := &displayers.VPCPeering{VPCPeerings: do.VPCPeerings{*peering}}
return c.Display(item)
}

// RunVPCPeeringList lists VPC Peerings
func RunVPCPeeringList(c *CmdConfig) error {
vpcID, err := c.Doit.GetString(c.NS, doctl.ArgVPCPeeringVPCID)
if err != nil {
return err
}

var list do.VPCPeerings
if vpcID == "" {
list, err = c.VPCs().ListVPCPeerings()
if err != nil {
return err
}
} else {
list, err = c.VPCs().ListVPCPeeringsByVPCID(vpcID)
if err != nil {
return err
}
}

item := &displayers.VPCPeering{VPCPeerings: list}
return c.Display(item)
}

// RunVPCPeeringUpdate updates an existing VPC Peering with new configuration.
func RunVPCPeeringUpdate(c *CmdConfig) error {
err := ensureOneArg(c)
if err != nil {
return err
}
peeringID := c.Args[0]

r := new(godo.VPCPeeringUpdateRequest)
name, err := c.Doit.GetString(c.NS, doctl.ArgVPCPeeringName)
if err != nil {
return err
}
r.Name = name

peering, err := c.VPCs().UpdateVPCPeering(peeringID, r)
if err != nil {
return err
}

item := &displayers.VPCPeering{VPCPeerings: do.VPCPeerings{*peering}}
return c.Display(item)
}

// RunVPCPeeringDelete deletes a VPC Peering by its identifier.
func RunVPCPeeringDelete(c *CmdConfig) error {
if len(c.Args) == 0 {
return doctl.NewMissingArgsErr(c.NS)
}

peeringID := c.Args[0]

force, err := c.Doit.GetBool(c.NS, doctl.ArgForce)
if err != nil {
return err
}

if force || AskForConfirmDelete("VPC Peering", 1) == nil {
vpcs := c.VPCs()
if err := vpcs.DeleteVPCPeering(peeringID); err != nil {
return err
}

wait, err := c.Doit.GetBool(c.NS, doctl.ArgCommandWait)
if err != nil {
return err
}

if wait {
notice("VPC Peering deletion is in progress, waiting for VPC Peering to be deleted")

err := waitForVPCPeering(vpcs, peeringID, "DELETED", true)
if err != nil {
return fmt.Errorf("VPC Peering couldn't be deleted : %v", err)
}
}

} else {
return fmt.Errorf("operation aborted")
}
notice("VPC Peering is successfully deleted")
return nil
}

func waitForVPCPeering(vpcService do.VPCsService, peeringID 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)
}
}

peering, err := vpcService.GetPeering(peeringID)
if err != nil {
if terminateOnNotFound && strings.Contains(err.Error(), "not found") {
return nil
}
return err
}

if peering.Status == errStatus {
return fmt.Errorf("VPC Peering (%s) entered status `%s`", peeringID, errStatus)
}

if peering.Status == wantStatus {
return nil
}

attempts++
time.Sleep(5 * time.Second)
}

return fmt.Errorf("timeout waiting for VPC Peering (%s) to become %s", peeringID, wantStatus)
}
Loading

0 comments on commit 42fc8b6

Please sign in to comment.