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