feat: new deployment provider: ctcccloud elb

This commit is contained in:
Fu Diwei 2025-06-14 21:37:18 +08:00
parent 5d6bc03f21
commit 898311c6e5
19 changed files with 1115 additions and 2 deletions

View File

@ -46,6 +46,7 @@ import (
pCTCCCloudAO "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ctcccloud-ao" pCTCCCloudAO "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ctcccloud-ao"
pCTCCCloudCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ctcccloud-cdn" pCTCCCloudCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ctcccloud-cdn"
pCTCCCloudCMS "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ctcccloud-cms" pCTCCCloudCMS "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ctcccloud-cms"
pCTCCCloudELB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ctcccloud-elb"
pCTCCCloudICDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ctcccloud-icdn" pCTCCCloudICDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ctcccloud-icdn"
pCTCCCloudLVDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ctcccloud-lvdn" pCTCCCloudLVDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ctcccloud-lvdn"
pDogeCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/dogecloud-cdn" pDogeCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/dogecloud-cdn"
@ -626,7 +627,7 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
return deployer, err return deployer, err
} }
case domain.DeploymentProviderTypeCTCCCloudAO, domain.DeploymentProviderTypeCTCCCloudCDN, domain.DeploymentProviderTypeCTCCCloudCMS, domain.DeploymentProviderTypeCTCCCloudICDN, domain.DeploymentProviderTypeCTCCCloudLVDN: case domain.DeploymentProviderTypeCTCCCloudAO, domain.DeploymentProviderTypeCTCCCloudCDN, domain.DeploymentProviderTypeCTCCCloudCMS, domain.DeploymentProviderTypeCTCCCloudELB, domain.DeploymentProviderTypeCTCCCloudICDN, domain.DeploymentProviderTypeCTCCCloudLVDN:
{ {
access := domain.AccessConfigForCTCCCloud{} access := domain.AccessConfigForCTCCCloud{}
if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil { if err := maputil.Populate(options.ProviderAccessConfig, &access); err != nil {
@ -657,6 +658,17 @@ func createDeployerProvider(options *deployerProviderOptions) (deployer.Deployer
}) })
return deployer, err return deployer, err
case domain.DeploymentProviderTypeCTCCCloudELB:
deployer, err := pCTCCCloudELB.NewDeployer(&pCTCCCloudELB.DeployerConfig{
AccessKeyId: access.AccessKeyId,
SecretAccessKey: access.SecretAccessKey,
RegionId: maputil.GetString(options.ProviderServiceConfig, "regionId"),
ResourceType: pCTCCCloudELB.ResourceType(maputil.GetString(options.ProviderServiceConfig, "resourceType")),
LoadbalancerId: maputil.GetString(options.ProviderServiceConfig, "loadbalancerId"),
ListenerId: maputil.GetString(options.ProviderServiceConfig, "listenerId"),
})
return deployer, err
case domain.DeploymentProviderTypeCTCCCloudICDN: case domain.DeploymentProviderTypeCTCCCloudICDN:
deployer, err := pCTCCCloudICDN.NewDeployer(&pCTCCCloudICDN.DeployerConfig{ deployer, err := pCTCCCloudICDN.NewDeployer(&pCTCCCloudICDN.DeployerConfig{
AccessKeyId: access.AccessKeyId, AccessKeyId: access.AccessKeyId,

View File

@ -218,7 +218,7 @@ const (
DeploymentProviderTypeCTCCCloudAO = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-ao") DeploymentProviderTypeCTCCCloudAO = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-ao")
DeploymentProviderTypeCTCCCloudCDN = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-cdn") DeploymentProviderTypeCTCCCloudCDN = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-cdn")
DeploymentProviderTypeCTCCCloudCMS = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-cms") DeploymentProviderTypeCTCCCloudCMS = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-cms")
DeploymentProviderTypeCTCCCloudELB = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-elb") // (预留) DeploymentProviderTypeCTCCCloudELB = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-elb")
DeploymentProviderTypeCTCCCloudICDN = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-icdn") DeploymentProviderTypeCTCCCloudICDN = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-icdn")
DeploymentProviderTypeCTCCCloudLVDN = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-ldvn") DeploymentProviderTypeCTCCCloudLVDN = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-ldvn")
DeploymentProviderTypeDogeCloudCDN = DeploymentProviderType(AccessProviderTypeDogeCloud + "-cdn") DeploymentProviderTypeDogeCloudCDN = DeploymentProviderType(AccessProviderTypeDogeCloud + "-cdn")

View File

@ -0,0 +1,10 @@
package ctcccloudelb
type ResourceType string
const (
// 资源类型:部署到指定负载均衡器。
RESOURCE_TYPE_LOADBALANCER = ResourceType("loadbalancer")
// 资源类型:部署到指定监听器。
RESOURCE_TYPE_LISTENER = ResourceType("listener")
)

View File

@ -0,0 +1,199 @@
package ctcccloudelb
import (
"context"
"errors"
"fmt"
"log/slog"
"strings"
"github.com/usual2970/certimate/internal/pkg/core/deployer"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/ctcccloud-elb"
ctyunelb "github.com/usual2970/certimate/internal/pkg/sdk3rd/ctyun/elb"
typeutil "github.com/usual2970/certimate/internal/pkg/utils/type"
)
type DeployerConfig struct {
// 天翼云 AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// 天翼云 SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
// 天翼云资源池 ID。
RegionId string `json:"regionId"`
// 部署资源类型。
ResourceType ResourceType `json:"resourceType"`
// 负载均衡实例 ID。
// 部署资源类型为 [RESOURCE_TYPE_LOADBALANCER] 时必填。
LoadbalancerId string `json:"loadbalancerId,omitempty"`
// 负载均衡监听器 ID。
// 部署资源类型为 [RESOURCE_TYPE_LISTENER] 时必填。
ListenerId string `json:"listenerId,omitempty"`
}
type DeployerProvider struct {
config *DeployerConfig
logger *slog.Logger
sdkClient *ctyunelb.Client
sslUploader uploader.Uploader
}
var _ deployer.Deployer = (*DeployerProvider)(nil)
func NewDeployer(config *DeployerConfig) (*DeployerProvider, error) {
if config == nil {
panic("config is nil")
}
client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey)
if err != nil {
return nil, fmt.Errorf("failed to create sdk client: %w", err)
}
uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{
AccessKeyId: config.AccessKeyId,
SecretAccessKey: config.SecretAccessKey,
RegionId: config.RegionId,
})
if err != nil {
return nil, fmt.Errorf("failed to create ssl uploader: %w", err)
}
return &DeployerProvider{
config: config,
logger: slog.Default(),
sdkClient: client,
sslUploader: uploader,
}, nil
}
func (d *DeployerProvider) WithLogger(logger *slog.Logger) deployer.Deployer {
if logger == nil {
d.logger = slog.New(slog.DiscardHandler)
} else {
d.logger = logger
}
return d
}
func (d *DeployerProvider) Deploy(ctx context.Context, certPEM string, privkeyPEM string) (*deployer.DeployResult, error) {
// 上传证书到 ELB
upres, err := d.sslUploader.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))
}
// 根据部署资源类型决定部署方式
switch d.config.ResourceType {
case RESOURCE_TYPE_LOADBALANCER:
if err := d.deployToLoadbalancer(ctx, upres.CertId); err != nil {
return nil, err
}
case RESOURCE_TYPE_LISTENER:
if err := d.deployToListener(ctx, upres.CertId); err != nil {
return nil, err
}
default:
return nil, fmt.Errorf("unsupported resource type '%s'", d.config.ResourceType)
}
return &deployer.DeployResult{}, nil
}
func (d *DeployerProvider) deployToLoadbalancer(ctx context.Context, cloudCertId string) error {
if d.config.LoadbalancerId == "" {
return errors.New("config `loadbalancerId` is required")
}
// 查询监听列表
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=24&api=5654&data=88&isNormal=1&vid=82
listenerIds := make([]string, 0)
for {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
listListenerReq := &ctyunelb.ListListenerRequest{
RegionID: typeutil.ToPtr(d.config.RegionId),
LoadBalancerID: typeutil.ToPtr(d.config.LoadbalancerId),
}
listListenerResp, err := d.sdkClient.ListListener(listListenerReq)
d.logger.Debug("sdk request 'elb.ListListener'", slog.Any("request", listListenerReq), slog.Any("response", listListenerResp))
if err != nil {
return fmt.Errorf("failed to execute sdk request 'elb.ListListener': %w", err)
}
for _, listener := range listListenerResp.ReturnObj {
if strings.EqualFold(listener.Protocol, "HTTPS") {
listenerIds = append(listenerIds, listener.ID)
}
}
break
}
// 遍历更新监听证书
if len(listenerIds) == 0 {
d.logger.Info("no elb listeners to deploy")
} else {
d.logger.Info("found https listeners to deploy", slog.Any("listenerIds", listenerIds))
var errs []error
for _, listenerId := range listenerIds {
select {
case <-ctx.Done():
return ctx.Err()
default:
if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil {
errs = append(errs, err)
}
}
}
if len(errs) > 0 {
return errors.Join(errs...)
}
}
return nil
}
func (d *DeployerProvider) deployToListener(ctx context.Context, cloudCertId string) error {
if d.config.ListenerId == "" {
return errors.New("config `listenerId` is required")
}
// 更新监听
if err := d.updateListenerCertificate(ctx, d.config.ListenerId, cloudCertId); err != nil {
return err
}
return nil
}
func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId string) error {
// 更新监听器
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=24&api=5652&data=88&isNormal=1&vid=82
setLoadBalancerHTTPSListenerAttributeReq := &ctyunelb.UpdateListenerRequest{
RegionID: typeutil.ToPtr(d.config.RegionId),
ListenerID: typeutil.ToPtr(cloudListenerId),
CertificateID: typeutil.ToPtr(cloudCertId),
}
setLoadBalancerHTTPSListenerAttributeResp, err := d.sdkClient.UpdateListener(setLoadBalancerHTTPSListenerAttributeReq)
d.logger.Debug("sdk request 'elb.UpdateListener'", slog.Any("request", setLoadBalancerHTTPSListenerAttributeReq), slog.Any("response", setLoadBalancerHTTPSListenerAttributeResp))
if err != nil {
return fmt.Errorf("failed to execute sdk request 'elb.UpdateListener': %w", err)
}
return nil
}
func createSdkClient(accessKeyId, secretAccessKey string) (*ctyunelb.Client, error) {
return ctyunelb.NewClient(accessKeyId, secretAccessKey)
}

View File

@ -0,0 +1,118 @@
package ctcccloudelb_test
import (
"context"
"flag"
"fmt"
"os"
"strings"
"testing"
provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/ctcccloud-elb"
)
var (
fInputCertPath string
fInputKeyPath string
fAccessKeyId string
fSecretAccessKey string
fRegionId string
fLoadbalancerId string
fListenerId string
)
func init() {
argsPrefix := "CERTIMATE_DEPLOYER_CTCCCLOUDELB_"
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
flag.StringVar(&fSecretAccessKey, argsPrefix+"SECRETACCESSKEY", "", "")
flag.StringVar(&fRegionId, argsPrefix+"REGIONID", "", "")
flag.StringVar(&fLoadbalancerId, argsPrefix+"LOADBALANCERID", "", "")
flag.StringVar(&fListenerId, argsPrefix+"LISTENERID", "", "")
}
/*
Shell command to run this test:
go test -v ./ctcccloud_elb_test.go -args \
--CERTIMATE_DEPLOYER_CTCCCLOUDELB_INPUTCERTPATH="/path/to/your-input-cert.pem" \
--CERTIMATE_DEPLOYER_CTCCCLOUDELB_INPUTKEYPATH="/path/to/your-input-key.pem" \
--CERTIMATE_DEPLOYER_CTCCCLOUDELB_ACCESSKEYID="your-access-key-id" \
--CERTIMATE_DEPLOYER_CTCCCLOUDELB_SECRETACCESSKEY="your-secret-access-key" \
--CERTIMATE_DEPLOYER_CTCCCLOUDELB_REGIONID="your-region-id" \
--CERTIMATE_DEPLOYER_CTCCCLOUDELB_LOADBALANCERID="your-elb-instance-id" \
--CERTIMATE_DEPLOYER_CTCCCLOUDELB_LISTENERID="your-elb-listener-id"
*/
func TestDeploy(t *testing.T) {
flag.Parse()
t.Run("Deploy_ToLoadbalancer", func(t *testing.T) {
t.Log(strings.Join([]string{
"args:",
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
fmt.Sprintf("SECRETACCESSKEY: %v", fSecretAccessKey),
fmt.Sprintf("REGIONID: %v", fRegionId),
fmt.Sprintf("LOADBALANCERID: %v", fLoadbalancerId),
}, "\n"))
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
AccessKeyId: fAccessKeyId,
SecretAccessKey: fSecretAccessKey,
RegionId: fRegionId,
ResourceType: provider.RESOURCE_TYPE_LOADBALANCER,
LoadbalancerId: fLoadbalancerId,
})
if err != nil {
t.Errorf("err: %+v", err)
return
}
fInputCertData, _ := os.ReadFile(fInputCertPath)
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
if err != nil {
t.Errorf("err: %+v", err)
return
}
t.Logf("ok: %v", res)
})
t.Run("Deploy_ToListener", func(t *testing.T) {
t.Log(strings.Join([]string{
"args:",
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
fmt.Sprintf("SECRETACCESSKEY: %v", fSecretAccessKey),
fmt.Sprintf("REGIONID: %v", fRegionId),
fmt.Sprintf("LISTENERID: %v", fListenerId),
}, "\n"))
deployer, err := provider.NewDeployer(&provider.DeployerConfig{
AccessKeyId: fAccessKeyId,
SecretAccessKey: fSecretAccessKey,
RegionId: fRegionId,
ResourceType: provider.RESOURCE_TYPE_LISTENER,
ListenerId: fListenerId,
})
if err != nil {
t.Errorf("err: %+v", err)
return
}
fInputCertData, _ := os.ReadFile(fInputCertPath)
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
res, err := deployer.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
if err != nil {
t.Errorf("err: %+v", err)
return
}
t.Logf("ok: %v", res)
})
}

View File

@ -0,0 +1,133 @@
package ctcccloudelb
import (
"context"
"fmt"
"log/slog"
"time"
"github.com/google/uuid"
"github.com/usual2970/certimate/internal/pkg/core/uploader"
ctyunelb "github.com/usual2970/certimate/internal/pkg/sdk3rd/ctyun/elb"
certutil "github.com/usual2970/certimate/internal/pkg/utils/cert"
typeutil "github.com/usual2970/certimate/internal/pkg/utils/type"
)
type UploaderConfig struct {
// 天翼云 AccessKeyId。
AccessKeyId string `json:"accessKeyId"`
// 天翼云 SecretAccessKey。
SecretAccessKey string `json:"secretAccessKey"`
// 天翼云资源池 ID。
RegionId string `json:"regionId"`
}
type UploaderProvider struct {
config *UploaderConfig
logger *slog.Logger
sdkClient *ctyunelb.Client
}
var _ uploader.Uploader = (*UploaderProvider)(nil)
func NewUploader(config *UploaderConfig) (*UploaderProvider, error) {
if config == nil {
panic("config is nil")
}
client, err := createSdkClient(config.AccessKeyId, config.SecretAccessKey)
if err != nil {
return nil, fmt.Errorf("failed to create sdk client: %w", err)
}
return &UploaderProvider{
config: config,
logger: slog.Default(),
sdkClient: client,
}, nil
}
func (u *UploaderProvider) WithLogger(logger *slog.Logger) uploader.Uploader {
if logger == nil {
u.logger = slog.New(slog.DiscardHandler)
} else {
u.logger = logger
}
return u
}
func (u *UploaderProvider) Upload(ctx context.Context, certPEM string, privkeyPEM string) (*uploader.UploadResult, error) {
// 解析证书内容
certX509, err := certutil.ParseCertificateFromPEM(certPEM)
if err != nil {
return nil, err
}
// 查询证书列表,避免重复上传
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=24&api=5692&data=88&isNormal=1&vid=82
listCertificateReq := &ctyunelb.ListCertificateRequest{
RegionID: typeutil.ToPtr(u.config.RegionId),
}
listCertificateResp, err := u.sdkClient.ListCertificate(listCertificateReq)
u.logger.Debug("sdk request 'elb.ListCertificate'", slog.Any("request", listCertificateReq), slog.Any("response", listCertificateResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'elb.ListCertificate': %w", err)
} else {
for _, certRecord := range listCertificateResp.ReturnObj {
var isSameCert bool
if certRecord.Certificate == certPEM {
isSameCert = true
} else {
oldCertX509, err := certutil.ParseCertificateFromPEM(certRecord.Certificate)
if err != nil {
continue
}
isSameCert = certutil.EqualCertificate(certX509, oldCertX509)
}
// 如果已存在相同证书,直接返回
if isSameCert {
u.logger.Info("ssl certificate already exists")
return &uploader.UploadResult{
CertId: certRecord.ID,
CertName: certRecord.Name,
}, nil
}
}
}
// 生成新证书名(需符合天翼云命名规则)
certName := fmt.Sprintf("certimate-%d", time.Now().UnixMilli())
// 创建证书
// REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=24&api=5685&data=88&isNormal=1&vid=82
createCertificateReq := &ctyunelb.CreateCertificateRequest{
ClientToken: typeutil.ToPtr(generateClientToken()),
RegionID: typeutil.ToPtr(u.config.RegionId),
Name: typeutil.ToPtr(certName),
Description: typeutil.ToPtr("upload from certimate"),
Type: typeutil.ToPtr("Server"),
Certificate: typeutil.ToPtr(certPEM),
PrivateKey: typeutil.ToPtr(privkeyPEM),
}
createCertificateResp, err := u.sdkClient.CreateCertificate(createCertificateReq)
u.logger.Debug("sdk request 'elb.CreateCertificate'", slog.Any("request", createCertificateReq), slog.Any("response", createCertificateResp))
if err != nil {
return nil, fmt.Errorf("failed to execute sdk request 'elb.CreateCertificate': %w", err)
}
return &uploader.UploadResult{
CertId: createCertificateResp.ReturnObj.ID,
CertName: certName,
}, nil
}
func createSdkClient(accessKeyId, secretAccessKey string) (*ctyunelb.Client, error) {
return ctyunelb.NewClient(accessKeyId, secretAccessKey)
}
func generateClientToken() string {
return uuid.New().String()
}

View File

@ -0,0 +1,77 @@
package ctcccloudelb_test
import (
"context"
"encoding/json"
"flag"
"fmt"
"os"
"strings"
"testing"
provider "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/ctcccloud-elb"
)
var (
fInputCertPath string
fInputKeyPath string
fAccessKeyId string
fSecretAccessKey string
fRegionId string
)
func init() {
argsPrefix := "CERTIMATE_UPLOADER_CTCCCLOUDELB_"
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "")
flag.StringVar(&fSecretAccessKey, argsPrefix+"SECRETACCESSKEY", "", "")
flag.StringVar(&fRegionId, argsPrefix+"REGIONID", "", "")
}
/*
Shell command to run this test:
go test -v ./ctcccloud_elb_test.go -args \
--CERTIMATE_UPLOADER_CTCCCLOUDELB_INPUTCERTPATH="/path/to/your-input-cert.pem" \
--CERTIMATE_UPLOADER_CTCCCLOUDELB_INPUTKEYPATH="/path/to/your-input-key.pem" \
--CERTIMATE_UPLOADER_CTCCCLOUDELB_ACCESSKEYID="your-access-key-id" \
--CERTIMATE_UPLOADER_CTCCCLOUDELB_SECRETACCESSKEY="your-secret-access-key" \
--CERTIMATE_UPLOADER_CTCCCLOUDELB_REGIONID="your-region-id"
*/
func TestDeploy(t *testing.T) {
flag.Parse()
t.Run("Deploy", func(t *testing.T) {
t.Log(strings.Join([]string{
"args:",
fmt.Sprintf("INPUTCERTPATH: %v", fInputCertPath),
fmt.Sprintf("INPUTKEYPATH: %v", fInputKeyPath),
fmt.Sprintf("ACCESSKEYID: %v", fAccessKeyId),
fmt.Sprintf("SECRETACCESSKEY: %v", fSecretAccessKey),
fmt.Sprintf("REGIONID: %v", fRegionId),
}, "\n"))
uploader, err := provider.NewUploader(&provider.UploaderConfig{
AccessKeyId: fAccessKeyId,
SecretAccessKey: fSecretAccessKey,
RegionId: fRegionId,
})
if err != nil {
t.Errorf("err: %+v", err)
return
}
fInputCertData, _ := os.ReadFile(fInputCertPath)
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
res, err := uploader.Upload(context.Background(), string(fInputCertData), string(fInputKeyData))
if err != nil {
t.Errorf("err: %+v", err)
return
}
sres, _ := json.Marshal(res)
t.Logf("ok: %s", string(sres))
})
}

View File

@ -0,0 +1,45 @@
package elb
import (
"context"
"net/http"
)
type CreateCertificateRequest struct {
ClientToken *string `json:"clientToken,omitempty"`
RegionID *string `json:"regionID,omitempty"`
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
Type *string `json:"type,omitempty"`
Certificate *string `json:"certificate,omitempty"`
PrivateKey *string `json:"privateKey,omitempty"`
}
type CreateCertificateResponse struct {
baseResult
ReturnObj *struct {
ID string `json:"id"`
} `json:"returnObj,omitempty"`
}
func (c *Client) CreateCertificate(req *CreateCertificateRequest) (*CreateCertificateResponse, error) {
return c.CreateCertificateWithContext(context.Background(), req)
}
func (c *Client) CreateCertificateWithContext(ctx context.Context, req *CreateCertificateRequest) (*CreateCertificateResponse, error) {
httpreq, err := c.newRequest(http.MethodPost, "/v4/elb/create-certificate")
if err != nil {
return nil, err
} else {
httpreq.SetBody(req)
httpreq.SetContext(ctx)
}
result := &CreateCertificateResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}

View File

@ -0,0 +1,56 @@
package elb
import (
"context"
"net/http"
)
type ListCertificateRequest struct {
ClientToken *string `json:"clientToken,omitempty"`
RegionID *string `json:"regionID,omitempty"`
IDs *string `json:"IDs,omitempty"`
Name *string `json:"name,omitempty"`
Type *string `json:"type,omitempty"`
}
type ListCertificateResponse struct {
baseResult
ReturnObj []*CertificateRecord `json:"returnObj,omitempty"`
}
func (c *Client) ListCertificate(req *ListCertificateRequest) (*ListCertificateResponse, error) {
return c.ListCertificateWithContext(context.Background(), req)
}
func (c *Client) ListCertificateWithContext(ctx context.Context, req *ListCertificateRequest) (*ListCertificateResponse, error) {
httpreq, err := c.newRequest(http.MethodGet, "/v4/elb/list-certificate")
if err != nil {
return nil, err
} else {
if req.ClientToken != nil {
httpreq.SetQueryParam("clientToken", *req.ClientToken)
}
if req.RegionID != nil {
httpreq.SetQueryParam("regionID", *req.RegionID)
}
if req.IDs != nil {
httpreq.SetQueryParam("IDs", *req.IDs)
}
if req.Name != nil {
httpreq.SetQueryParam("name", *req.Name)
}
if req.Type != nil {
httpreq.SetQueryParam("type", *req.Type)
}
httpreq.SetContext(ctx)
}
result := &ListCertificateResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}

View File

@ -0,0 +1,64 @@
package elb
import (
"context"
"net/http"
)
type ListListenerRequest struct {
ClientToken *string `json:"clientToken,omitempty"`
RegionID *string `json:"regionID,omitempty"`
ProjectID *string `json:"projectID,omitempty"`
IDs *string `json:"IDs,omitempty"`
Name *string `json:"name,omitempty"`
LoadBalancerID *string `json:"loadBalancerID,omitempty"`
AccessControlID *string `json:"accessControlID,omitempty"`
}
type ListListenerResponse struct {
baseResult
ReturnObj []*ListenerRecord `json:"returnObj,omitempty"`
}
func (c *Client) ListListener(req *ListListenerRequest) (*ListListenerResponse, error) {
return c.ListListenerWithContext(context.Background(), req)
}
func (c *Client) ListListenerWithContext(ctx context.Context, req *ListListenerRequest) (*ListListenerResponse, error) {
httpreq, err := c.newRequest(http.MethodGet, "/v4/elb/list-listener")
if err != nil {
return nil, err
} else {
if req.ClientToken != nil {
httpreq.SetQueryParam("clientToken", *req.ClientToken)
}
if req.RegionID != nil {
httpreq.SetQueryParam("regionID", *req.RegionID)
}
if req.ProjectID != nil {
httpreq.SetQueryParam("projectID", *req.ProjectID)
}
if req.IDs != nil {
httpreq.SetQueryParam("IDs", *req.IDs)
}
if req.Name != nil {
httpreq.SetQueryParam("name", *req.Name)
}
if req.LoadBalancerID != nil {
httpreq.SetQueryParam("loadBalancerID", *req.LoadBalancerID)
}
if req.LoadBalancerID != nil {
httpreq.SetQueryParam("accessControlID", *req.AccessControlID)
}
httpreq.SetContext(ctx)
}
result := &ListListenerResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}

View File

@ -0,0 +1,48 @@
package elb
import (
"context"
"net/http"
)
type ShowListenerRequest struct {
ClientToken *string `json:"clientToken,omitempty"`
RegionID *string `json:"regionID,omitempty"`
ListenerID *string `json:"listenerID,omitempty"`
}
type ShowListenerResponse struct {
baseResult
ReturnObj []*ListenerRecord `json:"returnObj,omitempty"`
}
func (c *Client) ShowListener(req *ShowListenerRequest) (*ShowListenerResponse, error) {
return c.ShowListenerWithContext(context.Background(), req)
}
func (c *Client) ShowListenerWithContext(ctx context.Context, req *ShowListenerRequest) (*ShowListenerResponse, error) {
httpreq, err := c.newRequest(http.MethodGet, "/v4/elb/show-listener")
if err != nil {
return nil, err
} else {
if req.ClientToken != nil {
httpreq.SetQueryParam("clientToken", *req.ClientToken)
}
if req.RegionID != nil {
httpreq.SetQueryParam("regionID", *req.RegionID)
}
if req.ListenerID != nil {
httpreq.SetQueryParam("listenerID", *req.ListenerID)
}
httpreq.SetContext(ctx)
}
result := &ShowListenerResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}

View File

@ -0,0 +1,44 @@
package elb
import (
"context"
"net/http"
)
type UpdateListenerRequest struct {
ClientToken *string `json:"clientToken,omitempty"`
RegionID *string `json:"regionID,omitempty"`
ListenerID *string `json:"listenerID,omitempty"`
Name *string `json:"name,omitempty"`
Description *string `json:"description,omitempty"`
CertificateID *string `json:"certificateID,omitempty"`
CaEnabled *bool `json:"caEnabled,omitempty"`
ClientCertificateID *string `json:"clientCertificateID,omitempty"`
}
type UpdateListenerResponse struct {
baseResult
ReturnObj []*ListenerRecord `json:"returnObj,omitempty"`
}
func (c *Client) UpdateListener(req *UpdateListenerRequest) (*UpdateListenerResponse, error) {
return c.UpdateListenerWithContext(context.Background(), req)
}
func (c *Client) UpdateListenerWithContext(ctx context.Context, req *UpdateListenerRequest) (*UpdateListenerResponse, error) {
httpreq, err := c.newRequest(http.MethodPost, "/v4/elb/update-listener")
if err != nil {
return nil, err
} else {
httpreq.SetBody(req)
httpreq.SetContext(ctx)
}
result := &UpdateListenerResponse{}
if _, err := c.doRequestWithResult(httpreq, result); err != nil {
return result, err
}
return result, nil
}

View File

@ -0,0 +1,50 @@
package elb
import (
"fmt"
"time"
"github.com/go-resty/resty/v2"
"github.com/usual2970/certimate/internal/pkg/sdk3rd/ctyun/openapi"
)
const endpoint = "https://ctelb-global.ctapi.ctyun.cn"
type Client struct {
client *openapi.Client
}
func NewClient(accessKeyId, secretAccessKey string) (*Client, error) {
client, err := openapi.NewClient(endpoint, accessKeyId, secretAccessKey)
if err != nil {
return nil, err
}
return &Client{client: client}, nil
}
func (c *Client) SetTimeout(timeout time.Duration) *Client {
c.client.SetTimeout(timeout)
return c
}
func (c *Client) newRequest(method string, path string) (*resty.Request, error) {
return c.client.NewRequest(method, path)
}
func (c *Client) doRequest(request *resty.Request) (*resty.Response, error) {
return c.client.DoRequest(request)
}
func (c *Client) doRequestWithResult(request *resty.Request, result baseResultInterface) (*resty.Response, error) {
response, err := c.client.DoRequestWithResult(request, result)
if err == nil {
statusCode := result.GetStatusCode()
errorCode := result.GetError()
if (statusCode != "" && statusCode != "200" && statusCode != "800") || (errorCode != "" && errorCode != "SUCCESS") {
return response, fmt.Errorf("sdkerr: api error, code='%s', message='%s', errorCode='%s', description='%s'", statusCode, result.GetMessage(), result.GetMessage(), result.GetDescription())
}
}
return response, err
}

View File

@ -0,0 +1,104 @@
package elb
import (
"bytes"
"encoding/json"
"strconv"
)
type baseResultInterface interface {
GetStatusCode() string
GetMessage() string
GetError() string
GetDescription() string
}
type baseResult struct {
StatusCode json.RawMessage `json:"statusCode,omitempty"`
Message *string `json:"message,omitempty"`
Error *string `json:"error,omitempty"`
Description *string `json:"description,omitempty"`
RequestId *string `json:"requestId,omitempty"`
}
func (r *baseResult) GetStatusCode() string {
if r.StatusCode == nil {
return ""
}
decoder := json.NewDecoder(bytes.NewReader(r.StatusCode))
token, err := decoder.Token()
if err != nil {
return ""
}
switch t := token.(type) {
case string:
return t
case float64:
return strconv.FormatFloat(t, 'f', -1, 64)
case json.Number:
return t.String()
default:
return ""
}
}
func (r *baseResult) GetMessage() string {
if r.Message == nil {
return ""
}
return *r.Message
}
func (r *baseResult) GetError() string {
if r.Error == nil {
return ""
}
return *r.Error
}
func (r *baseResult) GetDescription() string {
if r.Description == nil {
return ""
}
return *r.Description
}
var _ baseResultInterface = (*baseResult)(nil)
type CertificateRecord struct {
ID string `json:"ID"`
RegionID string `json:"regionID"`
AzName string `json:"azName"`
ProjectID string `json:"projectID"`
Name string `json:"name"`
Description string `json:"description"`
Type string `json:"type"`
Certificate string `json:"certificate"`
PrivateKey string `json:"privateKey"`
Status string `json:"status"`
CreatedTime string `json:"createdTime"`
UpdatedTime string `json:"updatedTime"`
}
type ListenerRecord struct {
ID string `json:"ID"`
RegionID string `json:"regionID"`
AzName string `json:"azName"`
ProjectID string `json:"projectID"`
Name string `json:"name"`
Description string `json:"description"`
LoadBalancerID string `json:"loadBalancerID"`
Protocol string `json:"protocol"`
ProtocolPort int32 `json:"protocolPort"`
CertificateID string `json:"certificateID,omitempty"`
CaEnabled bool `json:"caEnabled"`
ClientCertificateID string `json:"clientCertificateID,omitempty"`
Status string `json:"status"`
CreatedTime string `json:"createdTime"`
UpdatedTime string `json:"updatedTime"`
}

View File

@ -50,6 +50,7 @@ import DeployNodeConfigFormBytePlusCDNConfig from "./DeployNodeConfigFormBytePlu
import DeployNodeConfigFormCdnflyConfig from "./DeployNodeConfigFormCdnflyConfig"; import DeployNodeConfigFormCdnflyConfig from "./DeployNodeConfigFormCdnflyConfig";
import DeployNodeConfigFormCTCCCloudAOConfig from "./DeployNodeConfigFormCTCCCloudAOConfig"; import DeployNodeConfigFormCTCCCloudAOConfig from "./DeployNodeConfigFormCTCCCloudAOConfig";
import DeployNodeConfigFormCTCCCloudCDNConfig from "./DeployNodeConfigFormCTCCCloudCDNConfig"; import DeployNodeConfigFormCTCCCloudCDNConfig from "./DeployNodeConfigFormCTCCCloudCDNConfig";
import DeployNodeConfigFormCTCCCloudELBConfig from "./DeployNodeConfigFormCTCCCloudELBConfig";
import DeployNodeConfigFormCTCCCloudICDNConfig from "./DeployNodeConfigFormCTCCCloudICDNConfig"; import DeployNodeConfigFormCTCCCloudICDNConfig from "./DeployNodeConfigFormCTCCCloudICDNConfig";
import DeployNodeConfigFormCTCCCloudLVDNConfig from "./DeployNodeConfigFormCTCCCloudLVDNConfig"; import DeployNodeConfigFormCTCCCloudLVDNConfig from "./DeployNodeConfigFormCTCCCloudLVDNConfig";
import DeployNodeConfigFormDogeCloudCDNConfig from "./DeployNodeConfigFormDogeCloudCDNConfig"; import DeployNodeConfigFormDogeCloudCDNConfig from "./DeployNodeConfigFormDogeCloudCDNConfig";
@ -273,6 +274,8 @@ const DeployNodeConfigForm = forwardRef<DeployNodeConfigFormInstance, DeployNode
return <DeployNodeConfigFormCTCCCloudAOConfig {...nestedFormProps} />; return <DeployNodeConfigFormCTCCCloudAOConfig {...nestedFormProps} />;
case DEPLOYMENT_PROVIDERS.CTCCCLOUD_CDN: case DEPLOYMENT_PROVIDERS.CTCCCLOUD_CDN:
return <DeployNodeConfigFormCTCCCloudCDNConfig {...nestedFormProps} />; return <DeployNodeConfigFormCTCCCloudCDNConfig {...nestedFormProps} />;
case DEPLOYMENT_PROVIDERS.CTCCCLOUD_ELB:
return <DeployNodeConfigFormCTCCCloudELBConfig {...nestedFormProps} />;
case DEPLOYMENT_PROVIDERS.CTCCCLOUD_ICDN: case DEPLOYMENT_PROVIDERS.CTCCCLOUD_ICDN:
return <DeployNodeConfigFormCTCCCloudICDNConfig {...nestedFormProps} />; return <DeployNodeConfigFormCTCCCloudICDNConfig {...nestedFormProps} />;
case DEPLOYMENT_PROVIDERS.CTCCCLOUD_LVDN: case DEPLOYMENT_PROVIDERS.CTCCCLOUD_LVDN:

View File

@ -0,0 +1,121 @@
import { useTranslation } from "react-i18next";
import { Form, type FormInstance, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import Show from "@/components/Show";
type DeployNodeConfigFormCTCCCloudELBConfigFieldValues = Nullish<{
regionId: string;
resourceType: string;
loadbalancerId?: string;
listenerId?: string;
}>;
export type DeployNodeConfigFormCTCCCloudELBConfigProps = {
form: FormInstance;
formName: string;
disabled?: boolean;
initialValues?: DeployNodeConfigFormCTCCCloudELBConfigFieldValues;
onValuesChange?: (values: DeployNodeConfigFormCTCCCloudELBConfigFieldValues) => void;
};
const RESOURCE_TYPE_LOADBALANCER = "loadbalancer" as const;
const RESOURCE_TYPE_LISTENER = "listener" as const;
const initFormModel = (): DeployNodeConfigFormCTCCCloudELBConfigFieldValues => {
return {
resourceType: RESOURCE_TYPE_LISTENER,
};
};
const DeployNodeConfigFormCTCCCloudELBConfig = ({
form: formInst,
formName,
disabled,
initialValues,
onValuesChange,
}: DeployNodeConfigFormCTCCCloudELBConfigProps) => {
const { t } = useTranslation();
const formSchema = z.object({
resourceType: z.union([z.literal(RESOURCE_TYPE_LOADBALANCER), z.literal(RESOURCE_TYPE_LISTENER)], {
message: t("workflow_node.deploy.form.ctcccloud_elb_resource_type.placeholder"),
}),
regionId: z
.string({ message: t("workflow_node.deploy.form.ctcccloud_elb_region_id.placeholder") })
.nonempty(t("workflow_node.deploy.form.ctcccloud_elb_region_id.placeholder")),
loadbalancerId: z
.string()
.max(64, t("common.errmsg.string_max", { max: 64 }))
.nullish()
.refine((v) => fieldResourceType !== RESOURCE_TYPE_LOADBALANCER || !!v?.trim(), t("workflow_node.deploy.form.ctcccloud_elb_loadbalancer_id.placeholder")),
listenerId: z
.string()
.max(64, t("common.errmsg.string_max", { max: 64 }))
.nullish()
.refine((v) => fieldResourceType !== RESOURCE_TYPE_LISTENER || !!v?.trim(), t("workflow_node.deploy.form.ctcccloud_elb_listener_id.placeholder")),
});
const formRule = createSchemaFieldRule(formSchema);
const fieldResourceType = Form.useWatch("resourceType", formInst);
const handleFormChange = (_: unknown, values: z.infer<typeof formSchema>) => {
onValuesChange?.(values);
};
return (
<Form
form={formInst}
disabled={disabled}
initialValues={initialValues ?? initFormModel()}
layout="vertical"
name={formName}
onValuesChange={handleFormChange}
>
<Form.Item name="resourceType" label={t("workflow_node.deploy.form.ctcccloud_elb_resource_type.label")} rules={[formRule]}>
<Select placeholder={t("workflow_node.deploy.form.ctcccloud_elb_resource_type.placeholder")}>
<Select.Option key={RESOURCE_TYPE_LOADBALANCER} value={RESOURCE_TYPE_LOADBALANCER}>
{t("workflow_node.deploy.form.ctcccloud_elb_resource_type.option.loadbalancer.label")}
</Select.Option>
<Select.Option key={RESOURCE_TYPE_LISTENER} value={RESOURCE_TYPE_LISTENER}>
{t("workflow_node.deploy.form.ctcccloud_elb_resource_type.option.listener.label")}
</Select.Option>
</Select>
</Form.Item>
<Form.Item
name="regionId"
label={t("workflow_node.deploy.form.ctcccloud_elb_region_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.ctcccloud_elb_region_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.ctcccloud_elb_region_id.placeholder")} />
</Form.Item>
<Show when={fieldResourceType === RESOURCE_TYPE_LOADBALANCER}>
<Form.Item
name="loadbalancerId"
label={t("workflow_node.deploy.form.ctcccloud_elb_loadbalancer_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.ctcccloud_elb_loadbalancer_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.ctcccloud_elb_loadbalancer_id.placeholder")} />
</Form.Item>
</Show>
<Show when={fieldResourceType === RESOURCE_TYPE_LISTENER}>
<Form.Item
name="listenerId"
label={t("workflow_node.deploy.form.ctcccloud_elb_listener_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.ctcccloud_elb_listener_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.ctcccloud_elb_listener_id.placeholder")} />
</Form.Item>
</Show>
</Form>
);
};
export default DeployNodeConfigFormCTCCCloudELBConfig;

View File

@ -416,6 +416,7 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
CTCCCLOUD_AO: `${ACCESS_PROVIDERS.CTCCCLOUD}-ao`, CTCCCLOUD_AO: `${ACCESS_PROVIDERS.CTCCCLOUD}-ao`,
CTCCCLOUD_CDN: `${ACCESS_PROVIDERS.CTCCCLOUD}-cdn`, CTCCCLOUD_CDN: `${ACCESS_PROVIDERS.CTCCCLOUD}-cdn`,
CTCCCLOUD_CMS: `${ACCESS_PROVIDERS.CTCCCLOUD}-cms`, CTCCCLOUD_CMS: `${ACCESS_PROVIDERS.CTCCCLOUD}-cms`,
CTCCCLOUD_ELB: `${ACCESS_PROVIDERS.CTCCCLOUD}-elb`,
CTCCCLOUD_ICDN: `${ACCESS_PROVIDERS.CTCCCLOUD}-icdn`, CTCCCLOUD_ICDN: `${ACCESS_PROVIDERS.CTCCCLOUD}-icdn`,
CTCCCLOUD_LVDN: `${ACCESS_PROVIDERS.CTCCCLOUD}-lvdn`, CTCCCLOUD_LVDN: `${ACCESS_PROVIDERS.CTCCCLOUD}-lvdn`,
DOGECLOUD_CDN: `${ACCESS_PROVIDERS.DOGECLOUD}-cdn`, DOGECLOUD_CDN: `${ACCESS_PROVIDERS.DOGECLOUD}-cdn`,
@ -578,6 +579,7 @@ export const deploymentProvidersMap: Map<DeploymentProvider["type"] | string, De
[DEPLOYMENT_PROVIDERS.CTCCCLOUD_CDN, "provider.ctcccloud.cdn", DEPLOYMENT_CATEGORIES.CDN], [DEPLOYMENT_PROVIDERS.CTCCCLOUD_CDN, "provider.ctcccloud.cdn", DEPLOYMENT_CATEGORIES.CDN],
[DEPLOYMENT_PROVIDERS.CTCCCLOUD_ICDN, "provider.ctcccloud.icdn", DEPLOYMENT_CATEGORIES.CDN], [DEPLOYMENT_PROVIDERS.CTCCCLOUD_ICDN, "provider.ctcccloud.icdn", DEPLOYMENT_CATEGORIES.CDN],
[DEPLOYMENT_PROVIDERS.CTCCCLOUD_AO, "provider.ctcccloud.ao", DEPLOYMENT_CATEGORIES.CDN], [DEPLOYMENT_PROVIDERS.CTCCCLOUD_AO, "provider.ctcccloud.ao", DEPLOYMENT_CATEGORIES.CDN],
[DEPLOYMENT_PROVIDERS.CTCCCLOUD_ELB, "provider.ctcccloud.elb", DEPLOYMENT_CATEGORIES.LOADBALANCE],
[DEPLOYMENT_PROVIDERS.CTCCCLOUD_LVDN, "provider.ctcccloud.lvdn", DEPLOYMENT_CATEGORIES.AV], [DEPLOYMENT_PROVIDERS.CTCCCLOUD_LVDN, "provider.ctcccloud.lvdn", DEPLOYMENT_CATEGORIES.AV],
[DEPLOYMENT_PROVIDERS.CTCCCLOUD_CMS, "provider.ctcccloud.cms_upload", DEPLOYMENT_CATEGORIES.SSL], [DEPLOYMENT_PROVIDERS.CTCCCLOUD_CMS, "provider.ctcccloud.cms_upload", DEPLOYMENT_CATEGORIES.SSL],
[DEPLOYMENT_PROVIDERS.RAINYUN_RCDN, "provider.rainyun.rcdn", DEPLOYMENT_CATEGORIES.CDN], [DEPLOYMENT_PROVIDERS.RAINYUN_RCDN, "provider.rainyun.rcdn", DEPLOYMENT_CATEGORIES.CDN],

View File

@ -404,6 +404,20 @@
"workflow_node.deploy.form.ctcccloud_cdn_domain.label": "CTCC StateCloud CDN domain", "workflow_node.deploy.form.ctcccloud_cdn_domain.label": "CTCC StateCloud CDN domain",
"workflow_node.deploy.form.ctcccloud_cdn_domain.placeholder": "Please enter CTCC StateCloud CDN domain name", "workflow_node.deploy.form.ctcccloud_cdn_domain.placeholder": "Please enter CTCC StateCloud CDN domain name",
"workflow_node.deploy.form.ctcccloud_cdn_domain.tooltip": "For more information, see <a href=\"https://cdn-console.ctyun.cn\" target=\"_blank\">https://cdn-console.ctyun.cn</a>", "workflow_node.deploy.form.ctcccloud_cdn_domain.tooltip": "For more information, see <a href=\"https://cdn-console.ctyun.cn\" target=\"_blank\">https://cdn-console.ctyun.cn</a>",
"workflow_node.deploy.form.ctcccloud_elb_resource_type.label": "Resource type",
"workflow_node.deploy.form.ctcccloud_elb_resource_type.placeholder": "Please select resource type",
"workflow_node.deploy.form.ctcccloud_elb_resource_type.option.certificate.label": "ELB certificate",
"workflow_node.deploy.form.ctcccloud_elb_resource_type.option.loadbalancer.label": "ELB load balancer",
"workflow_node.deploy.form.ctcccloud_elb_resource_type.option.listener.label": "ELB listener",
"workflow_node.deploy.form.ctcccloud_elb_region_id.label": "CTCC StateCloud ELB region ID",
"workflow_node.deploy.form.ctcccloud_elb_region_id.placeholder": "Please enter CTCC StateCloud ELB region ID",
"workflow_node.deploy.form.ctcccloud_elb_region_id.tooltip": "For more information, see <a href=\"https://www.ctyun.cn/document/10026755/10196575\" target=\"_blank\">https://www.ctyun.cn/document/10026755/10196575</a>",
"workflow_node.deploy.form.ctcccloud_elb_loadbalancer_id.label": "CTCC StateCloud ELB load balancer ID",
"workflow_node.deploy.form.ctcccloud_elb_loadbalancer_id.placeholder": "Please enter CTCC StateCloud ELB load balancer ID",
"workflow_node.deploy.form.ctcccloud_elb_loadbalancer_id.tooltip": "For more information, see <a href=\"https://console.ctyun.cn/network/index/#/elb/elbList\" target=\"_blank\">https://console.ctyun.cn/network/index/#/elb/elbList</a>",
"workflow_node.deploy.form.ctcccloud_elb_listener_id.label": "CTCC StateCloud ELB listener ID",
"workflow_node.deploy.form.ctcccloud_elb_listener_id.placeholder": "Please enter CTCC StateCloud ELB listener ID",
"workflow_node.deploy.form.ctcccloud_elb_listener_id.tooltip": "For more information, see <a href=\"https://console.ctyun.cn/network/index/#/elb/elbList\" target=\"_blank\">https://console.ctyun.cn/network/index/#/elb/elbList</a>",
"workflow_node.deploy.form.ctcccloud_icdn_domain.label": "CTCC StateCloud ICDN domain", "workflow_node.deploy.form.ctcccloud_icdn_domain.label": "CTCC StateCloud ICDN domain",
"workflow_node.deploy.form.ctcccloud_icdn_domain.placeholder": "Please enter CTCC StateCloud ICDN domain name", "workflow_node.deploy.form.ctcccloud_icdn_domain.placeholder": "Please enter CTCC StateCloud ICDN domain name",
"workflow_node.deploy.form.ctcccloud_icdn_domain.tooltip": "For more information, see <a href=\"https://cdn-console.ctyun.cn\" target=\"_blank\">https://cdn-console.ctyun.cn</a>", "workflow_node.deploy.form.ctcccloud_icdn_domain.tooltip": "For more information, see <a href=\"https://cdn-console.ctyun.cn\" target=\"_blank\">https://cdn-console.ctyun.cn</a>",

View File

@ -403,6 +403,19 @@
"workflow_node.deploy.form.ctcccloud_cdn_domain.label": "天翼云 CDN 加速域名", "workflow_node.deploy.form.ctcccloud_cdn_domain.label": "天翼云 CDN 加速域名",
"workflow_node.deploy.form.ctcccloud_cdn_domain.placeholder": "请输入天翼云 CDN 加速域名(支持泛域名)", "workflow_node.deploy.form.ctcccloud_cdn_domain.placeholder": "请输入天翼云 CDN 加速域名(支持泛域名)",
"workflow_node.deploy.form.ctcccloud_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://cdn-console.ctyun.cn\" target=\"_blank\">https://cdn-console.ctyun.cn</a>", "workflow_node.deploy.form.ctcccloud_cdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://cdn-console.ctyun.cn\" target=\"_blank\">https://cdn-console.ctyun.cn</a>",
"workflow_node.deploy.form.ctcccloud_elb_resource_type.label": "证书部署方式",
"workflow_node.deploy.form.ctcccloud_elb_resource_type.placeholder": "请选择证书部署方式",
"workflow_node.deploy.form.ctcccloud_elb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS 监听器的证书",
"workflow_node.deploy.form.ctcccloud_elb_resource_type.option.listener.label": "替换指定监听器的证书",
"workflow_node.deploy.form.ctcccloud_elb_region_id.label": "天翼云 ELB 资源池 ID",
"workflow_node.deploy.form.ctcccloud_elb_region_id.placeholder": "请输入天翼云 ELB 资源池 ID",
"workflow_node.deploy.form.ctcccloud_elb_region_id.tooltip": "这是什么?请参阅 <a href=\"https://www.ctyun.cn/document/10026755/10196575\" target=\"_blank\">https://www.ctyun.cn/document/10026755/10196575</a>",
"workflow_node.deploy.form.ctcccloud_elb_loadbalancer_id.label": "天翼云 ELB 负载均衡器 ID",
"workflow_node.deploy.form.ctcccloud_elb_loadbalancer_id.placeholder": "请输入天翼云 ELB 负载均衡器 ID",
"workflow_node.deploy.form.ctcccloud_elb_loadbalancer_id.tooltip": "这是什么?请参阅 <a href=\"https://console.ctyun.cn/network/index/#/elb/elbList\" target=\"_blank\">https://console.ctyun.cn/network/index/#/elb/elbList</a>",
"workflow_node.deploy.form.ctcccloud_elb_listener_id.label": "天翼云 ELB 监听器 ID",
"workflow_node.deploy.form.ctcccloud_elb_listener_id.placeholder": "请输入天翼云 ELB 监听器 ID",
"workflow_node.deploy.form.ctcccloud_elb_listener_id.tooltip": "这是什么?请参阅 <a href=\"https://console.ctyun.cn/network/index/#/elb/elbList\" target=\"_blank\">https://console.ctyun.cn/network/index/#/elb/elbList</a>",
"workflow_node.deploy.form.ctcccloud_icdn_domain.label": "天翼云 ICDN 加速域名", "workflow_node.deploy.form.ctcccloud_icdn_domain.label": "天翼云 ICDN 加速域名",
"workflow_node.deploy.form.ctcccloud_icdn_domain.placeholder": "请输入天翼云 ICDN 加速域名(支持泛域名)", "workflow_node.deploy.form.ctcccloud_icdn_domain.placeholder": "请输入天翼云 ICDN 加速域名(支持泛域名)",
"workflow_node.deploy.form.ctcccloud_icdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://cdn-console.ctyun.cn\" target=\"_blank\">https://cdn-console.ctyun.cn</a>", "workflow_node.deploy.form.ctcccloud_icdn_domain.tooltip": "这是什么?请参阅 <a href=\"https://cdn-console.ctyun.cn\" target=\"_blank\">https://cdn-console.ctyun.cn</a>",