refactor: extend huaweicloud cdn sdk

This commit is contained in:
Fu Diwei 2024-10-31 10:14:27 +08:00
parent be495839b6
commit ce55365292
6 changed files with 150 additions and 112 deletions

View File

@ -15,13 +15,14 @@ import (
"github.com/usual2970/certimate/internal/domain"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
"github.com/usual2970/certimate/internal/pkg/utils/cast"
hcCdnEx "github.com/usual2970/certimate/internal/pkg/vendors/huaweicloud-cdn-sdk"
)
type HuaweiCloudCDNDeployer struct {
option *DeployerOption
infos []string
sdkClient *hcCdn.CdnClient
sdkClient *hcCdnEx.Client
sslUploader uploader.Uploader
}
@ -40,7 +41,6 @@ func NewHuaweiCloudCDNDeployer(option *DeployerOption) (Deployer, error) {
return nil, xerrors.Wrap(err, "failed to create sdk client")
}
// TODO: SCM 服务与 DNS 服务所支持的区域可能不一致,这里暂时不传而是使用默认值,仅支持华为云国内版
uploader, err := uploader.NewHuaweiCloudSCMUploader(&uploader.HuaweiCloudSCMUploaderConfig{
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
@ -82,10 +82,9 @@ func (d *HuaweiCloudCDNDeployer) Deploy(ctx context.Context) error {
// 更新加速域名配置
// REF: https://support.huaweicloud.com/api-cdn/UpdateDomainMultiCertificates.html
// REF: https://support.huaweicloud.com/usermanual-cdn/cdn_01_0306.html
updateDomainMultiCertificatesReqBodyContent := &huaweicloudCDNUpdateDomainMultiCertificatesRequestBodyContent{}
updateDomainMultiCertificatesReqBodyContent := &hcCdnEx.UpdateDomainMultiCertificatesExRequestBodyContent{}
updateDomainMultiCertificatesReqBodyContent.DomainName = d.option.DeployConfig.GetConfigAsString("domain")
updateDomainMultiCertificatesReqBodyContent.HttpsSwitch = 1
var updateDomainMultiCertificatesResp *hcCdnModel.UpdateDomainMultiCertificatesResponse
if d.option.DeployConfig.GetConfigAsBool("useSCM") {
// 上传证书到 SCM
upres, err := d.sslUploader.Upload(ctx, d.option.Certificate.Certificate, d.option.Certificate.PrivateKey)
@ -104,13 +103,13 @@ func (d *HuaweiCloudCDNDeployer) Deploy(ctx context.Context) error {
updateDomainMultiCertificatesReqBodyContent.Certificate = cast.StringPtr(d.option.Certificate.Certificate)
updateDomainMultiCertificatesReqBodyContent.PrivateKey = cast.StringPtr(d.option.Certificate.PrivateKey)
}
updateDomainMultiCertificatesReqBodyContent = mergeHuaweiCloudCDNConfig(showDomainFullConfigResp.Configs, updateDomainMultiCertificatesReqBodyContent)
updateDomainMultiCertificatesReq := &huaweicloudCDNUpdateDomainMultiCertificatesRequest{
Body: &huaweicloudCDNUpdateDomainMultiCertificatesRequestBody{
updateDomainMultiCertificatesReqBodyContent = updateDomainMultiCertificatesReqBodyContent.MergeConfig(showDomainFullConfigResp.Configs)
updateDomainMultiCertificatesReq := &hcCdnEx.UpdateDomainMultiCertificatesExRequest{
Body: &hcCdnEx.UpdateDomainMultiCertificatesExRequestBody{
Https: updateDomainMultiCertificatesReqBodyContent,
},
}
updateDomainMultiCertificatesResp, err = executeHuaweiCloudCDNUploadDomainMultiCertificates(d.sdkClient, updateDomainMultiCertificatesReq)
updateDomainMultiCertificatesResp, err := d.sdkClient.UploadDomainMultiCertificatesEx(updateDomainMultiCertificatesReq)
if err != nil {
return err
}
@ -120,7 +119,7 @@ func (d *HuaweiCloudCDNDeployer) Deploy(ctx context.Context) error {
return nil
}
func (d *HuaweiCloudCDNDeployer) createSdkClient(accessKeyId, secretAccessKey, region string) (*hcCdn.CdnClient, error) {
func (d *HuaweiCloudCDNDeployer) createSdkClient(accessKeyId, secretAccessKey, region string) (*hcCdnEx.Client, error) {
if region == "" {
region = "cn-north-1" // CDN 服务默认区域:华北一北京
}
@ -146,69 +145,6 @@ func (d *HuaweiCloudCDNDeployer) createSdkClient(accessKeyId, secretAccessKey, r
return nil, err
}
client := hcCdn.NewCdnClient(hcClient)
client := hcCdnEx.NewClient(hcClient)
return client, nil
}
type huaweicloudCDNUpdateDomainMultiCertificatesRequestBodyContent struct {
hcCdnModel.UpdateDomainMultiCertificatesRequestBodyContent `json:",inline"`
SCMCertificateId *string `json:"scm_certificate_id,omitempty"`
}
type huaweicloudCDNUpdateDomainMultiCertificatesRequestBody struct {
Https *huaweicloudCDNUpdateDomainMultiCertificatesRequestBodyContent `json:"https,omitempty"`
}
type huaweicloudCDNUpdateDomainMultiCertificatesRequest struct {
Body *huaweicloudCDNUpdateDomainMultiCertificatesRequestBody `json:"body,omitempty"`
}
func executeHuaweiCloudCDNUploadDomainMultiCertificates(client *hcCdn.CdnClient, request *huaweicloudCDNUpdateDomainMultiCertificatesRequest) (*hcCdnModel.UpdateDomainMultiCertificatesResponse, error) {
// 华为云官方 SDK 中目前提供的字段缺失,这里暂时先需自定义请求
// 可能需要等之后 SDK 更新
requestDef := hcCdn.GenReqDefForUpdateDomainMultiCertificates()
if resp, err := client.HcClient.Sync(request, requestDef); err != nil {
return nil, err
} else {
return resp.(*hcCdnModel.UpdateDomainMultiCertificatesResponse), nil
}
}
func mergeHuaweiCloudCDNConfig(src *hcCdnModel.ConfigsGetBody, dest *huaweicloudCDNUpdateDomainMultiCertificatesRequestBodyContent) *huaweicloudCDNUpdateDomainMultiCertificatesRequestBodyContent {
if src == nil {
return dest
}
// 华为云 API 中不传的字段表示使用默认值、而非保留原值,因此这里需要把原配置中的参数重新赋值回去
// 而且蛋疼的是查询接口返回的数据结构和更新接口传入的参数结构不一致,需要做很多转化
if *src.OriginProtocol == "follow" {
dest.AccessOriginWay = cast.Int32Ptr(1)
} else if *src.OriginProtocol == "http" {
dest.AccessOriginWay = cast.Int32Ptr(2)
} else if *src.OriginProtocol == "https" {
dest.AccessOriginWay = cast.Int32Ptr(3)
}
if src.ForceRedirect != nil {
dest.ForceRedirectConfig = &hcCdnModel.ForceRedirect{}
if src.ForceRedirect.Status == "on" {
dest.ForceRedirectConfig.Switch = 1
dest.ForceRedirectConfig.RedirectType = src.ForceRedirect.Type
} else {
dest.ForceRedirectConfig.Switch = 0
}
}
if src.Https != nil {
if *src.Https.Http2Status == "on" {
dest.Http2 = cast.Int32Ptr(1)
}
}
return dest
}

View File

@ -0,0 +1,22 @@
package x509
import (
"crypto/x509"
)
// 比较两个 x509.Certificate 对象,判断它们是否是同一张证书。
// 注意,这不是精确比较,而只是基于证书序列号和数字签名的快速判断,但对于权威 CA 签发的证书来说不会存在误判。
//
// 入参:
// - a: 待比较的第一个 x509.Certificate 对象。
// - b: 待比较的第二个 x509.Certificate 对象。
//
// 出参:
// - 是否相同。
func EqualCertificate(a, b *x509.Certificate) bool {
return string(a.Signature) == string(b.Signature) &&
a.SignatureAlgorithm == b.SignatureAlgorithm &&
a.SerialNumber.String() == b.SerialNumber.String() &&
a.Issuer.SerialNumber == b.Issuer.SerialNumber &&
a.Subject.SerialNumber == b.Subject.SerialNumber
}

View File

@ -0,0 +1,31 @@
package x509
import (
"crypto/ecdsa"
"crypto/x509"
"encoding/pem"
xerrors "github.com/pkg/errors"
)
// 将 ecdsa.PrivateKey 对象转换为 PEM 编码的字符串。
//
// 入参:
// - privkey: ecdsa.PrivateKey 对象。
//
// 出参:
// - privkeyPem: 私钥 PEM 内容。
// - err: 错误。
func ConvertECPrivateKeyToPEM(privkey *ecdsa.PrivateKey) (privkeyPem string, err error) {
data, err := x509.MarshalECPrivateKey(privkey)
if err != nil {
return "", xerrors.Wrap(err, "failed to marshal EC private key")
}
block := &pem.Block{
Type: "EC PRIVATE KEY",
Bytes: data,
}
return string(pem.EncodeToMemory(block)), nil
}

View File

@ -10,23 +10,6 @@ import (
xerrors "github.com/pkg/errors"
)
// 比较两个 x509.Certificate 对象,判断它们是否是同一张证书。
// 注意,这不是精确比较,而只是基于证书序列号和数字签名的快速判断,但对于权威 CA 签发的证书来说不会存在误判。
//
// 入参:
// - a: 待比较的第一个 x509.Certificate 对象。
// - b: 待比较的第二个 x509.Certificate 对象。
//
// 出参:
// - 是否相同。
func EqualCertificate(a, b *x509.Certificate) bool {
return string(a.Signature) == string(b.Signature) &&
a.SignatureAlgorithm == b.SignatureAlgorithm &&
a.SerialNumber.String() == b.SerialNumber.String() &&
a.Issuer.SerialNumber == b.Issuer.SerialNumber &&
a.Subject.SerialNumber == b.Subject.SerialNumber
}
// 从 PEM 编码的证书字符串解析并返回一个 x509.Certificate 对象。
//
// 入参:
@ -98,25 +81,3 @@ func ParsePKCS1PrivateKeyFromPEM(privkeyPem string) (privkey *rsa.PrivateKey, er
return privkey, nil
}
// 将 ecdsa.PrivateKey 对象转换为 PEM 编码的字符串。
//
// 入参:
// - privkey: ecdsa.PrivateKey 对象。
//
// 出参:
// - privkeyPem: 私钥 PEM 内容。
// - err: 错误。
func ConvertECPrivateKeyToPEM(privkey *ecdsa.PrivateKey) (privkeyPem string, err error) {
data, err := x509.MarshalECPrivateKey(privkey)
if err != nil {
return "", xerrors.Wrap(err, "failed to marshal EC private key")
}
block := &pem.Block{
Type: "EC PRIVATE KEY",
Bytes: data,
}
return string(pem.EncodeToMemory(block)), nil
}

View File

@ -0,0 +1,26 @@
package huaweicloudcdnsdk
import (
"github.com/huaweicloud/huaweicloud-sdk-go-v3/core"
hcCdn "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2"
)
type Client struct {
hcCdn.CdnClient
}
func NewClient(hcClient *core.HcHttpClient) *Client {
return &Client{
CdnClient: *hcCdn.NewCdnClient(hcClient),
}
}
func (c *Client) UploadDomainMultiCertificatesEx(request *UpdateDomainMultiCertificatesExRequest) (*UpdateDomainMultiCertificatesExResponse, error) {
requestDef := hcCdn.GenReqDefForUpdateDomainMultiCertificates()
if resp, err := c.HcClient.Sync(request, requestDef); err != nil {
return nil, err
} else {
return resp.(*UpdateDomainMultiCertificatesExResponse), nil
}
}

View File

@ -0,0 +1,62 @@
package huaweicloudcdnsdk
import (
hcCdnModel "github.com/huaweicloud/huaweicloud-sdk-go-v3/services/cdn/v2/model"
"github.com/usual2970/certimate/internal/pkg/utils/cast"
)
type UpdateDomainMultiCertificatesExRequestBodyContent struct {
hcCdnModel.UpdateDomainMultiCertificatesRequestBodyContent `json:",inline"`
// 华为云官方 SDK 中目前提供的字段缺失,这里暂时先需自定义请求,可能需要等之后 SDK 更新。
SCMCertificateId *string `json:"scm_certificate_id,omitempty"`
}
type UpdateDomainMultiCertificatesExRequestBody struct {
Https *UpdateDomainMultiCertificatesExRequestBodyContent `json:"https,omitempty"`
}
type UpdateDomainMultiCertificatesExRequest struct {
Body *UpdateDomainMultiCertificatesExRequestBody `json:"body,omitempty"`
}
type UpdateDomainMultiCertificatesExResponse struct {
hcCdnModel.UpdateDomainMultiCertificatesResponse
}
func (m *UpdateDomainMultiCertificatesExRequestBodyContent) MergeConfig(src *hcCdnModel.ConfigsGetBody) *UpdateDomainMultiCertificatesExRequestBodyContent {
if src == nil {
return m
}
// 华为云 API 中不传的字段表示使用默认值、而非保留原值,因此这里需要把原配置中的参数重新赋值回去。
// 而且蛋疼的是查询接口返回的数据结构和更新接口传入的参数结构不一致,需要做很多转化。
if *src.OriginProtocol == "follow" {
m.AccessOriginWay = cast.Int32Ptr(1)
} else if *src.OriginProtocol == "http" {
m.AccessOriginWay = cast.Int32Ptr(2)
} else if *src.OriginProtocol == "https" {
m.AccessOriginWay = cast.Int32Ptr(3)
}
if src.ForceRedirect != nil {
m.ForceRedirectConfig = &hcCdnModel.ForceRedirect{}
if src.ForceRedirect.Status == "on" {
m.ForceRedirectConfig.Switch = 1
m.ForceRedirectConfig.RedirectType = src.ForceRedirect.Type
} else {
m.ForceRedirectConfig.Switch = 0
}
}
if src.Https != nil {
if *src.Https.Http2Status == "on" {
m.Http2 = cast.Int32Ptr(1)
}
}
return m
}