feat(provider): new deployment provider: byteplus tos

This commit is contained in:
Fu Diwei
2026-06-04 01:14:21 +08:00
committed by RHQYZ
parent a8a9aaff11
commit 177b4f2a4f
10 changed files with 248 additions and 2 deletions
@@ -0,0 +1,29 @@
package deployers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
byteplustos "github.com/certimate-go/certimate/pkg/core/deployer/providers/byteplus-tos"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
Registries.MustRegister(domain.DeploymentProviderTypeBytePlusTOS, func(options *ProviderFactoryOptions) (core.Deployer, error) {
credentials := domain.AccessConfigForBytePlus{}
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := byteplustos.NewDeployer(&byteplustos.DeployerConfig{
AccessKeyId: credentials.AccessKeyId,
SecretAccessKey: credentials.SecretAccessKey,
ProjectName: credentials.ProjectName,
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
Bucket: xmaps.GetString(options.ProviderExtendedConfig, "bucket"),
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
})
return provider, err
})
}
+1
View File
@@ -340,6 +340,7 @@ const (
DeploymentProviderTypeBunnyCDN = DeploymentProviderType(AccessProviderTypeBunny + "-cdn")
DeploymentProviderTypeBytePlusCDN = DeploymentProviderType(AccessProviderTypeBytePlus + "-cdn")
DeploymentProviderTypeBytePlusCertCenter = DeploymentProviderType(AccessProviderTypeBytePlus + "-certcenter")
DeploymentProviderTypeBytePlusTOS = DeploymentProviderType(AccessProviderTypeBytePlus + "-tos")
DeploymentProviderTypeCacheFly = DeploymentProviderType(AccessProviderTypeCacheFly)
DeploymentProviderTypeCdnfly = DeploymentProviderType(AccessProviderTypeCdnfly)
DeploymentProviderTypeCPanel = DeploymentProviderType(AccessProviderTypeCPanel)
@@ -0,0 +1,125 @@
package byteplustos
import (
"context"
"fmt"
"log/slog"
"github.com/volcengine/ve-tos-golang-sdk/v2/tos"
"github.com/certimate-go/certimate/pkg/core/certmgr"
certmgrimpl "github.com/certimate-go/certimate/pkg/core/certmgr/providers/byteplus-certcenter"
"github.com/certimate-go/certimate/pkg/core/deployer"
)
type DeployerConfig struct {
// BytePlus AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// BytePlus SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
// BytePlus 项目名称。
ProjectName string `json:"projectName,omitempty"`
// BytePlus 地域。
Region string `json:"region"`
// 存储桶名。
Bucket string `json:"bucket"`
// 自定义域名(不支持泛域名)。
Domain string `json:"domain"`
}
type Deployer struct {
config *DeployerConfig
logger *slog.Logger
sdkClient *tos.ClientV2
sdkCertmgr certmgr.Provider
}
var _ deployer.Provider = (*Deployer)(nil)
func NewDeployer(config *DeployerConfig) (*Deployer, error) {
if config == nil {
return nil, fmt.Errorf("the configuration of the deployer provider is nil")
}
client, err := createSDKClient(config.AccessKeyId, config.SecretAccessKey, config.Region)
if err != nil {
return nil, fmt.Errorf("could not create client: %w", err)
}
pcertmgr, err := certmgrimpl.NewCertmgr(&certmgrimpl.CertmgrConfig{
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
ProjectName: config.ProjectName,
Region: config.Region,
})
if err != nil {
return nil, fmt.Errorf("could not create certmgr: %w", err)
}
return &Deployer{
config: config,
logger: slog.Default(),
sdkClient: client,
sdkCertmgr: pcertmgr,
}, nil
}
func (d *Deployer) SetLogger(logger *slog.Logger) {
if logger == nil {
d.logger = slog.New(slog.DiscardHandler)
} else {
d.logger = logger
}
d.sdkCertmgr.SetLogger(logger)
}
func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*deployer.DeployResult, error) {
if d.config.Bucket == "" {
return nil, fmt.Errorf("config `bucket` is required")
}
if d.config.Domain == "" {
return nil, fmt.Errorf("config `domain` is required")
}
// 上传证书
upres, err := d.sdkCertmgr.Upload(ctx, certPEM, privkeyPEM)
if err != nil {
return nil, fmt.Errorf("failed to upload certificate file: %w", err)
} else {
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
}
// 设置自定义域名
// REF: https://docs.byteplus.com/en/docs/tos/Putbucketcustomdomain
// REF: https://docs.byteplus.com/en/docs/tos/Manage-custom-domain-names-go-sdk
putBucketCustomDomainReq := &tos.PutBucketCustomDomainInput{
Bucket: d.config.Bucket,
Rule: tos.CustomDomainRule{
Domain: d.config.Domain,
CertID: upres.CertId,
},
}
putBucketCustomDomainResp, err := d.sdkClient.PutBucketCustomDomain(ctx, putBucketCustomDomainReq)
d.logger.Debug("sdk request 'tos.PutBucketCustomDomain'", slog.Any("request", putBucketCustomDomainReq), slog.Any("response", putBucketCustomDomainResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'tos.PutBucketCustomDomain': %w", err)
}
return &deployer.DeployResult{}, nil
}
func createSDKClient(accessKeyId, secretAccessKey, region string) (*tos.ClientV2, error) {
endpoint := fmt.Sprintf("tos-%s.bytepluses.com", region)
client, err := tos.NewClientV2(
endpoint,
tos.WithRegion(region),
tos.WithCredentials(tos.NewStaticCredentials(accessKeyId, secretAccessKey)),
)
if err != nil {
return nil, err
}
return client, nil
}
@@ -0,0 +1,61 @@
package byteplustos_test
import (
"testing"
"github.com/certimate-go/certimate/pkg/core/deployer/internal/tester"
impl "github.com/certimate-go/certimate/pkg/core/deployer/providers/byteplus-tos"
)
var (
fp = tester.Args("BYTEPLUSTOS_")
fTestCertPath string
fTestKeyPath string
fAccessKeyId string
fSecretAccessKey string
fRegion string
fBucket string
fDomain string
)
func init() {
fp.DefineString(&fTestCertPath, "TESTCERTPATH")
fp.DefineString(&fTestKeyPath, "TESTKEYPATH")
fp.DefineString(&fAccessKeyId, "ACCESSKEYID")
fp.DefineString(&fSecretAccessKey, "SECRETACCESSKEY")
fp.DefineString(&fRegion, "REGION")
fp.DefineString(&fBucket, "BUCKET")
fp.DefineString(&fDomain, "DOMAIN")
}
/*
Shell command to run this test:
go test -v ./byteplus_tos_test.go -args \
--BYTEPLUSTOS_TESTCERTPATH="/path/to/your-test-cert.pem" \
--BYTEPLUSTOS_TESTKEYPATH="/path/to/your-test-key.pem" \
--BYTEPLUSTOS_ACCESSKEYID="your-access-key-id" \
--BYTEPLUSTOS_SECRETACCESSKEY="your-secret-access-key" \
--BYTEPLUSTOS_REGION="cn-beijing" \
--BYTEPLUSTOS_BUCKET="your-tos-bucket" \
--BYTEPLUSTOS_DOMAIN="example.com"
*/
func TestProvider(t *testing.T) {
fp.Parse()
t.Run("Deploy", func(t *testing.T) {
provider, err := impl.NewDeployer(&impl.DeployerConfig{
AccessKeyId: fAccessKeyId,
SecretAccessKey: fSecretAccessKey,
Region: fRegion,
Bucket: fBucket,
Domain: fDomain,
})
if err != nil {
t.Errorf("err: %+v", err)
return
}
tester.TestDeploy(t, provider, tester.TestDeployArgs{CertPath: fTestCertPath, KeyPath: fTestKeyPath})
})
}
@@ -196,7 +196,7 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*dep
return nil, fmt.Errorf("failed to parse private key: %w", err)
}
privkeyAlg, _, _ := xcertkey.DetectPrivateKeyAlgorithm(privkey)
privkeyAlg, _, _ := xcertkey.GetPrivateKeyAlgorithm(privkey)
privkeyAlgStr := ""
switch privkeyAlg {
case x509.RSA:
+2
View File
@@ -623,6 +623,7 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
BUNNY_CDN: `${ACCESS_PROVIDERS.BUNNY}-cdn`,
BYTEPLUS_CDN: `${ACCESS_PROVIDERS.BYTEPLUS}-cdn`,
BYTEPLUS_CERTCENTER: `${ACCESS_PROVIDERS.BYTEPLUS}-certcenter`,
BYTEPLUS_TOS: `${ACCESS_PROVIDERS.BYTEPLUS}-tos`,
CACHEFLY: `${ACCESS_PROVIDERS.CACHEFLY}`,
CDNFLY: `${ACCESS_PROVIDERS.CDNFLY}`,
CPANEL: `${ACCESS_PROVIDERS.CPANEL}`,
@@ -825,6 +826,7 @@ export const deploymentProvidersMap: Map<DeploymentProvider["type"] | string, De
[DEPLOYMENT_PROVIDERS.DOGECLOUD_CDN, "provider.dogecloud_cdn", DEPLOYMENT_CATEGORIES.CDN],
[DEPLOYMENT_PROVIDERS.KSYUN_CDN, "provider.ksyun_cdn", DEPLOYMENT_CATEGORIES.CDN],
[DEPLOYMENT_PROVIDERS.KSYUN_SLB, "provider.ksyun_slb", DEPLOYMENT_CATEGORIES.LOADBALANCE],
[DEPLOYMENT_PROVIDERS.BYTEPLUS_TOS, "provider.byteplus_tos", DEPLOYMENT_CATEGORIES.STORAGE],
[DEPLOYMENT_PROVIDERS.BYTEPLUS_CDN, "provider.byteplus_cdn", DEPLOYMENT_CATEGORIES.CDN],
[DEPLOYMENT_PROVIDERS.BYTEPLUS_CERTCENTER, "provider.byteplus_certcenter_upload", DEPLOYMENT_CATEGORIES.SSL],
[DEPLOYMENT_PROVIDERS.UCLOUD_US3, "provider.ucloud_us3", DEPLOYMENT_CATEGORIES.STORAGE],
@@ -92,6 +92,7 @@
"byteplus_cdn": "BytePlus - CDN (Content Delivery Network)",
"byteplus_certcenter_upload": "BytePlus - Upload to Certificate Center",
"byteplus_dns": "BytePlus - DNS",
"byteplus_tos": "BytePlus - TOS (Torch Object Storage)",
"cachefly": "CacheFly",
"cdnfly": "Cdnfly",
"cloudflare": "Cloudflare",
@@ -1217,6 +1217,19 @@
"label": "BytePlus region",
"placeholder": "Please enter BytePlus Certificate Center region (e.g. ap-singapore-1)"
},
"byteplus_tos_region": {
"label": "BytePlus region",
"placeholder": "Please enter BytePlus TOS region (e.g. ap-singapore-1)",
"tooltip": "For more information, see <a href=\"https://www.volcengine.com/docs/6349/107356\" target=\"_blank\">https://www.volcengine.com/docs/6349/107356</a>"
},
"byteplus_tos_bucket": {
"label": "BytePlus TOS bucket",
"placeholder": "Please enter BytePlus TOS bucket name"
},
"byteplus_tos_domain": {
"label": "BytePlus TOS custom domain",
"placeholder": "Please enter BytePlus TOS bucket custom domain name"
},
"cdnfly_deploy_target": {
"option": {
"website": {
@@ -92,6 +92,7 @@
"byteplus_cdn": "BytePlus - 内容分发网络 CDN",
"byteplus_certcenter_upload": "BytePlus - 上传到证书中心",
"byteplus_dns": "BytePlus - DNS",
"byteplus_tos": "BytePlus - 对象存储 TOS",
"cachefly": "CacheFly",
"cdnfly": "Cdnfly",
"cloudflare": "Cloudflare",
@@ -1213,9 +1213,22 @@
"placeholder": "请输入 BytePlus CDN 域名"
},
"byteplus_certcenter_region": {
"label": "BytePlus 证书中心服务地域",
"label": "BytePlus 服务地域",
"placeholder": "请输入 BytePlus 证书中心服务地域(例如:ap-singapore-1)"
},
"byteplus_tos_region": {
"label": "BytePlus 服务地域",
"placeholder": "请输入 BytePlus TOS 服务地域(例如:ap-singapore-1)",
"tooltip": "这是什么?请参阅 <a href=\"https://www.volcengine.com/docs/6349/107356\" target=\"_blank\">https://www.volcengine.com/docs/6349/107356</a>"
},
"byteplus_tos_bucket": {
"label": "BytePlus TOS 存储桶名",
"placeholder": "请输入 BytePlus TOS 存储桶名"
},
"byteplus_tos_domain": {
"label": "BytePlus TOS 自定义域名",
"placeholder": "请输入 BytePlus TOS 自定义域名"
},
"cdnfly_deploy_target": {
"option": {
"website": {