From e2670a630f91e5032f892bc0e64095a4bdbf038a Mon Sep 17 00:00:00 2001 From: vs4vijay Date: Sun, 19 Apr 2020 22:33:56 +0530 Subject: [PATCH] Modularize some codebase --- cmd/root.go | 21 ++++++-- pkg/k8s/k8s.go | 117 +++++++++++++++++++++++++++++++--------- pkg/tui/tui.go | 142 +++++++++++++++++++++++++++++++------------------ 3 files changed, 201 insertions(+), 79 deletions(-) diff --git a/cmd/root.go b/cmd/root.go index 54cd0531..03f274ef 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -23,12 +23,12 @@ var ( var rootCmd = &cobra.Command{ Use: "lazykubectl", Short: "A Kubernetes Client", - Run: func(cmd *cobra.Command, args []string) { + RunE: func(cmd *cobra.Command, args []string) error { fmt.Println("lazykubectl") configData, err := ioutil.ReadFile(kubeConfigFile) if err != nil { - fmt.Errorf("Error in Reading Config: %v", err) + return fmt.Errorf("Error in Reading Config: %v", err) os.Exit(1) } @@ -37,7 +37,22 @@ var rootCmd = &cobra.Command{ Manifest: string(configData), } - tui.Start(kubeConfig) + kubeapi, err := k8s.NewKubeAPI(kubeConfig) + if err != nil { + return err + } + + fmt.Println("kubeapi", kubeapi) + + app, err := tui.NewApp(kubeapi) + if err != nil { + return err + } + + app.Start() + + // tui.StartApp(kubeConfig) + return nil }, } diff --git a/pkg/k8s/k8s.go b/pkg/k8s/k8s.go index 5bd68f18..5fb266fa 100644 --- a/pkg/k8s/k8s.go +++ b/pkg/k8s/k8s.go @@ -1,6 +1,7 @@ package k8s import ( + "errors" "fmt" "io" @@ -12,6 +13,42 @@ import ( "k8s.io/client-go/tools/clientcmd" ) +type KubeAPI struct { + Clientset *kubernetes.Clientset +} + +func NewKubeAPI(kubeConfig KubeConfig) (*KubeAPI, error) { + config, err := BuildConfig(kubeConfig) + if err != nil { + return nil, err + } + + clientset, err := kubernetes.NewForConfig(config) + if err != nil { + return nil, err + } + + kubeapi := &KubeAPI{ + Clientset: clientset, + } + + // HACK: Checking the connectivity of cluster + _, err = kubeapi.SearchNamespaces() + if err != nil { + return nil, errors.New("not able to connect to Kubernetes Cluster") + } + + return kubeapi, nil +} + +func (kubeapi *KubeAPI) SearchNamespaces() ([]v1.Namespace, error) { + namespaceList, err := kubeapi.Clientset.CoreV1().Namespaces().List(metav1.ListOptions{}) + if err != nil { + return nil, err + } + return namespaceList.Items, nil +} + func BuildConfig(kubeConfig KubeConfig) (*rest.Config, error) { var ( config *rest.Config @@ -37,24 +74,55 @@ func GetClientset(kubeConfig KubeConfig) (*kubernetes.Clientset, error) { } // api := clientset.CoreV1() + // c, _ :=clientcmd.DefaultClientConfig.ClientConfig() + + // clientcmd.DirectClientConfig + + // clientcmd.DirectClientConfig{} + + // rawConfig, _ := clientcmd.DirectClientConfig.RawConfig() + // // rawConfig. + // fmt.Println("rawConfig", rawConfig) return clientset, nil } -func SearchNamespaces(clientset *kubernetes.Clientset) ([]v1.Namespace, error) { - namespaceList, err := clientset.CoreV1().Namespaces().List(metav1.ListOptions{}) +// func (kubeapi *KubeAPI) SearchNamespaces() ([]v1.Namespace, error) { +// namespaceList, err := kubeapi.Clientset.CoreV1().Namespaces().List(metav1.ListOptions{}) +// if err != nil { +// return nil, err +// } +// // fmt.Println("Namespaces: ") +// // for _, pod := range namespaceList.Items { +// // fmt.Println("\t", pod.GetName()) +// // } +// return namespaceList.Items, nil +// } + +func (kubeapi *KubeAPI) SearchNodes() ([]v1.Node, error) { + nodeList, err := kubeapi.Clientset.CoreV1().Nodes().List(metav1.ListOptions{}) if err != nil { return nil, err } - // fmt.Println("Namespaces: ") - // for _, pod := range namespaceList.Items { - // fmt.Println("\t", pod.GetName()) + // for _, node := range nodeList.Items { + // fmt.Println("\t", node.GetName()) // } - return namespaceList.Items, nil + // for _, condition := range node.Status.Conditions { + // if condition.Reason == "KubeletReady" { + // if condition.Status == "True" { + // nodeStatus = "Ready" + // } else if condition.Reason == "False" { + // nodeStatus = "NotReady" + // } else { + // nodeStatus = "Unknown" + // } + // } + // } + return nodeList.Items, nil } -func SearchPods(clientset *kubernetes.Clientset, namespace string) ([]v1.Pod, error) { - podList, err := clientset.CoreV1().Pods(namespace).List(metav1.ListOptions{}) +func (kubeapi *KubeAPI) SearchPods(namespace string) ([]v1.Pod, error) { + podList, err := kubeapi.Clientset.CoreV1().Pods(namespace).List(metav1.ListOptions{}) if err != nil { return nil, err } @@ -65,8 +133,8 @@ func SearchPods(clientset *kubernetes.Clientset, namespace string) ([]v1.Pod, er return podList.Items, nil } -func SearchServices(clientset *kubernetes.Clientset, namespace string) ([]v1.Service, error) { - serviceList, err := clientset.CoreV1().Services(namespace).List(metav1.ListOptions{}) +func (kubeapi *KubeAPI) SearchServices(namespace string) ([]v1.Service, error) { + serviceList, err := kubeapi.Clientset.CoreV1().Services(namespace).List(metav1.ListOptions{}) if err != nil { return nil, err } @@ -74,32 +142,30 @@ func SearchServices(clientset *kubernetes.Clientset, namespace string) ([]v1.Ser // for _, service := range serviceList.Items { // fmt.Println("\t", service.GetName()) // } - return serviceList.Items, nil } -func GetContainers(clientset *kubernetes.Clientset, namespace string, podName string) ([]v1.Container, error) { - pod, err := clientset.CoreV1().Pods(namespace).Get(podName, metav1.GetOptions{}) +func (kubeapi *KubeAPI) GetContainers(namespace string, podName string) ([]v1.Container, error) { + pod, err := kubeapi.Clientset.CoreV1().Pods(namespace).Get(podName, metav1.GetOptions{}) if err != nil { return nil, err } - fmt.Println("Containers: ") - for _, container := range pod.Spec.Containers { - fmt.Println("\t", container.Name) - } + // fmt.Println("Containers: ") + // for _, container := range pod.Spec.Containers { + // fmt.Println("\t", container.Name) + // } return pod.Spec.Containers, nil } -func GetContainerLogs(clientset *kubernetes.Clientset, namespace string, podName string, containerName string, out io.Writer) error { - tailLines := int64(100) +func (kubeapi *KubeAPI) GetContainerLogs(namespace string, podName string, containerName string, out io.Writer) error { + // tailLines := int64(100) podLogOptions := v1.PodLogOptions{ Container: containerName, - TailLines: &tailLines, + // TailLines: &tailLines, } - fmt.Println("Logs: ") - - logRequest := clientset.CoreV1().Pods(namespace).GetLogs(podName, &podLogOptions) + // fmt.Println("Logs: ") + logRequest := kubeapi.Clientset.CoreV1().Pods(namespace).GetLogs(podName, &podLogOptions) readCloser, err := logRequest.Stream() if readCloser != nil { @@ -113,8 +179,9 @@ func GetContainerLogs(clientset *kubernetes.Clientset, namespace string, podName return err } -func SearchDeployments(clientset *kubernetes.Clientset, namespace v1.Namespace) ([]v1beta1.Deployment, error) { - deploymentList, err := clientset.ExtensionsV1beta1().Deployments(namespace.GetName()).List(metav1.ListOptions{}) +func (kubeapi *KubeAPI) SearchDeployments(namespace v1.Namespace) ([]v1beta1.Deployment, error) { + deploymentList, err := kubeapi.Clientset.ExtensionsV1beta1().Deployments(namespace.GetName()).List(metav1.ListOptions{}) + // AppsV1().Deployments(namespace).List(metav1.ListOptions{}) if err != nil { return nil, err } diff --git a/pkg/tui/tui.go b/pkg/tui/tui.go index 20b27109..0b2b4fa9 100644 --- a/pkg/tui/tui.go +++ b/pkg/tui/tui.go @@ -3,22 +3,32 @@ package tui import ( "fmt" "log" - "os" + "strings" "github.com/jroimartin/gocui" - "k8s.io/client-go/kubernetes" - "github.com/vs4vijay/lazykubectl/pkg/k8s" ) var ( - viewArr = []string{"Info", "Namespaces", "Pods", "Services"} - active = 0 - clientset *kubernetes.Clientset + app *App + viewArr = []string{"Info", "Namespaces", "Main", "Services"} + active = 0 + state = map[string]string{} ) -func Start(kubeConfig k8s.KubeConfig) { - clientset, _ = k8s.GetClientset(kubeConfig) +type App struct { + kubeapi *k8s.KubeAPI +} + +func NewApp(kubeapi *k8s.KubeAPI) (*App, error) { + app = &App{ + kubeapi: kubeapi, + } + return app, nil +} + +func (app *App) Start() { + // app.kubeapi.Clientset, _ = k8s.Getapp.kubeapi.Clientset(kubeConfig) g, err := gocui.NewGui(gocui.Output256) if err != nil { @@ -35,21 +45,28 @@ func Start(kubeConfig k8s.KubeConfig) { g.SetManagerFunc(layout) - // g.SetCurrentView("Info") - if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil { log.Panicln(err) } + // Rotating Views if err := g.SetKeybinding("", gocui.KeyTab, gocui.ModNone, nextView); err != nil { log.Panicln(err) } + if err := g.SetKeybinding("", gocui.KeyArrowRight, gocui.ModNone, nextView); err != nil { + log.Panicln(err) + } - if err := g.SetKeybinding("", gocui.MouseLeft, gocui.ModNone, selectWidgets); err != nil { + // Select Panel + if err := g.SetKeybinding("", gocui.MouseLeft, gocui.ModNone, selectPanel); err != nil { log.Panicln(err) } - if err := g.SetKeybinding("Namespaces", gocui.MouseLeft, gocui.ModNone, selectNamespace); err != nil { + if err := g.SetKeybinding("Namespaces", gocui.MouseLeft, gocui.ModNone, onSelectNamespace); err != nil { + log.Panicln(err) + } + + if err := g.SetKeybinding("Main", gocui.KeyEnter, gocui.ModNone, onSelectMain); err != nil { log.Panicln(err) } @@ -86,21 +103,25 @@ func layout(g *gocui.Gui) error { v.Highlight = true v.SelBgColor = gocui.ColorGreen v.SelFgColor = gocui.ColorBlack - namespaces, _ := k8s.SearchNamespaces(clientset) + + namespaces, _ := app.kubeapi.SearchNamespaces() for _, item := range namespaces { fmt.Fprintln(v, item.GetName()) } } - if v, err := g.SetView("Pods", gridX, 0, (gridX*4), (gridY*2)-pad); err != nil { + if v, err := g.SetView("Main", gridX, 0, gridX*4, (gridY*2)-pad); err != nil { if err != gocui.ErrUnknownView { return err } v.Title = "Pods" + v.Highlight = true + v.SelBgColor = gocui.ColorGreen + v.SelFgColor = gocui.ColorBlack // v.Autoscroll = true } - if v, err := g.SetView("Services", 0, gridY*2, (gridX*4), gridY*3); err != nil { + if v, err := g.SetView("Services", 0, gridY*2, gridX*4, gridY*3); err != nil { if err != gocui.ErrUnknownView { return err } @@ -124,7 +145,6 @@ func setCurrentViewOnTop(g *gocui.Gui, name string) (*gocui.View, error) { } func nextView(g *gocui.Gui, v *gocui.View) error { - // TODO: Try to use g.View() nextIndex := (active + 1) % len(viewArr) name := viewArr[nextIndex] @@ -136,30 +156,32 @@ func nextView(g *gocui.Gui, v *gocui.View) error { return nil } -func selectWidgets(g *gocui.Gui, v *gocui.View) error { +func selectPanel(g *gocui.Gui, v *gocui.View) error { _, err := g.SetCurrentView(v.Name()) return err } -func selectNamespace(g *gocui.Gui, v *gocui.View) error { +func onSelectNamespace(g *gocui.Gui, v *gocui.View) error { namespaceName := getSelectedText(v) + state["namespace"] = namespaceName g.Update(func(g *gocui.Gui) error { - podsView, _ := g.View("Pods") + podsView, _ := g.View("Main") podsView.Clear() - pods, _ := k8s.SearchPods(clientset, namespaceName) + pods, _ := app.kubeapi.SearchPods(namespaceName) + podsView.Title = fmt.Sprintf("Pods(%v) - %v", len(pods), namespaceName) + fmt.Fprintf(podsView, "%-20s %-15s\n", "POD NAME", "POD STATUS") for _, item := range pods { - podsView.Title = fmt.Sprintf("Pods(%s)", namespaceName) - fmt.Fprintln(podsView, item.GetName()) + fmt.Fprintf(podsView, "%-20s %-15s\n", item.GetName(), item.Status.Phase) } servicesView, _ := g.View("Services") servicesView.Clear() - services, _ := k8s.SearchServices(clientset, namespaceName) + services, _ := app.kubeapi.SearchServices(namespaceName) + servicesView.Title = fmt.Sprintf("Services(%s)", namespaceName) for _, item := range services { - servicesView.Title = fmt.Sprintf("Services(%s)", namespaceName) fmt.Fprintln(servicesView, item.GetName()) } return nil @@ -168,6 +190,51 @@ func selectNamespace(g *gocui.Gui, v *gocui.View) error { return nil } +func onSelectMain(g *gocui.Gui, view *gocui.View) error { + selectedData := getSelectedText(view) + + if selectedData == "" { + return nil + } + selectedData = strings.Fields(selectedData)[0] + + if strings.HasPrefix(view.Title, "Pods") { + // Handling for Pods View + state["pod"] = selectedData + + g.Update(func(g *gocui.Gui) error { + view.Clear() + view.SelBgColor = gocui.ColorBlue + + containers, _ := app.kubeapi.GetContainers(state["namespace"], state["pod"]) + fmt.Fprintf(view, "%-20s\n", "CONTAINER NAME") + view.Title = fmt.Sprintf("Containers(%v) - %v", len(containers), state["pod"]) + for _, item := range containers { + fmt.Fprintf(view, "%-20s\n", item.Name) + } + return nil + }) + } else if strings.HasPrefix(view.Title, "Containers") { + // Handling for Pods View + state["container"] = selectedData + + g.Update(func(g *gocui.Gui) error { + view.Clear() + view.Title = fmt.Sprintf("Logs - %v", state["container"]) + view.Highlight = false + // view.Editable = true + // view.Wrap = true + // view.FgColor = gocui.ColorWhite + // view.SelBgColor = gocui.ColorBlue + + app.kubeapi.GetContainerLogs(state["namespace"], state["pod"], state["container"], view) + return nil + }) + } + + return nil +} + func getSelectedText(view *gocui.View) string { _, cy := view.Cursor() line, err := view.Line(cy) @@ -176,30 +243,3 @@ func getSelectedText(view *gocui.View) string { } return line } - -// func renderList(list []interface{}, ) error { -// for _, item := range list { -// item.() -// } -// } - -func Try(kubeConfig k8s.KubeConfig) { - fmt.Println("Rendering") - // fmt.Println("kubeConfig", kubeConfig) - - clientset, _ := k8s.GetClientset(kubeConfig) - - k8s.SearchNamespaces(clientset) - - k8s.SearchPods(clientset, "kube-system") - - k8s.GetContainers(clientset, "kube-system", "kube-apiserver-kind-control-plane") - k8s.GetContainers(clientset, "kube-system", "kube-controller-manager-kind-control-plane") - k8s.GetContainers(clientset, "kube-system", "kube-scheduler-kind-control-plane") - - err := k8s.GetContainerLogs(clientset, "kube-system", "kube-apiserver-kind-control-plane", "kube-apiserver", os.Stdout) - - fmt.Println(err) - - // kube-apiserver-kind-control-plane -}