Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor havener pkg #626

Merged
merged 6 commits into from
May 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 15 additions & 17 deletions internal/cmd/nexec.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
package cmd

import (
"context"
"fmt"
"io"
"os"
Expand All @@ -35,7 +34,6 @@ import (
"github.com/gonvenience/term"
"github.com/homeport/havener/pkg/havener"
"github.com/spf13/cobra"
"k8s.io/client-go/kubernetes"
)

const nodeDefaultCommand = "/bin/sh"
Expand Down Expand Up @@ -110,20 +108,20 @@ func execInClusterNodes(hvnr havener.Havener, args []string) error {
switch {
case len(args) >= 2: // node name and command is given
input, command = args[0], args[1:]
nodes, err = lookupNodesByName(hvnr.Client(), input)
nodes, err = lookupNodesByName(hvnr, input)
if err != nil {
return err
}

case len(args) == 1: // only node name is given
input, command = args[0], []string{nodeDefaultCommand}
nodes, err = lookupNodesByName(hvnr.Client(), input)
nodes, err = lookupNodesByName(hvnr, input)
if err != nil {
return err
}

default: // no arguments
return availableNodesError(hvnr.Client(), "no node name and command specified")
return availableNodesError(hvnr, "no node name and command specified")
}

// In case the current process does not run in a terminal, disable the
Expand Down Expand Up @@ -214,21 +212,16 @@ func execInClusterNodes(hvnr havener.Havener, args []string) error {
return combineErrorsFromChannel("node command execution failed", errors)
}

func lookupNodesByName(client kubernetes.Interface, input string) ([]corev1.Node, error) {
func lookupNodesByName(h havener.Havener, input string) ([]corev1.Node, error) {
if input == "all" {
list, err := client.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, err
}

return list.Items, nil
return h.ListNodes()
}

var nodeList []corev1.Node
for _, nodeName := range strings.Split(input, ",") {
node, err := client.CoreV1().Nodes().Get(context.TODO(), nodeName, metav1.GetOptions{})
node, err := h.Client().CoreV1().Nodes().Get(h.Context(), nodeName, metav1.GetOptions{})
if err != nil {
return nil, availableNodesError(client, "node '%s' does not exist", nodeName)
return nil, availableNodesError(h, "node '%s' does not exist", nodeName)
}

nodeList = append(nodeList, *node)
Expand All @@ -237,8 +230,8 @@ func lookupNodesByName(client kubernetes.Interface, input string) ([]corev1.Node
return nodeList, nil
}

func availableNodesError(client kubernetes.Interface, title string, fArgs ...interface{}) error {
nodes, err := havener.ListNodes(client)
func availableNodesError(h havener.Havener, title string, fArgs ...interface{}) error {
nodes, err := h.ListNodes()
if err != nil {
return fmt.Errorf("failed to list all nodes in cluster: %w", err)
}
Expand All @@ -247,8 +240,13 @@ func availableNodesError(client kubernetes.Interface, title string, fArgs ...int
return fmt.Errorf("failed to find any node in cluster")
}

names := make([]string, len(nodes))
for i, node := range nodes {
names[i] = node.Name
}

return fmt.Errorf("%s: %w",
fmt.Sprintf(title, fArgs...),
bunt.Errorf("*list of available nodes:*\n%s\n\nor, use _all_ to target all nodes", strings.Join(nodes, "\n")),
bunt.Errorf("*list of available nodes:*\n%s\n\nor, use _all_ to target all nodes", strings.Join(names, "\n")),
)
}
7 changes: 6 additions & 1 deletion pkg/havener/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,12 @@ func outOfClusterAuthentication(kubeConfig string) (*kubernetes.Clientset, *rest
return nil, nil, fmt.Errorf("no kube config supplied")
}

logf(Verbose, "Connecting to Kubernetes cluster...")
clusterName, err := clusterName(kubeConfig)
if err != nil {
return nil, nil, fmt.Errorf("failed to look-up cluster name: %w", err)
}

logf(Verbose, "Connecting to Kubernetes cluster _%s_ ...", clusterName)

// BuildConfigFromFlags is a helper function that builds configs from a master
// url or a kubeconfig filepath.
Expand Down
17 changes: 11 additions & 6 deletions pkg/havener/kubexec.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,15 +93,15 @@ func (h *Hvnr) NodeExec(node corev1.Node, containerImage string, timeoutSeconds
)

// Make sure to stop pod after command execution
defer func() { _ = PurgePod(h.client, namespace, podName, 10, metav1.DeletePropagationForeground) }()
defer func() { _ = h.PurgePod(namespace, podName, 10, metav1.DeletePropagationForeground) }()

pod, err := h.preparePodOnNode(node, namespace, podName, containerImage, timeoutSeconds, stdin != nil)
if err != nil {
return err
}

// Execute command on pod and redirect output to users provided stdout and stderr
logf(Verbose, "Executing command on node: %#v", command)
logf(Verbose, "Executing command on node: `%v`", strings.Join(command, " "))
return h.PodExec(
pod,
"node-exec-container",
Expand All @@ -115,7 +115,7 @@ func (h *Hvnr) NodeExec(node corev1.Node, containerImage string, timeoutSeconds

func (h *Hvnr) preparePodOnNode(node corev1.Node, namespace string, name string, containerImage string, timeoutSeconds int, useStdin bool) (*corev1.Pod, error) {
// Add pod deletion to shutdown sequence list (in case of Ctrl+C exit)
AddShutdownFunction(func() { _ = PurgePod(h.client, namespace, name, 10, metav1.DeletePropagationBackground) })
AddShutdownFunction(func() { _ = h.PurgePod(namespace, name, 10, metav1.DeletePropagationBackground) })

// Pod configuration
pod := &corev1.Pod{
Expand All @@ -124,9 +124,14 @@ func (h *Hvnr) preparePodOnNode(node corev1.Node, namespace string, name string,
Namespace: namespace,
},
Spec: corev1.PodSpec{
NodeSelector: map[string]string{"kubernetes.io/hostname": node.Name}, // Deploy pod on specific node using label selector
HostPID: true,
RestartPolicy: corev1.RestartPolicyNever,
NodeSelector: map[string]string{
// Deploy pod on specific node using label selector
corev1.LabelHostname: node.Name,
},
HostPID: true,
HostNetwork: true,
RestartPolicy: corev1.RestartPolicyNever,
TerminationGracePeriodSeconds: pointer.Int64(0),
Containers: []corev1.Container{
{
Name: "node-exec-container",
Expand Down
28 changes: 5 additions & 23 deletions pkg/havener/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
package havener

import (
"context"
"fmt"
"strings"
"sync"
Expand All @@ -32,7 +31,6 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/kubernetes"

"github.com/gonvenience/text"
)
Expand Down Expand Up @@ -138,7 +136,7 @@ func (h *Hvnr) ListSecrets(namespaces ...string) (result []*corev1.Secret, err e
}

for _, namespace := range namespaces {
listResp, err := h.client.CoreV1().Secrets(namespace).List(context.TODO(), metav1.ListOptions{})
listResp, err := h.client.CoreV1().Secrets(namespace).List(h.ctx, metav1.ListOptions{})
if err != nil {
return nil, err
}
Expand All @@ -162,7 +160,7 @@ func (h *Hvnr) ListConfigMaps(namespaces ...string) (result []*corev1.ConfigMap,
}

for _, namespace := range namespaces {
listResp, err := h.client.CoreV1().ConfigMaps(namespace).List(context.TODO(), metav1.ListOptions{})
listResp, err := h.client.CoreV1().ConfigMaps(namespace).List(h.ctx, metav1.ListOptions{})
if err != nil {
return nil, err
}
Expand All @@ -188,32 +186,16 @@ func (h *Hvnr) ListCustomResourceDefinition(crdName string) (result []unstructur

if crdExist {
client, _ := dynamic.NewForConfig(h.restconfig)
list, _ := client.Resource(runtimeClassGVR).List(context.TODO(), metav1.ListOptions{})
list, _ := client.Resource(runtimeClassGVR).List(h.ctx, metav1.ListOptions{})
return list.Items, nil
}

return nil, fmt.Errorf("desired resource %s, was not found", crdName)
}

// ListNodes lists all nodes of the cluster
// Deprecated: Use Havener interface function ListNodeNames instead
func ListNodes(client kubernetes.Interface) ([]string, error) {
nodeList, err := client.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
if err != nil {
return nil, err
}

result := make([]string, len(nodeList.Items))
for i, node := range nodeList.Items {
result[i] = node.Name
}

return result, nil
}

// ListNodes returns a list of the nodes in the cluster
func (h *Hvnr) ListNodes() ([]corev1.Node, error) {
nodeList, err := h.client.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
nodeList, err := h.client.CoreV1().Nodes().List(h.ctx, metav1.ListOptions{})
if err != nil {
return nil, fmt.Errorf("failed to get list of nodes: %w", err)
}
Expand All @@ -223,7 +205,7 @@ func (h *Hvnr) ListNodes() ([]corev1.Node, error) {

// ListNodeNames returns a list of the names of the nodes in the cluster
func (h *Hvnr) ListNodeNames() ([]string, error) {
nodeList, err := h.client.CoreV1().Nodes().List(context.TODO(), metav1.ListOptions{})
nodeList, err := h.client.CoreV1().Nodes().List(h.ctx, metav1.ListOptions{})
if err != nil {
return nil, fmt.Errorf("failed to get list of nodes: %w", err)
}
Expand Down
9 changes: 2 additions & 7 deletions pkg/havener/purge.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,13 @@
package havener

import (
"context"

metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"k8s.io/client-go/kubernetes"
)

// PurgePod removes the pod in the given namespace.
func PurgePod(kubeClient kubernetes.Interface, namespace string, podName string, gracePeriodSeconds int64, propagationPolicy metav1.DeletionPropagation) error {
func (h *Hvnr) PurgePod(namespace string, podName string, gracePeriodSeconds int64, propagationPolicy metav1.DeletionPropagation) error {
logf(Verbose, "Deleting pod %s in namespace %s", podName, namespace)
return kubeClient.CoreV1().Pods(namespace).Delete(
context.TODO(),
return h.client.CoreV1().Pods(namespace).Delete(h.ctx,
podName,
metav1.DeleteOptions{
GracePeriodSeconds: &gracePeriodSeconds,
Expand Down