Pulse/pkg/pulsecli/fleet.go
2026-05-04 08:40:34 +01:00

117 lines
3.2 KiB
Go

package pulsecli
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"github.com/spf13/cobra"
)
type FleetDeps struct {
HTTPClient HTTPDoer
Getenv func(string) string
}
type fleetConnectionsOptions struct {
APIURL string
Token string
}
type fleetConnectionsResponse struct {
Connections []json.RawMessage `json:"connections"`
Systems []json.RawMessage `json:"systems,omitempty"`
}
func newFleetCmd(deps *FleetDeps) *cobra.Command {
fleetCmd := &cobra.Command{
Use: "fleet",
Short: "Inspect canonical Pulse fleet state",
}
fleetCmd.AddCommand(newFleetConnectionsCmd(deps))
return fleetCmd
}
func newFleetConnectionsCmd(deps *FleetDeps) *cobra.Command {
opts := fleetConnectionsOptions{
APIURL: strings.TrimSpace(fleetGetenv(deps, "PULSE_API_URL")),
Token: strings.TrimSpace(fleetGetenv(deps, "PULSE_API_TOKEN")),
}
if opts.APIURL == "" {
opts.APIURL = defaultPulseAPIURL
}
cmd := &cobra.Command{
Use: "connections",
Short: "List canonical fleet connection rows",
RunE: func(cmd *cobra.Command, args []string) error {
return runFleetConnections(cmd, deps, opts)
},
}
cmd.Flags().StringVar(&opts.APIURL, "api-url", opts.APIURL, "Pulse server URL or /api base URL")
cmd.Flags().StringVar(&opts.Token, "token", opts.Token, "Pulse API token; defaults to PULSE_API_TOKEN")
return cmd
}
func runFleetConnections(cmd *cobra.Command, deps *FleetDeps, opts fleetConnectionsOptions) error {
token := strings.TrimSpace(opts.Token)
if token == "" {
return fmt.Errorf("api token is required (use --token or PULSE_API_TOKEN)")
}
endpoint, err := pulseAPIEndpoint(opts.APIURL, "/connections")
if err != nil {
return err
}
httpReq, err := http.NewRequestWithContext(cmd.Context(), http.MethodGet, endpoint.String(), nil)
if err != nil {
return fmt.Errorf("failed to build fleet connections request: %w", err)
}
httpReq.Header.Set("Accept", "application/json")
httpReq.Header.Set("Authorization", "Bearer "+token)
resp, err := fleetHTTPClient(deps).Do(httpReq)
if err != nil {
return fmt.Errorf("fleet connections request failed: %w", err)
}
defer resp.Body.Close()
respBody, err := ReadBoundedHTTPBody(resp.Body, resp.ContentLength, maxPulseAPIResponseBytes, "fleet connections response")
if err != nil {
return fmt.Errorf("failed to read fleet connections response: %w", err)
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return apiStatusError("fleet connections request", resp.Status, respBody)
}
var connections fleetConnectionsResponse
if err := decodeJSONBytes(respBody, &connections); err != nil {
return fmt.Errorf("failed to decode fleet connections response: %w", err)
}
if connections.Connections == nil {
connections.Connections = []json.RawMessage{}
}
encoder := json.NewEncoder(cmd.OutOrStdout())
encoder.SetIndent("", " ")
if err := encoder.Encode(connections); err != nil {
return fmt.Errorf("failed to write fleet connections response: %w", err)
}
return nil
}
func fleetHTTPClient(deps *FleetDeps) HTTPDoer {
if deps != nil {
return cliHTTPClient(deps.HTTPClient)
}
return cliHTTPClient(nil)
}
func fleetGetenv(deps *FleetDeps, key string) string {
if deps != nil {
return cliGetenv(deps.Getenv, key)
}
return cliGetenv(nil, key)
}