blob: 21d5637fd352f053449571096922807c166f39b0 [file] [log] [blame]
package main
import (
"crypto/x509"
"errors"
"fmt"
"io"
"github.com/spf13/cobra"
"source.monogon.dev/metropolis/cli/metroctl/core"
"source.monogon.dev/metropolis/pkg/logtree"
"source.monogon.dev/metropolis/proto/api"
)
var nodeLogsCmd = &cobra.Command{
Short: "Get/stream logs from node",
Use: "logs [node-id]",
Args: cobra.MinimumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
// First connect to the main management service and figure out the node's IP
// address.
cc := dialAuthenticated(ctx)
mgmt := api.NewManagementClient(cc)
nodes, err := core.GetNodes(ctx, mgmt, fmt.Sprintf("node.id == %q", args[0]))
if err != nil {
return fmt.Errorf("when getting node info: %w", err)
}
if len(nodes) == 0 {
return fmt.Errorf("no such node")
}
if len(nodes) > 1 {
return fmt.Errorf("expression matched more than one node")
}
n := nodes[0]
if n.Status == nil || n.Status.ExternalAddress == "" {
return fmt.Errorf("node has no external address")
}
// TODO(q3k): save CA certificate on takeover
info, err := mgmt.GetClusterInfo(ctx, &api.GetClusterInfoRequest{})
if err != nil {
return fmt.Errorf("couldn't get cluster info: %w", err)
}
cacert, err := x509.ParseCertificate(info.CaCertificate)
if err != nil {
return fmt.Errorf("remote CA certificate invalid: %w", err)
}
fmt.Printf("Getting logs from %s (%s)...\n", n.Id, n.Status.ExternalAddress)
// Dial the actual node at its management port.
cl := dialAuthenticatedNode(ctx, n.Id, n.Status.ExternalAddress, cacert)
nmgmt := api.NewNodeManagementClient(cl)
srv, err := nmgmt.Logs(ctx, &api.GetLogsRequest{
Dn: "",
BacklogMode: api.GetLogsRequest_BACKLOG_ALL,
StreamMode: api.GetLogsRequest_STREAM_DISABLE,
Filters: []*api.LogFilter{
{
Filter: &api.LogFilter_WithChildren_{
WithChildren: &api.LogFilter_WithChildren{},
},
},
},
})
if err != nil {
return fmt.Errorf("failed to get logs: %w", err)
}
for {
res, err := srv.Recv()
if errors.Is(err, io.EOF) {
fmt.Println("Done.")
break
}
if err != nil {
return fmt.Errorf("log stream failed: %w", err)
}
for _, entry := range res.BacklogEntries {
entry, err := logtree.LogEntryFromProto(entry)
if err != nil {
fmt.Printf("invalid entry: %v\n", err)
continue
}
fmt.Println(entry.String())
}
}
return nil
},
}