blob: f2d8fc0d720f597c0a6b3f1ccf91f012a6d5dfd2 [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"
23 "io/ioutil"
24 "math/rand"
25 "os"
26 "strings"
27 "time"
28
29 "github.com/spf13/pflag"
30 "google.golang.org/grpc"
31 cliflag "k8s.io/component-base/cli/flag"
32 "k8s.io/kubectl/pkg/cmd/plugin"
33 "k8s.io/kubectl/pkg/util/logs"
34 "k8s.io/kubernetes/pkg/kubectl/cmd"
35
Serge Bazanskiefdb6e92020-07-13 17:19:27 +020036 apb "git.monogon.dev/source/nexantic.git/core/proto/api"
Lorenz Brun878f5f92020-05-12 16:15:39 +020037)
38
39func main() {
Serge Bazanskiefdb6e92020-07-13 17:19:27 +020040 ctx := context.Background()
Lorenz Brun878f5f92020-05-12 16:15:39 +020041 // Hardcode localhost since this should never be used to interface with a production node because of missing
42 // encryption & authentication
43 grpcClient, err := grpc.Dial("localhost:7837", grpc.WithInsecure())
44 if err != nil {
45 fmt.Printf("Failed to dial debug service (is it running): %v\n", err)
46 }
Serge Bazanskiefdb6e92020-07-13 17:19:27 +020047 debugClient := apb.NewNodeDebugServiceClient(grpcClient)
Lorenz Brun878f5f92020-05-12 16:15:39 +020048 if len(os.Args) < 2 {
49 fmt.Println("Please specify a subcommand")
50 os.Exit(1)
51 }
52
53 logsCmd := flag.NewFlagSet("logs", flag.ExitOnError)
54 logsTailN := logsCmd.Uint("tail", 0, "Get last n lines (0 = whole buffer)")
55 logsCmd.Usage = func() {
56 fmt.Fprintf(os.Stderr, "Usage: %s %s [options] component_path\n", os.Args[0], os.Args[1])
57 flag.PrintDefaults()
58
59 fmt.Fprintf(os.Stderr, "Example:\n %s %s --tail 5 kube.apiserver\n", os.Args[0], os.Args[1])
60 }
Serge Bazanski57b43752020-07-13 19:17:48 +020061 goldenticketCmd := flag.NewFlagSet("goldenticket", flag.ExitOnError)
Lorenz Brun878f5f92020-05-12 16:15:39 +020062 conditionCmd := flag.NewFlagSet("condition", flag.ExitOnError)
63 conditionCmd.Usage = func() {
64 fmt.Fprintf(os.Stderr, "Usage: %s %s [options] component_path\n", os.Args[0], os.Args[1])
65 flag.PrintDefaults()
66
67 fmt.Fprintf(os.Stderr, "Example:\n %s %s IPAssigned\n", os.Args[0], os.Args[1])
68 }
69 switch os.Args[1] {
70 case "logs":
71 logsCmd.Parse(os.Args[2:])
72 componentPath := strings.Split(logsCmd.Arg(0), ".")
Serge Bazanskiefdb6e92020-07-13 17:19:27 +020073 res, err := debugClient.GetComponentLogs(ctx, &apb.GetComponentLogsRequest{ComponentPath: componentPath, TailLines: uint32(*logsTailN)})
Lorenz Brun878f5f92020-05-12 16:15:39 +020074 if err != nil {
75 fmt.Fprintf(os.Stderr, "Failed to get logs: %v\n", err)
76 os.Exit(1)
77 }
78 for _, line := range res.Line {
79 fmt.Println(line)
80 }
81 return
Serge Bazanski57b43752020-07-13 19:17:48 +020082 case "goldenticket":
83 goldenticketCmd.Parse(os.Args[2:])
84 ip := goldenticketCmd.Arg(0)
85 res, err := debugClient.GetGoldenTicket(ctx, &apb.GetGoldenTicketRequest{ExternalIp: ip})
86 if err != nil {
87 fmt.Fprintf(os.Stderr, "Failed to get golden ticket: %v\n", err)
88 os.Exit(1)
89 }
90 fmt.Println(res.Ticket)
Lorenz Brun878f5f92020-05-12 16:15:39 +020091 case "kubectl":
92 // Always get a kubeconfig with cluster-admin (group system:masters), kubectl itself can impersonate
93 kubeconfigFile, err := ioutil.TempFile("", "dbg_kubeconfig")
94 if err != nil {
95 fmt.Fprintf(os.Stderr, "Failed to create kubeconfig temp file: %v\n", err)
96 os.Exit(1)
97 }
98 defer kubeconfigFile.Close()
99 defer os.Remove(kubeconfigFile.Name())
100
Serge Bazanskiefdb6e92020-07-13 17:19:27 +0200101 res, err := debugClient.GetDebugKubeconfig(ctx, &apb.GetDebugKubeconfigRequest{Id: "debug-user", Groups: []string{"system:masters"}})
Lorenz Brun878f5f92020-05-12 16:15:39 +0200102 if err != nil {
103 fmt.Fprintf(os.Stderr, "Failed to get kubeconfig: %v\n", err)
104 os.Exit(1)
105 }
106 if _, err := kubeconfigFile.WriteString(res.DebugKubeconfig); err != nil {
107 fmt.Fprintf(os.Stderr, "Failed to write kubeconfig: %v\n", err)
108 os.Exit(1)
109 }
110
111 // This magic sets up everything as if this was just the kubectl binary. It sets the KUBECONFIG environment
112 // variable so that it knows where the Kubeconfig is located and forcibly overwrites the arguments so that
113 // the "wrapper" arguments are not visible to its flags parser. The base code is straight from
114 // https://github.com/kubernetes/kubernetes/blob/master/cmd/kubectl/kubectl.go
115 os.Setenv("KUBECONFIG", kubeconfigFile.Name())
116 rand.Seed(time.Now().UnixNano())
117 pflag.CommandLine.SetNormalizeFunc(cliflag.WordSepNormalizeFunc)
118 pflag.CommandLine.AddGoFlagSet(flag.CommandLine)
119 logs.InitLogs()
120 defer logs.FlushLogs()
121 command := cmd.NewDefaultKubectlCommandWithArgs(cmd.NewDefaultPluginHandler(plugin.ValidPluginFilenamePrefixes), os.Args[2:], os.Stdin, os.Stdout, os.Stderr)
122 command.SetArgs(os.Args[2:])
123 if err := command.Execute(); err != nil {
124 os.Exit(1)
125 }
126 }
127}