feat: add ftp client

This commit is contained in:
Fu Diwei
2026-04-17 11:46:05 +08:00
committed by RHQYZ
parent 784a2f3fd0
commit 9d32c58e41
11 changed files with 385 additions and 89 deletions
+3
View File
@@ -45,6 +45,7 @@ require (
github.com/google/go-querystring v1.2.0
github.com/huaweicloud/huaweicloud-sdk-go-v3 v0.1.190
github.com/jdcloud-api/jdcloud-sdk-go v1.64.0
github.com/jlaffaye/ftp v0.2.0
github.com/kong/go-kong v0.73.1
github.com/luthermonson/go-proxmox v0.4.0
github.com/microcosm-cc/bluemonday v1.0.27
@@ -217,6 +218,8 @@ require (
github.com/go-ozzo/ozzo-validation/v4 v4.3.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/json-iterator/go v1.1.13-0.20220915233716-71ac16282d12 // indirect
+5
View File
@@ -530,6 +530,7 @@ github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslC
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hashicorp/consul/api v1.10.1/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
@@ -544,6 +545,8 @@ github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjh
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=
github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
@@ -585,6 +588,8 @@ github.com/jdcloud-api/jdcloud-sdk-go v1.64.0 h1:xZc/ZRcrOhDx9Ra9htu6ui2gUUttmLs
github.com/jdcloud-api/jdcloud-sdk-go v1.64.0/go.mod h1:UrKjuULIWLjHFlG6aSPunArE5QX57LftMmStAZJBEX8=
github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8=
github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg=
github.com/jlaffaye/ftp v0.2.0 h1:lXNvW7cBu7R/68bknOX3MrRIIqZ61zELs1P2RAiA3lg=
github.com/jlaffaye/ftp v0.2.0/go.mod h1:is2Ds5qkhceAPy2xD6RLI6hmp/qysSoymZ+Z2uTnspI=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
+297
View File
@@ -0,0 +1,297 @@
package ftp
import (
"bytes"
"context"
"fmt"
"io"
"net"
"path/filepath"
"strconv"
"strings"
"sync"
"github.com/jlaffaye/ftp"
)
type Client struct {
cli *ftp.ServerConn
wdMu sync.Mutex
}
func NewClient(config *Config) (*Client, error) {
if config == nil {
return nil, fmt.Errorf("the configuration of FTP client is nil")
}
client, err := createFtpClient(config)
if err != nil {
return nil, fmt.Errorf("ftp: %w", err)
}
return &Client{cli: client}, nil
}
func (c *Client) RawClient() *ftp.ServerConn {
return c.cli
}
func (c *Client) ChangeDir(ctx context.Context, path string) error {
_, err := wrapFuncCtx(ctx, func() (struct{}, error) {
c.wdMu.Lock()
defer c.wdMu.Unlock()
path = filepath.ToSlash(path)
err := c.cli.ChangeDir(path)
return struct{}{}, err
})
if err != nil {
return fmt.Errorf("ftp: failed to change directory: %w", err)
}
return nil
}
func (c *Client) CurrentDir(ctx context.Context) (string, error) {
currentDir, err := wrapFuncCtx(ctx, func() (string, error) {
c.wdMu.Lock()
defer c.wdMu.Unlock()
return c.cli.CurrentDir()
})
if err != nil {
return "", fmt.Errorf("ftp: failed to get current directory: %w", err)
}
return currentDir, nil
}
func (c *Client) Delete(ctx context.Context, path string) error {
_, err := wrapFuncCtx(ctx, func() (struct{}, error) {
path = filepath.Clean(path)
filename := filepath.Base(path)
if filename != path {
c.wdMu.Lock()
defer c.wdMu.Unlock()
currentDir, err := c.cli.CurrentDir()
if err != nil {
return struct{}{}, err
}
targetDir := filepath.Dir(path)
if targetDir != currentDir {
if err := c.cli.ChangeDir(targetDir); err != nil {
return struct{}{}, err
}
defer c.cli.ChangeDir(currentDir)
}
}
err := c.cli.Delete(filename)
return struct{}{}, err
})
if err != nil {
return fmt.Errorf("ftp: failed to delete file: %w", err)
}
return nil
}
func (c *Client) Mkdir(ctx context.Context, path string) error {
_, err := wrapFuncCtx(ctx, func() (struct{}, error) {
c.wdMu.Lock()
defer c.wdMu.Unlock()
err := c.cli.MakeDir(path)
return struct{}{}, err
})
if err != nil {
return fmt.Errorf("ftp: failed to create directory: %w", err)
}
return nil
}
func (c *Client) MkdirAll(ctx context.Context, path string) error {
if path == "" || path == "." {
return nil
}
_, err := wrapFuncCtx(ctx, func() (struct{}, error) {
c.wdMu.Lock()
defer c.wdMu.Unlock()
currentDir, err := c.cli.CurrentDir()
if err != nil {
return struct{}{}, err
}
path = filepath.ToSlash(filepath.Clean(path))
if path == "/" || currentDir == path {
return struct{}{}, nil
}
defer c.cli.ChangeDir(currentDir)
dirs := strings.Split(path, "/")
for i, dir := range dirs {
if i == 0 && filepath.IsAbs(path) {
if err := c.cli.ChangeDir("/"); err != nil {
return struct{}{}, err
}
}
if dir == "" || dir == "." {
continue
}
if err := c.cli.ChangeDir(dir); err != nil {
if err := c.cli.MakeDir(dir); err != nil {
return struct{}{}, err
}
if err := c.cli.ChangeDir(dir); err != nil {
return struct{}{}, err
}
}
}
return struct{}{}, nil
})
if err != nil {
return fmt.Errorf("ftp: failed to create directory: %w", err)
}
return nil
}
func (c *Client) Retrieve(ctx context.Context, path string) (*File, error) {
file, err := wrapFuncCtx(ctx, func() (*File, error) {
path = filepath.Clean(path)
filename := filepath.Base(path)
if filename != path {
c.wdMu.Lock()
defer c.wdMu.Unlock()
currentDir, err := c.cli.CurrentDir()
if err != nil {
return nil, err
}
targetDir := filepath.Dir(path)
if targetDir != currentDir {
if err := c.cli.ChangeDir(targetDir); err != nil {
return nil, err
}
defer c.cli.ChangeDir(currentDir)
}
}
return c.cli.Retr(filename)
})
if err != nil {
return nil, fmt.Errorf("ftp: failed to retrieve file: %w", err)
}
return file, err
}
func (c *Client) Store(ctx context.Context, path string, reader io.Reader, offset uint64) error {
_, err := wrapFuncCtx(ctx, func() (struct{}, error) {
path = filepath.Clean(path)
filename := filepath.Base(path)
if filename != path {
c.wdMu.Lock()
defer c.wdMu.Unlock()
currentDir, err := c.cli.CurrentDir()
if err != nil {
return struct{}{}, err
}
targetDir := filepath.Dir(path)
if targetDir != currentDir {
if err := c.cli.ChangeDir(targetDir); err != nil {
return struct{}{}, err
}
defer c.cli.ChangeDir(currentDir)
}
}
err := c.cli.StorFrom(filename, reader, offset)
return struct{}{}, err
})
if err != nil {
return fmt.Errorf("ftp: failed to store file: %w", err)
}
return nil
}
func (c *Client) StoreString(ctx context.Context, path string, data string) error {
reader := strings.NewReader(data)
return c.Store(ctx, path, reader, 0)
}
func (c *Client) StoreBytes(ctx context.Context, path string, data []byte) error {
reader := bytes.NewReader(data)
return c.Store(ctx, path, reader, 0)
}
func (c *Client) Quit(ctx context.Context) error {
_, err := wrapFuncCtx(ctx, func() (struct{}, error) {
c.cli.Logout()
return struct{}{}, c.cli.Quit()
})
if err != nil {
return fmt.Errorf("ftp: failed to quit: %w", err)
}
return nil
}
func createFtpClient(config *Config) (*ftp.ServerConn, error) {
client, err := ftp.Dial(resolveAddr(config.Host, config.Port))
if err != nil {
return nil, err
}
if config.Username != "" || config.Password != "" {
if err = client.Login(config.Username, config.Password); err != nil {
return nil, err
}
}
return client, nil
}
func resolveAddr(host string, port int) string {
if port == 0 {
port = defaultPort
}
return net.JoinHostPort(host, strconv.Itoa(port))
}
func wrapFuncCtx[T any](ctx context.Context, fn func() (T, error)) (T, error) {
type result struct {
res T
err error
}
done := make(chan result, 1)
go func() {
res, err := fn()
done <- result{res, err}
}()
select {
case <-ctx.Done():
var res T
return res, ctx.Err()
case r := <-done:
return r.res, r.err
}
}
+18
View File
@@ -0,0 +1,18 @@
package ftp
const (
defaultPort int = 21
)
type Config struct {
Host string
Port int
Username string
Password string
}
func NewDefaultConfig() *Config {
return &Config{
Port: defaultPort,
}
}
+7
View File
@@ -0,0 +1,7 @@
package ftp
import (
"github.com/jlaffaye/ftp"
)
type File = ftp.Response
+11 -7
View File
@@ -27,17 +27,21 @@ func NewClient(config *Config) (*Client, error) {
client, err := createS3Client(config)
if err != nil {
return nil, err
return nil, fmt.Errorf("s3: %w", err)
}
return &Client{cli: client}, nil
}
func (c *Client) PutObject(ctx context.Context, bucket, key string, reader io.Reader, size int64) error {
func (c *Client) RawClient() *minio.Client {
return c.cli
}
func (c *Client) PutObject(ctx context.Context, bucket, key string, reader io.Reader, size uint64) error {
putOpts := minio.PutObjectOptions{
DisableMultipart: true,
}
_, err := c.cli.PutObject(ctx, bucket, key, reader, size, putOpts)
_, err := c.cli.PutObject(ctx, bucket, key, reader, int64(size), putOpts)
if err != nil {
return fmt.Errorf("s3: failed to put object: %w", err)
}
@@ -47,12 +51,12 @@ func (c *Client) PutObject(ctx context.Context, bucket, key string, reader io.Re
func (c *Client) PutObjectString(ctx context.Context, bucket, key string, data string) error {
reader := strings.NewReader(data)
return c.PutObject(ctx, bucket, key, reader, reader.Size())
return c.PutObject(ctx, bucket, key, reader, uint64(reader.Size()))
}
func (c *Client) PutObjectBytes(ctx context.Context, bucket, key string, data []byte) error {
reader := bytes.NewReader(data)
return c.PutObject(ctx, bucket, key, reader, reader.Size())
return c.PutObject(ctx, bucket, key, reader, uint64(reader.Size()))
}
func (c *Client) RemoveObject(ctx context.Context, bucket, key string) error {
@@ -73,7 +77,7 @@ func createS3Client(config *Config) (*minio.Client, error) {
case SignatureV2:
clientCred = credentials.NewStaticV2(config.AccessKey, config.SecretKey, "")
default:
return nil, fmt.Errorf("s3: unsupported signature version: '%s'", config.SignatureVersion)
return nil, fmt.Errorf("unsupported signature version: '%s'", config.SignatureVersion)
}
endpoint, secure := resolveEndpoint(config.Endpoint)
@@ -92,7 +96,7 @@ func createS3Client(config *Config) (*minio.Client, error) {
client, err := minio.New(endpoint, clientOpts)
if err != nil {
return nil, fmt.Errorf("s3: %w", err)
return nil, err
}
return client, nil
+12 -3
View File
@@ -22,14 +22,23 @@ func NewClient(config *Config) (*Client, error) {
client, err := createSmtpClient(config)
if err != nil {
return nil, err
return nil, fmt.Errorf("smtp: %w", err)
}
return &Client{cli: client}, nil
}
func (c *Client) RawClient() *mail.Client {
return c.cli
}
func (c *Client) Close() error {
return c.cli.Close()
err := c.cli.Close()
if err != nil {
return fmt.Errorf("smtp: %w", err)
}
return nil
}
func (c *Client) Send(ctx context.Context, msg *Message) error {
@@ -87,7 +96,7 @@ func createSmtpClient(config *Config) (*mail.Client, error) {
client, err := mail.NewClient(config.Host, clientOptions...)
if err != nil {
return nil, fmt.Errorf("smtp: %w", err)
return nil, err
}
client.ErrorHandlerRegistry.RegisterHandler("smtp.qq.com", "QUIT", &wQQMailQuitErrorHandler{})
+20 -20
View File
@@ -31,12 +31,20 @@ func NewClient(config *Config) (*Client, error) {
conns[i].Close()
}
return nil, err
return nil, fmt.Errorf("ssh: %w", err)
}
return &Client{conns: conns, clis: clis}, nil
}
func (c *Client) RawClient() *ssh.Client {
if len(c.clis) == 0 {
return nil
}
return c.clis[len(c.clis)-1]
}
func (c *Client) Close() error {
errs := make([]error, 0)
@@ -57,20 +65,12 @@ func (c *Client) Close() error {
if len(errs) == 0 {
return nil
} else if len(errs) == 1 {
return errs[0]
return fmt.Errorf("ssh: %w", errs[0])
} else {
return errors.Join(errs...)
return fmt.Errorf("ssh: %w", errors.Join(errs...))
}
}
func (c *Client) GetClient() *ssh.Client {
if len(c.clis) == 0 {
return nil
}
return c.clis[len(c.clis)-1]
}
func createConnsAndSshClients(config *Config) (conns []net.Conn, clis []*ssh.Client, err error) {
conns = make([]net.Conn, 0)
clis = make([]*ssh.Client, 0)
@@ -134,7 +134,7 @@ func createConnsAndSshClients(config *Config) (conns []net.Conn, clis []*ssh.Cli
func createSshClientWithConn(config *ServerConfig, conn net.Conn) (*ssh.Client, error) {
if conn == nil {
return nil, fmt.Errorf("ssh: nil conn")
return nil, fmt.Errorf("nil conn")
}
authMethodType := lo.
@@ -147,17 +147,17 @@ func createSshClientWithConn(config *ServerConfig, conn net.Conn) (*ssh.Client,
case AuthMethodTypeNone:
{
if config.Username == "" {
return nil, fmt.Errorf("ssh: unset username")
return nil, fmt.Errorf("missing username")
}
}
case AuthMethodTypePassword:
{
if config.Username == "" {
return nil, fmt.Errorf("ssh: unset username")
return nil, fmt.Errorf("missing username")
}
if config.Password == "" {
return nil, fmt.Errorf("ssh: unset password")
return nil, fmt.Errorf("missing password")
}
password := config.Password
@@ -183,10 +183,10 @@ func createSshClientWithConn(config *ServerConfig, conn net.Conn) (*ssh.Client,
case AuthMethodTypeKey:
{
if config.Username == "" {
return nil, fmt.Errorf("ssh: unset username")
return nil, fmt.Errorf("missing username")
}
if config.Key == "" {
return nil, fmt.Errorf("ssh: unset key")
return nil, fmt.Errorf("missing key")
}
key := config.Key
@@ -200,14 +200,14 @@ func createSshClientWithConn(config *ServerConfig, conn net.Conn) (*ssh.Client,
signer, err = ssh.ParsePrivateKey([]byte(key))
}
if err != nil {
return nil, fmt.Errorf("ssh: %w", err)
return nil, err
}
authMethods = append(authMethods, ssh.PublicKeys(signer))
}
default:
return nil, fmt.Errorf("ssh: unsupported auth method '%s'", authMethodType)
return nil, fmt.Errorf("unsupported auth method '%s'", authMethodType)
}
addr := resolveAddr(config.Host, config.Port)
@@ -217,7 +217,7 @@ func createSshClientWithConn(config *ServerConfig, conn net.Conn) (*ssh.Client,
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
})
if err != nil {
return nil, fmt.Errorf("ssh: %w", err)
return nil, err
}
return ssh.NewClient(sshConn, chans, reqs), nil
@@ -14,7 +14,6 @@ import (
type ServerConfig struct {
// SSH 主机。
// 零值时默认值 "localhost"。
SshHost string `json:"sshHost,omitempty"`
// SSH 端口。
// 零值时默认值 22。
@@ -66,8 +65,8 @@ func (p *provider) Present(domain, token, keyAuth string) error {
defer client.Close()
challengeFilePath := filepath.Join(p.config.WebRootPath, http01.ChallengePath(token))
if err := xssh.WriteRemoteString(client.GetClient(), challengeFilePath, keyAuth, p.config.UseSCP); err != nil {
challengePath := filepath.Join(p.config.WebRootPath, http01.ChallengePath(token))
if err := xssh.WriteRemoteString(client.RawClient(), challengePath, keyAuth, p.config.UseSCP); err != nil {
return fmt.Errorf("failed to write file in webroot for HTTP challenge: %w", err)
}
@@ -83,8 +82,8 @@ func (p *provider) CleanUp(domain, token, keyAuth string) error {
defer client.Close()
// 删除质询文件
challengeFilePath := filepath.Join(p.config.WebRootPath, http01.ChallengePath(token))
xssh.RemoveRemote(client.GetClient(), challengeFilePath, p.config.UseSCP)
challengePath := filepath.Join(p.config.WebRootPath, http01.ChallengePath(token))
xssh.RemoveRemote(client.RawClient(), challengePath, p.config.UseSCP)
return nil
}
+8 -9
View File
@@ -15,7 +15,6 @@ import (
type ServerConfig struct {
// SSH 主机。
// 零值时默认值 "localhost"。
SshHost string `json:"sshHost,omitempty"`
// SSH 端口。
// 零值时默认值 22。
@@ -124,7 +123,7 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*dep
command = strings.ReplaceAll(command, "${CERTIMATE_DEPLOYER_CMDVAR_JKS_KEYPASS}", d.config.JksKeypass)
command = strings.ReplaceAll(command, "${CERTIMATE_DEPLOYER_CMDVAR_JKS_STOREPASS}", d.config.JksStorepass)
stdout, stderr, err := xssh.RunCommand(client.GetClient(), command)
stdout, stderr, err := xssh.RunCommand(client.RawClient(), command)
d.logger.Debug("run pre-command", slog.String("stdout", stdout), slog.String("stderr", stderr))
if err != nil {
return nil, fmt.Errorf("failed to execute pre-command (stdout: %s, stderr: %s): %w ", stdout, stderr, err)
@@ -135,26 +134,26 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*dep
switch d.config.OutputFormat {
case OUTPUT_FORMAT_PEM:
{
if err := xssh.WriteRemoteString(client.GetClient(), d.config.OutputCertPath, certPEM, d.config.UseSCP); err != nil {
if err := xssh.WriteRemoteString(client.RawClient(), d.config.OutputCertPath, certPEM, d.config.UseSCP); err != nil {
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
}
d.logger.Info("ssl certificate file uploaded", slog.String("path", d.config.OutputCertPath))
if d.config.OutputServerCertPath != "" {
if err := xssh.WriteRemoteString(client.GetClient(), d.config.OutputServerCertPath, serverCertPEM, d.config.UseSCP); err != nil {
if err := xssh.WriteRemoteString(client.RawClient(), d.config.OutputServerCertPath, serverCertPEM, d.config.UseSCP); err != nil {
return nil, fmt.Errorf("failed to save server certificate file: %w", err)
}
d.logger.Info("ssl server certificate file uploaded", slog.String("path", d.config.OutputServerCertPath))
}
if d.config.OutputIntermediaCertPath != "" {
if err := xssh.WriteRemoteString(client.GetClient(), d.config.OutputIntermediaCertPath, intermediaCertPEM, d.config.UseSCP); err != nil {
if err := xssh.WriteRemoteString(client.RawClient(), d.config.OutputIntermediaCertPath, intermediaCertPEM, d.config.UseSCP); err != nil {
return nil, fmt.Errorf("failed to save intermedia certificate file: %w", err)
}
d.logger.Info("ssl intermedia certificate file uploaded", slog.String("path", d.config.OutputIntermediaCertPath))
}
if err := xssh.WriteRemoteString(client.GetClient(), d.config.OutputKeyPath, privkeyPEM, d.config.UseSCP); err != nil {
if err := xssh.WriteRemoteString(client.RawClient(), d.config.OutputKeyPath, privkeyPEM, d.config.UseSCP); err != nil {
return nil, fmt.Errorf("failed to upload private key file: %w", err)
}
d.logger.Info("ssl private key file uploaded", slog.String("path", d.config.OutputKeyPath))
@@ -168,7 +167,7 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*dep
}
d.logger.Info("ssl certificate transformed to pfx")
if err := xssh.WriteRemote(client.GetClient(), d.config.OutputCertPath, pfxData, d.config.UseSCP); err != nil {
if err := xssh.WriteRemote(client.RawClient(), d.config.OutputCertPath, pfxData, d.config.UseSCP); err != nil {
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
}
d.logger.Info("ssl certificate file uploaded", slog.String("path", d.config.OutputCertPath))
@@ -182,7 +181,7 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*dep
}
d.logger.Info("ssl certificate transformed to jks")
if err := xssh.WriteRemote(client.GetClient(), d.config.OutputCertPath, jksData, d.config.UseSCP); err != nil {
if err := xssh.WriteRemote(client.RawClient(), d.config.OutputCertPath, jksData, d.config.UseSCP); err != nil {
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
}
d.logger.Info("ssl certificate file uploaded", slog.String("path", d.config.OutputCertPath))
@@ -204,7 +203,7 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*dep
command = strings.ReplaceAll(command, "${CERTIMATE_DEPLOYER_CMDVAR_JKS_KEYPASS}", d.config.JksKeypass)
command = strings.ReplaceAll(command, "${CERTIMATE_DEPLOYER_CMDVAR_JKS_STOREPASS}", d.config.JksStorepass)
stdout, stderr, err := xssh.RunCommand(client.GetClient(), command)
stdout, stderr, err := xssh.RunCommand(client.RawClient(), command)
d.logger.Debug("run post-command", slog.String("stdout", stdout), slog.String("stderr", stderr))
if err != nil {
return nil, fmt.Errorf("failed to execute post-command (stdout: %s, stderr: %s): %w ", stdout, stderr, err)
-45
View File
@@ -50,48 +50,3 @@ func TestCertHostnameUtil_IsMatch(t *testing.T) {
}
})
}
func TestCertHostnameUtil_IsMatch(t *testing.T) {
t.Run("IsMatch", func(t *testing.T) {
testCases := []struct {
pattern string
hostname string
expected bool
}{
{"*.example.com", "sub.example.com", true},
{"*.example.com", "sub.sub.example.com", false},
{"*.example.com", "*.example.com", true},
{"*.example.com", ".example.com", true},
{"*.example.com", "example.com", false},
{"*.*.example.com", "a.b.example.com", false},
{"*.*.example.com", "a.example.com", false},
{"*.*.example.com", "a.b.c.example.com", false},
{"example.com", "example.com", true},
{"example.com", "wrong.com", false},
{"", "example.com", false},
{"*.example.com", "", false},
{"*.sub.example.com", "a.sub.example.com", true},
{"*.sub.example.com", "a.b.sub.example.com", false},
{"*.sub.example.com", "sub.example.com", false},
{"*.Example.COM", "sub.example.com", true},
{"*.EXAMPLE.COM", "SUB.EXAMPLE.COM", true},
}
for _, tc := range testCases {
result := xcerthostname.IsMatch(tc.pattern, tc.hostname)
status := "✓"
pf := t.Logf
if result != tc.expected {
status = "✗"
pf = t.Errorf
}
pf("%s Pattern: %-20s Hostname: %-20s Expected: %-5v Got: %-5v\n", status, tc.pattern, tc.hostname, tc.expected, result)
}
})
}