package cmd
import (
"github.com/Kavinraja-G/node-gizmo/pkg/cmd"
"github.com/Kavinraja-G/node-gizmo/utils"
)
func init() {
// inits the clientset and other generic configs if any
utils.InitConfig()
}
// Execute drives the root 'nodegizmo' command
func Execute() error {
root := cmd.NewCmdRoot()
if err := root.Execute(); err != nil {
return err
}
return nil
}
package main
import (
"os"
"github.com/Kavinraja-G/node-gizmo/cmd"
)
func main() {
if err := cmd.Execute(); err != nil {
os.Exit(1)
}
}
package cmd
import (
"log"
"os"
"github.com/spf13/cobra"
"github.com/spf13/cobra/doc"
)
// NewCmdDocs initializes the 'docs' command
func NewCmdDocs(rootCmd *cobra.Command) *cobra.Command {
cmd := &cobra.Command{
Use: "docs",
Short: "Generates Markdown docs for nodegizmo in the current working directory",
Run: func(cmd *cobra.Command, args []string) {
cwd, err := os.Getwd()
if err != nil {
log.Fatal(err)
}
err = doc.GenMarkdownTree(rootCmd, cwd)
if err != nil {
log.Fatal(err)
}
},
}
return cmd
}
package cmd
import (
"context"
"errors"
"fmt"
"log"
"os"
"time"
"github.com/Kavinraja-G/node-gizmo/utils"
"github.com/docker/cli/cli/streams"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
k8errors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/tools/remotecommand"
)
var (
nodeshellPodNamespace string
nodeshellPodImage string
nodeshellPodTTL string
nodeshellPodNamePrefix = "nodeshell-"
podSCPrivileged = true
podTerminationGracePeriodSeconds = int64(0)
)
// NewCmdNodeExec initialises the 'exec' command
func NewCmdNodeExec() *cobra.Command {
cmd := &cobra.Command{
Use: "exec <node-name>",
Short: "Spawns a nsenter pod to exec into the provided node",
Aliases: []string{"ex"},
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return errors.New("please provide a nodeName to exec")
}
nodeName := args[0]
if !isValidNode(nodeName) {
return errors.New(fmt.Sprintf("%v is not a valid node", args[0]))
}
err := createExecPodInTargetedNode(nodeName)
if err != nil {
return err
}
return execIntoNode(cmd, args[0])
},
PostRunE: func(cmd *cobra.Command, args []string) error {
return cleanUpNodeshellPods(cmd, args[0])
},
}
// additional local flags
cmd.Flags().StringVarP(&nodeshellPodNamespace, "namespace", "n", "kube-system", "Namespace where nsenter pod to be created")
cmd.Flags().StringVarP(&nodeshellPodImage, "image", "i", "docker.io/alpine:3.18", "Image used by nsenter pod")
cmd.Flags().StringVarP(&nodeshellPodTTL, "ttl", "t", "3600", "Time to live (seconds) for the exec container. Defaults to 3600s")
return cmd
}
// isValidNode validates the given node is available in the cluster or not
func isValidNode(nodeName string) bool {
nodes, err := utils.Cfg.Clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
if err != nil {
log.Fatalf("Error while listing the nodes in the cluster: %v", err)
}
for _, node := range nodes.Items {
if node.Name == nodeName {
return true
}
}
return false
}
// createExecPodInTargetedNode creates the nsenter pod in the given node
func createExecPodInTargetedNode(nodeName string) error {
var nodeshellPodName = fmt.Sprintf("nodeshell-%v", nodeName)
pod := &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: nodeshellPodName,
Namespace: nodeshellPodNamespace,
Labels: map[string]string{
"app.kubernetes.io/name": nodeshellPodName,
"app.kubernetes.io/component": "exec",
"app.kubernetes.io/managed-by": "node-gizmo",
},
},
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "nodeshell",
Image: nodeshellPodImage,
Command: []string{"nsenter"},
Args: []string{"-t", "1", "-m", "-u", "-i", "-n", "sleep", nodeshellPodTTL},
SecurityContext: &corev1.SecurityContext{
Privileged: &podSCPrivileged,
},
},
},
RestartPolicy: corev1.RestartPolicyNever,
TerminationGracePeriodSeconds: &podTerminationGracePeriodSeconds,
HostNetwork: true,
HostPID: true,
HostIPC: true,
Tolerations: []corev1.Toleration{
{
Operator: corev1.TolerationOpExists, // this will attract any taints added to nodes
},
},
NodeSelector: map[string]string{
"kubernetes.io/hostname": nodeName,
},
NodeName: nodeName,
},
}
_, err := utils.Cfg.Clientset.CoreV1().Pods(nodeshellPodNamespace).Create(context.TODO(), pod, metav1.CreateOptions{})
if err != nil {
// validates if the pod already exists
if k8errors.IsAlreadyExists(err) {
return nil
}
return err
}
// wait for exec pod to get RUNNING
checkExecPodRunningStatus := func() (bool, error) {
pod, err := utils.Cfg.Clientset.CoreV1().Pods(nodeshellPodNamespace).Get(context.TODO(), nodeshellPodName, metav1.GetOptions{})
if err != nil {
return false, err
}
if pod.Status.Phase == corev1.PodRunning {
return true, nil
}
return false, nil
}
backoff := wait.Backoff{
Steps: 5, // Number of retry steps.
Duration: 10 * time.Second, // Initial backoff duration.
Factor: 1.0, // Multiplier for each step's duration.
Jitter: 0.1, // Jitter to randomize the duration slightly.
}
// waits exponentially for exec pod to be RUNNING
startTime := time.Now()
err = wait.ExponentialBackoff(backoff, func() (bool, error) {
elapsedWaitTime := time.Since(startTime)
log.Printf("Waiting for exec pod %s to be RUNNING. Elapsed time: %v", nodeshellPodName, elapsedWaitTime)
return checkExecPodRunningStatus()
})
if err != nil {
log.Fatalf("exec pod did not reached the RUNNING state: %v", err)
}
return err
}
// execIntoNode is the driver function used to exec into the nsenter pod deployed in the targeted node
func execIntoNode(cmd *cobra.Command, nodeName string) error {
var nodeshellPodName = nodeshellPodNamePrefix + nodeName
var podExecCmd = []string{"sh", "-c", "(bash || ash || sh)"}
req := utils.Cfg.Clientset.CoreV1().RESTClient().Post().Resource("pods").Name(nodeshellPodName).Namespace(nodeshellPodNamespace).SubResource("exec")
opts := &corev1.PodExecOptions{
Command: podExecCmd,
Stdin: true,
Stdout: true,
Stderr: true,
TTY: true,
}
req.VersionedParams(opts, scheme.ParameterCodec)
k8sConfig, err := utils.GetKubeConfig()
if err != nil {
log.Fatalf("Error while getting Kubeconfig: %v", err)
return err
}
// initiate the exec command on the nsenter pod and creates a bidirectional connection to the server
exec, err := remotecommand.NewSPDYExecutor(k8sConfig, "POST", req.URL())
if err != nil {
log.Fatalf("Error while running exec on nodeshell pod: %v", err)
return err
}
// inspired from https://github.com/kubernetes/client-go/issues/912
input := streams.NewIn(os.Stdin)
if err = input.SetRawTerminal(); err != nil {
log.Fatalf("Error setting rawTerminal: %v", err)
}
err = exec.StreamWithContext(context.TODO(), remotecommand.StreamOptions{
Stdin: input,
Stdout: os.Stdout,
Stderr: os.Stderr,
Tty: true,
})
return err
}
// cleanUpNodeshellPods cleans up the nsenter pod once the shell is exited
func cleanUpNodeshellPods(cmd *cobra.Command, nodeName string) error {
var nodeshellPodName = nodeshellPodNamePrefix + nodeName
err := utils.Cfg.Clientset.CoreV1().Pods(nodeshellPodNamespace).Delete(context.TODO(), nodeshellPodName, metav1.DeleteOptions{})
if err != nil {
log.Fatalf("Error while creating the nodeshell pod: %v", err)
return err
}
return err
}
package nodepool
import (
"context"
"github.com/Kavinraja-G/node-gizmo/pkg/outputs"
"github.com/Kavinraja-G/node-gizmo/utils"
"github.com/Kavinraja-G/node-gizmo/pkg"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
var sortByHeader string
func NewCmdNodepoolInfo() *cobra.Command {
cmd := &cobra.Command{
Use: "nodepool",
Short: "Displays detailed information about Nodepool",
Aliases: []string{"np", "ng"},
RunE: func(cmd *cobra.Command, args []string) error {
return showNodePoolInfo(cmd, args)
},
}
// additional local flags
cmd.Flags().StringVarP(&sortByHeader, "sort-by", "", "nodepool", "Sorts output using a valid Column name. Defaults to 'nodepool' if the column name is not valid")
return cmd
}
// showNodePoolInfo driver function for the 'nodepool' command
func showNodePoolInfo(cmd *cobra.Command, args []string) error {
var genericNodepoolInfos []pkg.GenericNodepoolInfo
nodes, err := utils.Cfg.Clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
if err != nil {
return err
}
for _, node := range nodes.Items {
cloudProvider, nodepoolID := getNodepoolIDAndProvider(node.Labels)
region, zone := pkg.GetNodeTopologyInfo(node.Labels)
genericNodepoolInfos = append(genericNodepoolInfos, pkg.GenericNodepoolInfo{
NodepoolID: nodepoolID,
Node: node.Name,
Provider: cloudProvider,
InstanceType: getNodeInstanceType(node.Labels),
Region: region,
Zone: zone,
K8sVersion: node.Status.NodeInfo.KubeletVersion,
})
}
outputHeaders, outputData := generateNodepoolInfoData(genericNodepoolInfos)
outputs.SortOutputBasedOnHeader(outputHeaders, outputData, sortByHeader)
outputs.TableOutput(outputHeaders, outputData)
return nil
}
// getNodepoolIDAndProvider returns the cloud provider type for the nodepool (EKS/Karpenter, GKE, AKS, can be Unknown)
func getNodepoolIDAndProvider(labels map[string]string) (string, string) {
if id, ok := labels[pkg.AwsNodepoolLabel]; ok {
return "EKS", id
}
if id, ok := labels[pkg.GkeNodepoolLabel]; ok {
return "GKE", id
}
if id, ok := labels[pkg.AksNodepoolLabel]; ok {
return "AKS", id
}
if id, ok := labels[pkg.KarpenterNodepool]; ok {
return "Karpenter", id
}
if id, ok := labels[pkg.KarpenterNodepoolV1]; ok {
return "Karpenter", id
}
if id, ok := labels[pkg.OpenshiftMachinepool]; ok {
return "Openshift", id
}
return "Unknown", "Unknown"
}
// getNodeInstanceType returns the node instanceType based on the instance-type label
func getNodeInstanceType(labels map[string]string) string {
if val, ok := labels[pkg.NodeInstanceTypeLabel]; ok {
return val
}
return "Unknown"
}
// generateNodepoolInfoData generates the Nodepool info outputs and the required headers for table-writer
func generateNodepoolInfoData(genericNodepoolInfos []pkg.GenericNodepoolInfo) ([]string, [][]string) {
var headers = []string{"NODEPOOL", "PROVIDER", "REGION", "ZONE", "INSTANCE-TYPE", "VERSION", "NODES"}
var outputData [][]string
for _, nodepoolInfo := range genericNodepoolInfos {
lineItems := []string{
nodepoolInfo.NodepoolID,
nodepoolInfo.Provider,
nodepoolInfo.Region,
nodepoolInfo.Zone,
nodepoolInfo.InstanceType,
nodepoolInfo.K8sVersion,
nodepoolInfo.Node,
}
outputData = append(outputData, lineItems)
}
return headers, outputData
}
package nodes
import (
"context"
"github.com/Kavinraja-G/node-gizmo/pkg"
"github.com/Kavinraja-G/node-gizmo/pkg/outputs"
"github.com/Kavinraja-G/node-gizmo/utils"
"github.com/spf13/cobra"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// NewCmdNodeCapacityInfo initializes the 'capacity' command
func NewCmdNodeCapacityInfo() *cobra.Command {
cmd := &cobra.Command{
Use: "capacity",
Short: "Displays Node capacity related information",
Aliases: []string{"capacities", "cap"},
RunE: func(cmd *cobra.Command, args []string) error {
return showNodeCapacities(cmd, args)
},
}
return cmd
}
// showNodeCapacities driver function for 'node capacity' command
func showNodeCapacities(cmd *cobra.Command, args []string) error {
var nodeCapacityInfo []pkg.NodeCapacities
labels, _ = cmd.Flags().GetString("labels")
nodes, err := utils.Cfg.Clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{LabelSelector: labels})
if err != nil {
return err
}
for _, node := range nodes.Items {
nodeCapacity := pkg.NodeCapacities{
NodeName: node.Name,
CPU: node.Status.Capacity.Cpu().String(),
Memory: node.Status.Capacity.Memory().Value(),
Disk: node.Status.Capacity.Storage().Value(),
EphemeralStorage: node.Status.Capacity.StorageEphemeral().Value(),
PodCapacity: node.Status.Capacity.Pods().String(),
}
nodeCapacityInfo = append(nodeCapacityInfo, nodeCapacity)
}
outputHeaders, outputData := generateNodeCapacityOutputData(nodeCapacityInfo)
outputs.SortOutputBasedOnHeader(outputHeaders, outputData, sortByHeader)
outputs.TableOutput(outputHeaders, outputData)
return nil
}
// generateNodeCapacityOutputData generates the NodeCapacity outputs and the required headers for table-writer
func generateNodeCapacityOutputData(nodeCapacityInfo []pkg.NodeCapacities) ([]string, [][]string) {
var headers = []string{"NAME", "CPU", "MEMORY", "DISK", "EPHEMERAL", "POD CAPACITY"}
var outputData [][]string
for _, nodeCapacity := range nodeCapacityInfo {
lineItems := []string{
nodeCapacity.NodeName,
nodeCapacity.CPU,
utils.PrettyByteSize(nodeCapacity.Memory),
utils.PrettyByteSize(nodeCapacity.Disk),
utils.PrettyByteSize(nodeCapacity.EphemeralStorage),
nodeCapacity.PodCapacity,
}
outputData = append(outputData, lineItems)
}
return headers, outputData
}
package nodes
import (
"context"
"fmt"
"strings"
"github.com/Kavinraja-G/node-gizmo/utils"
"github.com/Kavinraja-G/node-gizmo/pkg/outputs"
"github.com/Kavinraja-G/node-gizmo/pkg"
"github.com/spf13/cobra"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
var (
showTaints bool
showNodeProviderInfo bool
showNodeTopologyInfo bool
sortByHeader string
labels string
)
// NewCmdNodeInfo initializes the 'node' command
func NewCmdNodeInfo() *cobra.Command {
cmd := &cobra.Command{
Use: "node",
Short: "Displays generic node related information in the cluster",
Aliases: []string{"nodes"},
RunE: func(cmd *cobra.Command, args []string) error {
return showNodeInfo(cmd, args)
},
TraverseChildren: true,
}
// additional local flags
cmd.Flags().BoolVarP(&showTaints, "show-taints", "t", false, "Shows taints added on a node")
cmd.Flags().BoolVarP(&showNodeProviderInfo, "show-providers", "p", false, "Shows cloud provider name for a node")
cmd.Flags().BoolVarP(&showNodeTopologyInfo, "show-topology", "T", false, "Shows node topology info like region & zones for a node")
cmd.PersistentFlags().StringVarP(&sortByHeader, "sort-by", "", "name", "Sorts output using a valid Column name. Defaults to 'name' if the column name is not valid")
cmd.PersistentFlags().StringVarP(&labels, "labels", "l", "", "Filter based on node labels")
// additional sub-commands
cmd.AddCommand(NewCmdNodeCapacityInfo())
return cmd
}
// showNodeInfo driver function for generic node info command
func showNodeInfo(cmd *cobra.Command, args []string) error {
var nodeInfos []pkg.GenericNodeInfo
var outputOpts = pkg.OutputOptsForGenericNodeInfo{
ShowTaints: showTaints,
ShowNodeProviderInfo: showNodeProviderInfo,
ShowNodeTopologyInfo: showNodeTopologyInfo,
}
nodes, err := utils.Cfg.Clientset.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{LabelSelector: labels})
if err != nil {
return err
}
for _, node := range nodes.Items {
genericNodeInfo := pkg.GenericNodeInfo{
NodeName: node.Name,
K8sVersion: node.Status.NodeInfo.KubeletVersion,
Image: node.Status.NodeInfo.OSImage,
Os: node.Status.NodeInfo.OperatingSystem,
OsArch: node.Status.NodeInfo.Architecture,
NodeStatus: getNodeStatus(node.Status.Conditions),
}
if ok, _ := cmd.Flags().GetBool("show-taints"); ok {
genericNodeInfo.Taints = getNodeTaints(node.Spec.Taints)
}
if ok, _ := cmd.Flags().GetBool("show-providers"); ok {
genericNodeInfo.NodeProvider = getNodeProviderName(node.Spec.ProviderID)
}
if ok, _ := cmd.Flags().GetBool("show-topology"); ok {
genericNodeInfo.NodeTopologyRegion, genericNodeInfo.NodeTopologyZone = pkg.GetNodeTopologyInfo(node.Labels)
}
nodeInfos = append(nodeInfos, genericNodeInfo)
}
outputHeaders, outputData := generateNodeInfoOutputData(nodeInfos, outputOpts)
outputs.SortOutputBasedOnHeader(outputHeaders, outputData, sortByHeader)
outputs.TableOutput(outputHeaders, outputData)
return nil
}
// getNodeProviderName returns the providerName stripped from the providerID in the spec
// providerID = aws://someRandomNodeID => "aws"
// providerID = nil || "" => "others"
func getNodeProviderName(providerID string) string {
// providerID format <ProviderName>://<ProviderSpecificNodeID>
if providerID != "" {
return strings.Split(providerID, ":")[0]
}
return "others"
}
// getNodeTaints returns the taints that are added to the node
func getNodeTaints(rawTaints []corev1.Taint) []string {
var taints []string
for _, taint := range rawTaints {
taints = append(taints, fmt.Sprintf("%v=%v:%v", taint.Key, taint.Value, taint.Effect))
}
return taints
}
// getNodeStatus returns if the provided node is 'Ready' or 'NotReady'
func getNodeStatus(nodeConditions []corev1.NodeCondition) string {
for _, nodeCondition := range nodeConditions {
if nodeCondition.Type == corev1.NodeReady {
return "Ready"
}
}
return "NotReady"
}
// generateNodeInfoOutputData generates the NodeInfo outputs and the required headers for table-writer
func generateNodeInfoOutputData(genericNodeInfos []pkg.GenericNodeInfo, outputOpts pkg.OutputOptsForGenericNodeInfo) ([]string, [][]string) {
var headers = []string{"NAME", "VERSION", "IMAGE", "OS", "ARCHITECTURE", "STATUS"}
var outputData [][]string
if outputOpts.ShowTaints {
headers = append(headers, "TAINTS")
}
if outputOpts.ShowNodeProviderInfo {
headers = append(headers, "PROVIDER")
}
if outputOpts.ShowNodeTopologyInfo {
headers = append(headers, "REGION", "ZONE")
}
for _, nodeInfo := range genericNodeInfos {
lineItems := []string{
nodeInfo.NodeName,
nodeInfo.K8sVersion,
nodeInfo.Image,
nodeInfo.Os,
nodeInfo.OsArch,
nodeInfo.NodeStatus,
}
if outputOpts.ShowTaints {
lineItems = append(lineItems, strings.Join(nodeInfo.Taints, "\n"))
}
if outputOpts.ShowNodeProviderInfo {
lineItems = append(lineItems, nodeInfo.NodeProvider)
}
if outputOpts.ShowNodeTopologyInfo {
lineItems = append(lineItems, nodeInfo.NodeTopologyRegion, nodeInfo.NodeTopologyZone)
}
outputData = append(outputData, lineItems)
}
return headers, outputData
}
package cmd
import (
"github.com/Kavinraja-G/node-gizmo/pkg/cmd/nodepool"
"github.com/Kavinraja-G/node-gizmo/pkg/cmd/nodes"
"github.com/spf13/cobra"
)
// NewCmdRoot initializes the root command 'nodegizmo'
func NewCmdRoot() *cobra.Command {
cmd := &cobra.Command{
Use: "nodegizmo",
Aliases: []string{"ng"},
Short: "Nodegizmo - A CLI utility for your Kubernetes nodes",
RunE: func(c *cobra.Command, args []string) error {
if err := c.Help(); err != nil {
return err
}
return nil
},
}
// child commands
cmd.AddCommand(NewCmdDocs(cmd))
cmd.AddCommand(NewCmdNodeExec())
cmd.AddCommand(nodes.NewCmdNodeInfo())
cmd.AddCommand(nodepool.NewCmdNodepoolInfo())
return cmd
}
package pkg
// GetNodeTopologyInfo retrieves region and zone info from topology labels
func GetNodeTopologyInfo(labels map[string]string) (string, string) {
var region string
var zone string
if val, ok := labels[TopologyRegionLabel]; ok {
region = val
}
if val, ok := labels[TopologyZoneLabel]; ok {
zone = val
}
return region, zone
}
package outputs
import (
"k8s.io/utils/strings/slices"
"sort"
"strings"
)
// getSortKeyIdxFromHeader retrieves the index of the sortKey in the header slice
func getSortKeyIdxFromHeader(headers []string, sortKey string) int {
// defaults to first column always (usually node/nodepool name)
idx := 0
if slices.Contains(headers, strings.ToUpper(sortKey)) {
idx = slices.Index(headers, strings.ToUpper(sortKey))
}
return idx
}
// SortOutputBasedOnHeader sorts output based on the Header key provided in the flags
func SortOutputBasedOnHeader(headers []string, sortSlices [][]string, sortKey string) {
sortByHeaderIndex := getSortKeyIdxFromHeader(headers, sortKey)
sort.SliceStable(sortSlices, func(i, j int) bool {
return sortSlices[i][sortByHeaderIndex] < sortSlices[j][sortByHeaderIndex]
})
}
package outputs
import (
"os"
"github.com/olekukonko/tablewriter"
)
// TableOutput renders a table in terminal
func TableOutput(headers []string, outputData [][]string) {
table := tablewriter.NewWriter(os.Stdout)
// enables autoMerge only for nodepool infos
if headers[0] == "NODEPOOL" {
table.SetAutoMergeCells(true)
}
// default configs for the table-writer
table.SetRowLine(false)
table.SetBorder(false)
table.SetAutoWrapText(false)
table.SetAutoFormatHeaders(true)
table.SetHeaderLine(false)
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
table.SetAlignment(tablewriter.ALIGN_LEFT)
table.SetAutoWrapText(false)
table.SetCenterSeparator("")
table.SetColumnSeparator("")
table.SetRowSeparator("")
table.SetTablePadding("\t")
// set headers and add the outputData
table.SetHeader(headers)
table.AppendBulk(outputData)
table.Render()
}
package utils
import (
"log"
"path/filepath"
k8s "k8s.io/client-go/kubernetes"
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/util/homedir"
)
type Config struct {
Clientset *k8s.Clientset
}
var Cfg Config
// GetKubeConfig is used to fetch the kubeConfig based on the KUBECONFIG env or '~/.kube/config' location
func GetKubeConfig() (*rest.Config, error) {
var kubeConfigPath string
var defaultKubeConfigPath = "~/.kube/config"
if home := homedir.HomeDir(); home != "" {
defaultKubeConfigPath = filepath.Join(home, ".kube", "config")
}
kubeConfigPath = GetEnv("KUBECONFIG", defaultKubeConfigPath)
k8sConfig, err := clientcmd.BuildConfigFromFlags("", kubeConfigPath)
return k8sConfig, err
}
// k8sAuth is used to get the Kubernetes clientset from the config
func k8sAuth() (*k8s.Clientset, error) {
k8sConfig, err := GetKubeConfig()
if err != nil {
log.Fatalf("Error while getting kubeconfig: %v", err)
}
clientset, err := k8s.NewForConfig(k8sConfig)
if err != nil {
log.Fatalf("Error while creating the clientset: %v", err)
}
return clientset, err
}
// InitConfig initiates a kubernetes clientset & other generic configs with the current context
func InitConfig() {
var err error
Cfg.Clientset, err = k8sAuth()
if err != nil {
log.Fatalf("Error while authenticating to kubernetes: %v", err)
}
}
package utils
import (
"fmt"
"math"
"os"
)
// GetEnv Helper function for fetching envs with defaults
func GetEnv(env, defaults string) string {
if val, ok := os.LookupEnv(env); ok && val != "" {
return val
}
return defaults
}
// PrettyByteSize converts the bytes to human-readable format
func PrettyByteSize(b int64) string {
bf := float64(b)
for _, unit := range []string{"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"} {
if math.Abs(bf) < 1024.0 {
return fmt.Sprintf("%3.1f%sB", bf, unit)
}
bf /= 1024.0
}
return fmt.Sprintf("%.1fYiB", bf)
}