add tencent cdn deployer

This commit is contained in:
yoan
2024-08-29 11:34:08 +08:00
parent 1861e73531
commit 1e11b23fdc
15 changed files with 288 additions and 128 deletions

View File

@@ -1,17 +1,13 @@
package applicant
import (
"certimate/internal/domain"
"encoding/json"
"os"
"github.com/go-acme/lego/v4/providers/dns/alidns"
)
type aliyunAccess struct {
AccessKeyId string `json:"accessKeyId"`
AccessKeySecret string `json:"accessKeySecret"`
}
type aliyun struct {
option *ApplyOption
}
@@ -24,7 +20,7 @@ func NewAliyun(option *ApplyOption) Applicant {
func (a *aliyun) Apply() (*Certificate, error) {
access := &aliyunAccess{}
access := &domain.AliyunAccess{}
json.Unmarshal([]byte(a.option.Access), access)
os.Setenv("ALICLOUD_ACCESS_KEY", access.AccessKeyId)

View File

@@ -1,17 +1,13 @@
package applicant
import (
"certimate/internal/domain"
"encoding/json"
"os"
"github.com/go-acme/lego/v4/providers/dns/tencentcloud"
)
type tencentAccess struct {
SecretId string `json:"secretId"`
SecretKey string `json:"secretKey"`
}
type tencent struct {
option *ApplyOption
}
@@ -24,7 +20,7 @@ func NewTencent(option *ApplyOption) Applicant {
func (t *tencent) Apply() (*Certificate, error) {
access := &tencentAccess{}
access := &domain.TencentAccess{}
json.Unmarshal([]byte(t.option.Access), access)
os.Setenv("TENCENTCLOUD_SECRET_ID", access.SecretId)

View File

@@ -2,6 +2,7 @@ package deployer
import (
"certimate/internal/applicant"
"certimate/internal/domain"
"certimate/internal/utils/rand"
"context"
"encoding/json"
@@ -15,18 +16,13 @@ import (
"github.com/alibabacloud-go/tea/tea"
)
type aliyunAccess struct {
AccessKeyId string `json:"accessKeyId"`
AccessKeySecret string `json:"accessKeySecret"`
}
type aliyun struct {
client *cas20200407.Client
option *DeployerOption
}
func NewAliyun(option *DeployerOption) (Deployer, error) {
access := &aliyunAccess{}
access := &domain.AliyunAccess{}
json.Unmarshal([]byte(option.Access), access)
a := &aliyun{
option: option,

View File

@@ -1,6 +1,7 @@
package deployer
import (
"certimate/internal/domain"
"context"
"encoding/json"
"fmt"
@@ -17,7 +18,7 @@ type AliyunCdn struct {
}
func NewAliyunCdn(option *DeployerOption) (*AliyunCdn, error) {
access := &aliyunAccess{}
access := &domain.AliyunAccess{}
json.Unmarshal([]byte(option.Access), access)
a := &AliyunCdn{
option: option,

View File

@@ -14,10 +14,11 @@ const (
)
const (
targetAliyunOss = "aliyun-oss"
targetAliyunCdn = "aliyun-cdn"
targetSSH = "ssh"
targetWebhook = "webhook"
targetAliyunOss = "aliyun-oss"
targetAliyunCdn = "aliyun-cdn"
targetSSH = "ssh"
targetWebhook = "webhook"
targetTencentCdn = "tencent-cdn"
)
type DeployerOption struct {
@@ -58,6 +59,8 @@ func Get(record *models.Record, cert *applicant.Certificate) (Deployer, error) {
return NewSSH(option)
case targetWebhook:
return NewWebhook(option)
case targetTencentCdn:
return NewTencentCdn(option)
}
return nil, errors.New("not implemented")
}

View File

@@ -0,0 +1,161 @@
package deployer
import (
"certimate/internal/domain"
"context"
"encoding/json"
"errors"
"fmt"
"strings"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
"github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
ssl "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ssl/v20191205"
tag "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/tag/v20180813"
)
type tencentCdn struct {
option *DeployerOption
credential *common.Credential
}
func NewTencentCdn(option *DeployerOption) (Deployer, error) {
access := &domain.TencentAccess{}
if err := json.Unmarshal([]byte(option.Access), access); err != nil {
return nil, fmt.Errorf("failed to unmarshal tencent access: %w", err)
}
credential := common.NewCredential(
access.SecretId,
access.SecretKey,
)
return &tencentCdn{
option: option,
credential: credential,
}, nil
}
func (t *tencentCdn) Deploy(ctx context.Context) error {
// 查询有没有对应的资源
resource, err := t.resource()
if err != nil {
return fmt.Errorf("failed to get resource: %w", err)
}
// 上传证书
certId, err := t.uploadCert()
if err != nil {
return fmt.Errorf("failed to upload certificate: %w", err)
}
if err := t.deploy(resource, certId); err != nil {
return fmt.Errorf("failed to deploy: %w", err)
}
return nil
}
func (t *tencentCdn) uploadCert() (string, error) {
cpf := profile.NewClientProfile()
cpf.HttpProfile.Endpoint = "ssl.tencentcloudapi.com"
client, _ := ssl.NewClient(t.credential, "", cpf)
request := ssl.NewUploadCertificateRequest()
request.CertificatePublicKey = common.StringPtr(t.option.Certificate.Certificate)
request.CertificatePrivateKey = common.StringPtr(t.option.Certificate.PrivateKey)
request.Alias = common.StringPtr(t.option.Domain)
request.Repeatable = common.BoolPtr(true)
response, err := client.UploadCertificate(request)
if err != nil {
return "", fmt.Errorf("failed to upload certificate: %w", err)
}
return *response.Response.CertificateId, nil
}
func (t *tencentCdn) deploy(resource *tag.ResourceTagMapping, certId string) error {
cpf := profile.NewClientProfile()
cpf.HttpProfile.Endpoint = "ssl.tencentcloudapi.com"
// 实例化要请求产品的client对象,clientProfile是可选的
client, _ := ssl.NewClient(t.credential, "", cpf)
resourceId, err := getResourceId(resource)
if err != nil {
return fmt.Errorf("failed to get resource id: %w", err)
}
// 实例化一个请求对象,每个接口都会对应一个request对象
request := ssl.NewDeployCertificateInstanceRequest()
request.CertificateId = common.StringPtr(certId)
request.InstanceIdList = common.StringPtrs([]string{resourceId})
request.ResourceType = common.StringPtr("cdn")
request.Status = common.Int64Ptr(1)
// 返回的resp是一个DeployCertificateInstanceResponse的实例与请求对象对应
_, err = client.DeployCertificateInstance(request)
if err != nil {
return fmt.Errorf("failed to deploy certificate: %w", err)
}
return nil
}
func (t *tencentCdn) resource() (*tag.ResourceTagMapping, error) {
request := tag.NewGetResourcesRequest()
cpf := profile.NewClientProfile()
cpf.HttpProfile.Endpoint = "tag.tencentcloudapi.com"
client, err := tag.NewClient(t.credential, "", cpf)
if err != nil {
return nil, fmt.Errorf("failed to create client: %w", err)
}
response, err := client.GetResources(request)
if err != nil {
return nil, fmt.Errorf("failed to get resources: %w", err)
}
for _, resource := range response.Response.ResourceTagMappingList {
if t.compare(resource) {
return resource, nil
}
}
return nil, errors.New("no resource found")
}
func (t *tencentCdn) compare(resource *tag.ResourceTagMapping) bool {
slices := strings.Split(*resource.Resource, "/")
if len(slices) != 3 {
return false
}
typeSlices := strings.Split(slices[0], "::")
if len(typeSlices) != 3 {
return false
}
if typeSlices[1] != "cdn" || slices[2] != t.option.Domain {
return false
}
return true
}
func getResourceId(resource *tag.ResourceTagMapping) (string, error) {
slices := strings.Split(*resource.Resource, "/")
if len(slices) != 3 {
return "", errors.New("invalid resource")
}
return slices[2], nil
}

13
internal/domain/access.go Normal file
View File

@@ -0,0 +1,13 @@
package domain
type AliyunAccess struct {
AccessKeyId string `json:"accessKeyId"`
AccessKeySecret string `json:"accessKeySecret"`
}
type TencentAccess struct {
SecretId string `json:"secretId"`
SecretKey string `json:"secretKey"`
}