blob: 21d5637fd352f053449571096922807c166f39b0 [file] [log] [blame]
Serge Bazanskib91938f2023-03-29 14:31:22 +02001package main
2
3import (
4 "crypto/x509"
5 "errors"
6 "fmt"
7 "io"
8
9 "github.com/spf13/cobra"
10
11 "source.monogon.dev/metropolis/cli/metroctl/core"
12 "source.monogon.dev/metropolis/pkg/logtree"
13 "source.monogon.dev/metropolis/proto/api"
14)
15
16var nodeLogsCmd = &cobra.Command{
17 Short: "Get/stream logs from node",
18 Use: "logs [node-id]",
19 Args: cobra.MinimumNArgs(1),
20 RunE: func(cmd *cobra.Command, args []string) error {
21 ctx := cmd.Context()
22
23 // First connect to the main management service and figure out the node's IP
24 // address.
25 cc := dialAuthenticated(ctx)
26 mgmt := api.NewManagementClient(cc)
27 nodes, err := core.GetNodes(ctx, mgmt, fmt.Sprintf("node.id == %q", args[0]))
28 if err != nil {
29 return fmt.Errorf("when getting node info: %w", err)
30 }
31
32 if len(nodes) == 0 {
33 return fmt.Errorf("no such node")
34 }
35 if len(nodes) > 1 {
36 return fmt.Errorf("expression matched more than one node")
37 }
38 n := nodes[0]
39 if n.Status == nil || n.Status.ExternalAddress == "" {
40 return fmt.Errorf("node has no external address")
41 }
42
43 // TODO(q3k): save CA certificate on takeover
44 info, err := mgmt.GetClusterInfo(ctx, &api.GetClusterInfoRequest{})
45 if err != nil {
46 return fmt.Errorf("couldn't get cluster info: %w", err)
47 }
48 cacert, err := x509.ParseCertificate(info.CaCertificate)
49 if err != nil {
50 return fmt.Errorf("remote CA certificate invalid: %w", err)
51 }
52
53 fmt.Printf("Getting logs from %s (%s)...\n", n.Id, n.Status.ExternalAddress)
54 // Dial the actual node at its management port.
55 cl := dialAuthenticatedNode(ctx, n.Id, n.Status.ExternalAddress, cacert)
56 nmgmt := api.NewNodeManagementClient(cl)
57
58 srv, err := nmgmt.Logs(ctx, &api.GetLogsRequest{
59 Dn: "",
60 BacklogMode: api.GetLogsRequest_BACKLOG_ALL,
61 StreamMode: api.GetLogsRequest_STREAM_DISABLE,
62 Filters: []*api.LogFilter{
63 {
64 Filter: &api.LogFilter_WithChildren_{
65 WithChildren: &api.LogFilter_WithChildren{},
66 },
67 },
68 },
69 })
70 if err != nil {
71 return fmt.Errorf("failed to get logs: %w", err)
72 }
73 for {
74 res, err := srv.Recv()
75 if errors.Is(err, io.EOF) {
76 fmt.Println("Done.")
77 break
78 }
79 if err != nil {
80 return fmt.Errorf("log stream failed: %w", err)
81 }
82 for _, entry := range res.BacklogEntries {
83 entry, err := logtree.LogEntryFromProto(entry)
84 if err != nil {
85 fmt.Printf("invalid entry: %v\n", err)
86 continue
87 }
88 fmt.Println(entry.String())
89 }
90 }
91
92 return nil
93 },
94}