Skip to content

Commit

Permalink
Add TUI
Browse files Browse the repository at this point in the history
  • Loading branch information
vs4vijay committed Apr 16, 2020
1 parent 56f63c0 commit ce20b00
Show file tree
Hide file tree
Showing 7 changed files with 492 additions and 130 deletions.
74 changes: 74 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package cmd

import (
"fmt"
"io/ioutil"
"path/filepath"

"github.com/spf13/cobra"
"github.com/vs4vijay/lazykubectl/pkg/k8s"
"github.com/vs4vijay/lazykubectl/pkg/tui"

"os"

"github.com/spf13/viper"
)

var (
cfgFile string
kubeConfigFile string
home = k8s.Home()
)

var rootCmd = &cobra.Command{
Use: "lazykubectl",
Short: "A Kubernetes Client",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("lazykubectl")

configData, err := ioutil.ReadFile(kubeConfigFile)
if err != nil {
fmt.Errorf("Error in Reading Config: %v", err)
os.Exit(1)
}

kubeConfig := k8s.KubeConfig{
Type: "MANIFEST",
Manifest: string(configData),
}

tui.Start(kubeConfig)
},
}

func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}

func init() {
cobra.OnInitialize(initConfig)

rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.lazykubectl.yaml)")

flags := rootCmd.Flags()
flags.StringVarP(&kubeConfigFile, "kubeconfig", "c", filepath.Join(home, ".kube", "config"), "")
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
if cfgFile != "" {
viper.SetConfigFile(cfgFile)
} else {
viper.AddConfigPath(home)
viper.SetConfigName(".lazykubectl")
}

viper.AutomaticEnv()

if err := viper.ReadInConfig(); err == nil {
fmt.Println("Using config file:", viper.ConfigFileUsed())
}
}
16 changes: 8 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ module github.com/vs4vijay/lazykubectl
go 1.14

require (
github.com/imdario/mergo v0.3.9 // indirect
github.com/rivo/tview v0.0.0-20200404204604-ca37f83cb2e7
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d // indirect
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect
k8s.io/api v0.18.1
k8s.io/apimachinery v0.18.1
k8s.io/client-go v11.0.0+incompatible
k8s.io/utils v0.0.0-20200414100711-2df71ebbae66 // indirect
github.com/jroimartin/gocui v0.4.0
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/nsf/termbox-go v0.0.0-20200204031403-4d2b513ad8be // indirect
github.com/spf13/cobra v1.0.0
github.com/spf13/viper v1.6.3
k8s.io/api v0.17.0
k8s.io/apimachinery v0.17.0
k8s.io/client-go v0.17.0
)
280 changes: 224 additions & 56 deletions go.sum

Large diffs are not rendered by default.

58 changes: 2 additions & 56 deletions main.go
Original file line number Diff line number Diff line change
@@ -1,61 +1,7 @@
package main

import (
"github.com/rivo/tview"
)

const (
project = "lazykubectl"
)
import "github.com/vs4vijay/lazykubectl/cmd"

func main() {
app := tview.NewApplication()
box := tview.NewBox().SetBorder(true).SetTitle(project)
button := tview.NewButton("Hit Enter to close").SetSelectedFunc(func() {
app.Stop()
})

button.SetBorder(true).SetRect(0, 0, 22, 3)

dropdown := tview.NewDropDown().
SetLabel("Select an option (hit Enter): ").
SetOptions([]string{"First", "Second", "Third", "Fourth", "Fifth"}, nil)

flex := tview.NewFlex().
AddItem(tview.NewBox().SetBorder(true).SetTitle("Left (1/2 x width of Top)"), 0, 1, false).
AddItem(tview.NewFlex().SetDirection(tview.FlexRow).
AddItem(box, 0, 1, true).
AddItem(button, 0, 3, false).
AddItem(dropdown, 5, 1, false), 0, 2, false).
AddItem(tview.NewBox().SetBorder(true).SetTitle("Right (20 cols)"), 20, 1, false)

newPrimitive := func(text string) tview.Primitive {
return tview.NewTextView().
SetTextAlign(tview.AlignCenter).
SetText(text)
}
menu := newPrimitive("Menu")
main := newPrimitive("Main content")
sideBar := newPrimitive("Side Bar")

grid := tview.NewGrid().
SetRows(3, 0, 3).
SetColumns(30, 0, 30).
SetBorders(true).
AddItem(button, 0, 0, 1, 3, 0, 0, false).
AddItem(dropdown, 2, 0, 1, 3, 0, 0, false)

// Layout for screens narrower than 100 cells (menu and side bar are hidden).
grid.AddItem(menu, 0, 0, 0, 0, 0, 0, false).
AddItem(flex, 1, 0, 1, 3, 0, 0, false).
AddItem(sideBar, 0, 0, 0, 0, 0, 0, false)

// Layout for screens wider than 100 cells.
grid.AddItem(menu, 1, 0, 1, 1, 0, 100, false).
AddItem(main, 1, 1, 1, 1, 0, 100, false).
AddItem(sideBar, 1, 2, 1, 1, 0, 100, false)

if err := app.SetRoot(grid, true).SetFocus(grid).Run(); err != nil {
panic(err)
}
cmd.Execute()
}
9 changes: 9 additions & 0 deletions pkg/k8s/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package k8s

import (
"k8s.io/client-go/util/homedir"
)

func Home() string {
return homedir.HomeDir()
}
24 changes: 14 additions & 10 deletions pkg/k8s/k8s.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func BuildConfig(kubeConfig KubeConfig) (*rest.Config, error) {
return config, err
}

func GetClientSet(kubeConfig KubeConfig) (*kubernetes.Clientset, error) {
func GetClientset(kubeConfig KubeConfig) (*kubernetes.Clientset, error) {
config, err := BuildConfig(kubeConfig)
if err != nil {
return nil, err
Expand Down Expand Up @@ -53,8 +53,8 @@ func SearchNamespaces(clientset *kubernetes.Clientset) ([]v1.Namespace, error) {
return namespaceList.Items, nil
}

func SearchPods(clientset *kubernetes.Clientset, namespace v1.Namespace) ([]v1.Pod, error) {
podList, err := clientset.CoreV1().Pods(namespace.GetName()).List(metav1.ListOptions{})
func SearchPods(clientset *kubernetes.Clientset, namespace string) ([]v1.Pod, error) {
podList, err := clientset.CoreV1().Pods(namespace).List(metav1.ListOptions{})
if err != nil {
return nil, err
}
Expand All @@ -65,8 +65,8 @@ func SearchPods(clientset *kubernetes.Clientset, namespace v1.Namespace) ([]v1.P
return podList.Items, nil
}

func SearchServices(clientset *kubernetes.Clientset, namespace v1.Namespace) ([]v1.Service, error) {
serviceList, err := clientset.CoreV1().Services(namespace.GetName()).List(metav1.ListOptions{})
func SearchServices(clientset *kubernetes.Clientset, namespace string) ([]v1.Service, error) {
serviceList, err := clientset.CoreV1().Services(namespace).List(metav1.ListOptions{})
if err != nil {
return nil, err
}
Expand All @@ -82,24 +82,28 @@ func GetContainers(clientset *kubernetes.Clientset, namespace string, podName st
if err != nil {
return nil, err
}
fmt.Println("Services: ")
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(50)
tailLines := int64(100)
podLogOptions := v1.PodLogOptions{
Container: containerName,
TailLines: &tailLines,
Container: containerName,
TailLines: &tailLines,
}

fmt.Println("Logs: ")

logRequest := clientset.CoreV1().Pods(namespace).GetLogs(podName, &podLogOptions)

readCloser, err := logRequest.Stream()
defer readCloser.Close()
if readCloser != nil {
defer readCloser.Close()
}
if err != nil {
return err
}
Expand Down
161 changes: 161 additions & 0 deletions pkg/tui/tui.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
package tui

import (
"fmt"
"log"
"os"

"github.com/jroimartin/gocui"

"github.com/vs4vijay/lazykubectl/pkg/k8s"
)

var (
viewArr = []string{"Info", "Namespaces", "Pods", "Services"}
active = 0
)

func Start(kubeConfig k8s.KubeConfig) {
g, err := gocui.NewGui(gocui.Output256)
if err != nil {
log.Panicln(err)
}
defer g.Close()

g.InputEsc = true
g.Cursor = true
g.Mouse = true
g.Highlight = true
g.SelFgColor = gocui.ColorBlue

g.SetManagerFunc(layout)

if err := g.SetKeybinding("", gocui.KeyCtrlC, gocui.ModNone, quit); err != nil {
log.Panicln("SetKeybinding --- ", err)
}

if err := g.SetKeybinding("", gocui.KeyTab, gocui.ModNone, nextView); err != nil {
log.Panicln(err)
}

if err := g.SetKeybinding("Pods", gocui.KeyCtrlP, gocui.ModNone, quit); err != nil {
log.Panicln("SetKeybinding --- ", err)
}

if err := g.MainLoop(); err != nil && err != gocui.ErrQuit {
log.Panicln("MainLoop --- ", err)
}

// g.SetViewOnTop("Namespaces")
// g.SetCurrentView("Namespaces")
}

func layout(g *gocui.Gui) error {
maxX, maxY := g.Size()

pad := 1
gridX, gridY := maxX/4, maxY/3

if v, err := g.SetView("Info", 0, 0, gridX - pad, gridY - pad); err != nil {
if err != gocui.ErrUnknownView {
return err
}

v.Title = "Info"
v.Editable = true
v.Wrap = true

fmt.Fprintf(v, "Context: %v\n", "")
fmt.Fprintf(v, "Cluster: %v\n", "")
fmt.Fprintf(v, "User: %v\n", "")
fmt.Fprintf(v, "Nodes: %v\n", "")
}

if v, err := g.SetView("Namespaces", 0, gridY, gridX - pad, (gridY * 2) - pad) ; err != nil {
if err != gocui.ErrUnknownView {
return err
}
v.Title = "Namespaces"
v.Editable = true
v.Wrap = true
fmt.Fprintf(v, "Namespaces: %v\n", "")
}

if v, err := g.SetView("Pods", gridX, 0, (gridX * 4), (gridY * 2) - pad) ; err != nil {
if err != gocui.ErrUnknownView {
return err
}
v.Title = "Pods"
v.Editable = true
v.Autoscroll = true
fmt.Fprintf(v, "Pods: %v\n", "")
}

if v, err := g.SetView("Services", 0, gridY * 2, (gridX * 4), gridY * 3) ; err != nil {
if err != gocui.ErrUnknownView {
return err
}
v.Title = "Services"
v.Editable = true
v.Wrap = true
fmt.Fprintf(v, "Services: %v\n", "")
}

return nil
}

func quit(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit
}

func setCurrentViewOnTop(g *gocui.Gui, name string) (*gocui.View, error) {
if _, err := g.SetCurrentView(name); err != nil {
return nil, err
}
return g.SetViewOnTop(name)
}

func nextView(g *gocui.Gui, v *gocui.View) error {
nextIndex := (active + 1) % len(viewArr)
name := viewArr[nextIndex]

// out, err := g.View("Pods")
// if err != nil {
// return err
// }
// fmt.Fprintf(out, "Going from view %v to %v\n", v.Name(), name)

if _, err := setCurrentViewOnTop(g, name); err != nil {
return err
}

if nextIndex == 0 || nextIndex == 3 {
g.Cursor = true
} else {
g.Cursor = false
}

active = nextIndex
return nil
}

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
}

0 comments on commit ce20b00

Please sign in to comment.