github.com/wtfutil/wtf@v0.43.0/modules/kubernetes/widget.go (about) 1 package kubernetes 2 3 import ( 4 "context" 5 "fmt" 6 "sync" 7 8 "github.com/rivo/tview" 9 "github.com/wtfutil/wtf/utils" 10 "github.com/wtfutil/wtf/view" 11 12 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 13 ) 14 15 // Widget contains all the config for the widget 16 type Widget struct { 17 view.TextWidget 18 19 client *clientInstance 20 clientOnce sync.Once 21 22 objects []string 23 title string 24 kubeconfig string 25 namespaces []string 26 context string 27 settings *Settings 28 } 29 30 // NewWidget creates a new instance of the widget 31 func NewWidget(tviewApp *tview.Application, redrawChan chan bool, settings *Settings) *Widget { 32 widget := Widget{ 33 TextWidget: view.NewTextWidget(tviewApp, redrawChan, nil, settings.Common), 34 35 objects: settings.objects, 36 title: settings.title, 37 kubeconfig: settings.kubeconfig, 38 namespaces: settings.namespaces, 39 settings: settings, 40 context: settings.context, 41 } 42 43 widget.View.SetWrap(true) 44 45 return &widget 46 } 47 48 // Refresh executes the command and updates the view with the results 49 func (widget *Widget) Refresh() { 50 title := widget.generateTitle() 51 client, err := widget.getInstance() 52 53 if err != nil { 54 widget.Redraw(func() (string, string, bool) { return title, err.Error(), true }) 55 return 56 } 57 58 var content string 59 60 if utils.Includes(widget.objects, "nodes") { 61 nodeList, nodeError := client.getNodes() 62 if nodeError != nil { 63 widget.Redraw(func() (string, string, bool) { return title, "[red] Error getting node data [white]\n", true }) 64 return 65 } 66 content += fmt.Sprintf("[%s]Nodes[white]\n", widget.settings.Colors.Subheading) 67 for _, node := range nodeList { 68 content += fmt.Sprintf("%s\n", node) 69 } 70 content += "\n" 71 } 72 73 if utils.Includes(widget.objects, "deployments") { 74 deploymentList, deploymentError := client.getDeployments(widget.namespaces) 75 if deploymentError != nil { 76 widget.Redraw(func() (string, string, bool) { return title, "[red] Error getting deployment data [white]\n", true }) 77 return 78 } 79 content += fmt.Sprintf("[%s]Deployments[white]\n", widget.settings.Colors.Subheading) 80 for _, deployment := range deploymentList { 81 content += fmt.Sprintf("%s\n", deployment) 82 } 83 content += "\n" 84 } 85 86 if utils.Includes(widget.objects, "pods") { 87 podList, podError := client.getPods(widget.namespaces) 88 if podError != nil { 89 widget.Redraw(func() (string, string, bool) { return title, "[red] Error getting pod data [white]\n", false }) 90 return 91 } 92 content += fmt.Sprintf("[%s]Pods[white]\n", widget.settings.Colors.Subheading) 93 for _, pod := range podList { 94 content += fmt.Sprintf("%s\n", pod) 95 } 96 content += "\n" 97 } 98 99 widget.Redraw(func() (string, string, bool) { return title, content, false }) 100 } 101 102 /* -------------------- Unexported Functions -------------------- */ 103 104 // generateTitle generates a title for the widget 105 func (widget *Widget) generateTitle() string { 106 if widget.title != "" { 107 return widget.title 108 } 109 title := "Kube" 110 111 if widget.context != "" { 112 title = fmt.Sprintf("%s (%s)", title, widget.context) 113 } 114 115 if len(widget.namespaces) == 1 { 116 title += fmt.Sprintf(" - Namespace: %s", widget.namespaces[0]) 117 } else if len(widget.namespaces) > 1 { 118 title += fmt.Sprintf(" - Namespaces: %q", widget.namespaces) 119 } 120 return title 121 } 122 123 // getPods returns a slice of pod strings 124 func (client *clientInstance) getPods(namespaces []string) ([]string, error) { 125 var podList []string 126 if len(namespaces) != 0 { 127 for _, namespace := range namespaces { 128 pods, err := client.Client.CoreV1().Pods(namespace).List(context.Background(), metav1.ListOptions{}) 129 if err != nil { 130 return nil, err 131 } 132 133 for _, pod := range pods.Items { 134 var podString string 135 status := pod.Status.Phase 136 name := pod.ObjectMeta.Name 137 if len(namespaces) == 1 { 138 podString = fmt.Sprintf("%-50s %s", name, status) 139 } else { 140 podString = fmt.Sprintf("%-20s %-50s %s", namespace, name, status) 141 } 142 podList = append(podList, podString) 143 } 144 } 145 } else { 146 pods, err := client.Client.CoreV1().Pods("").List(context.Background(), metav1.ListOptions{}) 147 if err != nil { 148 return nil, err 149 } 150 for _, pod := range pods.Items { 151 podString := fmt.Sprintf("%-20s %-50s %s", pod.ObjectMeta.Namespace, pod.ObjectMeta.Name, pod.Status.Phase) 152 podList = append(podList, podString) 153 } 154 } 155 156 return podList, nil 157 } 158 159 // get Deployments returns a string slice of pod strings 160 func (client *clientInstance) getDeployments(namespaces []string) ([]string, error) { 161 var deploymentList []string 162 if len(namespaces) != 0 { 163 for _, namespace := range namespaces { 164 deployments, err := client.Client.AppsV1().Deployments(namespace).List(context.Background(), metav1.ListOptions{}) 165 if err != nil { 166 return nil, err 167 } 168 169 for _, deployment := range deployments.Items { 170 var deployString string 171 if len(namespaces) == 1 { 172 deployString = fmt.Sprintf("%-50s (%d/%d)", deployment.ObjectMeta.Name, deployment.Status.ReadyReplicas, deployment.Status.Replicas) 173 } else { 174 deployString = fmt.Sprintf("%-20s %-50s (%d/%d)", deployment.ObjectMeta.Namespace, deployment.ObjectMeta.Name, deployment.Status.ReadyReplicas, deployment.Status.Replicas) 175 } 176 deploymentList = append(deploymentList, deployString) 177 } 178 } 179 } else { 180 deployments, err := client.Client.AppsV1().Deployments("").List(context.Background(), metav1.ListOptions{}) 181 if err != nil { 182 return nil, err 183 } 184 185 for _, deployment := range deployments.Items { 186 deployString := fmt.Sprintf("%-20s %-50s (%d/%d)", deployment.ObjectMeta.Namespace, deployment.ObjectMeta.Name, deployment.Status.ReadyReplicas, deployment.Status.Replicas) 187 deploymentList = append(deploymentList, deployString) 188 } 189 } 190 return deploymentList, nil 191 } 192 193 // getNodes returns a string slice of nodes 194 func (client *clientInstance) getNodes() ([]string, error) { 195 var nodeList []string 196 197 nodes, err := client.Client.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{}) 198 if err != nil { 199 return nil, err 200 } 201 202 for _, node := range nodes.Items { 203 var nodeStatus string 204 for _, condition := range node.Status.Conditions { 205 if condition.Reason == "KubeletReady" { 206 switch { 207 case condition.Status == "True": 208 nodeStatus = "Ready" 209 case condition.Reason == "False": 210 nodeStatus = "NotReady" 211 default: 212 nodeStatus = "Unknown" 213 } 214 } 215 } 216 nodeString := fmt.Sprintf("%-50s %s", node.ObjectMeta.Name, nodeStatus) 217 nodeList = append(nodeList, nodeString) 218 } 219 return nodeList, nil 220 }