Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions internal/commands/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,13 @@ func runInstanceList(cmd *cobra.Command) error {
if privateIP == "" {
privateIP = vm.NetworkPrivateIP()
}
publicIP := vm.GetPublicIPAddress()
row := []string{
instanceDisplayID(vm),
vm.Name,
vm.State,
privateIP,
instance.StringVal(vm.PublicIP),
publicIP,
regionName,
}
if expandedOutput {
Expand All @@ -123,7 +124,7 @@ func runInstanceList(cmd *cobra.Command) error {
vm.Name,
vm.State,
privateIP,
instance.StringVal(vm.PublicIP),
publicIP,
regionName,
templateName,
vm.CreatedAt,
Expand Down Expand Up @@ -202,8 +203,8 @@ func runInstanceGet(cmd *cobra.Command, slug string) error {
regionName = vm.Region.Name
}
billingCycle := ""
if vm.BillingCycle != nil {
billingCycle = vm.BillingCycle.Name
if vm.Offering != nil && vm.Offering.BillingCycle != nil {
billingCycle = vm.Offering.BillingCycle.Name
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
storageName := ""
if vm.StorageSetting != nil {
Expand All @@ -215,6 +216,8 @@ func runInstanceGet(cmd *cobra.Command, slug string) error {
privateIP = vm.NetworkPrivateIP()
}

publicIP := vm.GetPublicIPAddress()

headers := []string{"FIELD", "VALUE"}
rows := [][]string{
{"ID", instanceDisplayID(*vm)},
Expand All @@ -224,7 +227,7 @@ func runInstanceGet(cmd *cobra.Command, slug string) error {
{"State", vm.State},
{"Username", vm.Username},
{"Private IP", privateIP},
{"Public IP", instance.StringVal(vm.PublicIP)},
{"Public IP", publicIP},
{"Region", regionName},
{"Template", templateName},
{"OS Family", osFamily},
Expand Down
42 changes: 39 additions & 3 deletions pkg/api/instance/instance.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ type VirtualMachine struct {
Hostname string `json:"hostname"`
Username string `json:"username"`
State string `json:"state"`
IPAddresses []IPAddresses `json:"ipaddresses"`
PublicIP *string `json:"public_ip"`
PrivateIP *string `json:"private_ip"`
FrozenAt *string `json:"frozen_at"`
Expand All @@ -71,6 +72,7 @@ type VirtualMachine struct {
IsVNF bool `json:"is_vnf"`
ConsoleURL *string `json:"console_url"`
Template *VMTemplate `json:"template"`
Offering *Offering `json:"offering"`
BillingCycle *BillingCycle `json:"billing_cycle"`
Region *Region `json:"region"`
CloudProvider *CloudProvider `json:"cloud_provider"`
Expand Down Expand Up @@ -107,21 +109,42 @@ type VMNetworkIP struct {
// NetworkPrivateIP returns the private IP from the default network, falling back
// to the first network with an IP if no default is set.
func (vm *VirtualMachine) NetworkPrivateIP() string {
var fallback string
fallback := "-"
for _, n := range vm.Networks {
if n.Pivot == nil || n.Pivot.IPAddress == "" {
continue
}
if n.IsDefault || n.Pivot.IsDefault {
return n.Pivot.IPAddress
}
if fallback == "" {
if fallback == "-" {
fallback = n.Pivot.IPAddress
}
}
return fallback
}

// Returns the PublicIP if a VM is assigned one
func (vm *VirtualMachine) GetPublicIPAddress() string {
fallback := "-"

for _, ip := range vm.IPAddresses {
if ip.IPAddress == "" {
continue
}

if ip.Type != "" {
return ip.IPAddress
}

if fallback == "-" {
fallback = ip.IPAddress
}
}

return fallback
}
Comment thread
cokerrd marked this conversation as resolved.

// VMTemplate represents the template/OS info on a VM.
type VMTemplate struct {
ID string `json:"id"`
Expand Down Expand Up @@ -154,6 +177,13 @@ type OSVersion struct {
PricingType string `json:"pricing_type"`
}

// IPAddresses represents the IP address of a VM
type IPAddresses struct {
ID string `json:"id"`
IPAddress string `json:"ipaddress"`
Type string `json:"type"`
}

// BillingCycle represents a billing period.
type BillingCycle struct {
ID string `json:"id"`
Expand All @@ -162,6 +192,10 @@ type BillingCycle struct {
Duration int `json:"duration"`
Unit string `json:"unit"`
}
type Offering struct {
ID string `json:"id"`
BillingCycle *BillingCycle `json:"billing_cycle"`
}

// Region represents a cloud region.
type Region struct {
Expand Down Expand Up @@ -345,7 +379,9 @@ func (s *Service) List(ctx context.Context, region, project string) ([]VirtualMa
// resolution) see the full set rather than just the first page.
var all []VirtualMachine
for page := 1; ; page++ {
q := url.Values{}
q := url.Values{
"include": {"networks,ipaddresses"},
}
if region != "" {
q.Set("filter[region]", region)
}
Expand Down
35 changes: 35 additions & 0 deletions pkg/api/instance/instance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,26 @@ func TestList(t *testing.T) {
}
}

func TestListEmptyResponse(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string]interface{}{
"status": "Success",
"data": []interface{}{},
"total": 0,
})
}))
defer srv.Close()

svc := instance.NewService(newClient(srv.URL))
vms, err := svc.List(context.Background(), "", "")
if err != nil {
t.Fatalf("List() error = %v", err)
}
if len(vms) != 0 {
t.Errorf("got %d VMs, want 0", len(vms))
}
}

func TestListPaginatesAllPages(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/virtual-machines" {
Expand Down Expand Up @@ -121,6 +141,21 @@ func TestGet(t *testing.T) {
}
}

func TestGetNotFound(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(map[string]interface{}{
"status": "Error",
"message": "not found",
})
}))
defer srv.Close()

svc := instance.NewService(newClient(srv.URL))
_, err := svc.Get(context.Background(), "nonexistent")
if err == nil {
t.Error("expected error for not found")
}
}
func TestStart(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPut || r.URL.Path != "/virtual-machines/test-vm/start" {
Expand Down
Loading