Skip to content

Commit

Permalink
Merge pull request #57 from squat/networks
Browse files Browse the repository at this point in the history
lib: add new network resource
  • Loading branch information
JamesClonk authored May 30, 2018
2 parents 164aa25 + ea56c30 commit aa43b5d
Show file tree
Hide file tree
Showing 4 changed files with 373 additions and 5 deletions.
82 changes: 82 additions & 0 deletions lib/network.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package lib

import (
"fmt"
"net"
"net/url"
"sort"
"strings"
)

// Network on Vultr account
type Network struct {
ID string `json:"NETWORKID"`
RegionID int `json:"DCID,string"`
Description string `json:"description"`
V4Subnet string `json:"v4_subnet"`
V4SubnetMask int `json:"v4_subnet_mask"`
Created string `json:"date_created"`
}

type networks []Network

func (n networks) Len() int { return len(n) }
func (n networks) Swap(i, j int) { n[i], n[j] = n[j], n[i] }
func (n networks) Less(i, j int) bool {
// sort order: description, created
if strings.ToLower(n[i].Description) < strings.ToLower(n[j].Description) {
return true
} else if strings.ToLower(n[i].Description) > strings.ToLower(n[j].Description) {
return false
}
return n[i].Created < n[j].Created
}

// GetNetworks returns a list of Networks from Vultr account
func (c *Client) GetNetworks() (nets []Network, err error) {
var netMap map[string]Network
if err := c.get(`network/list`, &netMap); err != nil {
return nil, err
}

for _, net := range netMap {
nets = append(nets, net)
}
sort.Sort(networks(nets))
return nets, nil
}

// CreateNetwork creates new Network on Vultr
func (c *Client) CreateNetwork(regionID int, description string, subnet *net.IPNet) (Network, error) {
var net string
var mask int
values := url.Values{
"DCID": {fmt.Sprintf("%v", regionID)},
"description": {description},
}
if subnet != nil && subnet.IP.To4() != nil {
net = subnet.IP.To4().String()
mask, _ = subnet.Mask.Size()
values.Add("v4_subnet", net)
values.Add("v4_subnet_mask", fmt.Sprintf("%v", mask))
}
var network Network
if err := c.post(`network/create`, values, &network); err != nil {
return Network{}, err
}
network.RegionID = regionID
network.Description = description
network.V4Subnet = net
network.V4SubnetMask = mask

return network, nil
}

// DeleteNetwork deletes an existing Network from Vultr account
func (c *Client) DeleteNetwork(id string) error {
values := url.Values{
"NETWORKID": {id},
}

return c.post(`network/destroy`, values, nil)
}
146 changes: 146 additions & 0 deletions lib/network_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package lib

import (
"net"
"net/http"
"testing"

"github.com/stretchr/testify/assert"
)

func Test_Networks_GetNetworks_Error(t *testing.T) {
server, client := getTestServerAndClient(http.StatusNotAcceptable, `{error}`)
defer server.Close()

nets, err := client.GetNetworks()
assert.Nil(t, nets)
if assert.NotNil(t, err) {
assert.Equal(t, `{error}`, err.Error())
}
}

func Test_Networks_GetNetworks_NoNets(t *testing.T) {
server, client := getTestServerAndClient(http.StatusOK, `[]`)
defer server.Close()

nets, err := client.GetNetworks()
if err != nil {
t.Error(err)
}
assert.Nil(t, nets)
}

func Test_Networks_GetNetworks_OK(t *testing.T) {
server, client := getTestServerAndClient(http.StatusOK, `{
"net539626f0798d7": {
"DCID": "1",
"NETWORKID": "net539626f0798d7",
"date_created": "2017-08-25 12:23:45",
"description": "test1",
"v4_subnet": "10.99.0.0",
"v4_subnet_mask": 24
},
"net53962b0f2341f": {
"DCID": "1",
"NETWORKID": "net53962b0f2341f",
"date_created": "2014-06-09 17:45:51",
"description": "vultr",
"v4_subnet": "0.0.0.0",
"v4_subnet_mask": 0
}
}`)
defer server.Close()

nets, err := client.GetNetworks()
if err != nil {
t.Error(err)
}
if assert.NotNil(t, nets) {
assert.Equal(t, 2, len(nets))

assert.Equal(t, "net539626f0798d7", nets[0].ID)
assert.Equal(t, 1, nets[0].RegionID)
assert.Equal(t, "test1", nets[0].Description)
assert.Equal(t, "2017-08-25 12:23:45", nets[0].Created)
assert.Equal(t, "10.99.0.0", nets[0].V4Subnet)
assert.Equal(t, 24, nets[0].V4SubnetMask)

assert.Equal(t, "net53962b0f2341f", nets[1].ID)
assert.Equal(t, 1, nets[1].RegionID)
assert.Equal(t, "vultr", nets[1].Description)
assert.Equal(t, "2014-06-09 17:45:51", nets[1].Created)
assert.Equal(t, "0.0.0.0", nets[1].V4Subnet)
assert.Equal(t, 0, nets[1].V4SubnetMask)
}
}

func Test_Networks_CreateNetwork_Error(t *testing.T) {
server, client := getTestServerAndClient(http.StatusNotAcceptable, `{error}`)
defer server.Close()

_, subnet, err := net.ParseCIDR("192.0.2.1/24")
if err != nil {
t.Error(err)
}

net, err := client.CreateNetwork(1, "test", subnet)
assert.Equal(t, Network{}, net)
if assert.NotNil(t, err) {
assert.Equal(t, `{error}`, err.Error())
}
}

func Test_Networks_CreateNetwork_NoNet(t *testing.T) {
server, client := getTestServerAndClient(http.StatusOK, `[]`)
defer server.Close()

_, subnet, err := net.ParseCIDR("192.0.2.1/24")
if err != nil {
t.Error(err)
}

net, err := client.CreateNetwork(1, "test", subnet)
if err != nil {
t.Error(err)
}
assert.Equal(t, "", net.ID)
}

func Test_Networks_CreateNetwork_OK(t *testing.T) {
server, client := getTestServerAndClient(http.StatusOK, `{"NETWORKID": "net59a0526477dd3"}`)
defer server.Close()

_, subnet, err := net.ParseCIDR("192.0.2.1/24")
if err != nil {
t.Error(err)
}
mask, _ := subnet.Mask.Size()

net, err := client.CreateNetwork(1, "test", subnet)
if err != nil {
t.Error(err)
}
if assert.NotNil(t, net) {
assert.Equal(t, "net59a0526477dd3", net.ID)
assert.Equal(t, "test", net.Description)
assert.Equal(t, subnet.IP.String(), net.V4Subnet)
assert.Equal(t, mask, net.V4SubnetMask)
}
}

func Test_Networks_DeleteNetwork_Error(t *testing.T) {
server, client := getTestServerAndClient(http.StatusNotAcceptable, `{error}`)
defer server.Close()

err := client.DeleteNetwork("id-1")
if assert.NotNil(t, err) {
assert.Equal(t, `{error}`, err.Error())
}
}

func Test_Networks_DeleteNetwork_OK(t *testing.T) {
server, client := getTestServerAndClient(http.StatusOK, `{no-response?!}`)
defer server.Close()

assert.Nil(t, client.DeleteNetwork("id-1"))
}
70 changes: 65 additions & 5 deletions lib/servers.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ type ServerOptions struct {
SSHKey string
ReservedIP string
IPV6 bool
Networks []string
PrivateNetworking bool
AutoBackups bool
DontNotifyOnActivate bool
Expand Down Expand Up @@ -293,16 +294,22 @@ func (c *Client) CreateServer(name string, regionID, planID, osID int, options *
values.Add("reserved_ip_v4", options.ReservedIP)
}

if options.Networks != nil && len(options.Networks) != 0 {
for _, n := range options.Networks {
values.Add("NETWORKID[]", n)
}
} else {
values.Add("enable_private_network", "no")
if options.PrivateNetworking {
values.Set("enable_private_network", "yes")
}
}

values.Add("enable_ipv6", "no")
if options.IPV6 {
values.Set("enable_ipv6", "yes")
}

values.Add("enable_private_network", "no")
if options.PrivateNetworking {
values.Set("enable_private_network", "yes")
}

values.Add("auto_backups", "no")
if options.AutoBackups {
values.Set("auto_backups", "yes")
Expand Down Expand Up @@ -585,3 +592,56 @@ func (c *Client) ListApplicationsforServer(id string) (apps []Application, err e
sort.Sort(applications(apps))
return apps, nil
}

// PrivateNetwork on Vultr
type PrivateNetwork struct {
ID string `json:"NETWORKID"`
MACAddress string `json:"mac_address"`
IPAddress string `json:"ip_address"`
}

type privateNetworks []PrivateNetwork

func (p privateNetworks) Len() int { return len(p) }
func (p privateNetworks) Swap(i, j int) { p[i], p[j] = p[j], p[i] }
func (p privateNetworks) Less(i, j int) bool {
return strings.ToLower(p[i].MACAddress) < strings.ToLower(p[j].MACAddress)
}

// ListPrivateNetworksForServer lists all the private networks to which an existing virtual machine is attached
func (c *Client) ListPrivateNetworksForServer(id string) (nets []PrivateNetwork, err error) {
var netMap map[string]PrivateNetwork
if err := c.get(`server/private_networks?SUBID=`+id, &netMap); err != nil {
return nil, err
}

for _, net := range netMap {
nets = append(nets, net)
}
sort.Sort(privateNetworks(nets))
return nets, nil
}

// DisablePrivateNetworkForServer removes the given virtual machine from the given private network
func (c *Client) DisablePrivateNetworkForServer(id, networkID string) error {
values := url.Values{
"SUBID": {id},
"NETWORKID": {networkID},
}

return c.post(`server/private_network_disable`, values, nil)
}

// EnablePrivateNetworkForServer enables private networking for the given virtual machine.
// If private networking is already enabled, then nothing occurs.
// If multiple private networks exist in the virtual machine's region, then the network ID must be specified.
func (c *Client) EnablePrivateNetworkForServer(id, networkID string) error {
values := url.Values{
"SUBID": {id},
}
if networkID != "" {
values.Add("NETWORKID", networkID)
}

return c.post(`server/private_network_enable`, values, nil)
}
Loading

0 comments on commit aa43b5d

Please sign in to comment.