mirror of
https://github.com/certimate-go/certimate.git
synced 2026-09-24 23:10:13 +08:00
feat(provider): support multiple certificates in deployment to tencentcloud edgeone
This commit is contained in:
@@ -2,10 +2,12 @@ package tencentcloudeo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
|
||||
@@ -18,6 +20,7 @@ import (
|
||||
"github.com/certimate-go/certimate/pkg/core/deployer/providers/tencentcloud-eo/internal"
|
||||
xcert "github.com/certimate-go/certimate/pkg/utils/cert"
|
||||
xcerthostname "github.com/certimate-go/certimate/pkg/utils/cert/hostname"
|
||||
xcryptokey "github.com/certimate-go/certimate/pkg/utils/crypto/key"
|
||||
)
|
||||
|
||||
type DeployerConfig struct {
|
||||
@@ -34,6 +37,8 @@ type DeployerConfig struct {
|
||||
DomainMatchPattern string `json:"domainMatchPattern,omitempty"`
|
||||
// 加速域名列表(支持泛域名)。
|
||||
Domains []string `json:"domains"`
|
||||
// 是否启用多证书模式。
|
||||
EnableMultipleSSL bool `json:"enableMultipleSSL,omitempty"`
|
||||
}
|
||||
|
||||
type Deployer struct {
|
||||
@@ -97,6 +102,12 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*dep
|
||||
d.logger.Info("ssl certificate uploaded", slog.Any("result", upres))
|
||||
}
|
||||
|
||||
// 获取全部可部署的域名信息
|
||||
domainsInZone, err := d.getAllDomainsInZone(ctx, d.config.ZoneId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 获取待部署的域名列表
|
||||
var domains []string
|
||||
switch d.config.DomainMatchPattern {
|
||||
@@ -115,11 +126,9 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*dep
|
||||
return nil, errors.New("config `domains` is required")
|
||||
}
|
||||
|
||||
domainCandidates, err := d.getAllDomainsInZone(ctx, d.config.ZoneId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domainCandidates := lo.Map(domainsInZone, func(domainInfo *tcteo.AccelerationDomain, _ int) string {
|
||||
return lo.FromPtr(domainInfo.DomainName)
|
||||
})
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
for _, configDomain := range d.config.Domains {
|
||||
if xcerthostname.IsMatch(configDomain, domain) {
|
||||
@@ -140,11 +149,9 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*dep
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domainCandidates, err := d.getAllDomainsInZone(ctx, d.config.ZoneId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
domainCandidates := lo.Map(domainsInZone, func(domainInfo *tcteo.AccelerationDomain, _ int) string {
|
||||
return lo.FromPtr(domainInfo.DomainName)
|
||||
})
|
||||
domains = lo.Filter(domainCandidates, func(domain string, _ int) bool {
|
||||
return certX509.VerifyHostname(domain) == nil
|
||||
})
|
||||
@@ -157,24 +164,110 @@ func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*dep
|
||||
return nil, fmt.Errorf("unsupported domain match pattern: '%s'", d.config.DomainMatchPattern)
|
||||
}
|
||||
|
||||
// 配置域名证书
|
||||
// REF: https://cloud.tencent.com/document/api/1552/80764
|
||||
modifyHostsCertificateReq := tcteo.NewModifyHostsCertificateRequest()
|
||||
modifyHostsCertificateReq.ZoneId = common.StringPtr(d.config.ZoneId)
|
||||
modifyHostsCertificateReq.Mode = common.StringPtr("sslcert")
|
||||
modifyHostsCertificateReq.Hosts = common.StringPtrs(domains)
|
||||
modifyHostsCertificateReq.ServerCertInfo = []*tcteo.ServerCertInfo{{CertId: common.StringPtr(upres.CertId)}}
|
||||
modifyHostsCertificateResp, err := d.sdkClient.ModifyHostsCertificate(modifyHostsCertificateReq)
|
||||
d.logger.Debug("sdk request 'teo.ModifyHostsCertificate'", slog.Any("request", modifyHostsCertificateReq), slog.Any("response", modifyHostsCertificateResp))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'teo.ModifyHostsCertificate': %w", err)
|
||||
// 跳过已部署过的域名
|
||||
domains = lo.Filter(domains, func(domain string, _ int) bool {
|
||||
var deployed bool
|
||||
|
||||
domainInfo, _ := lo.Find(domainsInZone, func(domainInfo *tcteo.AccelerationDomain) bool {
|
||||
return domain == lo.FromPtr(domainInfo.DomainName)
|
||||
})
|
||||
if domainInfo != nil && domainInfo.Certificate != nil {
|
||||
deployed = lo.ContainsBy(domainInfo.Certificate.List, func(certInfo *tcteo.CertificateInfo) bool {
|
||||
return upres.CertId == lo.FromPtr(certInfo.CertId)
|
||||
})
|
||||
}
|
||||
|
||||
return !deployed
|
||||
})
|
||||
|
||||
// 批量更新域名证书
|
||||
if len(domains) == 0 {
|
||||
d.logger.Info("no edgeone domains to deploy")
|
||||
} else {
|
||||
d.logger.Info("found edgeone domains to deploy", slog.Any("domains", domains))
|
||||
|
||||
// 配置域名证书
|
||||
// REF: https://cloud.tencent.com/document/api/1552/80764
|
||||
modifyHostsCertificateReqs := make([]*tcteo.ModifyHostsCertificateRequest, 0)
|
||||
|
||||
if d.config.EnableMultipleSSL {
|
||||
const algRSA = "RSA"
|
||||
const algECC = "ECC"
|
||||
|
||||
privkeyAlg, _, _ := xcryptokey.GetPrivateKeyAlgorithm(privkeyPEM)
|
||||
privkeyAlgStr := ""
|
||||
switch privkeyAlg {
|
||||
case x509.RSA:
|
||||
privkeyAlgStr = algRSA
|
||||
case x509.ECDSA:
|
||||
privkeyAlgStr = algECC
|
||||
}
|
||||
|
||||
for _, domain := range domains {
|
||||
modifyHostsCertificateReq := tcteo.NewModifyHostsCertificateRequest()
|
||||
modifyHostsCertificateReq.ZoneId = common.StringPtr(d.config.ZoneId)
|
||||
modifyHostsCertificateReq.Mode = common.StringPtr("sslcert")
|
||||
modifyHostsCertificateReq.Hosts = common.StringPtrs([]string{domain})
|
||||
modifyHostsCertificateReq.ServerCertInfo = []*tcteo.ServerCertInfo{{CertId: common.StringPtr(upres.CertId)}}
|
||||
|
||||
domainInfo, _ := lo.Find(domainsInZone, func(domainInfo *tcteo.AccelerationDomain) bool {
|
||||
return domain == lo.FromPtr(domainInfo.DomainName)
|
||||
})
|
||||
if domainInfo != nil && domainInfo.Certificate != nil {
|
||||
for _, certInfo := range domainInfo.Certificate.List {
|
||||
if lo.FromPtr(certInfo.CertId) == upres.CertId {
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.Split(lo.FromPtr(certInfo.SignAlgo), " ")[0] == privkeyAlgStr {
|
||||
continue
|
||||
}
|
||||
|
||||
certExpireTime, _ := time.Parse("2006-01-02T15:04:05Z", lo.FromPtr(certInfo.ExpireTime))
|
||||
if certExpireTime.Before(time.Now()) {
|
||||
continue
|
||||
}
|
||||
|
||||
modifyHostsCertificateReq.ServerCertInfo = append(modifyHostsCertificateReq.ServerCertInfo, &tcteo.ServerCertInfo{CertId: certInfo.CertId})
|
||||
}
|
||||
}
|
||||
|
||||
modifyHostsCertificateReqs = append(modifyHostsCertificateReqs, modifyHostsCertificateReq)
|
||||
}
|
||||
} else {
|
||||
modifyHostsCertificateReq := tcteo.NewModifyHostsCertificateRequest()
|
||||
modifyHostsCertificateReq.ZoneId = common.StringPtr(d.config.ZoneId)
|
||||
modifyHostsCertificateReq.Mode = common.StringPtr("sslcert")
|
||||
modifyHostsCertificateReq.Hosts = common.StringPtrs(domains)
|
||||
modifyHostsCertificateReq.ServerCertInfo = []*tcteo.ServerCertInfo{{CertId: common.StringPtr(upres.CertId)}}
|
||||
|
||||
modifyHostsCertificateReqs = append(modifyHostsCertificateReqs, modifyHostsCertificateReq)
|
||||
}
|
||||
|
||||
var errs []error
|
||||
for _, modifyHostsCertificateReq := range modifyHostsCertificateReqs {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
default:
|
||||
modifyHostsCertificateResp, err := d.sdkClient.ModifyHostsCertificate(modifyHostsCertificateReq)
|
||||
d.logger.Debug("sdk request 'teo.ModifyHostsCertificate'", slog.Any("request", modifyHostsCertificateReq), slog.Any("response", modifyHostsCertificateResp))
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to execute sdk request 'teo.ModifyHostsCertificate': %w", err)
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(errs) > 0 {
|
||||
return nil, errors.Join(errs...)
|
||||
}
|
||||
}
|
||||
|
||||
return &deployer.DeployResult{}, nil
|
||||
}
|
||||
|
||||
func (d *Deployer) getAllDomainsInZone(ctx context.Context, zoneId string) ([]string, error) {
|
||||
var domainsInZone []string
|
||||
func (d *Deployer) getAllDomainsInZone(ctx context.Context, zoneId string) ([]*tcteo.AccelerationDomain, error) {
|
||||
var domainsInZone []*tcteo.AccelerationDomain
|
||||
|
||||
const pageSize = 200
|
||||
for offset := 0; ; offset += pageSize {
|
||||
@@ -196,12 +289,13 @@ func (d *Deployer) getAllDomainsInZone(ctx context.Context, zoneId string) ([]st
|
||||
return nil, fmt.Errorf("failed to execute sdk request 'teo.DescribeAccelerationDomains': %w", err)
|
||||
}
|
||||
|
||||
ignoredStatuses := []string{"offline", "forbidden", "init"}
|
||||
for _, domainItem := range describeAccelerationDomainsResp.Response.AccelerationDomains {
|
||||
if domainItem == nil || domainItem.DomainName == nil {
|
||||
if lo.Contains(ignoredStatuses, lo.FromPtr(domainItem.DomainStatus)) {
|
||||
continue
|
||||
}
|
||||
|
||||
domainsInZone = append(domainsInZone, *domainItem.DomainName)
|
||||
domainsInZone = append(domainsInZone, domainItem)
|
||||
}
|
||||
|
||||
if len(describeAccelerationDomainsResp.Response.AccelerationDomains) < pageSize {
|
||||
|
||||
@@ -62,6 +62,7 @@ func TestDeploy(t *testing.T) {
|
||||
ZoneId: fZoneId,
|
||||
DomainMatchPattern: provider.DOMAIN_MATCH_PATTERN_EXACT,
|
||||
Domains: strings.Split(fDomains, ";"),
|
||||
EnableMultipleSSL: true,
|
||||
})
|
||||
if err != nil {
|
||||
t.Errorf("err: %+v", err)
|
||||
|
||||
Reference in New Issue
Block a user