blob: 176973f933954dfe35737eda3eabe519852b87bd [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 Brun878f5f92020-05-12 16:15:39 +020027 "time"
28
Serge Bazanskib0272182020-11-02 18:39:44 +010029 "git.monogon.dev/source/nexantic.git/core/pkg/logtree"
30
Lorenz Brun878f5f92020-05-12 16:15:39 +020031 "github.com/spf13/pflag"
32 "google.golang.org/grpc"
33 cliflag "k8s.io/component-base/cli/flag"
34 "k8s.io/kubectl/pkg/cmd/plugin"
35 "k8s.io/kubectl/pkg/util/logs"
36 "k8s.io/kubernetes/pkg/kubectl/cmd"
37
Serge Bazanskiefdb6e92020-07-13 17:19:27 +020038 apb "git.monogon.dev/source/nexantic.git/core/proto/api"
Lorenz Brun878f5f92020-05-12 16:15:39 +020039)
40
41func main() {
Serge Bazanskiefdb6e92020-07-13 17:19:27 +020042 ctx := context.Background()
Lorenz Brun878f5f92020-05-12 16:15:39 +020043 // Hardcode localhost since this should never be used to interface with a production node because of missing
44 // encryption & authentication
45 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 }
Serge Bazanski57b43752020-07-13 19:17:48 +020065 goldenticketCmd := flag.NewFlagSet("goldenticket", flag.ExitOnError)
Lorenz Brun878f5f92020-05-12 16:15:39 +020066 conditionCmd := flag.NewFlagSet("condition", flag.ExitOnError)
67 conditionCmd.Usage = func() {
68 fmt.Fprintf(os.Stderr, "Usage: %s %s [options] component_path\n", os.Args[0], os.Args[1])
69 flag.PrintDefaults()
70
71 fmt.Fprintf(os.Stderr, "Example:\n %s %s IPAssigned\n", os.Args[0], os.Args[1])
72 }
Serge Bazanskib0272182020-11-02 18:39:44 +010073
Lorenz Brun878f5f92020-05-12 16:15:39 +020074 switch os.Args[1] {
75 case "logs":
76 logsCmd.Parse(os.Args[2:])
Serge Bazanskib0272182020-11-02 18:39:44 +010077 dn := logsCmd.Arg(0)
78 req := &apb.GetLogsRequest{
79 Dn: dn,
80 BacklogMode: apb.GetLogsRequest_BACKLOG_DISABLE,
81 StreamMode: apb.GetLogsRequest_STREAM_DISABLE,
82 Filters: nil,
83 }
84
85 switch *logsTailN {
86 case 0:
87 case -1:
88 req.BacklogMode = apb.GetLogsRequest_BACKLOG_ALL
89 default:
90 req.BacklogMode = apb.GetLogsRequest_BACKLOG_COUNT
91 req.BacklogCount = int64(*logsTailN)
92 }
93
94 if *logsStream {
95 req.StreamMode = apb.GetLogsRequest_STREAM_UNBUFFERED
96 }
97
98 if *logsRecursive {
99 req.Filters = append(req.Filters, &apb.LogFilter{
100 Filter: &apb.LogFilter_WithChildren_{WithChildren: &apb.LogFilter_WithChildren{}},
101 })
102 }
103
104 stream, err := debugClient.GetLogs(ctx, req)
Lorenz Brun878f5f92020-05-12 16:15:39 +0200105 if err != nil {
106 fmt.Fprintf(os.Stderr, "Failed to get logs: %v\n", err)
107 os.Exit(1)
108 }
Serge Bazanskib0272182020-11-02 18:39:44 +0100109 for {
110 res, err := stream.Recv()
111 if err != nil {
112 if err == io.EOF {
113 os.Exit(0)
114 }
115 fmt.Fprintf(os.Stderr, "Failed to stream logs: %v\n", err)
116 os.Exit(1)
117 }
118 for _, entry := range res.BacklogEntries {
119 entry, err := logtree.LogEntryFromProto(entry)
120 if err != nil {
121 fmt.Printf("error decoding entry: %v", err)
122 continue
123 }
124 fmt.Println(entry.String())
125 }
Lorenz Brun878f5f92020-05-12 16:15:39 +0200126 }
Serge Bazanski57b43752020-07-13 19:17:48 +0200127 case "goldenticket":
128 goldenticketCmd.Parse(os.Args[2:])
129 ip := goldenticketCmd.Arg(0)
130 res, err := debugClient.GetGoldenTicket(ctx, &apb.GetGoldenTicketRequest{ExternalIp: ip})
131 if err != nil {
132 fmt.Fprintf(os.Stderr, "Failed to get golden ticket: %v\n", err)
133 os.Exit(1)
134 }
135 fmt.Println(res.Ticket)
Lorenz Brun878f5f92020-05-12 16:15:39 +0200136 case "kubectl":
137 // Always get a kubeconfig with cluster-admin (group system:masters), kubectl itself can impersonate
138 kubeconfigFile, err := ioutil.TempFile("", "dbg_kubeconfig")
139 if err != nil {
140 fmt.Fprintf(os.Stderr, "Failed to create kubeconfig temp file: %v\n", err)
141 os.Exit(1)
142 }
143 defer kubeconfigFile.Close()
144 defer os.Remove(kubeconfigFile.Name())
145
Serge Bazanskiefdb6e92020-07-13 17:19:27 +0200146 res, err := debugClient.GetDebugKubeconfig(ctx, &apb.GetDebugKubeconfigRequest{Id: "debug-user", Groups: []string{"system:masters"}})
Lorenz Brun878f5f92020-05-12 16:15:39 +0200147 if err != nil {
148 fmt.Fprintf(os.Stderr, "Failed to get kubeconfig: %v\n", err)
149 os.Exit(1)
150 }
151 if _, err := kubeconfigFile.WriteString(res.DebugKubeconfig); err != nil {
152 fmt.Fprintf(os.Stderr, "Failed to write kubeconfig: %v\n", err)
153 os.Exit(1)
154 }
155
156 // This magic sets up everything as if this was just the kubectl binary. It sets the KUBECONFIG environment
157 // variable so that it knows where the Kubeconfig is located and forcibly overwrites the arguments so that
158 // the "wrapper" arguments are not visible to its flags parser. The base code is straight from
159 // https://github.com/kubernetes/kubernetes/blob/master/cmd/kubectl/kubectl.go
160 os.Setenv("KUBECONFIG", kubeconfigFile.Name())
161 rand.Seed(time.Now().UnixNano())
162 pflag.CommandLine.SetNormalizeFunc(cliflag.WordSepNormalizeFunc)
163 pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
164 logs.InitLogs()
165 defer logs.FlushLogs()
166 command := cmd.NewDefaultKubectlCommandWithArgs(cmd.NewDefaultPluginHandler(plugin.ValidPluginFilenamePrefixes), os.Args[2:], os.Stdin, os.Stdout, os.Stderr)
167 command.SetArgs(os.Args[2:])
168 if err := command.Execute(); err != nil {
169 os.Exit(1)
170 }
171 }
172}