Files
EdgeAPI/internal/installers/installer_base.go

222 lines
5.4 KiB
Go
Raw Normal View History

2020-09-06 16:19:54 +08:00
package installers
import (
"errors"
2021-07-20 10:55:34 +08:00
"github.com/TeaOSLab/EdgeCommon/pkg/configutils"
"github.com/TeaOSLab/EdgeCommon/pkg/nodeconfigs"
2020-09-06 16:19:54 +08:00
"github.com/iwind/TeaGo/Tea"
stringutil "github.com/iwind/TeaGo/utils/string"
"golang.org/x/crypto/ssh"
"net"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
)
type BaseInstaller struct {
client *SSHClient
}
2021-07-20 10:55:34 +08:00
// Login 登录SSH服务
2020-09-06 16:19:54 +08:00
func (this *BaseInstaller) Login(credentials *Credentials) error {
var hostKeyCallback ssh.HostKeyCallback = nil
// 检查参数
if len(credentials.Host) == 0 {
return errors.New("'host' should not be empty")
}
if credentials.Port <= 0 {
return errors.New("'port' should be greater than 0")
}
if len(credentials.Password) == 0 && len(credentials.PrivateKey) == 0 {
return errors.New("require user 'password' or 'privateKey'")
}
// 不使用known_hosts
if hostKeyCallback == nil {
hostKeyCallback = func(hostname string, remote net.Addr, key ssh.PublicKey) error {
return nil
}
}
// 认证
var methods = []ssh.AuthMethod{}
if credentials.Method == "user" {
2020-09-06 16:19:54 +08:00
{
var authMethod = ssh.Password(credentials.Password)
2020-09-06 16:19:54 +08:00
methods = append(methods, authMethod)
}
{
authMethod := ssh.KeyboardInteractive(func(user, instruction string, questions []string, echos []bool) (answers []string, err error) {
if len(questions) == 0 {
return []string{}, nil
}
return []string{credentials.Password}, nil
})
methods = append(methods, authMethod)
}
} else if credentials.Method == "privateKey" {
2021-11-06 15:31:01 +08:00
var signer ssh.Signer
var err error
if len(credentials.Passphrase) > 0 {
signer, err = ssh.ParsePrivateKeyWithPassphrase([]byte(credentials.PrivateKey), []byte(credentials.Passphrase))
} else {
signer, err = ssh.ParsePrivateKey([]byte(credentials.PrivateKey))
}
2020-09-06 16:19:54 +08:00
if err != nil {
return errors.New("parse private key: " + err.Error())
}
authMethod := ssh.PublicKeys(signer)
methods = append(methods, authMethod)
} else {
return errors.New("invalid method '" + credentials.Method + "'")
2020-09-06 16:19:54 +08:00
}
// SSH客户端
if len(credentials.Username) == 0 {
credentials.Username = "root"
}
var config = &ssh.ClientConfig{
2020-09-06 16:19:54 +08:00
User: credentials.Username,
Auth: methods,
HostKeyCallback: hostKeyCallback,
Timeout: 5 * time.Second, // TODO 后期可以设置这个超时时间
}
2021-07-20 10:55:34 +08:00
sshClient, err := ssh.Dial("tcp", configutils.QuoteIP(credentials.Host)+":"+strconv.Itoa(credentials.Port), config)
2020-09-06 16:19:54 +08:00
if err != nil {
return err
}
client, err := NewSSHClient(sshClient)
if err != nil {
return err
}
2021-12-06 19:27:11 +08:00
if credentials.Sudo {
client.Sudo(credentials.Password)
}
2020-09-06 16:19:54 +08:00
this.client = client
2021-12-06 19:27:11 +08:00
2020-09-06 16:19:54 +08:00
return nil
}
2021-07-20 10:55:34 +08:00
// Close 关闭SSH服务
2020-09-06 16:19:54 +08:00
func (this *BaseInstaller) Close() error {
if this.client != nil {
return this.client.Close()
}
return nil
}
2021-07-20 10:55:34 +08:00
// LookupLatestInstaller 查找最新的版本的文件
2020-09-06 16:19:54 +08:00
func (this *BaseInstaller) LookupLatestInstaller(filePrefix string) (string, error) {
matches, err := filepath.Glob(Tea.Root + Tea.DS + "deploy" + Tea.DS + "*.zip")
if err != nil {
return "", err
}
pattern, err := regexp.Compile(filePrefix + `-v([\d.]+)\.zip`)
if err != nil {
return "", err
}
lastVersion := ""
result := ""
for _, match := range matches {
baseName := filepath.Base(match)
if !pattern.MatchString(baseName) {
continue
}
m := pattern.FindStringSubmatch(baseName)
if len(m) < 2 {
continue
}
version := m[1]
if len(lastVersion) == 0 || stringutil.VersionCompare(version, lastVersion) > 0 {
lastVersion = version
result = match
}
}
return result, nil
}
2021-07-20 10:55:34 +08:00
// InstallHelper 上传安装助手
func (this *BaseInstaller) InstallHelper(targetDir string, role nodeconfigs.NodeRole) (env *Env, err error) {
2022-11-18 15:44:53 +08:00
uname, stderr, err := this.client.Exec("/usr/bin/uname -a")
2020-09-06 16:19:54 +08:00
if err != nil {
return env, err
}
2021-12-06 19:27:11 +08:00
if len(uname) == 0 {
2022-11-18 15:44:53 +08:00
return nil, errors.New("unable to execute 'uname -a' on this system: " + stderr)
2021-12-06 19:27:11 +08:00
}
2020-09-06 16:19:54 +08:00
osName := ""
archName := ""
if strings.Contains(uname, "Darwin") {
2020-09-06 16:19:54 +08:00
osName = "darwin"
} else if strings.Contains(uname, "Linux") {
2020-09-06 16:19:54 +08:00
osName = "linux"
} else {
// TODO 支持freebsd, aix ...
return env, errors.New("installer not supported os '" + uname + "'")
}
if strings.Contains(uname, "aarch64") || strings.Contains(uname, "armv8") {
archName = "arm64"
} else if strings.Contains(uname, "aarch64_be") {
archName = "arm64be"
} else if strings.Contains(uname, "mips64el") {
archName = "mips64le"
} else if strings.Contains(uname, "mips64") {
archName = "mips64"
} else if strings.Contains(uname, "x86_64") {
2020-09-06 16:19:54 +08:00
archName = "amd64"
} else {
archName = "386"
}
2022-11-18 15:44:53 +08:00
var exeName = "edge-installer-helper-" + osName + "-" + archName
switch role {
case nodeconfigs.NodeRoleDNS:
exeName = "edge-installer-dns-helper-" + osName + "-" + archName
}
2022-11-18 15:44:53 +08:00
var exePath = Tea.Root + "/installers/" + exeName
2020-09-06 16:19:54 +08:00
2022-11-18 15:44:53 +08:00
var realHelperPath = ""
var firstCopyErr error
for _, path := range []string{
targetDir + "/" + exeName,
this.client.UserHome() + "/" + exeName,
"/tmp/" + exeName,
} {
err = this.client.Copy(exePath, path, 0777)
if err != nil {
if firstCopyErr == nil {
firstCopyErr = err
}
} else {
err = nil
firstCopyErr = nil
realHelperPath = path
break
}
}
if firstCopyErr != nil {
return env, errors.New("copy '" + exeName + "' to '" + targetDir + "' failed: " + firstCopyErr.Error())
2020-09-06 16:19:54 +08:00
}
env = &Env{
OS: osName,
Arch: archName,
2022-11-18 15:44:53 +08:00
HelperPath: realHelperPath,
2020-09-06 16:19:54 +08:00
}
return env, nil
}