Skip to content

Commit

Permalink
Refactor the code to display the data using go cli-runtime table
Browse files Browse the repository at this point in the history
Signed-off-by: cmoulliard <[email protected]>
  • Loading branch information
cmoulliard committed Nov 15, 2024
1 parent d7c1247 commit 3a41ab4
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 38 deletions.
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require (
k8s.io/api v0.30.5
k8s.io/apiextensions-apiserver v0.30.5
k8s.io/apimachinery v0.30.5
k8s.io/cli-runtime v0.30.5
k8s.io/client-go v0.30.5
k8s.io/klog/v2 v2.120.1
sigs.k8s.io/controller-runtime v0.18.5
Expand All @@ -26,6 +27,7 @@ require (

require (
dario.cat/mergo v1.0.0 // indirect
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v1.0.0 // indirect
Expand Down Expand Up @@ -69,6 +71,7 @@ require (
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
Expand Down
7 changes: 7 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ github.com/cnoe-io/argocd-api v0.0.0-20241031202925-3091d64cb3c4/go.mod h1:qItVg
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down Expand Up @@ -141,6 +143,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0=
github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
Expand Down Expand Up @@ -284,6 +288,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
Expand Down Expand Up @@ -357,6 +362,8 @@ k8s.io/apiextensions-apiserver v0.30.5 h1:JfXTIyzXf5+ryncbp7T/uaVjLdvkwtqoNG2vo7
k8s.io/apiextensions-apiserver v0.30.5/go.mod h1:uVLEME2UPA6UN22i+jTu66B9/0CnsjlHkId+Awo0lvs=
k8s.io/apimachinery v0.30.5 h1:CQZO19GFgw4zcOjY2H+mJ3k1u1o7zFACTNCB7nu4O18=
k8s.io/apimachinery v0.30.5/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
k8s.io/cli-runtime v0.30.5 h1:MWY6efoBVH3h0O6p2DgaQszabV5ZntHZwTHBkiz+PSI=
k8s.io/cli-runtime v0.30.5/go.mod h1:AKMWLDIJQUA5a7yEh5gmzkhpZqYpuDEVovanugfSnQk=
k8s.io/client-go v0.30.5 h1:vEDSzfTz0F8TXcWVdXl+aqV7NAV8M3UvC2qnGTTCoKw=
k8s.io/client-go v0.30.5/go.mod h1:/q5fHHBmhAUesOOFJACpD7VJ4e57rVtTPDOsvXrPpMk=
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
Expand Down
164 changes: 126 additions & 38 deletions pkg/cmd/get/clusters.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package get

import (
"bytes"
"context"
"fmt"
"github.com/cnoe-io/idpbuilder/pkg/cmd/helpers"
Expand All @@ -9,7 +10,9 @@ import (
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/cli-runtime/pkg/printers"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/clientcmd/api"
"os"
Expand All @@ -24,6 +27,34 @@ type ClusterManager struct {
clients map[string]client.Client // map of cluster name to client
}

type Cluster struct {
Name string
URLKubeApi string
KubePort int32
TlsCheck bool
ExternalPort int32
Nodes []Node
}

type Node struct {
Name string
InternalIP string
ExternalIP string
Capacity Capacity
Allocated Allocated
}

type Capacity struct {
Memory float64
Pods int64
Cpu int64
}

type Allocated struct {
Cpu string
Memory string
}

var ClustersCmd = &cobra.Command{
Use: "clusters",
Short: "Get idp clusters",
Expand All @@ -36,13 +67,18 @@ func preClustersE(cmd *cobra.Command, args []string) error {
return helpers.SetLogger()
}

// findClusterByName searches for a cluster by name in the kubeconfig
func findClusterByName(config *api.Config, name string) (*api.Cluster, bool) {
cluster, exists := config.Clusters[name]
return cluster, exists
func list(cmd *cobra.Command, args []string) error {
clusters, err := populateClusterList()
if err != nil {
return err
} else {
// Convert the list of the clusters to Table of clusters
printTable(printers.PrintOptions{}, generateClusterTable(clusters))
return nil
}
}

func list(cmd *cobra.Command, args []string) error {
func populateClusterList() ([]Cluster, error) {
logger := helpers.CmdLogger

detectOpt, err := util.DetectKindNodeProvider()
Expand Down Expand Up @@ -70,6 +106,9 @@ func list(cmd *cobra.Command, args []string) error {
os.Exit(1)
}

// Create an empty array of clusters to collect the information
clusterList := []Cluster{}

// List the idp builder clusters according to the provider: podman or docker
provider := cluster.NewProvider(cluster.ProviderWithLogger(kind.KindLoggerFromLogr(&logger)), detectOpt)
clusters, err := provider.List()
Expand All @@ -80,9 +119,8 @@ func list(cmd *cobra.Command, args []string) error {
// Populate a list of Kube client for each cluster/context matching a idpbuilder cluster
manager, _ := CreateKubeClientForEachIDPCluster(config, clusters)

fmt.Printf("\n")
for _, cluster := range clusters {
fmt.Printf("Cluster: %s\n", cluster)
aCluster := Cluster{Name: cluster}

// Search about the idp cluster within the kubeconfig file and show information
c, found := findClusterByName(config, "kind-"+cluster)
Expand All @@ -99,18 +137,18 @@ func list(cmd *cobra.Command, args []string) error {
if err != nil {
logger.Error(err, "failed to get the kubernetes ingress service.")
} else {
fmt.Printf("External Ingress Port: %d\n", targetPort)
aCluster.ExternalPort = targetPort
}

fmt.Printf("Host URL of the kube API server: %s\n", c.Server)
//fmt.Printf("TLS Verify: %t\n", c.InsecureSkipTLSVerify)
aCluster.URLKubeApi = c.Server
aCluster.TlsCheck = c.InsecureSkipTLSVerify

// Print the internal port running the Kuber API service
targetPort, err = findInternalKubeApiPort(cli)
kubeApiPort, err := findInternalKubeApiPort(cli)
if err != nil {
logger.Error(err, "failed to get the kubernetes default service.")
} else {
fmt.Printf("Internal Kubernetes API Port: %d\n", targetPort)
aCluster.KubePort = kubeApiPort
}

// Let's check what the current node reports
Expand All @@ -122,61 +160,101 @@ func list(cmd *cobra.Command, args []string) error {

for _, node := range nodeList.Items {
nodeName := node.Name
fmt.Printf("\n")
fmt.Printf("Node: %s\n", nodeName)

aNode := Node{}
aNode.Name = nodeName

for _, addr := range node.Status.Addresses {
switch addr.Type {
case corev1.NodeInternalIP:
fmt.Printf("Internal IP: %s\n", addr.Address)
aNode.InternalIP = addr.Address
case corev1.NodeExternalIP:
fmt.Printf("External IP: %s\n", addr.Address)
aNode.ExternalIP = addr.Address
}
}

// Show node capacity
fmt.Printf("Capacity of the node: \n")
printFormattedResourceList(node.Status.Capacity)
// Get Node capacity
aNode.Capacity = populateNodeCapacity(node.Status.Capacity)

// Show node allocated resources
err = printAllocatedResources(context.Background(), cli, node.Name)
// Get Node Allocated resources
allocated, err := printAllocatedResources(context.Background(), cli, node.Name)
if err != nil {
logger.Error(err, "Failed to get the node's allocated resources.")
logger.Error(err, "failed to get the allocated resources.")
}
aNode.Allocated = allocated
}
}
fmt.Println("----------------------------------------")
clusterList = append(clusterList, aCluster)
}

return nil
return clusterList, nil
}

func generateClusterTable(clusterTable []Cluster) metav1.Table {
table := &metav1.Table{}
table.ColumnDefinitions = []metav1.TableColumnDefinition{
{Name: "Name", Type: "string"},
{Name: "External-Port", Type: "string"},
{Name: "Kube-Api", Type: "string"},
{Name: "TLS", Type: "string"},
{Name: "Kube-Port", Type: "string"},
}
for _, cluster := range clusterTable {
row := metav1.TableRow{
Cells: []interface{}{
cluster.Name,
cluster.ExternalPort,
cluster.URLKubeApi,
cluster.TlsCheck,
cluster.KubePort,
},
}
table.Rows = append(table.Rows, row)
}
return *table
}

func printFormattedResourceList(resources corev1.ResourceList) {
// Define the fixed width for the resource name column (adjust as needed)
nameWidth := 20
func printTable(opts printers.PrintOptions, table metav1.Table) {
out := bytes.NewBuffer([]byte{})
printer := printers.NewTablePrinter(opts)
err := printer.PrintObj(&table, out)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(out.String())
}

func populateNodeCapacity(resources corev1.ResourceList) Capacity {
capacity := Capacity{}
for name, quantity := range resources {
if strings.HasPrefix(string(name), "hugepages-") {
continue
}

if name == corev1.ResourceMemory {
// Convert memory from bytes to gigabytes (GB)
memoryInBytes := quantity.Value() // .Value() gives the value in bytes
memoryInGB := float64(memoryInBytes) / (1024 * 1024 * 1024) // Convert to GB
fmt.Printf(" %-*s %.2f GB\n", nameWidth, name, memoryInGB)
} else {
// Format each line with the fixed name width and quantity
fmt.Printf(" %-*s %s\n", nameWidth, name, quantity.String())
capacity.Memory = memoryInGB
}

if name == corev1.ResourceCPU {
capacity.Cpu = quantity.Value()
}

if name == corev1.ResourcePods {
capacity.Pods = quantity.Value()
}

}
return capacity
}

func printAllocatedResources(ctx context.Context, k8sClient client.Client, nodeName string) error {
func printAllocatedResources(ctx context.Context, k8sClient client.Client, nodeName string) (Allocated, error) {
// List all pods on the specified node
var podList corev1.PodList
if err := k8sClient.List(ctx, &podList, client.MatchingFields{"spec.nodeName": nodeName}); err != nil {
return fmt.Errorf("failed to list pods on node %s: %w", nodeName, err)
return Allocated{}, fmt.Errorf("failed to list pods on node %s: %w", nodeName, err)
}

// Initialize counters for CPU and memory requests
Expand All @@ -196,11 +274,15 @@ func printAllocatedResources(ctx context.Context, k8sClient client.Client, nodeN
}

// Display the total allocated resources
fmt.Printf("Allocated resources on node:\n")
fmt.Printf(" CPU Requests: %s\n", totalCPU.String())
fmt.Printf(" Memory Requests: %s\n", totalMemory.String())
//fmt.Printf(" CPU Requests: %s\n", totalCPU.String())
//fmt.Printf(" Memory Requests: %s\n", totalMemory.String())

return nil
allocated := Allocated{
Memory: totalMemory.String(),
Cpu: totalCPU.String(),
}

return allocated, nil
}

func findExternalHTTPSPort(cli client.Client) (int32, error) {
Expand Down Expand Up @@ -245,6 +327,12 @@ func findInternalKubeApiPort(cli client.Client) (int32, error) {
return targetPort.TargetPort.IntVal, nil
}

// findClusterByName searches for a cluster by name in the kubeconfig
func findClusterByName(config *api.Config, name string) (*api.Cluster, bool) {
cluster, exists := config.Clusters[name]
return cluster, exists
}

// GetClientForCluster returns the client for the specified cluster/context name
func GetClientForCluster(m *ClusterManager, clusterName string) (client.Client, error) {
cl, exists := m.clients["kind-"+clusterName]
Expand Down

0 comments on commit 3a41ab4

Please sign in to comment.