mirror of
https://github.com/TeaOSLab/EdgeAPI.git
synced 2025-11-03 15:00:27 +08:00
阶段性提交
This commit is contained in:
168
internal/installers/installer_base.go
Normal file
168
internal/installers/installer_base.go
Normal file
@@ -0,0 +1,168 @@
|
||||
package installers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"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
|
||||
}
|
||||
|
||||
// 登录SSH服务
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// 认证
|
||||
methods := []ssh.AuthMethod{}
|
||||
if len(credentials.Password) > 0 {
|
||||
{
|
||||
authMethod := ssh.Password(credentials.Password)
|
||||
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 {
|
||||
signer, err := ssh.ParsePrivateKey([]byte(credentials.PrivateKey))
|
||||
if err != nil {
|
||||
return errors.New("parse private key: " + err.Error())
|
||||
}
|
||||
authMethod := ssh.PublicKeys(signer)
|
||||
methods = append(methods, authMethod)
|
||||
}
|
||||
|
||||
// SSH客户端
|
||||
config := &ssh.ClientConfig{
|
||||
User: credentials.Username,
|
||||
Auth: methods,
|
||||
HostKeyCallback: hostKeyCallback,
|
||||
Timeout: 5 * time.Second, // TODO 后期可以设置这个超时时间
|
||||
}
|
||||
|
||||
sshClient, err := ssh.Dial("tcp", credentials.Host+":"+strconv.Itoa(credentials.Port), config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
client, err := NewSSHClient(sshClient)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
this.client = client
|
||||
return nil
|
||||
}
|
||||
|
||||
// 关闭SSH服务
|
||||
func (this *BaseInstaller) Close() error {
|
||||
if this.client != nil {
|
||||
return this.client.Close()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 查找最新的版本的文件
|
||||
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
|
||||
}
|
||||
|
||||
// 上传安装助手
|
||||
func (this *BaseInstaller) InstallHelper(targetDir string) (env *Env, err error) {
|
||||
uname, _, err := this.client.Exec("uname -a")
|
||||
if err != nil {
|
||||
return env, err
|
||||
}
|
||||
|
||||
osName := ""
|
||||
archName := ""
|
||||
if strings.Index(uname, "Darwin") > 0 {
|
||||
osName = "darwin"
|
||||
} else if strings.Index(uname, "Linux") >= 0 {
|
||||
osName = "linux"
|
||||
} else {
|
||||
// TODO 支持freebsd, aix ...
|
||||
return env, errors.New("installer not supported os '" + uname + "'")
|
||||
}
|
||||
|
||||
if strings.Index(uname, "x86_64") > 0 {
|
||||
archName = "amd64"
|
||||
} else {
|
||||
// TODO 支持ARM和MIPS等架构
|
||||
archName = "386"
|
||||
}
|
||||
|
||||
exeName := "installer-helper-" + osName + "-" + archName
|
||||
exePath := Tea.Root + "/installers/" + exeName
|
||||
|
||||
err = this.client.Copy(exePath, targetDir+"/"+exeName, 0777)
|
||||
if err != nil {
|
||||
return env, err
|
||||
}
|
||||
|
||||
env = &Env{
|
||||
OS: osName,
|
||||
Arch: archName,
|
||||
HelperName: exeName,
|
||||
}
|
||||
return env, nil
|
||||
}
|
||||
Reference in New Issue
Block a user