feat(provider): new deployment provider: ucloud ualb

This commit is contained in:
Fu Diwei
2025-12-17 16:33:31 +08:00
committed by RHQYZ
parent 3078f9ff67
commit fbf538b556
16 changed files with 789 additions and 2 deletions
@@ -0,0 +1,31 @@
package deployers
import (
"fmt"
"github.com/certimate-go/certimate/internal/domain"
"github.com/certimate-go/certimate/pkg/core/deployer"
ucloudualb "github.com/certimate-go/certimate/pkg/core/deployer/providers/ucloud-ualb"
xmaps "github.com/certimate-go/certimate/pkg/utils/maps"
)
func init() {
Registries.MustRegister(domain.DeploymentProviderTypeUCloudUALB, func(options *ProviderFactoryOptions) (deployer.Provider, error) {
credentials := domain.AccessConfigForUCloud{}
if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil {
return nil, fmt.Errorf("failed to populate provider access config: %w", err)
}
provider, err := ucloudualb.NewDeployer(&ucloudualb.DeployerConfig{
PrivateKey: credentials.PrivateKey,
PublicKey: credentials.PublicKey,
ProjectId: credentials.ProjectId,
Region: xmaps.GetString(options.ProviderExtendedConfig, "region"),
ResourceType: xmaps.GetString(options.ProviderExtendedConfig, "resourceType"),
LoadbalancerId: xmaps.GetString(options.ProviderExtendedConfig, "loadbalancerId"),
ListenerId: xmaps.GetString(options.ProviderExtendedConfig, "listenerId"),
Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"),
})
return provider, err
})
}
@@ -0,0 +1,8 @@
package ucloudualb
const (
// 资源类型:部署到指定负载均衡器。
RESOURCE_TYPE_LOADBALANCER = "loadbalancer"
// 资源类型:部署到指定监听器。
RESOURCE_TYPE_LISTENER = "listener"
)
@@ -0,0 +1,329 @@
package ucloudualb
import (
"context"
"errors"
"fmt"
"log/slog"
"time"
"github.com/samber/lo"
ucloudlb "github.com/ucloud/ucloud-sdk-go/services/ulb"
"github.com/ucloud/ucloud-sdk-go/ucloud"
"github.com/ucloud/ucloud-sdk-go/ucloud/auth"
"github.com/certimate-go/certimate/pkg/core/certmgr"
mcertmgr "github.com/certimate-go/certimate/pkg/core/certmgr/providers/ucloud-ulb"
"github.com/certimate-go/certimate/pkg/core/deployer"
usdklb "github.com/certimate-go/certimate/pkg/sdk3rd/ucloud/ulb"
)
type DeployerConfig struct {
// 优刻得 API 私钥。
PrivateKey string `json:"privateKey"`
// 优刻得 API 公钥。
PublicKey string `json:"publicKey"`
// 优刻得项目 ID。
ProjectId string `json:"projectId,omitempty"`
// 优刻得地域。
Region string `json:"region"`
// 部署资源类型。
ResourceType string `json:"resourceType"`
// 负载均衡实例 ID。
// 部署资源类型为 [RESOURCE_TYPE_LOADBALANCER]、[RESOURCE_TYPE_LISTENER] 时必填。
LoadbalancerId string `json:"loadbalancerId,omitempty"`
// 负载均衡监听器 ID。
// 部署资源类型为 [RESOURCE_TYPE_LISTENER] 时必填。
ListenerId string `json:"listenerId,omitempty"`
// SNI 域名(不支持泛域名)。
// 部署资源类型为 [RESOURCE_TYPE_LISTENER] 时选填。
Domain string `json:"domain,omitempty"`
}
type Deployer struct {
config *DeployerConfig
logger *slog.Logger
sdkClient *usdklb.ULBClient
sdkCertmgr certmgr.Provider
}
var _ deployer.Provider = (*Deployer)(nil)
func NewDeployer(config *DeployerConfig) (*Deployer, error) {
if config == nil {
return nil, errors.New("the configuration of the deployer provider is nil")
}
client, err := createSDKClient(config.PrivateKey, config.PublicKey)
if err != nil {
return nil, fmt.Errorf("could not create client: %w", err)
}
pcertmgr, err := mcertmgr.NewCertmgr(&mcertmgr.CertmgrConfig{
PrivateKey: config.PrivateKey,
PublicKey: config.PublicKey,
ProjectId: config.ProjectId,
Region: config.Region,
})
if err != nil {
return nil, fmt.Errorf("could not create certmgr: %w", err)
}
return &Deployer{
config: config,
logger: slog.Default(),
sdkClient: client,
sdkCertmgr: pcertmgr,
}, nil
}
func (d *Deployer) SetLogger(logger *slog.Logger) {
if logger == nil {
d.logger = slog.New(slog.DiscardHandler)
} else {
d.logger = logger
}
d.sdkCertmgr.SetLogger(logger)
}
func (d *Deployer) Deploy(ctx context.Context, certPEM, privkeyPEM string) (*deployer.DeployResult, error) {
// 上传证书
upres, err := d.sdkCertmgr.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 *Deployer) deployToLoadbalancer(ctx context.Context, cloudCertId string) error {
if d.config.LoadbalancerId == "" {
return errors.New("config `loadbalancerId` is required")
}
// 获取 ALB 下的 HTTPS 监听器列表
// REF: https://docs.ucloud.cn/api/ulb-api/describe_listeners
listenerIds := make([]string, 0)
describeListenersOffset := 0
describeListenersLimit := 100
for {
select {
case <-ctx.Done():
return ctx.Err()
default:
}
describeListenerReq := d.sdkClient.NewDescribeListenersRequest()
describeListenerReq.Region = ucloud.String(d.config.Region)
describeListenerReq.LoadBalancerId = ucloud.String(d.config.LoadbalancerId)
describeListenerReq.Offset = ucloud.Int(describeListenersOffset)
describeListenerReq.Limit = ucloud.Int(describeListenersLimit)
if d.config.ProjectId != "" {
describeListenerReq.ProjectId = ucloud.String(d.config.ProjectId)
}
describeListenerResp, err := d.sdkClient.DescribeListeners(describeListenerReq)
d.logger.Debug("sdk request 'ulb.DescribeListeners'", slog.Any("request", describeListenerReq), slog.Any("response", describeListenerResp))
if err != nil {
return fmt.Errorf("failed to execute sdk request 'ulb.DescribeListeners': %w", err)
}
for _, listenerItem := range describeListenerResp.Listeners {
if listenerItem.ListenerProtocol == "HTTPS" {
listenerIds = append(listenerIds, listenerItem.ListenerId)
}
}
if len(describeListenerResp.Listeners) < describeListenersLimit {
break
}
describeListenersOffset += describeListenersLimit
}
// 遍历更新 Listener 证书
if len(listenerIds) == 0 {
d.logger.Info("no alb 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, d.config.LoadbalancerId, listenerId, cloudCertId); err != nil {
errs = append(errs, err)
}
}
}
if len(errs) > 0 {
return errors.Join(errs...)
}
}
return nil
}
func (d *Deployer) deployToListener(ctx context.Context, cloudCertId string) error {
if d.config.LoadbalancerId == "" {
return errors.New("config `loadbalancerId` is required")
}
if d.config.ListenerId == "" {
return errors.New("config `listenerId` is required")
}
if err := d.updateListenerCertificate(ctx, d.config.LoadbalancerId, d.config.ListenerId, cloudCertId); err != nil {
return err
}
return nil
}
func (d *Deployer) updateListenerCertificate(ctx context.Context, cloudLoadbalancerId, cloudListenerId string, cloudCertId string) error {
// 描述应用型负载均衡监听器
// REF: https://docs.ucloud.cn/api/ulb-api/describe_listeners
describeListenersReq := d.sdkClient.NewDescribeListenersRequest()
describeListenersReq.Region = ucloud.String(d.config.Region)
describeListenersReq.LoadBalancerId = ucloud.String(cloudLoadbalancerId)
describeListenersReq.ListenerId = ucloud.String(cloudListenerId)
describeListenersReq.Limit = ucloud.Int(1)
if d.config.ProjectId != "" {
describeListenersReq.ProjectId = ucloud.String(d.config.ProjectId)
}
describeListenerResp, err := d.sdkClient.DescribeListeners(describeListenersReq)
d.logger.Debug("sdk request 'ulb.DescribeListeners'", slog.Any("request", describeListenersReq), slog.Any("response", describeListenerResp))
if err != nil {
return fmt.Errorf("failed to execute sdk request 'ulb.DescribeListeners': %w", err)
}
if err != nil {
return fmt.Errorf("failed to execute sdk request 'ulb.DescribeListeners': %w", err)
} else if len(describeListenerResp.Listeners) == 0 {
return fmt.Errorf("could not find listener '%s'", cloudListenerId)
}
// 跳过已部署过的监听器
listenerInfo := describeListenerResp.Listeners[0]
if d.config.Domain == "" {
if lo.ContainsBy(listenerInfo.Certificates, func(item ucloudlb.Certificate) bool { return item.SSLId == cloudCertId && item.IsDefault }) {
return nil
}
} else {
if lo.ContainsBy(listenerInfo.Certificates, func(item ucloudlb.Certificate) bool { return item.SSLId == cloudCertId && !item.IsDefault }) {
return nil
}
}
if d.config.Domain == "" {
// 未指定 SNI,只需部署到监听器
updateListenerAttributeReq := d.sdkClient.NewUpdateListenerAttributeRequest()
updateListenerAttributeReq.Region = ucloud.String(d.config.Region)
updateListenerAttributeReq.LoadBalancerId = ucloud.String(cloudLoadbalancerId)
updateListenerAttributeReq.ListenerId = ucloud.String(cloudListenerId)
updateListenerAttributeReq.Certificates = []string{cloudCertId}
if d.config.ProjectId != "" {
updateListenerAttributeReq.ProjectId = ucloud.String(d.config.ProjectId)
}
updateListenerResp, err := d.sdkClient.UpdateListenerAttribute(updateListenerAttributeReq)
d.logger.Debug("sdk request 'ulb.UpdateListenerAttribute'", slog.Any("request", updateListenerAttributeReq), slog.Any("response", updateListenerResp))
if err != nil {
return fmt.Errorf("failed to execute sdk request 'ulb.UpdateListenerAttribute': %w", err)
}
} else {
// 指定 SNI,需部署到扩展域名
// 新增监听器扩展证书
// REF: https://docs.ucloud.cn/api/ulb-api/add_ssl_binding_json
addSSLBindingReq := d.sdkClient.NewAddSSLBindingRequest()
addSSLBindingReq.Region = ucloud.String(d.config.Region)
addSSLBindingReq.LoadBalancerId = ucloud.String(cloudLoadbalancerId)
addSSLBindingReq.ListenerId = ucloud.String(cloudListenerId)
addSSLBindingReq.SSLIds = []string{cloudCertId}
if d.config.ProjectId != "" {
addSSLBindingReq.ProjectId = ucloud.String(d.config.ProjectId)
}
addSSLBindingResp, err := d.sdkClient.AddSSLBinding(addSSLBindingReq)
d.logger.Debug("sdk request 'ulb.AddSSLBinding'", slog.Any("request", addSSLBindingReq), slog.Any("response", addSSLBindingResp))
if err != nil {
return fmt.Errorf("failed to execute sdk request 'ulb.AddSSLBinding': %w", err)
}
// 删除已过期的监听器绑定的扩展证书
// REF: https://docs.ucloud.cn/api/ulb-api/describe_sslv2
// REF: https://docs.ucloud.cn/api/ulb-api/delete_ssl_binding_json
for _, certItem := range listenerInfo.Certificates {
if certItem.IsDefault {
continue
}
describeSSLV2Req := d.sdkClient.NewDescribeSSLV2Request()
describeSSLV2Req.Region = ucloud.String(d.config.Region)
describeSSLV2Req.SSLId = ucloud.String(certItem.SSLId)
describeSSLV2Req.Limit = ucloud.Int(1)
if d.config.ProjectId != "" {
describeSSLV2Req.ProjectId = ucloud.String(d.config.ProjectId)
}
describeSSLV2Resp, err := d.sdkClient.DescribeSSLV2(describeSSLV2Req)
d.logger.Debug("sdk request 'ulb.DescribeSSLV2'", slog.Any("request", describeSSLV2Req), slog.Any("response", describeSSLV2Resp))
if err != nil {
continue
} else if len(describeSSLV2Resp.DataSet) == 0 {
continue
} else {
}
sslItem := describeSSLV2Resp.DataSet[0]
if sslItem.NotAfter == 0 || int64(sslItem.NotAfter) >= time.Now().Unix() {
continue
}
deleteSSLBindingReq := d.sdkClient.NewDeleteSSLBindingRequest()
deleteSSLBindingReq.Region = ucloud.String(d.config.Region)
deleteSSLBindingReq.LoadBalancerId = ucloud.String(cloudLoadbalancerId)
deleteSSLBindingReq.ListenerId = ucloud.String(cloudListenerId)
deleteSSLBindingReq.SSLIds = []string{sslItem.SSLId}
if d.config.ProjectId != "" {
deleteSSLBindingReq.ProjectId = ucloud.String(d.config.ProjectId)
}
deleteSSLBindingResp, err := d.sdkClient.DeleteSSLBinding(deleteSSLBindingReq)
d.logger.Debug("sdk request 'ulb.DeleteSSLBinding'", slog.Any("request", deleteSSLBindingReq), slog.Any("response", deleteSSLBindingResp))
if err != nil {
return fmt.Errorf("failed to execute sdk request 'ulb.DeleteSSLBinding': %w", err)
}
}
}
return nil
}
func createSDKClient(privateKey, publicKey string) (*usdklb.ULBClient, error) {
cfg := ucloud.NewConfig()
credential := auth.NewCredential()
credential.PrivateKey = privateKey
credential.PublicKey = publicKey
client := usdklb.NewClient(&cfg, &credential)
return client, nil
}
@@ -0,0 +1,86 @@
package ucloudualb_test
import (
"context"
"flag"
"fmt"
"os"
"strings"
"testing"
provider "github.com/certimate-go/certimate/pkg/core/deployer/providers/ucloud-ualb"
)
var (
fInputCertPath string
fInputKeyPath string
fPrivateKey string
fPublicKey string
fRegion string
fLoadbalancerId string
fListenerId string
)
func init() {
argsPrefix := "UCLOUDUALB_"
flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "")
flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "")
flag.StringVar(&fPrivateKey, argsPrefix+"PRIVATEKEY", "", "")
flag.StringVar(&fPublicKey, argsPrefix+"PUBLICKEY", "", "")
flag.StringVar(&fRegion, argsPrefix+"REGION", "", "")
flag.StringVar(&fLoadbalancerId, argsPrefix+"LOADBALANCERID", "", "")
flag.StringVar(&fListenerId, argsPrefix+"LISTENERID", "", "")
}
/*
Shell command to run this test:
go test -v ./ucloud_ualb_test.go -args \
--UCLOUDUALB_INPUTCERTPATH="/path/to/your-input-cert.pem" \
--UCLOUDUALB_INPUTKEYPATH="/path/to/your-input-key.pem" \
--UCLOUDUALB_PRIVATEKEY="your-private-key" \
--UCLOUDUALB_PUBLICKEY="your-public-key" \
--UCLOUDUALB_REGION="cn-bj2" \
--UCLOUDUALB_LOADBALANCERID="your-loadbalancer-id" \
--UCLOUDUALB_LISTENERID="your-listener-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("PRIVATEKEY: %v", fPrivateKey),
fmt.Sprintf("PUBLICKEY: %v", fPublicKey),
fmt.Sprintf("REGION: %v", fRegion),
fmt.Sprintf("LOADBALANCERID: %v", fLoadbalancerId),
fmt.Sprintf("LISTENERID: %v", fListenerId),
}, "\n"))
provider, err := provider.NewDeployer(&provider.DeployerConfig{
PrivateKey: fPrivateKey,
PublicKey: fPublicKey,
Region: fRegion,
ResourceType: provider.RESOURCE_TYPE_LISTENER,
LoadbalancerId: fLoadbalancerId,
ListenerId: fListenerId,
})
if err != nil {
t.Errorf("err: %+v", err)
return
}
fInputCertData, _ := os.ReadFile(fInputCertPath)
fInputKeyData, _ := os.ReadFile(fInputKeyPath)
res, err := provider.Deploy(context.Background(), string(fInputCertData), string(fInputKeyData))
if err != nil {
t.Errorf("err: %+v", err)
return
}
t.Logf("ok: %v", res)
})
}
@@ -0,0 +1,32 @@
package ulb
import (
ucloudlb "github.com/ucloud/ucloud-sdk-go/services/ulb"
)
type AddSSLBindingRequest = ucloudlb.AddSSLBindingRequest
type AddSSLBindingResponse = ucloudlb.AddSSLBindingResponse
func (c *ULBClient) NewAddSSLBindingRequest() *AddSSLBindingRequest {
req := &AddSSLBindingRequest{}
c.Client.SetupRequest(req)
req.SetRetryable(true)
return req
}
func (c *ULBClient) AddSSLBinding(req *AddSSLBindingRequest) (*AddSSLBindingResponse, error) {
var err error
var res AddSSLBindingResponse
reqCopier := *req
err = c.Client.InvokeAction("AddSSLBinding", &reqCopier, &res)
if err != nil {
return &res, err
}
return &res, nil
}
@@ -0,0 +1,32 @@
package ulb
import (
ucloudlb "github.com/ucloud/ucloud-sdk-go/services/ulb"
)
type DeleteSSLBindingRequest = ucloudlb.DeleteSSLBindingRequest
type DeleteSSLBindingResponse = ucloudlb.DeleteSSLBindingResponse
func (c *ULBClient) NewDeleteSSLBindingRequest() *DeleteSSLBindingRequest {
req := &DeleteSSLBindingRequest{}
c.Client.SetupRequest(req)
req.SetRetryable(true)
return req
}
func (c *ULBClient) DeleteSSLBinding(req *DeleteSSLBindingRequest) (*DeleteSSLBindingResponse, error) {
var err error
var res DeleteSSLBindingResponse
reqCopier := *req
err = c.Client.InvokeAction("DeleteSSLBinding", &reqCopier, &res)
if err != nil {
return &res, err
}
return &res, nil
}
@@ -0,0 +1,32 @@
package ulb
import (
ucloudlb "github.com/ucloud/ucloud-sdk-go/services/ulb"
)
type DescribeListenersRequest = ucloudlb.DescribeListenersRequest
type DescribeListenersResponse = ucloudlb.DescribeListenersResponse
func (c *ULBClient) NewDescribeListenersRequest() *DescribeListenersRequest {
req := &DescribeListenersRequest{}
c.Client.SetupRequest(req)
req.SetRetryable(true)
return req
}
func (c *ULBClient) DescribeListeners(req *DescribeListenersRequest) (*DescribeListenersResponse, error) {
var err error
var res DescribeListenersResponse
reqCopier := *req
err = c.Client.InvokeAction("DescribeListeners", &reqCopier, &res)
if err != nil {
return &res, err
}
return &res, nil
}
@@ -0,0 +1,32 @@
package ulb
import (
ucloudlb "github.com/ucloud/ucloud-sdk-go/services/ulb"
)
type DescribeSSLV2Request = ucloudlb.DescribeSSLV2Request
type DescribeSSLV2Response = ucloudlb.DescribeSSLV2Response
func (c *ULBClient) NewDescribeSSLV2Request() *DescribeSSLV2Request {
req := &DescribeSSLV2Request{}
c.Client.SetupRequest(req)
req.SetRetryable(true)
return req
}
func (c *ULBClient) DescribeSSLV2(req *DescribeSSLV2Request) (*DescribeSSLV2Response, error) {
var err error
var res DescribeSSLV2Response
reqCopier := *req
err = c.Client.InvokeAction("DescribeSSLV2", &reqCopier, &res)
if err != nil {
return &res, err
}
return &res, nil
}
@@ -0,0 +1,32 @@
package ulb
import (
ucloudlb "github.com/ucloud/ucloud-sdk-go/services/ulb"
)
type UpdateListenerAttributeRequest = ucloudlb.UpdateListenerAttributeRequest
type UpdateListenerAttributeResponse = ucloudlb.UpdateListenerAttributeResponse
func (c *ULBClient) NewUpdateListenerAttributeRequest() *UpdateListenerAttributeRequest {
req := &UpdateListenerAttributeRequest{}
c.Client.SetupRequest(req)
req.SetRetryable(true)
return req
}
func (c *ULBClient) UpdateListenerAttribute(req *UpdateListenerAttributeRequest) (*UpdateListenerAttributeResponse, error) {
var err error
var res UpdateListenerAttributeResponse
reqCopier := *req
err = c.Client.InvokeAction("UpdateListenerAttribute", &reqCopier, &res)
if err != nil {
return &res, err
}
return &res, nil
}
@@ -83,6 +83,7 @@ import BizDeployNodeConfigFieldsProviderTencentCloudSSLDeploy from "./BizDeployN
import BizDeployNodeConfigFieldsProviderTencentCloudSSLUpdate from "./BizDeployNodeConfigFieldsProviderTencentCloudSSLUpdate";
import BizDeployNodeConfigFieldsProviderTencentCloudVOD from "./BizDeployNodeConfigFieldsProviderTencentCloudVOD";
import BizDeployNodeConfigFieldsProviderTencentCloudWAF from "./BizDeployNodeConfigFieldsProviderTencentCloudWAF";
import BizDeployNodeConfigFieldsProviderUCloudUALB from "./BizDeployNodeConfigFieldsProviderUCloudUALB";
import BizDeployNodeConfigFieldsProviderUCloudUCDN from "./BizDeployNodeConfigFieldsProviderUCloudUCDN";
import BizDeployNodeConfigFieldsProviderUCloudUCLB from "./BizDeployNodeConfigFieldsProviderUCloudUCLB";
import BizDeployNodeConfigFieldsProviderUCloudUS3 from "./BizDeployNodeConfigFieldsProviderUCloudUS3";
@@ -189,6 +190,7 @@ const providerComponentMap: Partial<Record<DeploymentProviderType, React.Compone
[DEPLOYMENT_PROVIDERS.TENCENTCLOUD_SSL_UPDATE]: BizDeployNodeConfigFieldsProviderTencentCloudSSLUpdate,
[DEPLOYMENT_PROVIDERS.TENCENTCLOUD_VOD]: BizDeployNodeConfigFieldsProviderTencentCloudVOD,
[DEPLOYMENT_PROVIDERS.TENCENTCLOUD_WAF]: BizDeployNodeConfigFieldsProviderTencentCloudWAF,
[DEPLOYMENT_PROVIDERS.UCLOUD_UALB]: BizDeployNodeConfigFieldsProviderUCloudUALB,
[DEPLOYMENT_PROVIDERS.UCLOUD_UCDN]: BizDeployNodeConfigFieldsProviderUCloudUCDN,
[DEPLOYMENT_PROVIDERS.UCLOUD_UCLB]: BizDeployNodeConfigFieldsProviderUCloudUCLB,
[DEPLOYMENT_PROVIDERS.UCLOUD_US3]: BizDeployNodeConfigFieldsProviderUCloudUS3,
@@ -0,0 +1,139 @@
import { getI18n, useTranslation } from "react-i18next";
import { Form, Input, Select } from "antd";
import { createSchemaFieldRule } from "antd-zod";
import { z } from "zod";
import Show from "@/components/Show";
import { validDomainName } from "@/utils/validators";
import { useFormNestedFieldsContext } from "./_context";
const RESOURCE_TYPE_LOADBALANCER = "loadbalancer" as const;
const RESOURCE_TYPE_LISTENER = "listener" as const;
const BizDeployNodeConfigFieldsProviderUCloudUALB = () => {
const { i18n, t } = useTranslation();
const { parentNamePath } = useFormNestedFieldsContext();
const formSchema = z.object({
[parentNamePath]: getSchema({ i18n }),
});
const formRule = createSchemaFieldRule(formSchema);
const formInst = Form.useFormInstance();
const initialValues = getInitialValues();
const fieldResourceType = Form.useWatch([parentNamePath, "resourceType"], formInst);
return (
<>
<Form.Item
name={[parentNamePath, "region"]}
initialValue={initialValues.region}
label={t("workflow_node.deploy.form.ucloud_ualb_region.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.ucloud_ualb_region.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.ucloud_ualb_region.placeholder")} />
</Form.Item>
<Form.Item
name={[parentNamePath, "resourceType"]}
initialValue={initialValues.resourceType}
label={t("workflow_node.deploy.form.shared_resource_type.label")}
rules={[formRule]}
>
<Select
options={[RESOURCE_TYPE_LOADBALANCER, RESOURCE_TYPE_LISTENER].map((s) => ({
value: s,
label: t(`workflow_node.deploy.form.ucloud_ualb_resource_type.option.${s}.label`),
}))}
placeholder={t("workflow_node.deploy.form.shared_resource_type.placeholder")}
/>
</Form.Item>
<Form.Item
name={[parentNamePath, "loadbalancerId"]}
initialValue={initialValues.loadbalancerId}
label={t("workflow_node.deploy.form.ucloud_ualb_loadbalancer_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.ucloud_ualb_loadbalancer_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.ucloud_ualb_loadbalancer_id.placeholder")} />
</Form.Item>
<Show when={fieldResourceType === RESOURCE_TYPE_LISTENER}>
<Form.Item
name={[parentNamePath, "listenerId"]}
initialValue={initialValues.listenerId}
label={t("workflow_node.deploy.form.ucloud_ualb_listener_id.label")}
rules={[formRule]}
tooltip={<span dangerouslySetInnerHTML={{ __html: t("workflow_node.deploy.form.ucloud_ualb_listener_id.tooltip") }}></span>}
>
<Input placeholder={t("workflow_node.deploy.form.ucloud_ualb_listener_id.placeholder")} />
</Form.Item>
</Show>
<Show when={fieldResourceType === RESOURCE_TYPE_LOADBALANCER || fieldResourceType === RESOURCE_TYPE_LISTENER}>
<Form.Item
name={[parentNamePath, "domain"]}
initialValue={initialValues.domain}
label={t("workflow_node.deploy.form.ucloud_ualb_snidomain.label")}
extra={t("workflow_node.deploy.form.ucloud_ualb_snidomain.help")}
rules={[formRule]}
>
<Input allowClear placeholder={t("workflow_node.deploy.form.ucloud_ualb_snidomain.placeholder")} />
</Form.Item>
</Show>
</>
);
};
const getInitialValues = (): Nullish<z.infer<ReturnType<typeof getSchema>>> => {
return {
region: "",
resourceType: RESOURCE_TYPE_LISTENER,
loadbalancerId: "",
listenerId: "",
};
};
const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType<typeof getI18n> }) => {
const { t } = i18n;
return z
.object({
endpoint: z.string().nullish(),
resourceType: z.literal([RESOURCE_TYPE_LOADBALANCER, RESOURCE_TYPE_LISTENER], t("workflow_node.deploy.form.shared_resource_type.placeholder")),
region: z.string().nonempty(t("workflow_node.deploy.form.ucloud_ualb_region.placeholder")),
loadbalancerId: z.string().nonempty(t("workflow_node.deploy.form.ucloud_ualb_loadbalancer_id.placeholder")),
listenerId: z.string().nullish(),
domain: z
.string()
.nullish()
.refine((v) => {
return !v || validDomainName(v);
}, t("common.errmsg.domain_invalid")),
})
.superRefine((values, ctx) => {
switch (values.resourceType) {
case RESOURCE_TYPE_LISTENER:
{
if (!values.listenerId?.trim()) {
ctx.addIssue({
code: "custom",
message: t("workflow_node.deploy.form.ucloud_ualb_listener_id.placeholder"),
path: ["listenerId"],
});
}
}
break;
}
});
};
const _default = Object.assign(BizDeployNodeConfigFieldsProviderUCloudUALB, {
getInitialValues,
getSchema,
});
export default _default;
+2
View File
@@ -617,6 +617,7 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({
TENCENTCLOUD_SSL_UPDATE: `${ACCESS_PROVIDERS.TENCENTCLOUD}-sslupdate`,
TENCENTCLOUD_VOD: `${ACCESS_PROVIDERS.TENCENTCLOUD}-vod`,
TENCENTCLOUD_WAF: `${ACCESS_PROVIDERS.TENCENTCLOUD}-waf`,
UCLOUD_UALB: `${ACCESS_PROVIDERS.UCLOUD}-ualb`,
UCLOUD_UCDN: `${ACCESS_PROVIDERS.UCLOUD}-ucdn`,
UCLOUD_UCLB: `${ACCESS_PROVIDERS.UCLOUD}-uclb`,
UCLOUD_US3: `${ACCESS_PROVIDERS.UCLOUD}-us3`,
@@ -738,6 +739,7 @@ export const deploymentProvidersMap: Map<DeploymentProvider["type"] | string, De
[DEPLOYMENT_PROVIDERS.UCLOUD_US3, "provider.ucloud_us3", DEPLOYMENT_CATEGORIES.STORAGE],
[DEPLOYMENT_PROVIDERS.UCLOUD_UCDN, "provider.ucloud_ucdn", DEPLOYMENT_CATEGORIES.CDN],
[DEPLOYMENT_PROVIDERS.UCLOUD_UCLB, "provider.ucloud_uclb", DEPLOYMENT_CATEGORIES.LOADBALANCE],
[DEPLOYMENT_PROVIDERS.UCLOUD_UALB, "provider.ucloud_ualb", DEPLOYMENT_CATEGORIES.LOADBALANCE],
[DEPLOYMENT_PROVIDERS.CTCCCLOUD_CDN, "provider.ctcccloud_cdn", DEPLOYMENT_CATEGORIES.CDN],
[DEPLOYMENT_PROVIDERS.CTCCCLOUD_ICDN, "provider.ctcccloud_icdn", DEPLOYMENT_CATEGORIES.CDN],
[DEPLOYMENT_PROVIDERS.CTCCCLOUD_AO, "provider.ctcccloud_ao", DEPLOYMENT_CATEGORIES.CDN],
+1
View File
@@ -183,6 +183,7 @@
"provider.tencentcloud_vod": "Tencent Cloud - VOD (Video on Demand)",
"provider.tencentcloud_waf": "Tencent Cloud - WAF (Web Application Firewall)",
"provider.ucloud": "UCloud",
"provider.ucloud_ualb": "UCloud - UALB (Application Load Balancer)",
"provider.ucloud_ucdn": "UCloud - UCDN (Content Delivery Network)",
"provider.ucloud_uclb": "UCloud - UCLB (Classic Load Balancer)",
"provider.ucloud_udnr": "UCloud - UDNR (Domain Name Registrar)",
+16 -2
View File
@@ -910,6 +910,20 @@
"workflow_node.deploy.form.tencentcloud_waf_instance_id.label": "Tencent Cloud WAF instance ID",
"workflow_node.deploy.form.tencentcloud_waf_instance_id.placeholder": "Please enter Tencent Cloud WAF instance ID",
"workflow_node.deploy.form.tencentcloud_waf_instance_id.tooltip": "For more information, see <a href=\"https://console.tencentcloud.com/waf\" target=\"_blank\">https://console.tencentcloud.com/waf</a>",
"workflow_node.deploy.form.ucloud_ualb_region.label": "UCloud region",
"workflow_node.deploy.form.ucloud_ualb_region.placeholder": "Please enter UCloud ALB region (e.g. cn-bj2)",
"workflow_node.deploy.form.ucloud_ualb_region.tooltip": "For more information, see <a href=\"https://www.ucloud-global.com/en/docs/api/summary/regionlist\" target=\"_blank\">https://www.ucloud-global.com/en/docs/api/summary/regionlist</a>",
"workflow_node.deploy.form.ucloud_ualb_resource_type.option.loadbalancer.label": "UALB load balancer",
"workflow_node.deploy.form.ucloud_ualb_resource_type.option.listener.label": "UALB listener",
"workflow_node.deploy.form.ucloud_ualb_loadbalancer_id.label": "UCloud UALB load balancer ID",
"workflow_node.deploy.form.ucloud_ualb_loadbalancer_id.placeholder": "Please enter UCloud ALB load balancer ID",
"workflow_node.deploy.form.ucloud_ualb_loadbalancer_id.tooltip": "For more information, see <a href=\"https://console.ucloud-global.com/ulb/alb\" target=\"_blank\">https://console.ucloud-global.com/ulb/alb</a>",
"workflow_node.deploy.form.ucloud_ualb_listener_id.label": "UCloud UALB listener ID",
"workflow_node.deploy.form.ucloud_ualb_listener_id.placeholder": "Please enter UCloud ALB listener ID",
"workflow_node.deploy.form.ucloud_ualb_listener_id.tooltip": "For more information, see <a href=\"https://console.ucloud-global.com/ulb/alb\" target=\"_blank\">https://console.ucloud-global.com/ulb/alb</a>",
"workflow_node.deploy.form.ucloud_ualb_snidomain.label": "UCloud UALB SNI domain (Optional)",
"workflow_node.deploy.form.ucloud_ualb_snidomain.placeholder": "Please enter UCloud UALB SNI domain name",
"workflow_node.deploy.form.ucloud_ualb_snidomain.help": "",
"workflow_node.deploy.form.ucloud_ucdn_domain_id.label": "UCloud UCDN domain ID",
"workflow_node.deploy.form.ucloud_ucdn_domain_id.placeholder": "Please enter UCloud UCDN domain ID",
"workflow_node.deploy.form.ucloud_ucdn_domain_id.tooltip": "For more information, see <a href=\"https://console.ucloud-global.com/ucdn\" target=\"_blank\">https://console.ucloud-global.com/ucdn</a>",
@@ -960,8 +974,8 @@
"workflow_node.deploy.form.volcengine_alb_listener_id.label": "VolcEngine ALB listener ID",
"workflow_node.deploy.form.volcengine_alb_listener_id.placeholder": "Please enter VolcEngine ALB listener ID",
"workflow_node.deploy.form.volcengine_alb_listener_id.tooltip": "For more information, see <a href=\"https://console.volcengine.com/alb\" target=\"_blank\">https://console.volcengine.com/alb</a>",
"workflow_node.deploy.form.volcengine_alb_snidomain.label": "VolcEngine CLB SNI domain (Optional)",
"workflow_node.deploy.form.volcengine_alb_snidomain.placeholder": "Please enter VolcEngine CLB SNI domain name",
"workflow_node.deploy.form.volcengine_alb_snidomain.label": "VolcEngine ALB SNI domain (Optional)",
"workflow_node.deploy.form.volcengine_alb_snidomain.placeholder": "Please enter VolcEngine ALB SNI domain name",
"workflow_node.deploy.form.volcengine_alb_snidomain.help": "",
"workflow_node.deploy.form.volcengine_cdn_domain.label": "VolcEngine CDN domain",
"workflow_node.deploy.form.volcengine_cdn_domain.placeholder": "Please enter VolcEngine CDN domain name",
+1
View File
@@ -183,6 +183,7 @@
"provider.tencentcloud_vod": "腾讯云 - 云点播 VOD",
"provider.tencentcloud_waf": "腾讯云 - Web 应用防火墙 WAF",
"provider.ucloud": "优刻得",
"provider.ucloud_ualb": "优刻得 - 应用型负载均衡 ALB",
"provider.ucloud_ucdn": "优刻得 - 内容分发 UCDN",
"provider.ucloud_uclb": "优刻得 - 传统型负载均衡 CLB",
"provider.ucloud_udnr": "优刻得 - 域名服务 UDNR",
@@ -908,6 +908,20 @@
"workflow_node.deploy.form.tencentcloud_waf_instance_id.label": "腾讯云 WAF 实例 ID",
"workflow_node.deploy.form.tencentcloud_waf_instance_id.placeholder": "请输入腾讯云 WAF 实例 ID",
"workflow_node.deploy.form.tencentcloud_waf_instance_id.tooltip": "这是什么?请参阅 <a href=\"https://console.cloud.tencent.com/waf\" target=\"_blank\">https://console.cloud.tencent.com/waf</a>",
"workflow_node.deploy.form.ucloud_ualb_region.label": "优刻得服务地域",
"workflow_node.deploy.form.ucloud_ualb_region.placeholder": "优刻得 UALB 服务地域(例如:cn-bj2",
"workflow_node.deploy.form.ucloud_ualb_region.tooltip": "这是什么?请参阅 <a href=\"https://docs.ucloud.cn/api/summary/regionlist\" target=\"_blank\">https://docs.ucloud.cn/api/summary/regionlist</a>",
"workflow_node.deploy.form.ucloud_ualb_resource_type.option.loadbalancer.label": "部署到指定负载均衡器下的全部 HTTPS 监听器",
"workflow_node.deploy.form.ucloud_ualb_resource_type.option.listener.label": "部署到指定 HTTPS 监听器",
"workflow_node.deploy.form.ucloud_ualb_loadbalancer_id.label": "优刻得 UALB 负载均衡器实例 ID",
"workflow_node.deploy.form.ucloud_ualb_loadbalancer_id.placeholder": "请输入优刻得 UALB 负载均衡器实例 ID",
"workflow_node.deploy.form.ucloud_ualb_loadbalancer_id.tooltip": "这是什么?请参阅 <a href=\"https://console.ucloud.cn/ulb/alb\" target=\"_blank\">https://console.ucloud.cn/ulb/alb</a>",
"workflow_node.deploy.form.ucloud_ualb_listener_id.label": "优刻得 UALB 监听器 ID",
"workflow_node.deploy.form.ucloud_ualb_listener_id.placeholder": "请输入优刻得 UALB 监听器 ID",
"workflow_node.deploy.form.ucloud_ualb_listener_id.tooltip": "这是什么?请参阅 <a href=\"https://console.ucloud.cn/ulb/alb\" target=\"_blank\">https://console.ucloud.cn/ulb/alb</a>",
"workflow_node.deploy.form.ucloud_ualb_snidomain.label": "优刻得 UALB 扩展域名(可选)",
"workflow_node.deploy.form.ucloud_ualb_snidomain.placeholder": "请输入优刻得 UALB 扩展域名",
"workflow_node.deploy.form.ucloud_ualb_snidomain.help": "提示:不填写时,将替换监听器的默认证书;否则,将替换扩展域名证书。",
"workflow_node.deploy.form.ucloud_ucdn_domain_id.label": "优刻得 UCDN 域名 ID",
"workflow_node.deploy.form.ucloud_ucdn_domain_id.placeholder": "请输入优刻得 UCDN 域名 ID",
"workflow_node.deploy.form.ucloud_ucdn_domain_id.tooltip": "这是什么?请参阅 <a href=\"https://console.ucloud.cn/ucdn\" target=\"_blank\">https://console.ucloud.cn/ucdn</a>",