feat(provider): new purge provider: jdcloud ssl

This commit is contained in:
Fu Diwei
2026-08-27 17:14:31 +08:00
parent 38099a79e1
commit f0ec7a12ea
9 changed files with 241 additions and 1 deletions
@@ -0,0 +1,25 @@
package purgers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core"
pgrimpl "github.com/certimate-go/certimate/pkg/core/purger/providers/jdcloud-ssl"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
Registries.MustRegister(domain.PurgeProviderTypeJDCloudSSL, func(options *ProviderFactoryOptions) (core.Purger, error) {
credentials := domain.AccessConfigForJDCloud{}
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := pgrimpl.NewPurger(&pgrimpl.PurgerConfig{
AccessKeyId: credentials.AccessKeyId,
AccessKeySecret: credentials.AccessKeySecret,
})
return provider, err
})
}
+1
View File
@@ -499,6 +499,7 @@ const (
PurgeProviderTypeHuaweiCloudELB = PurgeProviderType(AccessProviderTypeHuaweiCloud + "-elb")
PurgeProviderTypeHuaweiCloudSCM = PurgeProviderType(AccessProviderTypeHuaweiCloud + "-scm")
PurgeProviderTypeHuaweiCloudWAF = PurgeProviderType(AccessProviderTypeHuaweiCloud + "-waf")
PurgeProviderTypeJDCloudSSL = PurgeProviderType(AccessProviderTypeJDCloud + "-ssl")
PurgeProviderTypeTencentCloudGAAP = PurgeProviderType(AccessProviderTypeTencentCloud + "-gaap")
PurgeProviderTypeTencentCloudSSL = PurgeProviderType(AccessProviderTypeTencentCloud + "-ssl")
PurgeProviderTypeTencentCloudTSE = PurgeProviderType(AccessProviderTypeTencentCloud + "-tse")
@@ -280,7 +280,7 @@ func (d *Deployer) updateListenerSniCertificate(ctx context.Context, cloudLoadba
sslItem := describeSSLV2Resp.DataSet[0]
if sslItem.Domains == d.config.Domain {
sslIdsToDelete = append(sslIdsToDelete, sslItem.SSLId) // 同域名证书需要删除
sslIdsToDelete = append(sslIdsToDelete, sslItem.SSLId)
continue
}
}
@@ -0,0 +1,44 @@
//go:build tester
package jdcloudssl_test
import (
"testing"
"github.com/stretchr/testify/require"
tester "github.com/certimate-go/certimate/pkg/core/purger/providers-tester"
impl "github.com/certimate-go/certimate/pkg/core/purger/providers/jdcloud-ssl"
)
var (
fp = tester.InitArgs("JDCLOUDSSL_")
fAccessKeyId string
fAccessKeySecret string
)
func init() {
fp.DefineString(&fAccessKeyId, "ACCESSKEYID")
fp.DefineString(&fAccessKeySecret, "ACCESSKEYSECRET")
}
/*
Shell command to run this test:
go test -tags=tester -v ./jdcloud_ssl_test.go -args \
--JDCLOUDSSL_ACCESSKEYID="your-access-key-id" \
--JDCLOUDSSL_ACCESSKEYSECRET="your-access-key-secret"
*/
func TestProvider(t *testing.T) {
fp.Parse()
t.Run("Purge", func(t *testing.T) {
provider, err := impl.NewPurger(&impl.PurgerConfig{
AccessKeyId: fAccessKeyId,
AccessKeySecret: fAccessKeySecret,
})
require.NoError(t, err)
tester.Purge(t, provider, tester.PurgeInput{})
})
}
@@ -0,0 +1,147 @@
package jdcloudssl
import (
"context"
"fmt"
"log/slog"
"time"
jdcore "github.com/jdcloud-api/jdcloud-sdk-go/core"
jdsslapis "github.com/jdcloud-api/jdcloud-sdk-go/services/ssl/apis"
jdssl "github.com/certimate-go/certimate/pkg/sdk3rd-trimmed/github.com/jdcloud-api/jdcloud-sdk-go/services/ssl/client"
"github.com/certimate-go/certimate/pkg/core"
xloop "github.com/certimate-go/certimate/pkg/utils/loop"
xwait "github.com/certimate-go/certimate/pkg/utils/wait"
)
type (
Provider = core.Purger
PurgeResult = core.PurgerPurgeResult
)
type PurgerConfig struct {
// 京东云 AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// 京东云 AccessKeySecret。
AccessKeySecret string `json:"accessKeySecret"`
}
type Purger struct {
config *PurgerConfig
logger *slog.Logger
sdkClient *jdssl.SslClient
}
var _ Provider = (*Purger)(nil)
func NewPurger(config *PurgerConfig) (*Purger, error) {
if config == nil {
return nil, fmt.Errorf("the configuration of the purger provider is nil")
}
client, err := createSDKClient(config.AccessKeyId, config.AccessKeySecret)
if err != nil {
return nil, fmt.Errorf("could not create client: %w", err)
}
return &Purger{
config: config,
logger: slog.Default(),
sdkClient: client,
}, nil
}
func (p *Purger) SetLogger(logger *slog.Logger) {
if logger == nil {
p.logger = slog.New(slog.DiscardHandler)
} else {
p.logger = logger
}
}
func (p *Purger) Purge(ctx context.Context, expiry time.Duration) (*PurgeResult, error) {
purgingCertIds := make([]string, 0)
// 查看证书列表
// REF: https://docs.jdcloud.com/cn/ssl-certificate/api/describecerts
describeCertsPageNumber := 1
describeCertsPageSize := 10
for {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
describeCertsReq := jdsslapis.NewDescribeCertsRequestWithoutParam()
describeCertsReq.SetPageNumber(describeCertsPageNumber)
describeCertsReq.SetPageSize(describeCertsPageSize)
describeCertsResp, err := p.sdkClient.DescribeCerts(describeCertsReq)
p.logger.Debug("sdk request 'ssl.DescribeCerts'", slog.Any("request", describeCertsReq), slog.Any("response", describeCertsResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'ssl.DescribeCerts': %w", err)
}
for _, certItem := range describeCertsResp.Result.CertListDetails {
certNotAfter, err := time.Parse(time.RFC3339, certItem.EndTime)
if err != nil {
continue
}
if time.Since(certNotAfter) >= expiry {
purgingCertIds = append(purgingCertIds, certItem.CertId)
}
}
if len(describeCertsResp.Result.CertListDetails) < int(describeCertsPageSize) {
break
}
describeCertsPageNumber++
}
// 删除证书
// REF: https://docs.jdcloud.com/cn/ssl-certificate/api/deletecerts
purgedCount := 0
if len(purgingCertIds) > 0 {
p.logger.Info("found certificates to purge", slog.Any("certIds", purgingCertIds))
if err := xloop.ForRangeAllWithContext(ctx, purgingCertIds, func(ctx context.Context, certId string, i int) error {
if i > 0 {
if err := xwait.DelayWithContext(ctx, 1*time.Second); err != nil {
return err
}
}
deleteCertsReq := jdsslapis.NewDeleteCertsRequestWithoutParam()
deleteCertsReq.SetCertId(certId)
deleteCertResp, err := p.sdkClient.DeleteCerts(deleteCertsReq)
p.logger.Debug("sdk request 'ssl.DeleteCerts'", slog.Any("request", deleteCertsReq), slog.Any("response", deleteCertResp))
if err != nil {
if deleteCertResp != nil && deleteCertResp.Error.Code == 404 {
return nil
}
return fmt.Errorf("failed to execute sdk request 'ssl.DeleteCerts': %w", err)
}
purgedCount++
return nil
}); err != nil {
return nil, err
}
}
return &PurgeResult{
Count: purgedCount,
}, nil
}
func createSDKClient(accessKeyId, accessKeySecret string) (*jdssl.SslClient, error) {
clientCredentials := jdcore.NewCredentials(accessKeyId, accessKeySecret)
client := jdssl.NewSslClient(clientCredentials)
client.DisableLogger()
return client, nil
}
@@ -35,6 +35,25 @@ func (c *SslClient) DisableLogger() {
c.Logger = core.NewDummyLogger()
}
func (c *SslClient) DeleteCerts(request *ssl.DeleteCertsRequest) (*ssl.DeleteCertsResponse, error) {
if request == nil {
return nil, errors.New("Request object is nil.")
}
resp, err := c.Send(request, c.ServiceName)
if err != nil {
return nil, err
}
jdResp := &ssl.DeleteCertsResponse{}
err = json.Unmarshal(resp, jdResp)
if err != nil {
c.Logger.Log(core.LogError, "Unmarshal json failed, resp: %s", string(resp))
return nil, err
}
return jdResp, err
}
func (c *SslClient) DescribeCerts(request *ssl.DescribeCertsRequest) (*ssl.DescribeCertsResponse, error) {
if request == nil {
return nil, errors.New("Request object is nil.")
+2
View File
@@ -993,6 +993,7 @@ export const PURGE_PROVIDERS = Object.freeze(
HUAWEICLOUD_ELB: `${ACCESS_PROVIDERS.HUAWEICLOUD}-elb`,
HUAWEICLOUD_SCM: `${ACCESS_PROVIDERS.HUAWEICLOUD}-scm`,
HUAWEICLOUD_WAF: `${ACCESS_PROVIDERS.HUAWEICLOUD}-waf`,
JDCLOUD_SSL: `${ACCESS_PROVIDERS.JDCLOUD}-ssl`,
TENCENTCLOUD_GAAP: `${ACCESS_PROVIDERS.TENCENTCLOUD}-gaap`,
TENCENTCLOUD_SSL: `${ACCESS_PROVIDERS.TENCENTCLOUD}-ssl`,
TENCENTCLOUD_TSE: `${ACCESS_PROVIDERS.TENCENTCLOUD}-tse`,
@@ -1021,6 +1022,7 @@ export const purgeProvidersMap: Map<PurgeProvider["type"] | string, PurgeProvide
[PURGE_PROVIDERS.HUAWEICLOUD_ELB, "provider.huaweicloud_elb"],
[PURGE_PROVIDERS.HUAWEICLOUD_WAF, "provider.huaweicloud_waf"],
[PURGE_PROVIDERS.BAIDUCLOUD_CERT, "provider.baiducloud_cert"],
[PURGE_PROVIDERS.JDCLOUD_SSL, "provider.jdcloud_ssl"],
[PURGE_PROVIDERS.UCLOUD_USSL, "provider.ucloud_ussl"],
] satisfies Array<[PurgeProviderType, string, "builtin"] | [PurgeProviderType, string]>
).map(([type, name]) => [
@@ -183,6 +183,7 @@
"jdcloud_dns": "JD Cloud - DNS",
"jdcloud_live": "JD Cloud - Live Video",
"jdcloud_vod": "JD Cloud - VOD (Video on Demand)",
"jdcloud_ssl": "JD Cloud - SSL Certificate",
"jdcloud_ssl_upload": "JD Cloud - Upload to SSL Certificate",
"jdcloud_waf": "JD Cloud - WAF (Web Application Firewall)",
"kong": "Kong",
@@ -183,6 +183,7 @@
"jdcloud_dns": "京东云 - 云解析 DNS",
"jdcloud_live": "京东云 - 视频直播 Live",
"jdcloud_vod": "京东云 - 视频点播 VOD",
"jdcloud_ssl": "京东云 - SSL 数字证书",
"jdcloud_ssl_upload": "京东云 - 上传到 SSL 数字证书",
"jdcloud_waf": "京东云 - Web 应用防火墙 WAF",
"kong": "Kong",