diff --git a/README.md b/README.md index f744e1fe..3aa3f158 100644 --- a/README.md +++ b/README.md @@ -130,7 +130,7 @@ make local.run | [百度智能云](https://cloud.baidu.com/) | 可部署到百度智能云 CDN 等服务 | | [华为云](https://www.huaweicloud.com/) | 可部署到华为云 CDN、ELB、WAF 等服务 | | [火山引擎](https://www.volcengine.com/) | 可部署到火山引擎 TOS、CDN、DCDN、CLB、ImageX、Live 等服务 | -| [京东云](https://www.jdcloud.com/) | 可部署到京东云 CDN、视频直播等服务 | +| [京东云](https://www.jdcloud.com/) | 可部署到京东云 CDN、ALB、视频直播等服务 | | [七牛云](https://www.qiniu.com/) | 可部署到七牛云 CDN、直播云等服务 | | [白山云](https://www.baishan.com/) | 可部署到白山云 CDN | | [多吉云](https://www.dogecloud.com/) | 可部署到多吉云 CDN | diff --git a/README_EN.md b/README_EN.md index 05003b60..c9a5bdf4 100644 --- a/README_EN.md +++ b/README_EN.md @@ -129,7 +129,7 @@ The following hosting providers are supported: | [Baidu AI Cloud](https://intl.cloud.baidu.com/) | Supports deployment to Baidu AI CLoud CDN | | [Huawei Cloud](https://www.huaweicloud.com/) | Supports deployment to Huawei Cloud CDN, ELB, WAF | | [Volcengine](https://www.volcengine.com/) | Supports deployment to Volcengine TOS, CDN, DCDN, CLB, ImageX, Live | -| [JD Cloud](https://www.jdcloud.com/) | Supports deployment to JD Cloud CDN, Live Video | +| [JD Cloud](https://www.jdcloud.com/) | Supports deployment to JD Cloud CDN, ALB, Live Video | | [Qiniu Cloud](https://www.qiniu.com/) | Supports deployment to Qiniu Cloud CDN, Pili | | [Baishan Cloud](https://intl.baishancloud.com/) | Supports deployment to Baishan Cloud CDN | | [Doge Cloud](https://www.dogecloud.com/) | Supports deployment to Doge Cloud CDN | diff --git a/internal/deployer/providers.go b/internal/deployer/providers.go index 9210a7e5..24fc32f4 100644 --- a/internal/deployer/providers.go +++ b/internal/deployer/providers.go @@ -30,6 +30,7 @@ import ( pHuaweiCloudCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/huaweicloud-cdn" pHuaweiCloudELB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/huaweicloud-elb" pHuaweiCloudWAF "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/huaweicloud-waf" + pJDCloudALB "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-alb" pJDCloudCDN "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-cdn" pJDCloudLive "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-live" pK8sSecret "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/k8s-secret" @@ -417,7 +418,7 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } } - case domain.DeployProviderTypeJDCloudCDN, domain.DeployProviderTypeJDCloudLive: + case domain.DeployProviderTypeJDCloudALB, domain.DeployProviderTypeJDCloudCDN, domain.DeployProviderTypeJDCloudLive: { access := domain.AccessConfigForJDCloud{} if err := maps.Populate(options.ProviderAccessConfig, &access); err != nil { @@ -425,6 +426,17 @@ func createDeployer(options *deployerOptions) (deployer.Deployer, error) { } switch options.Provider { + case domain.DeployProviderTypeJDCloudALB: + deployer, err := pJDCloudALB.NewDeployer(&pJDCloudALB.DeployerConfig{ + AccessKeyId: access.AccessKeyId, + AccessKeySecret: access.AccessKeySecret, + RegionId: maps.GetValueAsString(options.ProviderDeployConfig, "regionId"), + ResourceType: pJDCloudALB.ResourceType(maps.GetValueAsString(options.ProviderDeployConfig, "resourceType")), + LoadbalancerId: maps.GetValueAsString(options.ProviderDeployConfig, "loadbalancerId"), + ListenerId: maps.GetValueAsString(options.ProviderDeployConfig, "listenerId"), + }) + return deployer, err + case domain.DeployProviderTypeJDCloudCDN: deployer, err := pJDCloudCDN.NewDeployer(&pJDCloudCDN.DeployerConfig{ AccessKeyId: access.AccessKeyId, diff --git a/internal/domain/provider.go b/internal/domain/provider.go index b39af710..63528231 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -127,6 +127,7 @@ const ( DeployProviderTypeHuaweiCloudCDN = DeployProviderType("huaweicloud-cdn") DeployProviderTypeHuaweiCloudELB = DeployProviderType("huaweicloud-elb") DeployProviderTypeHuaweiCloudWAF = DeployProviderType("huaweicloud-waf") + DeployProviderTypeJDCloudALB = DeployProviderType("jdcloud-alb") DeployProviderTypeJDCloudCDN = DeployProviderType("jdcloud-cdn") DeployProviderTypeJDCloudLive = DeployProviderType("jdcloud-live") DeployProviderTypeKubernetesSecret = DeployProviderType("k8s-secret") diff --git a/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go b/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go index 27261a63..39600c7b 100644 --- a/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go +++ b/internal/pkg/core/deployer/providers/aliyun-alb/aliyun_alb.go @@ -254,7 +254,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL d.logger.Logt("已更新 ALB 监听配置", updateListenerAttributeResp) } else { - // 指定 SNI,需部署到扩展域名(支持泛域名) + // 指定 SNI,需部署到扩展域名 // 查询监听证书列表 // REF: https://help.aliyun.com/zh/slb/application-load-balancer/developer-reference/api-alb-2020-06-16-listlistenercertificates diff --git a/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go b/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go index e18dccc4..21a1e471 100644 --- a/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go +++ b/internal/pkg/core/deployer/providers/aliyun-clb/aliyun_clb.go @@ -211,8 +211,6 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL // 修改监听配置 // REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-setloadbalancerhttpslistenerattribute - // - // 注意修改监听配置要放在修改扩展域名之后 setLoadBalancerHTTPSListenerAttributeReq := &aliyunSlb.SetLoadBalancerHTTPSListenerAttributeRequest{ RegionId: tea.String(d.config.Region), LoadBalancerId: tea.String(cloudLoadbalancerId), @@ -226,7 +224,7 @@ func (d *DeployerProvider) updateListenerCertificate(ctx context.Context, cloudL d.logger.Logt("已更新 CLB HTTPS 监听配置", setLoadBalancerHTTPSListenerAttributeResp) } else { - // 指定 SNI,需部署到扩展域名(支持泛域名) + // 指定 SNI,需部署到扩展域名 // 查询扩展域名 // REF: https://help.aliyun.com/zh/slb/classic-load-balancer/developer-reference/api-slb-2014-05-15-describedomainextensions diff --git a/internal/pkg/core/deployer/providers/jdcloud-alb/consts.go b/internal/pkg/core/deployer/providers/jdcloud-alb/consts.go new file mode 100644 index 00000000..13525c20 --- /dev/null +++ b/internal/pkg/core/deployer/providers/jdcloud-alb/consts.go @@ -0,0 +1,10 @@ +package jdcloudalb + +type ResourceType string + +const ( + // 资源类型:部署到指定负载均衡器。 + RESOURCE_TYPE_LOADBALANCER = ResourceType("loadbalancer") + // 资源类型:部署到指定监听器。 + RESOURCE_TYPE_LISTENER = ResourceType("listener") +) diff --git a/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb.go b/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb.go new file mode 100644 index 00000000..a300bb4d --- /dev/null +++ b/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb.go @@ -0,0 +1,251 @@ +package jdcloudalb + +import ( + "context" + "errors" + "fmt" + "strings" + + jdCore "github.com/jdcloud-api/jdcloud-sdk-go/core" + jdCommon "github.com/jdcloud-api/jdcloud-sdk-go/services/common/models" + jdLbApi "github.com/jdcloud-api/jdcloud-sdk-go/services/lb/apis" + jdLbClient "github.com/jdcloud-api/jdcloud-sdk-go/services/lb/client" + jdLbModel "github.com/jdcloud-api/jdcloud-sdk-go/services/lb/models" + xerrors "github.com/pkg/errors" + + "github.com/usual2970/certimate/internal/pkg/core/deployer" + "github.com/usual2970/certimate/internal/pkg/core/logger" + "github.com/usual2970/certimate/internal/pkg/core/uploader" + uploadersp "github.com/usual2970/certimate/internal/pkg/core/uploader/providers/jdcloud-ssl" + "github.com/usual2970/certimate/internal/pkg/utils/slices" +) + +type DeployerConfig struct { + // 京东云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 京东云 AccessKeySecret。 + AccessKeySecret string `json:"accessKeySecret"` + // 京东云地域 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"` + // SNI 域名(支持泛域名)。 + // 部署资源类型为 [RESOURCE_TYPE_LOADBALANCER]、[RESOURCE_TYPE_LISTENER] 时选填。 + Domain string `json:"domain,omitempty"` +} + +type DeployerProvider struct { + config *DeployerConfig + logger logger.Logger + sdkClient *jdLbClient.LbClient + 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.AccessKeySecret) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create sdk client") + } + + uploader, err := uploadersp.NewUploader(&uploadersp.UploaderConfig{ + AccessKeyId: config.AccessKeyId, + AccessKeySecret: config.AccessKeySecret, + }) + if err != nil { + return nil, xerrors.Wrap(err, "failed to create ssl uploader") + } + + return &DeployerProvider{ + config: config, + logger: logger.NewNilLogger(), + sdkClient: client, + sslUploader: uploader, + }, nil +} + +func (d *DeployerProvider) WithLogger(logger logger.Logger) *DeployerProvider { + d.logger = logger + return d +} + +func (d *DeployerProvider) Deploy(ctx context.Context, certPem string, privkeyPem string) (*deployer.DeployResult, error) { + // 上传证书到 SSL + upres, err := d.sslUploader.Upload(ctx, certPem, privkeyPem) + if err != nil { + return nil, xerrors.Wrap(err, "failed to upload certificate file") + } else { + d.logger.Logt("certificate file uploaded", 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://docs.jdcloud.com/cn/load-balancer/api/describeloadbalancer + describeLoadBalancerReq := jdLbApi.NewDescribeLoadBalancerRequest(d.config.RegionId, d.config.LoadbalancerId) + describeLoadBalancerResp, err := d.sdkClient.DescribeLoadBalancer(describeLoadBalancerReq) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'lb.DescribeLoadBalancer'") + } else { + d.logger.Logt("已查询到负载均衡器详情", describeLoadBalancerResp) + } + + // 查询监听器列表 + // REF: https://docs.jdcloud.com/cn/load-balancer/api/describelisteners + listenerIds := make([]string, 0) + describeListenersPageNumber := 1 + describeListenersPageSize := 100 + for { + describeListenersReq := jdLbApi.NewDescribeListenersRequest(d.config.RegionId) + describeListenersReq.SetFilters([]jdCommon.Filter{{Name: "loadBalancerId", Values: []string{d.config.LoadbalancerId}}}) + describeListenersReq.SetPageSize(describeListenersPageNumber) + describeListenersReq.SetPageSize(describeListenersPageSize) + describeListenersResp, err := d.sdkClient.DescribeListeners(describeListenersReq) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'lb.DescribeListeners'") + } + + for _, listener := range describeListenersResp.Result.Listeners { + if strings.EqualFold(listener.Protocol, "https") || strings.EqualFold(listener.Protocol, "tls") { + listenerIds = append(listenerIds, listener.ListenerId) + } + } + + if len(describeListenersResp.Result.Listeners) < int(describeListenersPageSize) { + break + } else { + describeListenersPageNumber++ + } + } + + // 遍历更新监听器证书 + if len(listenerIds) == 0 { + return errors.New("listener not found") + } else { + d.logger.Logt("已查询到负载均衡器下的全部 HTTPS/TLS 监听器", listenerIds) + + var errs []error + + for _, listenerId := range listenerIds { + 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://docs.jdcloud.com/cn/load-balancer/api/describelistener + describeListenerReq := jdLbApi.NewDescribeListenerRequest(d.config.RegionId, cloudListenerId) + describeListenerResp, err := d.sdkClient.DescribeListener(describeListenerReq) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'lb.DescribeListener'") + } else { + d.logger.Logt("已查询到监听器详情", describeListenerResp) + } + + if d.config.Domain == "" { + // 未指定 SNI,只需部署到监听器 + + // 修改监听器信息 + // REF: https://docs.jdcloud.com/cn/load-balancer/api/updatelistener + updateListenerReq := jdLbApi.NewUpdateListenerRequest(d.config.RegionId, cloudListenerId) + updateListenerReq.SetCertificateSpecs([]jdLbModel.CertificateSpec{{CertificateId: cloudCertId}}) + updateListenerResp, err := d.sdkClient.UpdateListener(updateListenerReq) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'lb.UpdateListener'") + } else { + d.logger.Logt("已修改监听器信息", updateListenerResp) + } + } else { + // 指定 SNI,需部署到扩展证书 + + extCertSpecs := slices.Filter(describeListenerResp.Result.Listener.ExtensionCertificateSpecs, func(extCertSpec jdLbModel.ExtensionCertificateSpec) bool { + return extCertSpec.Domain == d.config.Domain + }) + if len(extCertSpecs) == 0 { + return errors.New("extension certificate spec not found") + } + + // 批量修改扩展证书 + // REF: https://docs.jdcloud.com/cn/load-balancer/api/updatelistenercertificates + updateListenerCertificatesReq := jdLbApi.NewUpdateListenerCertificatesRequest( + d.config.RegionId, + cloudListenerId, + slices.Map(extCertSpecs, func(extCertSpec jdLbModel.ExtensionCertificateSpec) jdLbModel.ExtCertificateUpdateSpec { + return jdLbModel.ExtCertificateUpdateSpec{ + CertificateBindId: extCertSpec.CertificateBindId, + CertificateId: &cloudCertId, + Domain: &extCertSpec.Domain, + } + }), + ) + updateListenerCertificatesResp, err := d.sdkClient.UpdateListenerCertificates(updateListenerCertificatesReq) + if err != nil { + return xerrors.Wrap(err, "failed to execute sdk request 'lb.UpdateListenerCertificates'") + } else { + d.logger.Logt("已批量修改扩展证书", updateListenerCertificatesResp) + } + } + + return nil +} + +func createSdkClient(accessKeyId, accessKeySecret string) (*jdLbClient.LbClient, error) { + clientCredentials := jdCore.NewCredentials(accessKeyId, accessKeySecret) + client := jdLbClient.NewLbClient(clientCredentials) + return client, nil +} diff --git a/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb_test.go b/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb_test.go new file mode 100644 index 00000000..9c9cc9cc --- /dev/null +++ b/internal/pkg/core/deployer/providers/jdcloud-alb/jdcloud_alb_test.go @@ -0,0 +1,118 @@ +package jdcloudalb_test + +import ( + "context" + "flag" + "fmt" + "os" + "strings" + "testing" + + provider "github.com/usual2970/certimate/internal/pkg/core/deployer/providers/jdcloud-alb" +) + +var ( + fInputCertPath string + fInputKeyPath string + fAccessKeyId string + fAccessKeySecret string + fRegionId string + fLoadbalancerId string + fListenerId string +) + +func init() { + argsPrefix := "CERTIMATE_DEPLOYER_JDCLOUDALB_" + + flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") + flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") + flag.StringVar(&fAccessKeyId, argsPrefix+"ACCESSKEYID", "", "") + flag.StringVar(&fAccessKeySecret, argsPrefix+"ACCESSKEYSECRET", "", "") + flag.StringVar(&fRegionId, argsPrefix+"REGIONID", "", "") + flag.StringVar(&fLoadbalancerId, argsPrefix+"LOADBALANCERID", "", "") + flag.StringVar(&fListenerId, argsPrefix+"LISTENERID", "", "") +} + +/* +Shell command to run this test: + + go test -v ./jdcloud_alb_test.go -args \ + --CERTIMATE_DEPLOYER_JDCLOUDALB_INPUTCERTPATH="/path/to/your-input-cert.pem" \ + --CERTIMATE_DEPLOYER_JDCLOUDALB_INPUTKEYPATH="/path/to/your-input-key.pem" \ + --CERTIMATE_DEPLOYER_JDCLOUDALB_ACCESSKEYID="your-access-key-id" \ + --CERTIMATE_DEPLOYER_JDCLOUDALB_ACCESSKEYSECRET="your-secret-access-key" \ + --CERTIMATE_DEPLOYER_JDCLOUDALB_REGION_ID="cn-north-1" \ + --CERTIMATE_DEPLOYER_JDCLOUDALB_LOADBALANCERID="your-alb-loadbalancer-id" \ + --CERTIMATE_DEPLOYER_JDCLOUDALB_LISTENERID="your-alb-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("ACCESSKEYSECRET: %v", fAccessKeySecret), + fmt.Sprintf("REGIONID: %v", fRegionId), + fmt.Sprintf("LOADBALANCERID: %v", fLoadbalancerId), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + 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("ACCESSKEYSECRET: %v", fAccessKeySecret), + fmt.Sprintf("REGIONID: %v", fRegionId), + fmt.Sprintf("LISTENERID: %v", fListenerId), + }, "\n")) + + deployer, err := provider.NewDeployer(&provider.DeployerConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + 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) + }) +} diff --git a/internal/pkg/core/deployer/providers/jdcloud-cdn/jdcloud_cdn_test.go b/internal/pkg/core/deployer/providers/jdcloud-cdn/jdcloud_cdn_test.go index b95636dc..2d2f7ed0 100644 --- a/internal/pkg/core/deployer/providers/jdcloud-cdn/jdcloud_cdn_test.go +++ b/internal/pkg/core/deployer/providers/jdcloud-cdn/jdcloud_cdn_test.go @@ -20,7 +20,7 @@ var ( ) func init() { - argsPrefix := "CERTIMATE_DEPLOYER_BAIDUCLOUDCDN_" + argsPrefix := "CERTIMATE_DEPLOYER_JDCLOUDCDN_" flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") diff --git a/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live_test.go b/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live_test.go index 510198cc..076202b5 100644 --- a/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live_test.go +++ b/internal/pkg/core/deployer/providers/jdcloud-live/jdcloud_live_test.go @@ -20,7 +20,7 @@ var ( ) func init() { - argsPrefix := "CERTIMATE_DEPLOYER_BAIDUCLOUDCDN_" + argsPrefix := "CERTIMATE_DEPLOYER_JDCLOUDLIVE_" flag.StringVar(&fInputCertPath, argsPrefix+"INPUTCERTPATH", "", "") flag.StringVar(&fInputKeyPath, argsPrefix+"INPUTKEYPATH", "", "") diff --git a/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb.go b/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb.go index acaf84b2..521f3e34 100644 --- a/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb.go +++ b/internal/pkg/core/deployer/providers/tencentcloud-clb/tencentcloud_clb.go @@ -138,7 +138,7 @@ func (d *DeployerProvider) deployViaSslService(ctx context.Context, cloudCertId // 未指定 SNI,只需部署到监听器 deployCertificateInstanceReq.InstanceIdList = common.StringPtrs([]string{fmt.Sprintf("%s|%s", d.config.LoadbalancerId, d.config.ListenerId)}) } else { - // 指定 SNI,需部署到域名(支持泛域名) + // 指定 SNI,需部署到域名 deployCertificateInstanceReq.InstanceIdList = common.StringPtrs([]string{fmt.Sprintf("%s|%s|%s", d.config.LoadbalancerId, d.config.ListenerId, d.config.Domain)}) } deployCertificateInstanceResp, err := d.sdkClients.ssl.DeployCertificateInstance(deployCertificateInstanceReq) diff --git a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx index 3ae859b6..f62191d0 100644 --- a/ui/src/components/workflow/node/DeployNodeConfigForm.tsx +++ b/ui/src/components/workflow/node/DeployNodeConfigForm.tsx @@ -38,6 +38,7 @@ import DeployNodeConfigFormGcoreCDNConfig from "./DeployNodeConfigFormGcoreCDNCo import DeployNodeConfigFormHuaweiCloudCDNConfig from "./DeployNodeConfigFormHuaweiCloudCDNConfig"; import DeployNodeConfigFormHuaweiCloudELBConfig from "./DeployNodeConfigFormHuaweiCloudELBConfig"; import DeployNodeConfigFormHuaweiCloudWAFConfig from "./DeployNodeConfigFormHuaweiCloudWAFConfig"; +import DeployNodeConfigFormJDCloudALBConfig from "./DeployNodeConfigFormJDCloudALBConfig"; import DeployNodeConfigFormJDCloudCDNConfig from "./DeployNodeConfigFormJDCloudCDNConfig"; import DeployNodeConfigFormJDCloudLiveConfig from "./DeployNodeConfigFormJDCloudLiveConfig"; import DeployNodeConfigFormKubernetesSecretConfig from "./DeployNodeConfigFormKubernetesSecretConfig"; @@ -180,6 +181,8 @@ const DeployNodeConfigForm = forwardRef; case DEPLOY_PROVIDERS.HUAWEICLOUD_WAF: return ; + case DEPLOY_PROVIDERS.JDCLOUD_ALB: + return ; case DEPLOY_PROVIDERS.JDCLOUD_CDN: return ; case DEPLOY_PROVIDERS.JDCLOUD_LIVE: diff --git a/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudALBConfig.tsx b/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudALBConfig.tsx new file mode 100644 index 00000000..f54477ce --- /dev/null +++ b/ui/src/components/workflow/node/DeployNodeConfigFormJDCloudALBConfig.tsx @@ -0,0 +1,142 @@ +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"; +import { validDomainName } from "@/utils/validators"; + +type DeployNodeConfigFormJDCloudALBConfigFieldValues = Nullish<{ + resourceType: string; + regionId: string; + loadbalancerId?: string; + listenerId?: string; + domain?: string; +}>; + +export type DeployNodeConfigFormJDCloudALBConfigProps = { + form: FormInstance; + formName: string; + disabled?: boolean; + initialValues?: DeployNodeConfigFormJDCloudALBConfigFieldValues; + onValuesChange?: (values: DeployNodeConfigFormJDCloudALBConfigFieldValues) => void; +}; + +const RESOURCE_TYPE_LOADBALANCER = "loadbalancer" as const; +const RESOURCE_TYPE_LISTENER = "listener" as const; + +const initFormModel = (): DeployNodeConfigFormJDCloudALBConfigFieldValues => { + return {}; +}; + +const DeployNodeConfigFormJDCloudALBConfig = ({ + form: formInst, + formName, + disabled, + initialValues, + onValuesChange, +}: DeployNodeConfigFormJDCloudALBConfigProps) => { + 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.jdcloud_alb_resource_type.placeholder"), + }), + regionId: z + .string({ message: t("workflow_node.deploy.form.jdcloud_alb_region_id.placeholder") }) + .nonempty(t("workflow_node.deploy.form.jdcloud_alb_region_id.placeholder")) + .trim(), + loadbalancerId: z + .string() + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim() + .nullish() + .refine((v) => fieldResourceType !== RESOURCE_TYPE_LOADBALANCER || !!v?.trim(), t("workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.placeholder")), + listenerId: z + .string() + .max(64, t("common.errmsg.string_max", { max: 64 })) + .trim() + .nullish() + .refine((v) => fieldResourceType !== RESOURCE_TYPE_LISTENER || !!v?.trim(), t("workflow_node.deploy.form.jdcloud_alb_listener_id.placeholder")), + domain: z + .string() + .nullish() + .refine((v) => { + if (![RESOURCE_TYPE_LOADBALANCER, RESOURCE_TYPE_LISTENER].includes(fieldResourceType)) return true; + return !v || validDomainName(v!, { allowWildcard: true }); + }, t("common.errmsg.domain_invalid")), + }); + const formRule = createSchemaFieldRule(formSchema); + + const fieldResourceType = Form.useWatch("resourceType", formInst); + + const handleFormChange = (_: unknown, values: z.infer) => { + onValuesChange?.(values); + }; + + return ( +
+ + + + + } + > + + + + + } + > + + + + + + } + > + + + + + + } + > + + + +
+ ); +}; + +export default DeployNodeConfigFormJDCloudALBConfig; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index a2b09baf..e2475247 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -221,6 +221,7 @@ export const DEPLOY_PROVIDERS = Object.freeze({ HUAWEICLOUD_CDN: `${ACCESS_PROVIDERS.HUAWEICLOUD}-cdn`, HUAWEICLOUD_ELB: `${ACCESS_PROVIDERS.HUAWEICLOUD}-elb`, HUAWEICLOUD_WAF: `${ACCESS_PROVIDERS.HUAWEICLOUD}-waf`, + JDCLOUD_ALB: `${ACCESS_PROVIDERS.JDCLOUD}-alb`, JDCLOUD_CDN: `${ACCESS_PROVIDERS.JDCLOUD}-cdn`, JDCLOUD_LIVE: `${ACCESS_PROVIDERS.JDCLOUD}-live`, KUBERNETES_SECRET: `${ACCESS_PROVIDERS.KUBERNETES}-secret`, @@ -309,6 +310,7 @@ export const deployProvidersMap: Maphttps://console-intl.huaweicloud.com/console/#/waf/domain/list", + "workflow_node.deploy.form.jdcloud_alb_resource_type.label": "Resource type", + "workflow_node.deploy.form.jdcloud_alb_resource_type.placeholder": "Please select resource type", + "workflow_node.deploy.form.jdcloud_alb_resource_type.option.loadbalancer.label": "ALB load balancer", + "workflow_node.deploy.form.jdcloud_alb_resource_type.option.listener.label": "ALB listener", + "workflow_node.deploy.form.jdcloud_alb_region_id.label": "JD Cloud ALB region ID", + "workflow_node.deploy.form.jdcloud_alb_region_id.placeholder": "Please enter JD Cloud ALB region ID (e.g. cn-north-1)", + "workflow_node.deploy.form.jdcloud_alb_region_id.tooltip": "For more information, see https://docs.jdcloud.com/en/common-declaration/api/introduction", + "workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.label": "JD Cloud ALB load balancer ID", + "workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.placeholder": "Please enter JD Cloud ALB load balancer ID", + "workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.tooltip": "For more information, see https://cns-console.jdcloud.com/host/loadBalance/list", + "workflow_node.deploy.form.jdcloud_alb_listener_id.label": "JD Cloud ALB listener ID", + "workflow_node.deploy.form.jdcloud_alb_listener_id.placeholder": "Please enter JD Cloud ALB listener ID", + "workflow_node.deploy.form.jdcloud_alb_listener_id.tooltip": "For more information, see https://cns-console.jdcloud.com/host/loadBalance/list", + "workflow_node.deploy.form.jdcloud_alb_snidomain.label": "JD Cloud ALB SNI domain (Optional)", + "workflow_node.deploy.form.jdcloud_alb_snidomain.placeholder": "Please enter JD Cloud ALB SNI domain name", + "workflow_node.deploy.form.jdcloud_alb_snidomain.tooltip": "For more information, see https://cns-console.jdcloud.com/host/loadBalance/list", "workflow_node.deploy.form.jdcloud_cdn_domain.label": "JD Cloud CDN domain", "workflow_node.deploy.form.jdcloud_cdn_domain.placeholder": "Please enter JD Cloud CDN domain name", "workflow_node.deploy.form.jdcloud_cdn_domain.tooltip": "For more information, see https://cdn-console.jdcloud.com/", diff --git a/ui/src/i18n/locales/zh/nls.provider.json b/ui/src/i18n/locales/zh/nls.provider.json index 0d8e2911..dfa3022c 100644 --- a/ui/src/i18n/locales/zh/nls.provider.json +++ b/ui/src/i18n/locales/zh/nls.provider.json @@ -54,6 +54,7 @@ "provider.huaweicloud.elb": "华为云 - 弹性负载均衡 ELB", "provider.huaweicloud.waf": "华为云 - Web 应用防火墙 WAF", "provider.jdcloud": "京东云", + "provider.jdcloud.alb": "京东云 - 应用负载均衡 ALB", "provider.jdcloud.cdn": "京东云 - 内容分发网络 CDN", "provider.jdcloud.dns": "京东云 - 云解析 DNS", "provider.jdcloud.live": "京东云 - 视频直播", diff --git a/ui/src/i18n/locales/zh/nls.workflow.nodes.json b/ui/src/i18n/locales/zh/nls.workflow.nodes.json index a7331d08..0e68333b 100644 --- a/ui/src/i18n/locales/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/locales/zh/nls.workflow.nodes.json @@ -104,7 +104,7 @@ "workflow_node.deploy.form.aliyun_alb_listener_id.placeholder": "请输入阿里云 ALB 监听器 ID", "workflow_node.deploy.form.aliyun_alb_listener_id.tooltip": "这是什么?请参阅 https://slb.console.aliyun.com/alb", "workflow_node.deploy.form.aliyun_alb_snidomain.label": "阿里云 ALB 扩展域名(可选)", - "workflow_node.deploy.form.aliyun_alb_snidomain.placeholder": "请输入阿里云 ALB 扩展域名", + "workflow_node.deploy.form.aliyun_alb_snidomain.placeholder": "请输入阿里云 ALB 扩展域名(支持泛域名)", "workflow_node.deploy.form.aliyun_alb_snidomain.tooltip": "这是什么?请参阅 https://slb.console.aliyun.com/alb

不填写时,将替换监听器的默认证书。", "workflow_node.deploy.form.aliyun_cas_deploy.guide": "小贴士:由于阿里云证书部署任务是异步的,此节点若执行成功仅代表已创建部署任务,实际部署结果需要你自行前往阿里云控制台查询。", "workflow_node.deploy.form.aliyun_cas_deploy_region.label": "阿里云 CAS 服务地域", @@ -136,7 +136,7 @@ "workflow_node.deploy.form.aliyun_clb_listener_port.placeholder": "请输入阿里云 CLB 监听端口", "workflow_node.deploy.form.aliyun_clb_listener_port.tooltip": "这是什么?请参阅 https://slb.console.aliyun.com/clb", "workflow_node.deploy.form.aliyun_clb_snidomain.label": "阿里云 CLB 扩展域名(可选)", - "workflow_node.deploy.form.aliyun_clb_snidomain.placeholder": "请输入阿里云 CLB 扩展域名", + "workflow_node.deploy.form.aliyun_clb_snidomain.placeholder": "请输入阿里云 CLB 扩展域名(支持泛域名)", "workflow_node.deploy.form.aliyun_clb_snidomain.tooltip": "这是什么?请参阅 https://slb.console.aliyun.com/clb

不填写时,将替换监听器的默认证书。", "workflow_node.deploy.form.aliyun_cdn_domain.label": "阿里云 CDN 加速域名", "workflow_node.deploy.form.aliyun_cdn_domain.placeholder": "请输入阿里云 CDN 加速域名(支持泛域名)", @@ -267,6 +267,22 @@ "workflow_node.deploy.form.huaweicloud_waf_domain.label": "华为云 WAF 防护域名", "workflow_node.deploy.form.huaweicloud_waf_domain.placeholder": "请输入华为云 WAF 防护域名(支持泛域名)", "workflow_node.deploy.form.huaweicloud_waf_domain.tooltip": "这是什么?请参阅 https://console.huaweicloud.com/console/#/waf/domain/list", + "workflow_node.deploy.form.jdcloud_alb_resource_type.label": "证书替换方式", + "workflow_node.deploy.form.jdcloud_alb_resource_type.placeholder": "请选择证书替换方式", + "workflow_node.deploy.form.jdcloud_alb_resource_type.option.loadbalancer.label": "替换指定负载均衡器下的全部 HTTPS/TLS 监听的证书", + "workflow_node.deploy.form.jdcloud_alb_resource_type.option.listener.label": "替换指定负载均衡监听器的证书", + "workflow_node.deploy.form.jdcloud_alb_region_id.label": "京东云 ALB 服务地域 ID", + "workflow_node.deploy.form.jdcloud_alb_region_id.placeholder": "请输入京东云 ALB 服务地域 ID(例如:cn-north-1", + "workflow_node.deploy.form.jdcloud_alb_region_id.tooltip": "这是什么?请参阅 https://docs.jdcloud.com/cn/common-declaration/api/introduction", + "workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.label": "京东云 ALB 负载均衡器 ID", + "workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.placeholder": "请输入京东云 ALB 负载均衡器 ID", + "workflow_node.deploy.form.jdcloud_alb_loadbalancer_id.tooltip": "这是什么?请参阅 https://cns-console.jdcloud.com/host/loadBalance/list", + "workflow_node.deploy.form.jdcloud_alb_listener_id.label": "京东云 ALB 监听器 ID", + "workflow_node.deploy.form.jdcloud_alb_listener_id.placeholder": "请输入京东云 ALB 监听器 ID", + "workflow_node.deploy.form.jdcloud_alb_listener_id.tooltip": "这是什么?请参阅 https://cns-console.jdcloud.com/host/loadBalance/list", + "workflow_node.deploy.form.jdcloud_alb_snidomain.label": "京东云 ALB 扩展域名(可选)", + "workflow_node.deploy.form.jdcloud_alb_snidomain.placeholder": "请输入京东云 ALB 扩展域名(支持泛域名)", + "workflow_node.deploy.form.jdcloud_alb_snidomain.tooltip": "这是什么?请参阅 https://cns-console.jdcloud.com/host/loadBalance/list

不填写时,将替换监听器的默认证书。", "workflow_node.deploy.form.jdcloud_cdn_domain.label": "京东云 CDN 加速域名", "workflow_node.deploy.form.jdcloud_cdn_domain.placeholder": "请输入京东云 CDN 加速域名(支持泛域名)", "workflow_node.deploy.form.jdcloud_cdn_domain.tooltip": "这是什么?请参阅 https://cdn-console.jdcloud.com/",