From fbf538b5568abe8798ee4c2d2e31aac13bd2af37 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Wed, 17 Dec 2025 16:33:31 +0800 Subject: [PATCH] feat(provider): new deployment provider: ucloud ualb --- internal/certmgmt/deployers/sp_ucloud_ualb.go | 31 ++ .../deployer/providers/ucloud-ualb/consts.go | 8 + .../providers/ucloud-ualb/ucloud_ualb.go | 329 ++++++++++++++++++ .../providers/ucloud-ualb/ucloud_ualb_test.go | 86 +++++ pkg/sdk3rd/ucloud/ulb/api_add_ssl_binding.go | 32 ++ .../ucloud/ulb/api_delete_ssl_binding.go | 32 ++ .../ucloud/ulb/api_describe_listeners.go | 32 ++ pkg/sdk3rd/ucloud/ulb/api_describe_ssl_v2.go | 32 ++ .../ulb/api_update_listener_attribute.go | 32 ++ .../BizDeployNodeConfigFieldsProvider.tsx | 2 + ...ployNodeConfigFieldsProviderUCloudUALB.tsx | 139 ++++++++ ui/src/domain/provider.ts | 2 + ui/src/i18n/locales/en/nls.provider.json | 1 + .../i18n/locales/en/nls.workflow.nodes.json | 18 +- ui/src/i18n/locales/zh/nls.provider.json | 1 + .../i18n/locales/zh/nls.workflow.nodes.json | 14 + 16 files changed, 789 insertions(+), 2 deletions(-) create mode 100644 internal/certmgmt/deployers/sp_ucloud_ualb.go create mode 100644 pkg/core/deployer/providers/ucloud-ualb/consts.go create mode 100644 pkg/core/deployer/providers/ucloud-ualb/ucloud_ualb.go create mode 100644 pkg/core/deployer/providers/ucloud-ualb/ucloud_ualb_test.go create mode 100644 pkg/sdk3rd/ucloud/ulb/api_add_ssl_binding.go create mode 100644 pkg/sdk3rd/ucloud/ulb/api_delete_ssl_binding.go create mode 100644 pkg/sdk3rd/ucloud/ulb/api_describe_listeners.go create mode 100644 pkg/sdk3rd/ucloud/ulb/api_describe_ssl_v2.go create mode 100644 pkg/sdk3rd/ucloud/ulb/api_update_listener_attribute.go create mode 100644 ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderUCloudUALB.tsx diff --git a/internal/certmgmt/deployers/sp_ucloud_ualb.go b/internal/certmgmt/deployers/sp_ucloud_ualb.go new file mode 100644 index 000000000..2d641f54a --- /dev/null +++ b/internal/certmgmt/deployers/sp_ucloud_ualb.go @@ -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 + }) +} diff --git a/pkg/core/deployer/providers/ucloud-ualb/consts.go b/pkg/core/deployer/providers/ucloud-ualb/consts.go new file mode 100644 index 000000000..d858a8663 --- /dev/null +++ b/pkg/core/deployer/providers/ucloud-ualb/consts.go @@ -0,0 +1,8 @@ +package ucloudualb + +const ( + // 资源类型:部署到指定负载均衡器。 + RESOURCE_TYPE_LOADBALANCER = "loadbalancer" + // 资源类型:部署到指定监听器。 + RESOURCE_TYPE_LISTENER = "listener" +) diff --git a/pkg/core/deployer/providers/ucloud-ualb/ucloud_ualb.go b/pkg/core/deployer/providers/ucloud-ualb/ucloud_ualb.go new file mode 100644 index 000000000..f2f185918 --- /dev/null +++ b/pkg/core/deployer/providers/ucloud-ualb/ucloud_ualb.go @@ -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 +} diff --git a/pkg/core/deployer/providers/ucloud-ualb/ucloud_ualb_test.go b/pkg/core/deployer/providers/ucloud-ualb/ucloud_ualb_test.go new file mode 100644 index 000000000..080409a28 --- /dev/null +++ b/pkg/core/deployer/providers/ucloud-ualb/ucloud_ualb_test.go @@ -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) + }) +} diff --git a/pkg/sdk3rd/ucloud/ulb/api_add_ssl_binding.go b/pkg/sdk3rd/ucloud/ulb/api_add_ssl_binding.go new file mode 100644 index 000000000..7b63e749f --- /dev/null +++ b/pkg/sdk3rd/ucloud/ulb/api_add_ssl_binding.go @@ -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 +} diff --git a/pkg/sdk3rd/ucloud/ulb/api_delete_ssl_binding.go b/pkg/sdk3rd/ucloud/ulb/api_delete_ssl_binding.go new file mode 100644 index 000000000..3abf397b4 --- /dev/null +++ b/pkg/sdk3rd/ucloud/ulb/api_delete_ssl_binding.go @@ -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 +} diff --git a/pkg/sdk3rd/ucloud/ulb/api_describe_listeners.go b/pkg/sdk3rd/ucloud/ulb/api_describe_listeners.go new file mode 100644 index 000000000..80f01767e --- /dev/null +++ b/pkg/sdk3rd/ucloud/ulb/api_describe_listeners.go @@ -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 +} diff --git a/pkg/sdk3rd/ucloud/ulb/api_describe_ssl_v2.go b/pkg/sdk3rd/ucloud/ulb/api_describe_ssl_v2.go new file mode 100644 index 000000000..6559d3353 --- /dev/null +++ b/pkg/sdk3rd/ucloud/ulb/api_describe_ssl_v2.go @@ -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 +} diff --git a/pkg/sdk3rd/ucloud/ulb/api_update_listener_attribute.go b/pkg/sdk3rd/ucloud/ulb/api_update_listener_attribute.go new file mode 100644 index 000000000..0b64c5cbd --- /dev/null +++ b/pkg/sdk3rd/ucloud/ulb/api_update_listener_attribute.go @@ -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 +} diff --git a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx index 92cbaed4f..33526e006 100644 --- a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx +++ b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx @@ -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 { + 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 ( + <> + } + > + + + + + + + + + } + > + + + + + + + + + + + ); +}; + +const getInitialValues = (): Nullish>> => { + return { + region: "", + resourceType: RESOURCE_TYPE_LISTENER, + loadbalancerId: "", + listenerId: "", + }; +}; + +const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) => { + 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; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index c148199d5..13b22e2bf 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -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: Maphttps://console.tencentcloud.com/waf", + "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 https://www.ucloud-global.com/en/docs/api/summary/regionlist", + "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 https://console.ucloud-global.com/ulb/alb", + "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 https://console.ucloud-global.com/ulb/alb", + "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 https://console.ucloud-global.com/ucdn", @@ -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 https://console.volcengine.com/alb", - "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", diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json index 6b0db58b4..d357ac3c4 100644 --- a/ui/src/i18n/locales/zh/nls.provider.json +++ b/ui/src/i18n/locales/zh/nls.provider.json @@ -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", diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json index 674a89a16..68cda533c 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json @@ -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": "这是什么?请参阅 https://console.cloud.tencent.com/waf", + "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": "这是什么?请参阅 https://docs.ucloud.cn/api/summary/regionlist", + "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": "这是什么?请参阅 https://console.ucloud.cn/ulb/alb", + "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": "这是什么?请参阅 https://console.ucloud.cn/ulb/alb", + "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": "这是什么?请参阅 https://console.ucloud.cn/ucdn",