blob: 557a9f209a2b774c9e0c24a4e0e046343d19a47b [file] [log] [blame]
Lorenz Brun878f5f92020-05-12 16:15:39 +02001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17package main
18
19import (
20 "context"
21 "flag"
22 "fmt"
Serge Bazanskib0272182020-11-02 18:39:44 +010023 "io"
Lorenz Brun878f5f92020-05-12 16:15:39 +020024 "io/ioutil"
25 "math/rand"
26 "os"
Lorenz Brun09c275b2021-03-30 12:47:09 +020027 "strings"
Lorenz Brun878f5f92020-05-12 16:15:39 +020028 "time"
29
30 "github.com/spf13/pflag"
31 "google.golang.org/grpc"
32 cliflag "k8s.io/component-base/cli/flag"
33 "k8s.io/kubectl/pkg/cmd/plugin"
34 "k8s.io/kubectl/pkg/util/logs"
35 "k8s.io/kubernetes/pkg/kubectl/cmd"
36
Serge Bazanski31370b02021-01-07 16:31:14 +010037 "source.monogon.dev/metropolis/pkg/logtree"
38 apb "source.monogon.dev/metropolis/proto/api"
Lorenz Brun878f5f92020-05-12 16:15:39 +020039)
40
41func main() {
Serge Bazanskiefdb6e92020-07-13 17:19:27 +020042 ctx := context.Background()
Serge Bazanski216fe7b2021-05-21 18:36:16 +020043 // Hardcode localhost since this should never be used to interface with a
44 // production node because of missing encryption & authentication
Lorenz Brun878f5f92020-05-12 16:15:39 +020045 grpcClient, err := grpc.Dial("localhost:7837", grpc.WithInsecure())
46 if err != nil {
47 fmt.Printf("Failed to dial debug service (is it running): %v\n", err)
48 }
Serge Bazanskiefdb6e92020-07-13 17:19:27 +020049 debugClient := apb.NewNodeDebugServiceClient(grpcClient)
Lorenz Brun878f5f92020-05-12 16:15:39 +020050 if len(os.Args) < 2 {
51 fmt.Println("Please specify a subcommand")
52 os.Exit(1)
53 }
54
55 logsCmd := flag.NewFlagSet("logs", flag.ExitOnError)
Serge Bazanskib0272182020-11-02 18:39:44 +010056 logsTailN := logsCmd.Int("tail", -1, "Get last n lines (-1 = whole buffer, 0 = disable)")
57 logsStream := logsCmd.Bool("follow", false, "Stream log entries live from the system")
58 logsRecursive := logsCmd.Bool("recursive", false, "Get entries from entire DN subtree")
Lorenz Brun878f5f92020-05-12 16:15:39 +020059 logsCmd.Usage = func() {
Serge Bazanskib0272182020-11-02 18:39:44 +010060 fmt.Fprintf(os.Stderr, "Usage: %s %s [options] dn\n", os.Args[0], os.Args[1])
Lorenz Brun878f5f92020-05-12 16:15:39 +020061 flag.PrintDefaults()
62
Serge Bazanskib0272182020-11-02 18:39:44 +010063 fmt.Fprintf(os.Stderr, "Example:\n %s %s --tail 5 --follow init\n", os.Args[0], os.Args[1])
Lorenz Brun878f5f92020-05-12 16:15:39 +020064 }
65 conditionCmd := flag.NewFlagSet("condition", flag.ExitOnError)
66 conditionCmd.Usage = func() {
67 fmt.Fprintf(os.Stderr, "Usage: %s %s [options] component_path\n", os.Args[0], os.Args[1])
68 flag.PrintDefaults()
69
70 fmt.Fprintf(os.Stderr, "Example:\n %s %s IPAssigned\n", os.Args[0], os.Args[1])
71 }
Serge Bazanskib0272182020-11-02 18:39:44 +010072
Lorenz Brun09c275b2021-03-30 12:47:09 +020073 traceCmd := flag.NewFlagSet("trace", flag.ExitOnError)
74 traceCmd.Usage = func() {
75 fmt.Fprintf(os.Stderr, "Usage: %v %v [options] tracer\n", os.Args[0], os.Args[1])
76 flag.PrintDefaults()
77 }
78 functionFilter := traceCmd.String("function_filter", "", "Only trace functions matched by this filter (comma-separated, supports wildcards via *)")
79 functionGraphFilter := traceCmd.String("function_graph_filter", "", "Only trace functions matched by this filter and their children (syntax same as function_filter)")
80
Lorenz Brun878f5f92020-05-12 16:15:39 +020081 switch os.Args[1] {
82 case "logs":
83 logsCmd.Parse(os.Args[2:])
Serge Bazanskib0272182020-11-02 18:39:44 +010084 dn := logsCmd.Arg(0)
85 req := &apb.GetLogsRequest{
86 Dn: dn,
87 BacklogMode: apb.GetLogsRequest_BACKLOG_DISABLE,
88 StreamMode: apb.GetLogsRequest_STREAM_DISABLE,
89 Filters: nil,
90 }
91
92 switch *logsTailN {
93 case 0:
94 case -1:
95 req.BacklogMode = apb.GetLogsRequest_BACKLOG_ALL
96 default:
97 req.BacklogMode = apb.GetLogsRequest_BACKLOG_COUNT
98 req.BacklogCount = int64(*logsTailN)
99 }
100
101 if *logsStream {
102 req.StreamMode = apb.GetLogsRequest_STREAM_UNBUFFERED
103 }
104
105 if *logsRecursive {
106 req.Filters = append(req.Filters, &apb.LogFilter{
107 Filter: &apb.LogFilter_WithChildren_{WithChildren: &apb.LogFilter_WithChildren{}},
108 })
109 }
110
111 stream, err := debugClient.GetLogs(ctx, req)
Lorenz Brun878f5f92020-05-12 16:15:39 +0200112 if err != nil {
113 fmt.Fprintf(os.Stderr, "Failed to get logs: %v\n", err)
114 os.Exit(1)
115 }
Serge Bazanskib0272182020-11-02 18:39:44 +0100116 for {
117 res, err := stream.Recv()
118 if err != nil {
119 if err == io.EOF {
120 os.Exit(0)
121 }
122 fmt.Fprintf(os.Stderr, "Failed to stream logs: %v\n", err)
123 os.Exit(1)
124 }
125 for _, entry := range res.BacklogEntries {
126 entry, err := logtree.LogEntryFromProto(entry)
127 if err != nil {
128 fmt.Printf("error decoding entry: %v", err)
129 continue
130 }
131 fmt.Println(entry.String())
132 }
Lorenz Brun878f5f92020-05-12 16:15:39 +0200133 }
Lorenz Brun878f5f92020-05-12 16:15:39 +0200134 case "kubectl":
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200135 // Always get a kubeconfig with cluster-admin (group system:masters),
136 // kubectl itself can impersonate
Lorenz Brun878f5f92020-05-12 16:15:39 +0200137 kubeconfigFile, err := ioutil.TempFile("", "dbg_kubeconfig")
138 if err != nil {
139 fmt.Fprintf(os.Stderr, "Failed to create kubeconfig temp file: %v\n", err)
140 os.Exit(1)
141 }
142 defer kubeconfigFile.Close()
143 defer os.Remove(kubeconfigFile.Name())
144
Serge Bazanskiefdb6e92020-07-13 17:19:27 +0200145 res, err := debugClient.GetDebugKubeconfig(ctx, &apb.GetDebugKubeconfigRequest{Id: "debug-user", Groups: []string{"system:masters"}})
Lorenz Brun878f5f92020-05-12 16:15:39 +0200146 if err != nil {
147 fmt.Fprintf(os.Stderr, "Failed to get kubeconfig: %v\n", err)
148 os.Exit(1)
149 }
150 if _, err := kubeconfigFile.WriteString(res.DebugKubeconfig); err != nil {
151 fmt.Fprintf(os.Stderr, "Failed to write kubeconfig: %v\n", err)
152 os.Exit(1)
153 }
154
Serge Bazanski216fe7b2021-05-21 18:36:16 +0200155 // This magic sets up everything as if this was just the kubectl
156 // binary. It sets the KUBECONFIG environment variable so that it knows
157 // where the Kubeconfig is located and forcibly overwrites the
158 // arguments so that the "wrapper" arguments are not visible to its
159 // flags parser.
160 // The base code is straight from:
161 // https://github.com/kubernetes/kubernetes/blob/master/cmd/kubectl/kubectl.go
Lorenz Brun878f5f92020-05-12 16:15:39 +0200162 os.Setenv("KUBECONFIG", kubeconfigFile.Name())
163 rand.Seed(time.Now().UnixNano())
164 pflag.CommandLine.SetNormalizeFunc(cliflag.WordSepNormalizeFunc)
165 pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
166 logs.InitLogs()
167 defer logs.FlushLogs()
168 command := cmd.NewDefaultKubectlCommandWithArgs(cmd.NewDefaultPluginHandler(plugin.ValidPluginFilenamePrefixes), os.Args[2:], os.Stdin, os.Stdout, os.Stderr)
169 command.SetArgs(os.Args[2:])
170 if err := command.Execute(); err != nil {
171 os.Exit(1)
172 }
Lorenz Brun09c275b2021-03-30 12:47:09 +0200173 case "trace":
174 traceCmd.Parse(os.Args[2:])
175 tracer := traceCmd.Arg(0)
176 var fgf []string
177 var ff []string
178 if len(*functionGraphFilter) > 0 {
179 fgf = strings.Split(*functionGraphFilter, ",")
180 }
181 if len(*functionFilter) > 0 {
182 ff = strings.Split(*functionFilter, ",")
183 }
184 req := apb.TraceRequest{
185 GraphFunctionFilter: fgf,
186 FunctionFilter: ff,
187 Tracer: tracer,
188 }
189 traceEvents, err := debugClient.Trace(ctx, &req)
190 if err != nil {
191 fmt.Fprintf(os.Stderr, "failed to trace: %v", err)
192 os.Exit(1)
193 }
194 for {
195 traceEvent, err := traceEvents.Recv()
196 if err != nil {
197 if err == io.EOF {
198 break
199 }
200 fmt.Fprintf(os.Stderr, "stream aborted unexpectedly: %v", err)
201 os.Exit(1)
202 }
203 fmt.Println(traceEvent.RawLine)
204 }
Lorenz Brun878f5f92020-05-12 16:15:39 +0200205 }
206}