mirror of https://github.com/portainer/portainer
110 lines
3.1 KiB
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
|
|
}
|