Skip to content

Commit 1d12b69

Browse files
committed
pkg: fix listing resources in cluster mode
1 parent 291cf7f commit 1d12b69

12 files changed

Lines changed: 309 additions & 167 deletions

File tree

cmd/cli/get.go

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package cli
22

33
import (
4+
v1 "cloudstackctl/apis/v1"
45
"cloudstackctl/pkg/handlers"
56
"encoding/json"
67
"log"
78
"net/url"
89

10+
cs "github.com/apache/cloudstack-go/v2/cloudstack"
11+
912
"github.com/spf13/cobra"
1013
)
1114

@@ -36,8 +39,10 @@ var getCmd = &cobra.Command{
3639
payload["name"] = name
3740
}
3841
raw, _ := json.Marshal(payload)
39-
if err := handlers.GetCloudStackResource(raw); err != nil {
42+
if resp, err := handlers.GetCloudStackResource(raw); err != nil {
4043
log.Fatalf("Local get failed: %v", err)
44+
} else {
45+
handlers.PrintCloudStackResource(resourceType, resp)
4146
}
4247
return
4348
}
@@ -54,10 +59,82 @@ var getCmd = &cobra.Command{
5459
if err != nil {
5560
log.Fatalf("Failed to query controller: %v", err)
5661
}
57-
println(string(body))
62+
63+
// If controller returned VM objects from DB, pretty-print them
64+
if resourceType == "VirtualMachine" {
65+
var vms []v1.VirtualMachine
66+
if err := json.Unmarshal(body, &vms); err == nil {
67+
handlers.PrintVMsFromDB(vms)
68+
return
69+
}
70+
}
71+
72+
tryDecodeAndPrint(resourceType, body)
5873
},
5974
}
6075

6176
func init() {
6277
rootCmd.AddCommand(getCmd)
6378
}
79+
80+
// tryDecodeAndPrint attempts to decode controller JSON into known typed
81+
// responses and prints them using the shared handlers. Returns true if
82+
// printing was performed.
83+
func tryDecodeAndPrint(resourceType string, body []byte) bool {
84+
// VMs are returned from the controller DB as []v1.VirtualMachine
85+
if resourceType == "VirtualMachine" {
86+
var vms []v1.VirtualMachine
87+
if err := json.Unmarshal(body, &vms); err == nil {
88+
handlers.PrintVMsFromDB(vms)
89+
return true
90+
}
91+
}
92+
93+
// Try CloudStack SDK response types for unmanaged resources so we can
94+
// preserve typed slices (e.g., []*cs.Network) when printing.
95+
switch resourceType {
96+
case "Network":
97+
var resp cs.ListNetworksResponse
98+
if err := json.Unmarshal(body, &resp); err == nil {
99+
handlers.PrintCloudStackResource(resourceType, &resp)
100+
return true
101+
}
102+
case "Volume":
103+
var resp cs.ListVolumesResponse
104+
if err := json.Unmarshal(body, &resp); err == nil {
105+
handlers.PrintCloudStackResource(resourceType, &resp)
106+
return true
107+
}
108+
case "Template":
109+
var resp cs.ListTemplatesResponse
110+
if err := json.Unmarshal(body, &resp); err == nil {
111+
handlers.PrintCloudStackResource(resourceType, &resp)
112+
return true
113+
}
114+
case "SSHKey":
115+
var resp cs.ListSSHKeyPairsResponse
116+
if err := json.Unmarshal(body, &resp); err == nil {
117+
handlers.PrintCloudStackResource(resourceType, &resp)
118+
return true
119+
}
120+
case "SecurityGroup":
121+
var resp cs.ListSecurityGroupsResponse
122+
if err := json.Unmarshal(body, &resp); err == nil {
123+
handlers.PrintCloudStackResource(resourceType, &resp)
124+
return true
125+
}
126+
case "AffinityGroup":
127+
var resp cs.ListAffinityGroupsResponse
128+
if err := json.Unmarshal(body, &resp); err == nil {
129+
handlers.PrintCloudStackResource(resourceType, &resp)
130+
return true
131+
}
132+
case "UserData":
133+
var resp cs.ListUserDataResponse
134+
if err := json.Unmarshal(body, &resp); err == nil {
135+
handlers.PrintCloudStackResource(resourceType, &resp)
136+
return true
137+
}
138+
}
139+
return false
140+
}

controller/controller.go

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ func (c *Controller) Start() {
239239
return
240240
}
241241
kind := r.URL.Query().Get("kind")
242+
name := r.URL.Query().Get("name")
242243
switch kind {
243244
case "Application":
244245
var apps []v1.Application
@@ -285,36 +286,21 @@ func (c *Controller) Start() {
285286
w.WriteHeader(http.StatusOK)
286287
w.Write(b)
287288
return
288-
// Unmanaged kinds: delegate to handlers/CloudStack client for listing and return raw JSON
289-
case "Network":
290-
resp, err := c.csClient.Network.ListNetworks(c.csClient.Network.NewListNetworksParams())
291-
if err != nil {
292-
http.Error(w, "cloudstack list networks failed", http.StatusInternalServerError)
293-
return
289+
// Unmanaged kinds: delegate to handlers.GetCloudStackResource which
290+
// centralizes CloudStack listing logic. Controller should not call
291+
// CloudStack APIs directly.
292+
case "Network", "Volume", "SSHKey", "SecurityGroup", "AffinityGroup", "UserData", "Template":
293+
payload := map[string]string{"kind": kind}
294+
if name != "" {
295+
payload["name"] = name
294296
}
295-
b, _ := json.Marshal(resp.Networks)
296-
w.Header().Set("Content-Type", "application/json")
297-
w.WriteHeader(http.StatusOK)
298-
w.Write(b)
299-
return
300-
case "Volume":
301-
resp, err := c.csClient.Volume.ListVolumes(c.csClient.Volume.NewListVolumesParams())
302-
if err != nil {
303-
http.Error(w, "cloudstack list volumes failed", http.StatusInternalServerError)
304-
return
305-
}
306-
b, _ := json.Marshal(resp.Volumes)
307-
w.Header().Set("Content-Type", "application/json")
308-
w.WriteHeader(http.StatusOK)
309-
w.Write(b)
310-
return
311-
case "Template":
312-
resp, err := c.csClient.Template.ListTemplates(c.csClient.Template.NewListTemplatesParams(""))
297+
raw, _ := json.Marshal(payload)
298+
obj, err := handlers.GetCloudStackResource(raw)
313299
if err != nil {
314-
http.Error(w, "cloudstack list templates failed", http.StatusInternalServerError)
300+
http.Error(w, fmt.Sprintf("failed to list %s: %v", kind, err), http.StatusInternalServerError)
315301
return
316302
}
317-
b, _ := json.Marshal(resp.Templates)
303+
b, _ := json.Marshal(obj)
318304
w.Header().Set("Content-Type", "application/json")
319305
w.WriteHeader(http.StatusOK)
320306
w.Write(b)
@@ -354,6 +340,7 @@ func (c *Controller) Start() {
354340
case "Template":
355341
params := c.csClient.Template.NewListTemplatesParams("")
356342
params.SetName(name)
343+
params.SetTemplatefilter("all")
357344
resp, err := c.csClient.Template.ListTemplates(params)
358345
if err != nil || resp == nil || len(resp.Templates) == 0 {
359346
http.Error(w, "template not found", http.StatusNotFound)

pkg/handlers/affinitygroup.go

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import (
44
"encoding/json"
55
"fmt"
66
"log"
7-
"os"
8-
"text/tabwriter"
97

108
v1 "cloudstackctl/apis/v1"
119
"cloudstackctl/pkg/cloudstack"
@@ -42,27 +40,20 @@ func ApplyAffinityGroup(ag *v1.AffinityGroup) error {
4240
}
4341

4442
// ListAffinityGroups lists affinity groups in CloudStack.
45-
func ListAffinityGroups(name string) error {
43+
func ListAffinityGroups(name string) (any, error) {
4644
client, err := cloudstack.NewClient()
4745
if err != nil {
48-
return fmt.Errorf("failed to create CloudStack client: %w", err)
46+
return nil, fmt.Errorf("failed to create CloudStack client: %w", err)
4947
}
5048
params := client.AffinityGroup.NewListAffinityGroupsParams()
5149
if name != "" {
5250
params.SetName(name)
5351
}
5452
resp, err := client.AffinityGroup.ListAffinityGroups(params)
5553
if err != nil {
56-
return fmt.Errorf("cloudstack API error: %w", err)
54+
return nil, fmt.Errorf("cloudstack API error: %w", err)
5755
}
58-
// Print header then rows using tabwriter
59-
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
60-
fmt.Fprintln(w, "NAME\tID")
61-
for _, a := range resp.AffinityGroups {
62-
fmt.Fprintf(w, "%s\t%s\n", a.Name, a.Id)
63-
}
64-
w.Flush()
65-
return nil
56+
return resp, err
6657
}
6758

6859
// DescribeAffinityGroup prints details for an affinity group by name.

pkg/handlers/network.go

Lines changed: 7 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@ import (
44
"encoding/json"
55
"fmt"
66
"log"
7-
"os"
87
"strconv"
98
"strings"
10-
"text/tabwriter"
119

1210
v1 "cloudstackctl/apis/v1"
1311
"cloudstackctl/pkg/cloudstack"
@@ -37,57 +35,25 @@ func ResolveNetwork(name string) (string, error) {
3735
}
3836

3937
// ListNetworks queries CloudStack and prints a table of networks.
40-
func ListNetworks(name string) error {
38+
func ListNetworks(name string) (any, error) {
4139
client, err := cloudstack.NewClient()
4240
if err != nil {
43-
return fmt.Errorf("failed to create CloudStack client: %w", err)
41+
return nil, fmt.Errorf("failed to create CloudStack client: %w", err)
4442
}
4543
params := client.Network.NewListNetworksParams()
4644
if name != "" {
4745
params.SetName(name)
4846
}
4947
resp, err := client.Network.ListNetworks(params)
5048
if err != nil {
51-
return fmt.Errorf("cloudstack API error: %w", err)
52-
}
53-
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
54-
fmt.Fprintln(w, "NAME\tID\tZONE\tVLAN\tDISPLAY TEXT\tTYPE\tSTATE")
55-
for _, n := range resp.Networks {
56-
display := n.Displaytext
57-
if display == "" {
58-
display = n.Name
59-
}
60-
61-
zoneName := n.Zoneid
62-
if n.Zoneid != "" {
63-
zp := client.Zone.NewListZonesParams()
64-
zp.SetId(n.Zoneid)
65-
zr, zerr := client.Zone.ListZones(zp)
66-
if zerr == nil && zr != nil && len(zr.Zones) > 0 {
67-
zoneName = zr.Zones[0].Name
68-
}
69-
}
70-
71-
// Attempt to extract VLAN information from the returned network object
72-
// via JSON to avoid SDK field name differences across versions.
73-
vlan := ""
74-
if b, merr := json.Marshal(n); merr == nil {
75-
var m map[string]interface{}
76-
if uerr := json.Unmarshal(b, &m); uerr == nil {
77-
if v, ok := m["vlan"].(string); ok {
78-
vlan = v
79-
} else if v, ok := m["vlanid"].(string); ok {
80-
vlan = v
81-
}
82-
}
83-
}
84-
85-
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\n", n.Name, n.Id, zoneName, vlan, display, n.Type, n.State)
49+
return nil, fmt.Errorf("cloudstack API error: %w", err)
8650
}
87-
w.Flush()
88-
return nil
51+
return resp, err
8952
}
9053

54+
// PrintNetworks prints a table of networks from the SDK slice.
55+
// PrintNetworks moved to print.go
56+
9157
// DescribeNetwork prints JSON for a single network identified by name.
9258
func DescribeNetwork(name string) error {
9359
client, err := cloudstack.NewClient()

0 commit comments

Comments
 (0)