add `sudo_password` to use sudo mod. (#2402)

* fix: graceful delete runtime dir.

Signed-off-by: joyceliu <joyceliu@yunify.com>

* fix: graceful delete runtime dir.

Signed-off-by: joyceliu <joyceliu@yunify.com>

* fix: add `sudo` and SHELL in connector.

Signed-off-by: joyceliu <joyceliu@yunify.com>

---------

Signed-off-by: joyceliu <joyceliu@yunify.com>
Co-authored-by: joyceliu <joyceliu@yunify.com>
This commit is contained in:
liujian 2024-09-11 15:38:59 +08:00 committed by GitHub
parent d1026e15a5
commit 289a6b0659
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 79 additions and 27 deletions

View File

@ -36,7 +36,7 @@ const (
connectedKubernetes = "kubernetes"
)
var shell = commandShell()
var localShell = commandShell()
// Connector is the interface for connecting to a remote host
type Connector interface {

View File

@ -114,7 +114,7 @@ func (c *kubernetesConnector) PutFile(_ context.Context, src []byte, dst string,
func (c *kubernetesConnector) FetchFile(ctx context.Context, src string, dst io.Writer) error {
// add "--kubeconfig" to src command
klog.V(5).InfoS("exec local command", "cmd", src)
command := c.Cmd.CommandContext(ctx, "/bin/sh", "-c", src)
command := c.Cmd.CommandContext(ctx, localShell, "-c", src)
command.SetDir(c.homeDir)
command.SetEnv([]string{"KUBECONFIG=" + filepath.Join(c.homeDir, kubeconfigRelPath)})
command.SetStdout(dst)
@ -127,7 +127,7 @@ func (c *kubernetesConnector) FetchFile(ctx context.Context, src string, dst io.
func (c *kubernetesConnector) ExecuteCommand(ctx context.Context, cmd string) ([]byte, error) {
// add "--kubeconfig" to src command
klog.V(5).InfoS("exec local command", "cmd", cmd)
command := c.Cmd.CommandContext(ctx, shell, "-c", cmd)
command := c.Cmd.CommandContext(ctx, localShell, "-c", cmd)
command.SetDir(c.homeDir)
command.SetEnv([]string{"KUBECONFIG=" + filepath.Join(c.homeDir, kubeconfigRelPath)})

View File

@ -28,7 +28,6 @@ import (
"k8s.io/klog/v2"
"k8s.io/utils/exec"
"k8s.io/utils/ptr"
_const "github.com/kubesphere/kubekey/v4/pkg/const"
"github.com/kubesphere/kubekey/v4/pkg/variable"
@ -38,17 +37,16 @@ var _ Connector = &localConnector{}
var _ GatherFacts = &localConnector{}
func newLocalConnector(connectorVars map[string]any) *localConnector {
sudo, err := variable.BoolVar(nil, connectorVars, _const.VariableConnectorSudo)
sudo, err := variable.StringVar(nil, connectorVars, _const.VariableConnectorSudoPassword)
if err != nil {
klog.InfoS("get connector sudo failed use default port 22", "error", err)
sudo = ptr.To(true)
klog.V(4).InfoS("get connector sudo password failed, execute command without sudo", "error", err)
}
return &localConnector{Sudo: *sudo, Cmd: exec.New()}
return &localConnector{Sudo: sudo, Cmd: exec.New()}
}
type localConnector struct {
Sudo bool
Sudo string
Cmd exec.Interface
}
@ -97,11 +95,16 @@ func (c *localConnector) FetchFile(_ context.Context, src string, dst io.Writer)
// ExecuteCommand in local host
func (c *localConnector) ExecuteCommand(ctx context.Context, cmd string) ([]byte, error) {
klog.V(5).InfoS("exec local command", "cmd", cmd)
if c.Sudo {
return c.Cmd.CommandContext(ctx, "sudo", "-E", shell, "-c", cmd).CombinedOutput()
// find command interpreter in env. default /bin/bash
if c.Sudo != "" {
command := c.Cmd.CommandContext(ctx, "sudo", "-E", localShell, "-c", cmd)
command.SetStdin(bytes.NewBufferString(c.Sudo + "\n"))
return command.CombinedOutput()
}
return c.Cmd.CommandContext(ctx, shell, "-c", cmd).CombinedOutput()
return c.Cmd.CommandContext(ctx, localShell, "-c", cmd).CombinedOutput()
}
// HostInfo for GatherFacts

View File

@ -26,6 +26,7 @@ import (
"os"
"os/user"
"path/filepath"
"strings"
"time"
"github.com/pkg/sftp"
@ -38,8 +39,9 @@ import (
)
const (
defaultSSHPort = 22
defaultSSHUser = "root"
defaultSSHPort = 22
defaultSSHUser = "root"
defaultSSHSHELL = "/bin/bash"
)
var defaultSSHPrivateKey string
@ -56,16 +58,15 @@ var _ Connector = &sshConnector{}
var _ GatherFacts = &sshConnector{}
func newSSHConnector(host string, connectorVars map[string]any) *sshConnector {
sudo, err := variable.BoolVar(nil, connectorVars, _const.VariableConnectorSudo)
sudo, err := variable.StringVar(nil, connectorVars, _const.VariableConnectorSudoPassword)
if err != nil {
klog.InfoS("get connector sudo failed use default port 22", "error", err)
sudo = ptr.To(true)
klog.V(4).InfoS("get connector sudo password failed, execute command without sudo", "error", err)
}
// get host in connector variable. if empty, set default host: host_name.
hostParam, err := variable.StringVar(nil, connectorVars, _const.VariableConnectorHost)
if err != nil {
klog.InfoS("get connector host failed use current hostname", "error", err)
klog.V(4).InfoS("get connector host failed use current hostname", "error", err)
hostParam = host
}
// get port in connector variable. if empty, set default port: 22.
@ -93,23 +94,27 @@ func newSSHConnector(host string, connectorVars map[string]any) *sshConnector {
}
return &sshConnector{
Sudo: *sudo,
Sudo: sudo,
Host: hostParam,
Port: *portParam,
User: userParam,
Password: passwdParam,
PrivateKey: keyParam,
shell: defaultSSHSHELL,
}
}
type sshConnector struct {
Sudo bool
Sudo string
Host string
Port int
User string
Password string
PrivateKey string
client *ssh.Client
client *ssh.Client
// shell to execute command
shell string
}
// Init connector, get ssh.Client
@ -147,6 +152,22 @@ func (c *sshConnector) Init(context.Context) error {
}
c.client = sshClient
// get shell from env
session, err := sshClient.NewSession()
if err != nil {
return fmt.Errorf("create session error: %w", err)
}
defer session.Close()
output, err := session.CombinedOutput("echo $SHELL")
if err != nil {
return fmt.Errorf("env command error: %w", err)
}
if strings.TrimSuffix(string(output), "\n") != "" {
c.shell = strings.TrimSuffix(string(output), "\n")
}
return nil
}
@ -231,11 +252,39 @@ func (c *sshConnector) ExecuteCommand(_ context.Context, cmd string) ([]byte, er
}
defer session.Close()
if c.Sudo {
return session.CombinedOutput(fmt.Sprintf("sudo -E %s -c \"%q\"", shell, cmd))
if c.Sudo != "" {
cmd = fmt.Sprintf("sudo -E %s -c \"%q\"", c.shell, cmd)
// get pipe from session
stdin, _ := session.StdinPipe()
stdout, _ := session.StdoutPipe()
stderr, _ := session.StderrPipe()
// Request a pseudo-terminal (required for sudo password input)
if err := session.RequestPty("xterm", 80, 40, ssh.TerminalModes{}); err != nil {
return nil, err
}
// Start the remote command
if err := session.Start(cmd); err != nil {
return nil, err
}
// Write sudo password to the standard input
if _, err := io.WriteString(stdin, c.Sudo+"\n"); err != nil {
return nil, err
}
// Read the command output
output := make([]byte, 0)
stdoutData, _ := io.ReadAll(stdout)
stderrData, _ := io.ReadAll(stderr)
output = append(output, stdoutData...)
output = append(output, stderrData...)
// Wait for the command to complete
if err := session.Wait(); err != nil {
return nil, err
}
return output, nil
}
return session.CombinedOutput(fmt.Sprintf("%s -c \"%q\"", shell, cmd))
return session.CombinedOutput(fmt.Sprintf("%s -c \"%q\"", c.shell, cmd))
}
// HostInfo for GatherFacts

View File

@ -30,8 +30,8 @@ const ( // === From inventory ===
VariableConnector = "connector"
// VariableConnectorType is connected type for VariableConnector.
VariableConnectorType = "type"
// VariableConnectorSudo is connected address for VariableConnector.
VariableConnectorSudo = "sudo"
// VariableConnectorSudoPassword is connected address for VariableConnector.
VariableConnectorSudoPassword = "sudo_password"
// VariableConnectorHost is connected address for VariableConnector.
VariableConnectorHost = "host"
// VariableConnectorPort is connected address for VariableConnector.

View File

@ -168,7 +168,7 @@ func (e pipelineExecutor) execBatchHosts(ctx context.Context, play kkprojectv1.P
option: e.option,
hosts: serials,
ignoreErrors: play.IgnoreErrors,
blocks: play.Tasks,
blocks: play.PostTasks,
tags: play.Taggable,
}.Exec(ctx)); err != nil {
return fmt.Errorf("execute post-tasks error: %w", err)