mirror of
https://github.com/usual2970/certimate.git
synced 2025-06-08 05:29:51 +00:00
feat: implement local, ssh, webhook Deployer
This commit is contained in:
parent
aa7fb7da06
commit
6367785b1b
@ -6,8 +6,8 @@ type AliyunAccess struct {
|
||||
}
|
||||
|
||||
type ByteplusAccess struct {
|
||||
AccessKey string
|
||||
SecretKey string
|
||||
AccessKey string `json:"accessKey"`
|
||||
SecretKey string `json:"secretKey"`
|
||||
}
|
||||
|
||||
type TencentAccess struct {
|
||||
@ -27,9 +27,9 @@ type BaiduCloudAccess struct {
|
||||
}
|
||||
|
||||
type AwsAccess struct {
|
||||
Region string `json:"region"`
|
||||
AccessKeyId string `json:"accessKeyId"`
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
Region string `json:"region"`
|
||||
HostedZoneId string `json:"hostedZoneId"`
|
||||
}
|
||||
|
||||
@ -62,8 +62,8 @@ type PdnsAccess struct {
|
||||
}
|
||||
|
||||
type VolcengineAccess struct {
|
||||
AccessKeyID string
|
||||
SecretAccessKey string
|
||||
AccessKeyID string `json:"accessKeyId"`
|
||||
SecretAccessKey string `json:"secretAccessKey"`
|
||||
}
|
||||
|
||||
type HttpreqAccess struct {
|
||||
|
107
internal/pkg/core/deployer/logger.go
Normal file
107
internal/pkg/core/deployer/logger.go
Normal file
@ -0,0 +1,107 @@
|
||||
package deployer
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 表示定义证书部署器的日志记录器的抽象类型接口。
|
||||
type Logger interface {
|
||||
// 追加一条日志记录。
|
||||
// 该方法会将 `data` 以 JSON 序列化后拼接到 `tag` 结尾。
|
||||
//
|
||||
// 入参:
|
||||
// - tag:标签。
|
||||
// - data:数据。
|
||||
Appendt(tag string, data ...any)
|
||||
|
||||
// 追加一条日志记录。
|
||||
// 该方法会将 `args` 以 `format` 格式化。
|
||||
//
|
||||
// 入参:
|
||||
// - format:格式化字符串。
|
||||
// - args:格式化参数。
|
||||
Appendf(format string, args ...any)
|
||||
|
||||
// 获取所有日志记录。
|
||||
GetRecords() []string
|
||||
}
|
||||
|
||||
// 表示默认的日志记录器类型。
|
||||
type DefaultLogger struct {
|
||||
records []string
|
||||
}
|
||||
|
||||
var _ Logger = (*DefaultLogger)(nil)
|
||||
|
||||
func (l *DefaultLogger) Appendt(tag string, data ...any) {
|
||||
l.ensureInitialized()
|
||||
|
||||
temp := make([]string, len(data)+1)
|
||||
temp[0] = tag
|
||||
for i, v := range data {
|
||||
s := ""
|
||||
if v != nil {
|
||||
switch reflect.ValueOf(v).Kind() {
|
||||
case reflect.String:
|
||||
s = v.(string)
|
||||
case reflect.Bool,
|
||||
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
|
||||
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
|
||||
reflect.Float32, reflect.Float64:
|
||||
s = fmt.Sprintf("%v", v)
|
||||
default:
|
||||
jsonData, _ := json.Marshal(v)
|
||||
s = string(jsonData)
|
||||
}
|
||||
}
|
||||
|
||||
temp[i+1] = s
|
||||
}
|
||||
|
||||
l.records = append(l.records, strings.Join(temp, ": "))
|
||||
}
|
||||
|
||||
func (l *DefaultLogger) Appendf(format string, args ...any) {
|
||||
l.ensureInitialized()
|
||||
|
||||
l.records = append(l.records, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
func (l *DefaultLogger) GetRecords() []string {
|
||||
l.ensureInitialized()
|
||||
|
||||
temp := make([]string, len(l.records))
|
||||
copy(temp, l.records)
|
||||
return temp
|
||||
}
|
||||
|
||||
func (l *DefaultLogger) ensureInitialized() {
|
||||
if l.records == nil {
|
||||
l.records = make([]string, 0)
|
||||
}
|
||||
}
|
||||
|
||||
func NewDefaultLogger() *DefaultLogger {
|
||||
return &DefaultLogger{
|
||||
records: make([]string, 0),
|
||||
}
|
||||
}
|
||||
|
||||
// 表示空的日志记录器类型。
|
||||
// 该日志记录器不会执行任何操作。
|
||||
type NilLogger struct{}
|
||||
|
||||
var _ Logger = (*NilLogger)(nil)
|
||||
|
||||
func (l *NilLogger) Appendt(string, ...any) {}
|
||||
func (l *NilLogger) Appendf(string, ...any) {}
|
||||
func (l *NilLogger) GetRecords() []string {
|
||||
return make([]string, 0)
|
||||
}
|
||||
|
||||
func NewNilLogger() *NilLogger {
|
||||
return &NilLogger{}
|
||||
}
|
17
internal/pkg/core/deployer/providers/local/define.go
Normal file
17
internal/pkg/core/deployer/providers/local/define.go
Normal file
@ -0,0 +1,17 @@
|
||||
package local
|
||||
|
||||
type OutputFormatType string
|
||||
|
||||
const (
|
||||
OUTPUT_FORMAT_PEM = OutputFormatType("PEM")
|
||||
OUTPUT_FORMAT_PFX = OutputFormatType("PFX")
|
||||
OUTPUT_FORMAT_JKS = OutputFormatType("JKS")
|
||||
)
|
||||
|
||||
type ShellEnvType string
|
||||
|
||||
const (
|
||||
SHELL_ENV_SH = ShellEnvType("sh")
|
||||
SHELL_ENV_CMD = ShellEnvType("cmd")
|
||||
SHELL_ENV_POWERSHELL = ShellEnvType("powershell")
|
||||
)
|
177
internal/pkg/core/deployer/providers/local/local.go
Normal file
177
internal/pkg/core/deployer/providers/local/local.go
Normal file
@ -0,0 +1,177 @@
|
||||
package local
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
|
||||
xerrors "github.com/pkg/errors"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||
"github.com/usual2970/certimate/internal/pkg/utils/fs"
|
||||
"github.com/usual2970/certimate/internal/pkg/utils/x509"
|
||||
)
|
||||
|
||||
type LocalDeployerConfig struct {
|
||||
// Shell 执行环境。
|
||||
// 空值时默认根据操作系统决定。
|
||||
ShellEnv ShellEnvType `json:"shellEnv,omitempty"`
|
||||
// 前置命令。
|
||||
PreCommand string `json:"preCommand,omitempty"`
|
||||
// 后置命令。
|
||||
PostCommand string `json:"postCommand,omitempty"`
|
||||
// 输出证书格式。
|
||||
// 空值时默认为 [OUTPUT_FORMAT_PEM]。
|
||||
OutputFormat OutputFormatType `json:"outputFormat,omitempty"`
|
||||
// 输出证书文件路径。
|
||||
OutputCertPath string `json:"outputCertPath,omitempty"`
|
||||
// 输出私钥文件路径。
|
||||
OutputKeyPath string `json:"outputKeyPath,omitempty"`
|
||||
// PFX 导出密码。
|
||||
// 证书格式为 PFX 时必填。
|
||||
PfxPassword string `json:"pfxPassword,omitempty"`
|
||||
// JKS 别名。
|
||||
// 证书格式为 JKS 时必填。
|
||||
JksAlias string `json:"jksAlias,omitempty"`
|
||||
// JKS 密钥密码。
|
||||
// 证书格式为 JKS 时必填。
|
||||
JksKeypass string `json:"jksKeypass,omitempty"`
|
||||
// JKS 存储密码。
|
||||
// 证书格式为 JKS 时必填。
|
||||
JksStorepass string `json:"jksStorepass,omitempty"`
|
||||
}
|
||||
|
||||
type LocalDeployer struct {
|
||||
config *LocalDeployerConfig
|
||||
logger deployer.Logger
|
||||
}
|
||||
|
||||
func New(config *LocalDeployerConfig) (*LocalDeployer, error) {
|
||||
return NewWithLogger(config, deployer.NewNilLogger())
|
||||
}
|
||||
|
||||
func NewWithLogger(config *LocalDeployerConfig, logger deployer.Logger) (*LocalDeployer, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("config is nil")
|
||||
}
|
||||
|
||||
if logger == nil {
|
||||
return nil, errors.New("logger is nil")
|
||||
}
|
||||
|
||||
return &LocalDeployer{
|
||||
logger: logger,
|
||||
config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *LocalDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
||||
// 执行前置命令
|
||||
if d.config.PreCommand != "" {
|
||||
stdout, stderr, err := execCommand(d.config.ShellEnv, d.config.PreCommand)
|
||||
if err != nil {
|
||||
return nil, xerrors.Wrapf(err, "failed to run pre-command, stdout: %s, stderr: %s", stdout, stderr)
|
||||
}
|
||||
|
||||
d.logger.Appendt("pre-command executed", stdout)
|
||||
}
|
||||
|
||||
// 写入证书和私钥文件
|
||||
switch d.config.OutputFormat {
|
||||
case "", OUTPUT_FORMAT_PEM:
|
||||
if err := fs.WriteFileString(d.config.OutputCertPath, certPem); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d.logger.Appendt("certificate file saved")
|
||||
|
||||
if err := fs.WriteFileString(d.config.OutputKeyPath, privkeyPem); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d.logger.Appendt("private key file saved")
|
||||
|
||||
case OUTPUT_FORMAT_PFX:
|
||||
pfxData, err := x509.TransformCertificateFromPEMToPFX(certPem, privkeyPem, d.config.PfxPassword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d.logger.Appendt("certificate transformed to PFX")
|
||||
|
||||
if err := fs.WriteFile(d.config.OutputCertPath, pfxData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d.logger.Appendt("certificate file saved")
|
||||
|
||||
case OUTPUT_FORMAT_JKS:
|
||||
jksData, err := x509.TransformCertificateFromPEMToJKS(certPem, privkeyPem, d.config.JksAlias, d.config.JksKeypass, d.config.JksStorepass)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d.logger.Appendt("certificate transformed to JKS")
|
||||
|
||||
if err := fs.WriteFile(d.config.OutputCertPath, jksData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d.logger.Appendt("certificate file uploaded")
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported output format: %s", d.config.OutputFormat)
|
||||
}
|
||||
|
||||
// 执行后置命令
|
||||
if d.config.PostCommand != "" {
|
||||
stdout, stderr, err := execCommand(d.config.ShellEnv, d.config.PostCommand)
|
||||
if err != nil {
|
||||
return nil, xerrors.Wrapf(err, "failed to run command, stdout: %s, stderr: %s", stdout, stderr)
|
||||
}
|
||||
|
||||
d.logger.Appendt("post-command executed", stdout)
|
||||
}
|
||||
|
||||
return &deployer.DeployResult{}, nil
|
||||
}
|
||||
|
||||
func execCommand(shellEnv ShellEnvType, command string) (string, string, error) {
|
||||
var cmd *exec.Cmd
|
||||
|
||||
switch shellEnv {
|
||||
case SHELL_ENV_SH:
|
||||
cmd = exec.Command("sh", "-c", command)
|
||||
|
||||
case SHELL_ENV_CMD:
|
||||
cmd = exec.Command("cmd", "/C", command)
|
||||
|
||||
case SHELL_ENV_POWERSHELL:
|
||||
cmd = exec.Command("powershell", "-Command", command)
|
||||
|
||||
case "":
|
||||
if runtime.GOOS == "windows" {
|
||||
cmd = exec.Command("cmd", "/C", command)
|
||||
} else {
|
||||
cmd = exec.Command("sh", "-c", command)
|
||||
}
|
||||
|
||||
default:
|
||||
return "", "", fmt.Errorf("unsupported shell env: %s", shellEnv)
|
||||
}
|
||||
|
||||
var stdoutBuf bytes.Buffer
|
||||
cmd.Stdout = &stdoutBuf
|
||||
var stderrBuf bytes.Buffer
|
||||
cmd.Stderr = &stderrBuf
|
||||
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return "", "", xerrors.Wrap(err, "failed to execute shell command")
|
||||
}
|
||||
|
||||
return stdoutBuf.String(), stderrBuf.String(), nil
|
||||
}
|
9
internal/pkg/core/deployer/providers/ssh/define.go
Normal file
9
internal/pkg/core/deployer/providers/ssh/define.go
Normal file
@ -0,0 +1,9 @@
|
||||
package ssh
|
||||
|
||||
type OutputFormatType string
|
||||
|
||||
const (
|
||||
OUTPUT_FORMAT_PEM = OutputFormatType("PEM")
|
||||
OUTPUT_FORMAT_PFX = OutputFormatType("PFX")
|
||||
OUTPUT_FORMAT_JKS = OutputFormatType("JKS")
|
||||
)
|
251
internal/pkg/core/deployer/providers/ssh/ssh.go
Normal file
251
internal/pkg/core/deployer/providers/ssh/ssh.go
Normal file
@ -0,0 +1,251 @@
|
||||
package ssh
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
xerrors "github.com/pkg/errors"
|
||||
"github.com/pkg/sftp"
|
||||
"golang.org/x/crypto/ssh"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||
"github.com/usual2970/certimate/internal/pkg/utils/x509"
|
||||
)
|
||||
|
||||
type SshDeployerConfig struct {
|
||||
// SSH 主机。
|
||||
// 空值时默认为 "localhost"。
|
||||
SshHost string `json:"sshHost,omitempty"`
|
||||
// SSH 端口。
|
||||
// 空值时默认为 22。
|
||||
SshPort int32 `json:"sshPort,omitempty"`
|
||||
// SSH 登录用户名。
|
||||
SshUsername string `json:"sshUsername,omitempty"`
|
||||
// SSH 登录密码。
|
||||
SshPassword string `json:"sshPassword,omitempty"`
|
||||
// SSH 登录私钥。
|
||||
SshKey string `json:"sshKey,omitempty"`
|
||||
// SSH 登录私钥口令。
|
||||
SshKeyPassphrase string `json:"sshKeyPassphrase,omitempty"`
|
||||
// 前置命令。
|
||||
PreCommand string `json:"preCommand,omitempty"`
|
||||
// 后置命令。
|
||||
PostCommand string `json:"postCommand,omitempty"`
|
||||
// 输出证书格式。
|
||||
// 空值时默认为 [OUTPUT_FORMAT_PEM]。
|
||||
OutputFormat OutputFormatType `json:"outputFormat,omitempty"`
|
||||
// 输出证书文件路径。
|
||||
OutputCertPath string `json:"outputCertPath,omitempty"`
|
||||
// 输出私钥文件路径。
|
||||
OutputKeyPath string `json:"outputKeyPath,omitempty"`
|
||||
// PFX 导出密码。
|
||||
// 证书格式为 PFX 时必填。
|
||||
PfxPassword string `json:"pfxPassword,omitempty"`
|
||||
// JKS 别名。
|
||||
// 证书格式为 JKS 时必填。
|
||||
JksAlias string `json:"jksAlias,omitempty"`
|
||||
// JKS 密钥密码。
|
||||
// 证书格式为 JKS 时必填。
|
||||
JksKeypass string `json:"jksKeypass,omitempty"`
|
||||
// JKS 存储密码。
|
||||
// 证书格式为 JKS 时必填。
|
||||
JksStorepass string `json:"jksStorepass,omitempty"`
|
||||
}
|
||||
|
||||
type SshDeployer struct {
|
||||
config *SshDeployerConfig
|
||||
logger deployer.Logger
|
||||
}
|
||||
|
||||
func New(config *SshDeployerConfig) (*SshDeployer, error) {
|
||||
return NewWithLogger(config, deployer.NewNilLogger())
|
||||
}
|
||||
|
||||
func NewWithLogger(config *SshDeployerConfig, logger deployer.Logger) (*SshDeployer, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("config is nil")
|
||||
}
|
||||
|
||||
if logger == nil {
|
||||
return nil, errors.New("logger is nil")
|
||||
}
|
||||
|
||||
return &SshDeployer{
|
||||
logger: logger,
|
||||
config: config,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *SshDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
||||
// 连接
|
||||
client, err := createSshClient(
|
||||
d.config.SshHost,
|
||||
d.config.SshPort,
|
||||
d.config.SshUsername,
|
||||
d.config.SshPassword,
|
||||
d.config.SshKey,
|
||||
d.config.SshKeyPassphrase,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer client.Close()
|
||||
|
||||
d.logger.Appendt("SSH connected")
|
||||
|
||||
// 执行前置命令
|
||||
if d.config.PreCommand != "" {
|
||||
stdout, stderr, err := execSshCommand(client, d.config.PreCommand)
|
||||
if err != nil {
|
||||
return nil, xerrors.Wrapf(err, "failed to run pre-command: stdout: %s, stderr: %s", stdout, stderr)
|
||||
}
|
||||
|
||||
d.logger.Appendt("SSH pre-command executed", stdout)
|
||||
}
|
||||
|
||||
// 上传证书和私钥文件
|
||||
switch d.config.OutputFormat {
|
||||
case "", OUTPUT_FORMAT_PEM:
|
||||
if err := writeSftpFileString(client, d.config.OutputCertPath, certPem); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d.logger.Appendt("certificate file uploaded")
|
||||
|
||||
if err := writeSftpFileString(client, d.config.OutputKeyPath, privkeyPem); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d.logger.Appendt("private key file uploaded")
|
||||
|
||||
case OUTPUT_FORMAT_PFX:
|
||||
pfxData, err := x509.TransformCertificateFromPEMToPFX(certPem, privkeyPem, d.config.PfxPassword)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d.logger.Appendt("certificate transformed to PFX")
|
||||
|
||||
if err := writeSftpFile(client, d.config.OutputCertPath, pfxData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d.logger.Appendt("certificate file uploaded")
|
||||
|
||||
case OUTPUT_FORMAT_JKS:
|
||||
jksData, err := x509.TransformCertificateFromPEMToJKS(certPem, privkeyPem, d.config.JksAlias, d.config.JksKeypass, d.config.JksStorepass)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d.logger.Appendt("certificate transformed to JKS")
|
||||
|
||||
if err := writeSftpFile(client, d.config.OutputCertPath, jksData); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d.logger.Appendt("certificate file uploaded")
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported output format: %s", d.config.OutputFormat)
|
||||
}
|
||||
|
||||
// 执行后置命令
|
||||
if d.config.PostCommand != "" {
|
||||
stdout, stderr, err := execSshCommand(client, d.config.PostCommand)
|
||||
if err != nil {
|
||||
return nil, xerrors.Wrapf(err, "failed to run command, stdout: %s, stderr: %s", stdout, stderr)
|
||||
}
|
||||
|
||||
d.logger.Appendt("SSH post-command executed", stdout)
|
||||
}
|
||||
|
||||
return &deployer.DeployResult{}, nil
|
||||
}
|
||||
|
||||
func createSshClient(host string, port int32, username string, password string, key string, keyPassphrase string) (*ssh.Client, error) {
|
||||
if host == "" {
|
||||
host = "localhost"
|
||||
}
|
||||
|
||||
if port == 0 {
|
||||
port = 22
|
||||
}
|
||||
|
||||
var authMethod ssh.AuthMethod
|
||||
if key != "" {
|
||||
var signer ssh.Signer
|
||||
var err error
|
||||
|
||||
if keyPassphrase != "" {
|
||||
signer, err = ssh.ParsePrivateKeyWithPassphrase([]byte(key), []byte(keyPassphrase))
|
||||
} else {
|
||||
signer, err = ssh.ParsePrivateKey([]byte(key))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
authMethod = ssh.PublicKeys(signer)
|
||||
} else {
|
||||
authMethod = ssh.Password(password)
|
||||
}
|
||||
|
||||
return ssh.Dial("tcp", fmt.Sprintf("%s:%d", host, port), &ssh.ClientConfig{
|
||||
User: username,
|
||||
Auth: []ssh.AuthMethod{authMethod},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
})
|
||||
}
|
||||
|
||||
func execSshCommand(sshCli *ssh.Client, command string) (string, string, error) {
|
||||
session, err := sshCli.NewSession()
|
||||
if err != nil {
|
||||
return "", "", xerrors.Wrap(err, "failed to create ssh session")
|
||||
}
|
||||
|
||||
defer session.Close()
|
||||
var stdoutBuf bytes.Buffer
|
||||
session.Stdout = &stdoutBuf
|
||||
var stderrBuf bytes.Buffer
|
||||
session.Stderr = &stderrBuf
|
||||
err = session.Run(command)
|
||||
if err != nil {
|
||||
return "", "", xerrors.Wrap(err, "failed to execute ssh command")
|
||||
}
|
||||
|
||||
return stdoutBuf.String(), stderrBuf.String(), nil
|
||||
}
|
||||
|
||||
func writeSftpFileString(sshCli *ssh.Client, path string, content string) error {
|
||||
return writeSftpFile(sshCli, path, []byte(content))
|
||||
}
|
||||
|
||||
func writeSftpFile(sshCli *ssh.Client, path string, data []byte) error {
|
||||
sftpCli, err := sftp.NewClient(sshCli)
|
||||
if err != nil {
|
||||
return xerrors.Wrap(err, "failed to create sftp client")
|
||||
}
|
||||
defer sftpCli.Close()
|
||||
|
||||
if err := sftpCli.MkdirAll(filepath.Dir(path)); err != nil {
|
||||
return xerrors.Wrap(err, "failed to create remote directory")
|
||||
}
|
||||
|
||||
file, err := sftpCli.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC)
|
||||
if err != nil {
|
||||
return xerrors.Wrap(err, "failed to open remote file")
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = file.Write(data)
|
||||
if err != nil {
|
||||
return xerrors.Wrap(err, "failed to write to remote file")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
81
internal/pkg/core/deployer/providers/webhook/webhook.go
Normal file
81
internal/pkg/core/deployer/providers/webhook/webhook.go
Normal file
@ -0,0 +1,81 @@
|
||||
package webhook
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
xerrors "github.com/pkg/errors"
|
||||
|
||||
"github.com/usual2970/certimate/internal/pkg/core/deployer"
|
||||
"github.com/usual2970/certimate/internal/pkg/utils/x509"
|
||||
xhttp "github.com/usual2970/certimate/internal/utils/http"
|
||||
)
|
||||
|
||||
type WebhookDeployerConfig struct {
|
||||
// Webhook URL。
|
||||
Url string `json:"url"`
|
||||
// Webhook 变量字典。
|
||||
Variables map[string]string `json:"variables,omitempty"`
|
||||
}
|
||||
|
||||
type WebhookDeployer struct {
|
||||
config *WebhookDeployerConfig
|
||||
logger deployer.Logger
|
||||
}
|
||||
|
||||
var _ deployer.Deployer = (*WebhookDeployer)(nil)
|
||||
|
||||
func New(config *WebhookDeployerConfig) (*WebhookDeployer, error) {
|
||||
return NewWithLogger(config, deployer.NewNilLogger())
|
||||
}
|
||||
|
||||
func NewWithLogger(config *WebhookDeployerConfig, logger deployer.Logger) (*WebhookDeployer, error) {
|
||||
if config == nil {
|
||||
return nil, errors.New("config is nil")
|
||||
}
|
||||
|
||||
if logger == nil {
|
||||
return nil, errors.New("logger is nil")
|
||||
}
|
||||
|
||||
return &WebhookDeployer{
|
||||
config: config,
|
||||
logger: logger,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type webhookData struct {
|
||||
SubjectAltNames string `json:"subjectAltNames"`
|
||||
Certificate string `json:"certificate"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
Variables map[string]string `json:"variables"`
|
||||
}
|
||||
|
||||
func (d *WebhookDeployer) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) {
|
||||
certX509, err := x509.ParseCertificateFromPEM(certPem)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
data := &webhookData{
|
||||
SubjectAltNames: strings.Join(certX509.DNSNames, ","),
|
||||
Certificate: certPem,
|
||||
PrivateKey: privkeyPem,
|
||||
Variables: d.config.Variables,
|
||||
}
|
||||
body, _ := json.Marshal(data)
|
||||
resp, err := xhttp.Req(d.config.Url, http.MethodPost, bytes.NewReader(body), map[string]string{
|
||||
"Content-Type": "application/json",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, xerrors.Wrap(err, "failed to send webhook request")
|
||||
}
|
||||
|
||||
d.logger.Appendt("Webhook Response", string(resp))
|
||||
|
||||
return &deployer.DeployResult{}, nil
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user