mirror of
https://github.com/usual2970/certimate.git
synced 2025-10-05 14:04:54 +00:00
support ssh deployemnt
This commit is contained in:
@@ -16,6 +16,7 @@ const (
|
||||
const (
|
||||
targetAliyunOss = "aliyun-oss"
|
||||
targetAliyunCdn = "aliyun-cdn"
|
||||
targetSSH = "ssh"
|
||||
)
|
||||
|
||||
type DeployerOption struct {
|
||||
@@ -47,6 +48,8 @@ func Get(record *models.Record) (Deployer, error) {
|
||||
return NewAliyun(option)
|
||||
case targetAliyunCdn:
|
||||
return NewAliyunCdn(option)
|
||||
case targetSSH:
|
||||
return NewSSH(option)
|
||||
}
|
||||
return nil, errors.New("not implemented")
|
||||
}
|
||||
@@ -54,5 +57,8 @@ func Get(record *models.Record) (Deployer, error) {
|
||||
func getProduct(record *models.Record) string {
|
||||
targetType := record.GetString("targetType")
|
||||
rs := strings.Split(targetType, "-")
|
||||
if len(rs) < 2 {
|
||||
return ""
|
||||
}
|
||||
return rs[1]
|
||||
}
|
||||
|
125
internal/deployer/ssh.go
Normal file
125
internal/deployer/ssh.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package deployer
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
xpath "path"
|
||||
|
||||
"github.com/pkg/sftp"
|
||||
sshPkg "golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type ssh struct {
|
||||
option *DeployerOption
|
||||
}
|
||||
|
||||
type sshAccess struct {
|
||||
Host string `json:"host"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Key string `json:"key"`
|
||||
Port string `json:"port"`
|
||||
Command string `json:"command"`
|
||||
CertPath string `json:"certPath"`
|
||||
KeyPath string `json:"keyPath"`
|
||||
}
|
||||
|
||||
func NewSSH(option *DeployerOption) (Deployer, error) {
|
||||
return &ssh{
|
||||
option: option,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *ssh) Deploy(ctx context.Context) error {
|
||||
access := &sshAccess{}
|
||||
if err := json.Unmarshal([]byte(s.option.Access), access); err != nil {
|
||||
return err
|
||||
}
|
||||
// 连接
|
||||
client, err := s.getClient(access)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
// 上传
|
||||
session, err := client.NewSession()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create session: %w", err)
|
||||
}
|
||||
defer session.Close()
|
||||
|
||||
// 上传证书
|
||||
if err := s.upload(client, s.option.Certificate.Certificate, access.CertPath); err != nil {
|
||||
return fmt.Errorf("failed to upload certificate: %w", err)
|
||||
}
|
||||
|
||||
// 上传私钥
|
||||
if err := s.upload(client, s.option.Certificate.PrivateKey, access.KeyPath); err != nil {
|
||||
return fmt.Errorf("failed to upload private key: %w", err)
|
||||
}
|
||||
|
||||
// 执行命令
|
||||
var stdoutBuf bytes.Buffer
|
||||
session.Stdout = &stdoutBuf
|
||||
var stderrBuf bytes.Buffer
|
||||
session.Stderr = &stderrBuf
|
||||
|
||||
if err := session.Run(access.Command); err != nil {
|
||||
return fmt.Errorf("failed to run command: %w, stdout: %s, stderr: %s", err, stdoutBuf.String(), stderrBuf.String())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ssh) upload(client *sshPkg.Client, content, path string) error {
|
||||
|
||||
sftpCli, err := sftp.NewClient(client)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create sftp client: %w", err)
|
||||
}
|
||||
defer sftpCli.Close()
|
||||
|
||||
if err := sftpCli.MkdirAll(xpath.Base(path)); err != nil {
|
||||
return fmt.Errorf("failed to create remote directory: %w", err)
|
||||
}
|
||||
|
||||
file, err := sftpCli.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open remote file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = file.Write([]byte(content))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to write to remote file: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ssh) getClient(access *sshAccess) (*sshPkg.Client, error) {
|
||||
|
||||
var authMethod sshPkg.AuthMethod
|
||||
|
||||
if access.Key != "" {
|
||||
signer, err := sshPkg.ParsePrivateKey([]byte(access.Key))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authMethod = sshPkg.PublicKeys(signer)
|
||||
} else {
|
||||
authMethod = sshPkg.Password(access.Password)
|
||||
}
|
||||
|
||||
return sshPkg.Dial("tcp", fmt.Sprintf("%s:%s", access.Host, access.Port), &sshPkg.ClientConfig{
|
||||
User: access.Username,
|
||||
Auth: []sshPkg.AuthMethod{
|
||||
authMethod,
|
||||
},
|
||||
HostKeyCallback: sshPkg.InsecureIgnoreHostKey(),
|
||||
})
|
||||
}
|
@@ -21,7 +21,11 @@ const (
|
||||
)
|
||||
|
||||
func deploy(ctx context.Context, record *models.Record) error {
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
app.GetApp().Logger().Error("部署失败", "err", r)
|
||||
}
|
||||
}()
|
||||
currRecord, err := app.GetApp().Dao().FindRecordById("domains", record.Id)
|
||||
history := NewHistory(record)
|
||||
defer history.commit()
|
||||
@@ -86,7 +90,7 @@ func deploy(ctx context.Context, record *models.Record) error {
|
||||
history.record(applyPhase, "保存证书成功", nil, true)
|
||||
|
||||
// ############3.部署证书
|
||||
history.record(deployPhase, "开始部署", nil)
|
||||
history.record(deployPhase, "开始部署", nil, false)
|
||||
deployer, err := deployer.Get(currRecord)
|
||||
if err != nil {
|
||||
history.record(deployPhase, "获取deployer失败", err)
|
||||
|
@@ -34,8 +34,8 @@ func NewHistory(record *models.Record) *history {
|
||||
|
||||
func (a *history) record(phase Phase, msg string, err error, pass ...bool) {
|
||||
a.Phase = phase
|
||||
if len(pass) > 0 && pass[0] {
|
||||
a.PhaseSuccess = true
|
||||
if len(pass) > 0 {
|
||||
a.PhaseSuccess = pass[0]
|
||||
}
|
||||
|
||||
errMsg := ""
|
||||
|
Reference in New Issue
Block a user