mirror of
https://github.com/Control-D-Inc/ctrld.git
synced 2026-02-03 22:18:39 +00:00
- Update all Init*Cmd function signatures to accept rootCmd parameter: * InitServiceCmd(rootCmd *cobra.Command) * InitClientsCmd(rootCmd *cobra.Command) * InitLogCmd(rootCmd *cobra.Command) * InitUpgradeCmd(rootCmd *cobra.Command) * InitRunCmd(rootCmd *cobra.Command) * InitInterfacesCmd(rootCmd *cobra.Command) - Update function calls in cli.go to pass rootCmd parameter - Update InitInterfacesCmd call in commands_service.go Benefits: - Eliminates global state dependency on rootCmd variable - Makes dependencies explicit in function signatures - Improves testability by allowing different root commands - Better encapsulation and modularity
142 lines
3.4 KiB
Go
142 lines
3.4 KiB
Go
package cli
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/kardianos/service"
|
|
"github.com/olekukonko/tablewriter"
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/Control-D-Inc/ctrld/internal/clientinfo"
|
|
)
|
|
|
|
// ClientsCommand handles clients-related operations
|
|
type ClientsCommand struct {
|
|
controlClient *controlClient
|
|
}
|
|
|
|
// NewClientsCommand creates a new clients command handler
|
|
func NewClientsCommand() (*ClientsCommand, error) {
|
|
dir, err := socketDir()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to find ctrld home dir: %w", err)
|
|
}
|
|
|
|
cc := newControlClient(filepath.Join(dir, ctrldControlUnixSock))
|
|
return &ClientsCommand{
|
|
controlClient: cc,
|
|
}, nil
|
|
}
|
|
|
|
// ListClients lists all connected clients
|
|
func (cc *ClientsCommand) ListClients(cmd *cobra.Command, args []string) error {
|
|
// Check service status first
|
|
sc := NewServiceCommand()
|
|
s, _, err := sc.initializeServiceManager()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
status, err := s.Status()
|
|
if errors.Is(err, service.ErrNotInstalled) {
|
|
mainLog.Load().Warn().Msg("service not installed")
|
|
return nil
|
|
}
|
|
if status == service.StatusStopped {
|
|
mainLog.Load().Warn().Msg("service is not running")
|
|
return nil
|
|
}
|
|
|
|
resp, err := cc.controlClient.post(listClientsPath, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get clients: %w", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
var clients []*clientinfo.Client
|
|
if err := json.NewDecoder(resp.Body).Decode(&clients); err != nil {
|
|
return fmt.Errorf("failed to decode clients result: %w", err)
|
|
}
|
|
|
|
map2Slice := func(m map[string]struct{}) []string {
|
|
s := make([]string, 0, len(m))
|
|
for k := range m {
|
|
if k == "" { // skip empty source from output.
|
|
continue
|
|
}
|
|
s = append(s, k)
|
|
}
|
|
sort.Strings(s)
|
|
return s
|
|
}
|
|
|
|
// If metrics is enabled, server set this for all clients, so we can check only the first one.
|
|
// Ideally, we may have a field in response to indicate that query count should be shown, but
|
|
// it would break earlier version of ctrld, which only look list of clients in response.
|
|
withQueryCount := len(clients) > 0 && clients[0].IncludeQueryCount
|
|
data := make([][]string, len(clients))
|
|
for i, c := range clients {
|
|
row := []string{
|
|
c.IP.String(),
|
|
c.Hostname,
|
|
c.Mac,
|
|
strings.Join(map2Slice(c.Source), ","),
|
|
}
|
|
if withQueryCount {
|
|
row = append(row, strconv.FormatInt(c.QueryCount, 10))
|
|
}
|
|
data[i] = row
|
|
}
|
|
|
|
table := tablewriter.NewWriter(os.Stdout)
|
|
headers := []string{"IP", "Hostname", "Mac", "Discovered"}
|
|
if withQueryCount {
|
|
headers = append(headers, "Queries")
|
|
}
|
|
table.SetHeader(headers)
|
|
table.SetAutoFormatHeaders(false)
|
|
table.AppendBulk(data)
|
|
table.Render()
|
|
|
|
return nil
|
|
}
|
|
|
|
// InitClientsCmd creates the clients command with proper logic
|
|
func InitClientsCmd(rootCmd *cobra.Command) *cobra.Command {
|
|
listClientsCmd := &cobra.Command{
|
|
Use: "list",
|
|
Short: "List clients that ctrld discovered",
|
|
Args: cobra.NoArgs,
|
|
PreRun: func(cmd *cobra.Command, args []string) {
|
|
checkHasElevatedPrivilege()
|
|
},
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
cc, err := NewClientsCommand()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return cc.ListClients(cmd, args)
|
|
},
|
|
}
|
|
|
|
clientsCmd := &cobra.Command{
|
|
Use: "clients",
|
|
Short: "Manage clients",
|
|
Args: cobra.OnlyValidArgs,
|
|
ValidArgs: []string{
|
|
listClientsCmd.Use,
|
|
},
|
|
}
|
|
clientsCmd.AddCommand(listClientsCmd)
|
|
rootCmd.AddCommand(clientsCmd)
|
|
|
|
return clientsCmd
|
|
}
|