Skip to content

vs4vijay/lazykubectl

Repository files navigation

lazykubectl

A Terminal UI client for kubernetes

NOTE: this project is still in an early stage

Release Release Tag Go Report Card Go Version GoDoc Downloads


Installation

Homebrew

brew install vs4vijay/tap/lazykubectl

Release Binaries

Via GoBinaries

curl -sf https://gobinaries.com/vs4vijay/lazykubectl | sh

Running

lazykubectl

Screenshots

LazyKubectl Pods

LazyKubectl Containers

LazyKubectl Logs


References


ToDo

  • Auth
  • Custom Error Handler, for custom errors
  • Proper Logger
  • CORS
  • Validator
  • Version
  • ENV
  • Handle Up / Down Arrow
  • Stream Logs
  • Events
  • CPU
  • MEM
  • View Logs
  • Execute Shell

License

MIT


Development Notes

// Echo Error Handler

e.HTTPErrorHandler = func(err error, c echo.Context) {
    // Take required information from error and context and send it to a service like New Relic
    fmt.Println(c.Path(), c.QueryParams(), err.Error())

    switch err.(type) {
    case orchestrator.CustomError:
        fmt.Println("custom")
    default:
        fmt.Println("normal") // here v has type interface{}
    }

    // Call the default handler to return the HTTP response
    e.DefaultHTTPErrorHandler(err, c)
}


// Docker 

import (
    "github.com/docker/docker/client"
    "github.com/docker/docker/api/types"
)

cli, err := client.NewEnvClient()
cli.Info(context.Background())
cli.DiskUsage(context.Background())
cli.ContainerList(context.Background(), types.ContainerListOptions{All: true})

Client.ContainerList(context.Background(), types.ContainerListOptions{All: true})
stream, err := c.Client.ContainerStats(context.Background(), container.ID, true)
images, err := c.Client.ImageList(context.Background(), types.ImageListOptions{})
result, err := c.Client.VolumeList(context.Background(), filters.Args{})



// Kubernetes

import (
    "k8s.io/client-go/kubernetes"
    "k8s.io/client-go/tools/clientcmd"
)

// Various Configs
config    clientcmd.ClientConfig
restConfig *rest.Config

// Clients
clientset  *kubernetes.Clientset
dynamicClient 

// Client Config
config, err = clientcmd.NewClientConfigFromBytes([]byte(manifest))

// Rest Config
restConfig, err = clientcmd.RESTConfigFromKubeConfig([]byte(manifest))
// OR
restConfig, err := config.ClientConfig()

// Client
clientset, err := kubernetes.NewForConfig(restConfig)

// Dynamic Client
dynamicClient, err := dynamic.NewForConfig(restConfig)



// Default Way
clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(
		&clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig},
		&clientcmd.ConfigOverrides{CurrentContext: kubeContext})

rawConfig, err := clientConfig.RawConfig()
rawConfig.CurrentContext

// List Resources
clientset.CoreV1().Nodes().List(metav1.ListOptions{})
clientset.CoreV1().Pods(namespace).List(metav1.ListOptions{})

// Create Resources
clientset.CoreV1().Namespaces().Create(ns)

// Parse Manifest YAML
obj, gvk, err := scheme.Codecs.UniversalDeserializer().Decode([]byte(manifest), nil, nil)


// Create Resources (Generic Method) - Using Dynamic Client and Unstructrued

groupResources, err := restmapper.GetAPIGroupResources(clientset.Discovery())
rm := restmapper.NewDiscoveryRESTMapper(groupResources)

unstructuredMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
unstructuredObj := &unstructured.Unstructured{Object: unstructuredMap}

gvk := obj.GetObjectKind().GroupVersionKind()
gk := schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind}

mapping, err := rm.RESTMapping(gk, gvk.Version)
createdUnstructuredObj, err := dynamicClient.Resource(mapping.Resource).Create(unstructuredObj, metav1.CreateOptions{})



// Watcher

watch, _ := api.Services("").Watch(metav1.ListOptions{})

go func() {
    for event := range watch.ResultChan() {
        fmt.Printf("Type: %v\n", event.Type)
        p, ok := event.Object.(*v1.Pod)
        if !ok {
            log.Fatal("unexpected type")
        }
        fmt.Println(p.Status.ContainerStatuses)
        fmt.Println(p.Status.Phase)
    }
}()

time.Sleep(5 * time.Second)


// Controller

https://engineering.bitnami.com/articles/a-deep-dive-into-kubernetes-controllers.html
https://engineering.bitnami.com/articles/kubewatch-an-example-of-kubernetes-custom-controller.html

cache.NewInformer
NewSharedIndexInformer
---
lw := cache.NewListWatchFromClient()
sharedInformer := cache.NewSharedInformer(lw, &api.Pod{}, resyncPeriod)
---
factory := informers.NewSharedInformerFactory(clientset, 0)
informer := factory.Core().V1().Nodes().Informer()
stopper := make(chan struct{})
defer close(stopper)
defer runtime.HandleCrash()
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
    AddFunc: onAdd,
})
go informer.Run(stopper)

// Informer

watchlist := cache.NewListWatchFromClient(clientset.Core().RESTClient(), "pods", "", fields.Everything())
_, controller := cache.NewInformer(
    watchlist,
    &v1.Pod{},
    time.Second * 0,
    cache.ResourceEventHandlerFuncs{
        AddFunc: func(obj interface{}) {
            fmt.Printf("add: %s \n", obj)
        },
        DeleteFunc: func(obj interface{}) {
            fmt.Printf("delete: %s \n", obj)
        },
        UpdateFunc:func(oldObj, newObj interface{}) {
            fmt.Printf("old: %s, new: %s \n", oldObj, newObj)
        },
    },
)
stop := make(chan struct{})
go controller.Run(stop)


// Shared Index Informer

informer := cache.NewSharedIndexInformer(
        &cache.ListWatch{
            ListFunc: func(options meta_v1.ListOptions) (runtime.Object, error) {
                return kubeClient.CoreV1().Pods(conf.Namespace).List(options)
            },
            WatchFunc: func(options meta_v1.ListOptions) (watch.Interface, error) {
                return kubeClient.CoreV1().Pods(conf.Namespace).Watch(options)
            },
        },
        &api_v1.Pod{},
        0, //Skip resync
        cache.Indexers{},
    )



if v, err := g.SetView("help", maxX-25, 0, maxX-1, 9); err != nil {
    if err != gocui.ErrUnknownView {
        return err
    }
    fmt.Fprintln(v, "KEYBINDINGS")
    fmt.Fprintln(v, "Space: New View")
    fmt.Fprintln(v, "Tab: Next View")
    fmt.Fprintln(v, "← ↑ → ↓: Move View")
    fmt.Fprintln(v, "Backspace: Delete View")
    fmt.Fprintln(v, "t: Set view on top")
    fmt.Fprintln(v, "b: Set view on bottom")
    fmt.Fprintln(v, "^C: Exit")
}


func Loader() string {
	characters := "|/-\\"
	now := time.Now()
	nanos := now.UnixNano()
	index := nanos / 50000000 % int64(len(characters))
	return characters[index : index+1]
}


https://github.com/alitari/kubexp

https://github.com/JulienBreux/pody

https://stackoverflow.com/questions/40975307/how-to-watch-events-on-a-kubernetes-service-using-its-go-client

https://github.com/NetApp/trident/blob/master/k8s_client/k8s_client.go
https://github.com/vladimirvivien/k8s-client-examples
https://github.com/dtan4/k8stail/blob/master/tail.go


Test Data:

https://raw.githubusercontent.com/kubernetes/kubernetes/master/hack/testdata/recursive/pod/pod/busybox.yaml
https://raw.githubusercontent.com/istio/istio/master/samples/sleep/sleep.yaml


kubectl create clusterrolebinding dashboard-admin-sa --clusterrole=cluster-admin --serviceaccount=default:dashboard-admin-sa

kubectl describe secret dashboard-admin-sa-token-kw7vn

kubectl get secret $(kubectl get serviceaccount dashboard -o jsonpath="{.secrets[0].name}") -o jsonpath="{.data.token}" | base64 --decode

curl -fsSL https://raw.githubusercontent.com/micro/micro/master/scripts/install.sh | /bin/bash