portainer/pkg/libkubectl/drain.go

110 lines
3.1 KiB
Go

package libkubectl
import (
"bytes"
"context"
"fmt"
"time"
"github.com/rs/zerolog/log"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubectl/pkg/cmd/util"
"k8s.io/kubectl/pkg/drain"
)
// DrainNode drains a node from the cluster
func (c *Client) DrainNode(nodeName string) (string, error) {
log.Debug().
Str("context", "libkubectl").
Str("node_name", nodeName).
Msg("Starting node drain operation")
buf := new(bytes.Buffer)
// Get clientset from factory
clientset, err := c.factory.KubernetesClientSet()
if err != nil {
log.Error().
Str("context", "libkubectl").
Str("node_name", nodeName).
Err(err).
Msg("Failed to get kubernetes clientset for node drain")
return "", fmt.Errorf("failed to get kubernetes clientset for node drain: %w", err)
}
log.Debug().
Str("context", "libkubectl").
Str("node_name", nodeName).
Msg("Successfully obtained kubernetes clientset")
drainer := &drain.Helper{
Ctx: context.Background(),
Client: clientset,
Force: false, // Don't force delete standalone pods
GracePeriodSeconds: -1, // Use pod's own grace period
IgnoreAllDaemonSets: true, // Skip DaemonSet pods
Timeout: 60 * time.Second, // Overall timeout
DeleteEmptyDirData: true, // Delete pods with emptyDir
DisableEviction: false, // Use eviction API when possible
Out: buf,
ErrOut: buf,
DryRunStrategy: util.DryRunNone,
}
log.Debug().
Str("context", "libkubectl").
Str("node_name", nodeName).
Bool("force", drainer.Force).
Bool("ignore_daemon_sets", drainer.IgnoreAllDaemonSets).
Dur("timeout", drainer.Timeout).
Msg("Configured drain helper")
// Get the node first
node, err := clientset.CoreV1().Nodes().Get(context.Background(), nodeName, metav1.GetOptions{})
if err != nil {
log.Error().
Str("context", "libkubectl").
Str("node_name", nodeName).
Err(err).
Msg("Failed to retrieve node for drain operation")
return "", fmt.Errorf("failed to get node %s for drain operation: %w", nodeName, err)
}
log.Debug().
Str("context", "libkubectl").
Str("node_name", nodeName).
Msg("Successfully retrieved node, proceeding to cordon")
// First cordon the node
if err := drain.RunCordonOrUncordon(drainer, node, true); err != nil {
log.Error().
Str("context", "libkubectl").
Str("node_name", nodeName).
Err(err).
Msg("Failed to cordon node during drain operation")
return "", fmt.Errorf("failed to cordon node %s during drain operation: %w", nodeName, err)
}
log.Debug().
Str("context", "libkubectl").
Str("node_name", nodeName).
Msg("Successfully cordoned node, proceeding to drain")
// Then drain it
if err := drain.RunNodeDrain(drainer, nodeName); err != nil {
log.Error().
Str("context", "libkubectl").
Str("node_name", nodeName).
Err(err).
Msg("Failed to drain node")
return "", fmt.Errorf("failed to drain node %s: %w", nodeName, err)
}
log.Debug().
Str("context", "libkubectl").
Str("node_name", nodeName).
Msg("Successfully completed node drain operation")
return buf.String(), nil
}