From 33c871b3ed0a8043cd821161f739ee8ab6037806 Mon Sep 17 00:00:00 2001 From: Fu Diwei Date: Mon, 15 Jun 2026 18:05:56 +0800 Subject: [PATCH] feat(provider): new deployment provider: cmcccloud vlb --- go.mod | 7 +- .../certmgmt/deployers/sp_cmcccloud_vlb.go | 30 ++ internal/domain/provider.go | 1 + .../dns01/cmcccloud/internal/lego.go | 12 +- .../baiducloud-cert/baiducloud_cert.go | 2 +- .../providers/cmcccloud-vlb/cmcccloud_vlb.go | 156 ++++++++ .../cmcccloud-vlb/cmcccloud_vlb_test.go | 53 +++ .../providers/cmcccloud-vlb/cmcccloud_vlb.go | 310 +++++++++++++++ .../cmcccloud-vlb/cmcccloud_vlb_test.go | 77 ++++ .../providers/cmcccloud-vlb/consts.go | 8 + .../providers/ctcccloud-elb/ctcccloud_elb.go | 6 +- .../ecloud/ecloudsdkvlb/client.go | 373 ++++++++++++++++++ .../BizDeployNodeConfigFieldsProvider.tsx | 2 + ...oyNodeConfigFieldsProviderCMCCCloudVLB.tsx | 155 ++++++++ ui/src/domain/provider.ts | 2 + ui/src/i18n/resources/en/nls.provider.json | 1 + .../i18n/resources/en/nls.workflow.nodes.json | 33 +- ui/src/i18n/resources/zh/nls.provider.json | 1 + .../i18n/resources/zh/nls.workflow.nodes.json | 30 ++ 19 files changed, 1244 insertions(+), 15 deletions(-) create mode 100644 internal/certmgmt/deployers/sp_cmcccloud_vlb.go create mode 100644 pkg/core/certmgr/providers/cmcccloud-vlb/cmcccloud_vlb.go create mode 100644 pkg/core/certmgr/providers/cmcccloud-vlb/cmcccloud_vlb_test.go create mode 100644 pkg/core/deployer/providers/cmcccloud-vlb/cmcccloud_vlb.go create mode 100644 pkg/core/deployer/providers/cmcccloud-vlb/cmcccloud_vlb_test.go create mode 100644 pkg/core/deployer/providers/cmcccloud-vlb/consts.go create mode 100644 pkg/sdk3rd-trimmed/gitlab.ecloud.com/ecloud/ecloudsdkvlb/client.go create mode 100644 ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProviderCMCCCloudVLB.tsx diff --git a/go.mod b/go.mod index 788ce1445..879d0edb3 100644 --- a/go.mod +++ b/go.mod @@ -88,7 +88,8 @@ require ( gitlab.ecloud.com/ecloud/ecloudsdkcloudcore v1.0.0 gitlab.ecloud.com/ecloud/ecloudsdkclouddns v1.0.1 gitlab.ecloud.com/ecloud/ecloudsdkcmcdn v1.0.0 - gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.2 + gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.6 + gitlab.ecloud.com/ecloud/ecloudsdkvlb v1.0.7 golang.org/x/crypto v0.52.0 golang.org/x/oauth2 v0.36.0 golang.org/x/sync v0.20.0 @@ -297,3 +298,7 @@ replace gitlab.ecloud.com/ecloud/ecloudsdkclouddns v1.0.1 => ./pkg/sdk3rd-forked replace gitlab.ecloud.com/ecloud/ecloudsdkcmcdn v1.0.0 => ./pkg/sdk3rd-forked/gitlab.ecloud.com/ecloud/ecloudsdkcmcdn@v1.0.0 replace gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.2 => ./pkg/sdk3rd-forked/gitlab.ecloud.com/ecloud/ecloudsdkcore@v1.0.2 + +replace gitlab.ecloud.com/ecloud/ecloudsdkcore v1.0.6 => ./pkg/sdk3rd-forked/gitlab.ecloud.com/ecloud/ecloudsdkcore@v1.0.6 + +replace gitlab.ecloud.com/ecloud/ecloudsdkvlb v1.0.7 => ./pkg/sdk3rd-forked/gitlab.ecloud.com/ecloud/ecloudsdkvlb@v1.0.7 diff --git a/internal/certmgmt/deployers/sp_cmcccloud_vlb.go b/internal/certmgmt/deployers/sp_cmcccloud_vlb.go new file mode 100644 index 000000000..4a5a9902e --- /dev/null +++ b/internal/certmgmt/deployers/sp_cmcccloud_vlb.go @@ -0,0 +1,30 @@ +package deployers + +import ( + "fmt" + + "github.com/certimate-go/certimate/internal/domain" + "github.com/certimate-go/certimate/pkg/core" + dplyimpl "github.com/certimate-go/certimate/pkg/core/deployer/providers/cmcccloud-vlb" + xmaps "github.com/certimate-go/certimate/pkg/utils/maps" +) + +func init() { + Registries.MustRegister(domain.DeploymentProviderTypeCMCCCloudVLB, func(options *ProviderFactoryOptions) (core.Deployer, error) { + credentials := domain.AccessConfigForCMCCCloud{} + if err := xmaps.Populate(options.ProviderAccessConfig, &credentials); err != nil { + return nil, fmt.Errorf("failed to populate provider access config: %w", err) + } + + provider, err := dplyimpl.NewDeployer(&dplyimpl.DeployerConfig{ + AccessKeyId: credentials.AccessKeyId, + AccessKeySecret: credentials.AccessKeySecret, + PoolId: xmaps.GetString(options.ProviderExtendedConfig, "poolId"), + DeployTarget: xmaps.GetString(options.ProviderExtendedConfig, "deployTarget"), + LoadbalancerId: xmaps.GetString(options.ProviderExtendedConfig, "loadbalancerId"), + ListenerId: xmaps.GetString(options.ProviderExtendedConfig, "listenerId"), + Domain: xmaps.GetString(options.ProviderExtendedConfig, "domain"), + }) + return provider, err + }) +} diff --git a/internal/domain/provider.go b/internal/domain/provider.go index e20ec8205..e177bafdc 100644 --- a/internal/domain/provider.go +++ b/internal/domain/provider.go @@ -358,6 +358,7 @@ const ( DeploymentProviderTypeCdnfly = DeploymentProviderType(AccessProviderTypeCdnfly) DeploymentProviderTypeCloudflareSSL = DeploymentProviderType(AccessProviderTypeCloudflare + "-ssl") DeploymentProviderTypeCMCCCloudCDN = DeploymentProviderType(AccessProviderTypeCMCCCloud + "-cdn") + DeploymentProviderTypeCMCCCloudVLB = DeploymentProviderType(AccessProviderTypeCMCCCloud + "-vlb") DeploymentProviderTypeCPanel = DeploymentProviderType(AccessProviderTypeCPanel) DeploymentProviderTypeCTCCCloudAO = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-ao") DeploymentProviderTypeCTCCCloudCDN = DeploymentProviderType(AccessProviderTypeCTCCCloud + "-cdn") diff --git a/pkg/core/certifier/challengers/dns01/cmcccloud/internal/lego.go b/pkg/core/certifier/challengers/dns01/cmcccloud/internal/lego.go index 868e78753..fa0eaf12f 100644 --- a/pkg/core/certifier/challengers/dns01/cmcccloud/internal/lego.go +++ b/pkg/core/certifier/challengers/dns01/cmcccloud/internal/lego.go @@ -35,9 +35,9 @@ type Config struct { AccessKey string SecretKey string + TTL int PropagationTimeout time.Duration PollingInterval time.Duration - TTL int ReadTimeout int ConnectTimeout int } @@ -52,11 +52,11 @@ type DNSProvider struct { func NewDefaultConfig() *Config { return &Config{ - ReadTimeout: env.GetOrDefaultInt(EnvReadTimeout, 30), - ConnectTimeout: env.GetOrDefaultInt(EnvConnectTimeout, 30), TTL: env.GetOrDefaultInt(EnvTTL, 600), PropagationTimeout: env.GetOrDefaultSecond(EnvPropagationTimeout, 10*time.Minute), PollingInterval: env.GetOrDefaultSecond(EnvPollingInterval, dns01.DefaultPollingInterval), + ReadTimeout: env.GetOrDefaultInt(EnvReadTimeout, 30), + ConnectTimeout: env.GetOrDefaultInt(EnvConnectTimeout, 30), } } @@ -79,10 +79,8 @@ func NewDNSProviderConfig(cfg *Config) (*DNSProvider, error) { } client := ecloudsdkclouddns.NewClient(&config.Config{ - AccessKey: cfg.AccessKey, - SecretKey: cfg.SecretKey, - // 资源池常量见: https://ecloud.10086.cn/op-help-center/doc/article/54462 - // 默认全局 + AccessKey: cfg.AccessKey, + SecretKey: cfg.SecretKey, PoolId: "CIDC-CORE-00", ReadTimeOut: cfg.ReadTimeout, ConnectTimeout: cfg.ConnectTimeout, diff --git a/pkg/core/certmgr/providers/baiducloud-cert/baiducloud_cert.go b/pkg/core/certmgr/providers/baiducloud-cert/baiducloud_cert.go index c2764f111..db92b9e6f 100644 --- a/pkg/core/certmgr/providers/baiducloud-cert/baiducloud_cert.go +++ b/pkg/core/certmgr/providers/baiducloud-cert/baiducloud_cert.go @@ -92,7 +92,7 @@ func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*Uplo // 对比证书内容 getCertDetailResp, err := c.sdkClient.GetCertRawData(certItem.CertId) - c.logger.Debug("sdk request 'cert.GetCertRawData'", slog.Any("certId", certItem.CertId), slog.Any("response", getCertDetailResp)) + c.logger.Debug("sdk request 'cert.GetCertRawData'", slog.String("params.certId", certItem.CertId), slog.Any("response", getCertDetailResp)) if err != nil { return nil, fmt.Errorf("failed to execute sdk request 'cert.GetCertRawData': %w", err) } else { diff --git a/pkg/core/certmgr/providers/cmcccloud-vlb/cmcccloud_vlb.go b/pkg/core/certmgr/providers/cmcccloud-vlb/cmcccloud_vlb.go new file mode 100644 index 000000000..5d8765b87 --- /dev/null +++ b/pkg/core/certmgr/providers/cmcccloud-vlb/cmcccloud_vlb.go @@ -0,0 +1,156 @@ +package cmcccloudvlb + +import ( + "context" + "fmt" + "log/slog" + "time" + + "github.com/samber/lo" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/config" + "gitlab.ecloud.com/ecloud/ecloudsdkvlb/model" + + "github.com/certimate-go/certimate/pkg/sdk3rd-trimmed/gitlab.ecloud.com/ecloud/ecloudsdkvlb" + + "github.com/certimate-go/certimate/pkg/core" + xcert "github.com/certimate-go/certimate/pkg/utils/cert" +) + +type ( + Provider = core.Certmgr + UploadResult = core.CertmgrUploadResult + ReplaceResult = core.CertmgrReplaceResult +) + +type CertmgrConfig struct { + // 移动云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 移动云 AccessKeySecret。 + AccessKeySecret string `json:"accessKeySecret"` + // 移动云资源池 ID。 + PoolId string `json:"poolId"` + // 是否是 SNI 证书。 + IsSNI bool `json:"isSni,omitempty"` +} + +type Certmgr struct { + config *CertmgrConfig + logger *slog.Logger + sdkClient *ecloudsdkvlb.Client +} + +var _ Provider = (*Certmgr)(nil) + +func NewCertmgr(config *CertmgrConfig) (*Certmgr, error) { + if config == nil { + return nil, fmt.Errorf("the configuration of the certmgr provider is nil") + } + + client, err := createSDKClient(config.AccessKeyId, config.AccessKeySecret, config.PoolId) + if err != nil { + return nil, fmt.Errorf("could not create client: %w", err) + } + + return &Certmgr{ + config: config, + logger: slog.Default(), + sdkClient: client, + }, nil +} + +func (c *Certmgr) SetLogger(logger *slog.Logger) { + if logger == nil { + c.logger = slog.New(slog.DiscardHandler) + } else { + c.logger = logger + } +} + +func (c *Certmgr) Upload(ctx context.Context, certPEM, privkeyPEM string) (*UploadResult, error) { + // 解析证书内容 + certX509, err := xcert.ParseCertificateFromPEM(certPEM) + if err != nil { + return nil, err + } + + // 查询弹性负载均衡证书列表,避免重复上传 + // REF: https://ecloud.10086.cn/op-help-center/doc/article/97613 + // REF: https://ecloud.10086.cn/op-help-center/doc/article/97399 + listLoadbalanceCertificationResp, err := c.sdkClient.ListLoadbalanceCertificationResp() + c.logger.Debug("sdk request 'vlb.ListLoadbalanceCertification'", slog.Any("response", listLoadbalanceCertificationResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'vlb.ListLoadbalanceCertification': %w", err) + } else { + if listLoadbalanceCertificationResp.Body != nil && listLoadbalanceCertificationResp.Body.Content != nil { + for _, certItem := range *listLoadbalanceCertificationResp.Body.Content { + // 对比证书有效期 + oldCertNotAfter, _ := time.Parse(time.DateTime, lo.FromPtr(certItem.ExpirationTime)) + if !certX509.NotAfter.Equal(oldCertNotAfter) { + continue + } + + // 对比证书内容 + getLoadbalanceCertificationDetailReq := &model.GetLoadbalanceCertificationDetailRespRequest{ + &model.GetLoadbalanceCertificationDetailRespPath{ + ContainerUuid: certItem.Id, + }, + } + getLoadbalanceCertificationDetailResp, err := c.sdkClient.GetLoadbalanceCertificationDetailResp(getLoadbalanceCertificationDetailReq) + c.logger.Debug("sdk request 'vlb.GetLoadbalanceCertificationDetail'", slog.Any("request", getLoadbalanceCertificationDetailReq), slog.Any("response", getLoadbalanceCertificationDetailResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'vlb.GetLoadbalanceCertificationDetail': %w", err) + } else { + if !xcert.EqualCertificatesFromPEM(certPEM, lo.FromPtr(getLoadbalanceCertificationDetailResp.Body.PublicKey)) { + continue + } + } + + // 如果以上信息都一致,则视为已存在相同证书,直接返回 + c.logger.Info("ssl certificate already exists") + return &UploadResult{ + CertId: lo.FromPtr(certItem.Id), + CertName: lo.FromPtr(certItem.Name), + }, nil + } + } + } + + // 生成新证书名(需符合移动云命名规则) + certName := fmt.Sprintf("certimate_%d", time.Now().UnixMilli()) + + // 创建弹性负载均衡证书 + // REF: https://ecloud.10086.cn/op-help-center/doc/article/97397 + createLoadbalanceCertificationReq := &model.CreateLoadbalanceCertificationRequest{ + &model.CreateLoadbalanceCertificationBody{ + Name: lo.ToPtr(certName), + Type: lo.ToPtr(lo.Ternary(c.config.IsSNI, model.CreateLoadbalanceCertificationBodyTypeEnumSni, model.CreateLoadbalanceCertificationBodyTypeEnumServer)), + Description: lo.ToPtr("upload from certimate"), + PublicKey: lo.ToPtr(certPEM), + PrivateKey: lo.ToPtr(privkeyPEM), + }, + } + createLoadbalanceCertificationResp, err := c.sdkClient.CreateLoadbalanceCertification(createLoadbalanceCertificationReq) + c.logger.Debug("sdk request 'vlb.CreateLoadbalanceCertification'", slog.Any("request", createLoadbalanceCertificationReq), slog.Any("response", createLoadbalanceCertificationResp)) + if err != nil { + return nil, fmt.Errorf("failed to execute sdk request 'vlb.CreateLoadbalanceCertification': %w", err) + } + + return &UploadResult{ + CertId: lo.FromPtr(createLoadbalanceCertificationResp.Body), + CertName: certName, + }, nil +} + +func (c *Certmgr) Replace(ctx context.Context, certIdOrName string, certPEM, privkeyPEM string) (*ReplaceResult, error) { + return nil, core.ErrUnsupported +} + +func createSDKClient(accessKeyId, accessKeySecret, poolId string) (*ecloudsdkvlb.Client, error) { + client := ecloudsdkvlb.NewClient(&config.Config{ + AccessKey: &accessKeyId, + SecretKey: &accessKeySecret, + RegionId: &poolId, + }) + + return client, nil +} diff --git a/pkg/core/certmgr/providers/cmcccloud-vlb/cmcccloud_vlb_test.go b/pkg/core/certmgr/providers/cmcccloud-vlb/cmcccloud_vlb_test.go new file mode 100644 index 000000000..df0f7a1a7 --- /dev/null +++ b/pkg/core/certmgr/providers/cmcccloud-vlb/cmcccloud_vlb_test.go @@ -0,0 +1,53 @@ +package cmcccloudvlb_test + +import ( + "testing" + + "github.com/certimate-go/certimate/pkg/core/certmgr/internal/tester" + impl "github.com/certimate-go/certimate/pkg/core/certmgr/providers/cmcccloud-vlb" +) + +var ( + fp = tester.Args("CMCCCLOUDVLB_") + fTestCertPath string + fTestKeyPath string + fAccessKeyId string + fAccessKeySecret string + fPoolId string +) + +func init() { + fp.DefineString(&fTestCertPath, "TESTCERTPATH") + fp.DefineString(&fTestKeyPath, "TESTKEYPATH") + fp.DefineString(&fAccessKeyId, "ACCESSKEYID") + fp.DefineString(&fAccessKeySecret, "ACCESSKEYSECRET") + fp.DefineString(&fPoolId, "POOLID") +} + +/* +Shell command to run this test: + + go test -v ./cmcccloud_vlb_test.go -args \ + --CMCCCLOUDVLB_TESTCERTPATH="/path/to/your-test-cert.pem" \ + --CMCCCLOUDVLB_TESTKEYPATH="/path/to/your-test-key.pem" \ + --CMCCCLOUDVLB_ACCESSKEYID="your-access-key-id" \ + --CMCCCLOUDVLB_ACCESSKEYSECRET="your-access-key-secret" \ + --CMCCCLOUDVLB_POOLID="CIDC-RP-29" +*/ +func TestProvider(t *testing.T) { + fp.Parse() + + t.Run("Upload", func(t *testing.T) { + provider, err := impl.NewCertmgr(&impl.CertmgrConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + PoolId: fPoolId, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + tester.TestUpload(t, provider, tester.TestUploadArgs{CertPath: fTestCertPath, KeyPath: fTestKeyPath}) + }) +} diff --git a/pkg/core/deployer/providers/cmcccloud-vlb/cmcccloud_vlb.go b/pkg/core/deployer/providers/cmcccloud-vlb/cmcccloud_vlb.go new file mode 100644 index 000000000..ffd0e3b17 --- /dev/null +++ b/pkg/core/deployer/providers/cmcccloud-vlb/cmcccloud_vlb.go @@ -0,0 +1,310 @@ +package cmcccloudvlb + +import ( + "context" + "errors" + "fmt" + "log/slog" + + "github.com/samber/lo" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/config" + "gitlab.ecloud.com/ecloud/ecloudsdkvlb/model" + + "github.com/certimate-go/certimate/pkg/sdk3rd-trimmed/gitlab.ecloud.com/ecloud/ecloudsdkvlb" + + "github.com/certimate-go/certimate/pkg/core" + cmgrimpl "github.com/certimate-go/certimate/pkg/core/certmgr/providers/cmcccloud-vlb" +) + +type ( + Provider = core.Deployer + DeployResult = core.DeployerDeployResult +) + +type DeployerConfig struct { + // 移动云 AccessKeyId。 + AccessKeyId string `json:"accessKeyId"` + // 移动云 AccessKeySecret。 + AccessKeySecret string `json:"accessKeySecret"` + // 移动云资源池 ID。 + PoolId string `json:"poolId"` + // 部署目标。 + DeployTarget string `json:"deployTarget"` + // 负载均衡实例 ID。 + // 部署目标为 [DEPLOY_TARGET_LOADBALANCER] 时必填。 + LoadbalancerId string `json:"loadbalancerId,omitempty"` + // 负载均衡监听器 ID。 + // 部署目标为 [DEPLOY_TARGET_LISTENER] 时必填。 + ListenerId string `json:"listenerId,omitempty"` + // SNI 域名(支持泛域名)。 + // 部署目标为 [DEPLOY_TARGET_LOADBALANCER]、[DEPLOY_TARGET_LISTENER] 时选填。 + Domain string `json:"domain,omitempty"` +} + +type Deployer struct { + config *DeployerConfig + logger *slog.Logger + sdkClient *ecloudsdkvlb.Client + sdkCertmgr core.Certmgr +} + +var _ Provider = (*Deployer)(nil) + +func NewDeployer(config *DeployerConfig) (*Deployer, error) { + if config == nil { + return nil, fmt.Errorf("the configuration of the deployer provider is nil") + } + + client, err := createSDKClient(config.AccessKeyId, config.AccessKeySecret, config.PoolId) + if err != nil { + return nil, fmt.Errorf("could not create client: %w", err) + } + + pcertmgr, err := cmgrimpl.NewCertmgr(&cmgrimpl.CertmgrConfig{ + AccessKeyId: config.AccessKeyId, + AccessKeySecret: config.AccessKeySecret, + PoolId: config.PoolId, + IsSNI: config.Domain != "", + }) + 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) (*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.DeployTarget { + case DEPLOY_TARGET_LOADBALANCER: + if err := d.deployToLoadbalancer(ctx, upres.CertId); err != nil { + return nil, err + } + + case DEPLOY_TARGET_LISTENER: + if err := d.deployToListener(ctx, upres.CertId); err != nil { + return nil, err + } + + default: + return nil, fmt.Errorf("unsupported deploy target '%s'", d.config.DeployTarget) + } + + return &DeployResult{}, nil +} + +func (d *Deployer) deployToLoadbalancer(ctx context.Context, cloudCertId string) error { + if d.config.LoadbalancerId == "" { + return fmt.Errorf("config `loadbalancerId` is required") + } + + // 查询 HTTPS 监听器列表 + // REF: https://ecloud.10086.cn/op-help-center/doc/article/97026 + listenerIds := make([]string, 0) + listLoadBalanceHTTPSListenerPage := 1 + listLoadBalanceHTTPSListenerPageSize := 10 + for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + listLoadBalanceHTTPSListenerReq := &model.ListLoadBalanceHTTPSListenerRequest{ + ListLoadBalanceHTTPSListenerPath: &model.ListLoadBalanceHTTPSListenerPath{ + LoadBalanceId: lo.ToPtr(d.config.LoadbalancerId), + }, + ListLoadBalanceHTTPSListenerQuery: &model.ListLoadBalanceHTTPSListenerQuery{ + Page: lo.ToPtr(int32(listLoadBalanceHTTPSListenerPage)), + PageSize: lo.ToPtr(int32(listLoadBalanceHTTPSListenerPageSize)), + }, + } + listLoadBalanceHTTPSListenerResp, err := d.sdkClient.ListLoadBalanceHTTPSListener(listLoadBalanceHTTPSListenerReq) + d.logger.Debug("sdk request 'vlb.ListLoadBalanceHTTPSListener'", slog.Any("request", listLoadBalanceHTTPSListenerReq), slog.Any("response", listLoadBalanceHTTPSListenerResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'vlb.ListLoadBalanceHTTPSListener': %w", err) + } + + if listLoadBalanceHTTPSListenerResp.Body == nil || listLoadBalanceHTTPSListenerResp.Body.Content == nil { + break + } + + for _, listener := range *listLoadBalanceHTTPSListenerResp.Body.Content { + listenerIds = append(listenerIds, lo.FromPtr(listener.Id)) + } + + if len(*listLoadBalanceHTTPSListenerResp.Body.Content) < listLoadBalanceHTTPSListenerPageSize { + break + } + + listLoadBalanceHTTPSListenerPage++ + } + + // 遍历更新监听证书 + if len(listenerIds) == 0 { + d.logger.Info("no elb listeners to deploy") + } else { + d.logger.Info("found https listeners to deploy", slog.Any("listenerIds", listenerIds)) + var errs []error + + for _, listenerId := range listenerIds { + select { + case <-ctx.Done(): + return ctx.Err() + default: + if err := d.updateListenerCertificate(ctx, listenerId, cloudCertId); err != nil { + errs = append(errs, err) + } + } + } + + if len(errs) > 0 { + return errors.Join(errs...) + } + } + + return nil +} + +func (d *Deployer) deployToListener(ctx context.Context, cloudCertId string) error { + if d.config.ListenerId == "" { + return fmt.Errorf("config `listenerId` is required") + } + + // 更新监听 + if err := d.updateListenerCertificate(ctx, d.config.ListenerId, cloudCertId); err != nil { + return err + } + + return nil +} + +func (d *Deployer) updateListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId string) error { + // 查询 HTTPS 监听器信息 + // REF: https://ecloud.10086.cn/op-help-center/doc/article/97026 + var listenerInfo *model.ListLoadBalanceHTTPSListenerResponseContent + listLoadBalanceHTTPSListenerPage := 1 + listLoadBalanceHTTPSListenerPageSize := 10 + for { + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + + listLoadBalanceHTTPSListenerReq := &model.ListLoadBalanceHTTPSListenerRequest{ + ListLoadBalanceHTTPSListenerPath: &model.ListLoadBalanceHTTPSListenerPath{ + LoadBalanceId: lo.ToPtr(d.config.LoadbalancerId), + }, + ListLoadBalanceHTTPSListenerQuery: &model.ListLoadBalanceHTTPSListenerQuery{ + Page: lo.ToPtr(int32(listLoadBalanceHTTPSListenerPage)), + PageSize: lo.ToPtr(int32(listLoadBalanceHTTPSListenerPageSize)), + }, + } + listLoadBalanceHTTPSListenerResp, err := d.sdkClient.ListLoadBalanceHTTPSListener(listLoadBalanceHTTPSListenerReq) + d.logger.Debug("sdk request 'vlb.ListLoadBalanceHTTPSListener'", slog.Any("request", listLoadBalanceHTTPSListenerReq), slog.Any("response", listLoadBalanceHTTPSListenerResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'vlb.ListLoadBalanceHTTPSListener': %w", err) + } + + if listLoadBalanceHTTPSListenerResp.Body == nil || listLoadBalanceHTTPSListenerResp.Body.Content == nil { + break + } + + for _, listener := range *listLoadBalanceHTTPSListenerResp.Body.Content { + if lo.FromPtr(listener.Id) == cloudListenerId { + listenerInfo = &listener + break + } + } + + if len(*listLoadBalanceHTTPSListenerResp.Body.Content) < listLoadBalanceHTTPSListenerPageSize { + break + } + + listLoadBalanceHTTPSListenerPage++ + } + if listenerInfo == nil { + return fmt.Errorf("could not find listener '%s'", cloudListenerId) + } + + if d.config.Domain == "" { + // 未指定 SNI,只需部署到默认证书 + + if lo.FromPtr(listenerInfo.DefaultTlsContainerId) == cloudCertId { + d.logger.Info("ssl certificate already deployed") + return nil + } + + // 修改 HTTPS 监听器 + // REF: https://ecloud.10086.cn/op-help-center/doc/article/97024 + updateListenerReq := &model.UpdateListenerRequest{ + &model.UpdateListenerBody{ + Id: lo.ToPtr(cloudListenerId), + DefaultTlsContainerId: lo.ToPtr(cloudCertId), + }, + } + updateListenerResp, err := d.sdkClient.UpdateListener(updateListenerReq) + d.logger.Debug("sdk request 'vlb.UpdateListener'", slog.Any("request", updateListenerReq), slog.Any("response", updateListenerResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'vlb.UpdateListener': %w", err) + } + } else { + // 指定 SNI,需部署到 SNI 证书 + + if lo.Contains(listenerInfo.SniContainerIdList, cloudCertId) { + d.logger.Info("ssl certificate already deployed") + return nil + } + + // 修改 HTTPS 监听器 + // REF: https://ecloud.10086.cn/op-help-center/doc/article/97024 + updateListenerReq := &model.UpdateListenerRequest{ + &model.UpdateListenerBody{ + Id: lo.ToPtr(cloudListenerId), + SniUp: lo.ToPtr(true), + SniContainerIds: append(listenerInfo.SniContainerIdList, cloudCertId), + }, + } + updateListenerResp, err := d.sdkClient.UpdateListener(updateListenerReq) + d.logger.Debug("sdk request 'vlb.UpdateListener'", slog.Any("request", updateListenerReq), slog.Any("response", updateListenerResp)) + if err != nil { + return fmt.Errorf("failed to execute sdk request 'vlb.UpdateListener': %w", err) + } + } + + return nil +} + +func createSDKClient(accessKeyId, accessKeySecret, poolId string) (*ecloudsdkvlb.Client, error) { + client := ecloudsdkvlb.NewClient(&config.Config{ + AccessKey: &accessKeyId, + SecretKey: &accessKeySecret, + PoolId: &poolId, + }) + + return client, nil +} diff --git a/pkg/core/deployer/providers/cmcccloud-vlb/cmcccloud_vlb_test.go b/pkg/core/deployer/providers/cmcccloud-vlb/cmcccloud_vlb_test.go new file mode 100644 index 000000000..3430bfaa5 --- /dev/null +++ b/pkg/core/deployer/providers/cmcccloud-vlb/cmcccloud_vlb_test.go @@ -0,0 +1,77 @@ +package cmcccloudvlb_test + +import ( + "testing" + + "github.com/certimate-go/certimate/pkg/core/deployer/internal/tester" + impl "github.com/certimate-go/certimate/pkg/core/deployer/providers/cmcccloud-vlb" +) + +var ( + fp = tester.Args("CMCCCLOUDVLB_") + fTestCertPath string + fTestKeyPath string + fAccessKeyId string + fAccessKeySecret string + fPoolId string + fLoadbalancerId string + fListenerId string +) + +func init() { + fp.DefineString(&fTestCertPath, "TESTCERTPATH") + fp.DefineString(&fTestKeyPath, "TESTKEYPATH") + fp.DefineString(&fAccessKeyId, "ACCESSKEYID") + fp.DefineString(&fAccessKeySecret, "ACCESSKEYSECRET") + fp.DefineString(&fPoolId, "POOLID") + fp.DefineString(&fLoadbalancerId, "LOADBALANCERID") + fp.DefineString(&fListenerId, "LISTENERID") +} + +/* +Shell command to run this test: + + go test -v ./cmcccloud_vlb_test.go -args \ + --CMCCCLOUDVLB_TESTCERTPATH="/path/to/your-test-cert.pem" \ + --CMCCCLOUDVLB_TESTKEYPATH="/path/to/your-test-key.pem" \ + --CMCCCLOUDVLB_ACCESSKEYID="your-access-key-id" \ + --CMCCCLOUDVLB_ACCESSKEYSECRET="your-access-key-secret" \ + --CMCCCLOUDVLB_POOLID="CIDC-RP-29" \ + --CMCCCLOUDVLB_LOADBALANCERID="your-vlb-instance-id" \ + --CMCCCLOUDVLB_LISTENERID="your-vlb-listener-id" +*/ +func TestProvider(t *testing.T) { + fp.Parse() + + t.Run("Deploy_ToLoadbalancer", func(t *testing.T) { + provider, err := impl.NewDeployer(&impl.DeployerConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + PoolId: fPoolId, + DeployTarget: impl.DEPLOY_TARGET_LOADBALANCER, + LoadbalancerId: fLoadbalancerId, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + tester.TestDeploy(t, provider, tester.TestDeployArgs{CertPath: fTestCertPath, KeyPath: fTestKeyPath}) + }) + + t.Run("Deploy_ToListener", func(t *testing.T) { + provider, err := impl.NewDeployer(&impl.DeployerConfig{ + AccessKeyId: fAccessKeyId, + AccessKeySecret: fAccessKeySecret, + PoolId: fPoolId, + DeployTarget: impl.DEPLOY_TARGET_LISTENER, + ListenerId: fListenerId, + }) + if err != nil { + t.Errorf("err: %+v", err) + return + } + + tester.TestDeploy(t, provider, tester.TestDeployArgs{CertPath: fTestCertPath, KeyPath: fTestKeyPath}) + }) +} diff --git a/pkg/core/deployer/providers/cmcccloud-vlb/consts.go b/pkg/core/deployer/providers/cmcccloud-vlb/consts.go new file mode 100644 index 000000000..fcd0fa25b --- /dev/null +++ b/pkg/core/deployer/providers/cmcccloud-vlb/consts.go @@ -0,0 +1,8 @@ +package cmcccloudvlb + +const ( + // 部署目标:部署到指定负载均衡器。 + DEPLOY_TARGET_LOADBALANCER = "loadbalancer" + // 部署目标:部署到指定监听器。 + DEPLOY_TARGET_LISTENER = "listener" +) diff --git a/pkg/core/deployer/providers/ctcccloud-elb/ctcccloud_elb.go b/pkg/core/deployer/providers/ctcccloud-elb/ctcccloud_elb.go index 7cb34021a..d8dceef48 100644 --- a/pkg/core/deployer/providers/ctcccloud-elb/ctcccloud_elb.go +++ b/pkg/core/deployer/providers/ctcccloud-elb/ctcccloud_elb.go @@ -178,13 +178,13 @@ func (d *Deployer) deployToListener(ctx context.Context, cloudCertId string) err func (d *Deployer) updateListenerCertificate(ctx context.Context, cloudListenerId string, cloudCertId string) error { // 更新监听器 // REF: https://eop.ctyun.cn/ebp/ctapiDocument/search?sid=24&api=5652&data=88&isNormal=1&vid=82 - setLoadBalancerHTTPSListenerAttributeReq := &ctyunelb.UpdateListenerRequest{ + updateListenerReq := &ctyunelb.UpdateListenerRequest{ RegionID: lo.ToPtr(d.config.RegionId), ListenerID: lo.ToPtr(cloudListenerId), CertificateID: lo.ToPtr(cloudCertId), } - setLoadBalancerHTTPSListenerAttributeResp, err := d.sdkClient.UpdateListenerWithContext(ctx, setLoadBalancerHTTPSListenerAttributeReq) - d.logger.Debug("sdk request 'elb.UpdateListener'", slog.Any("request", setLoadBalancerHTTPSListenerAttributeReq), slog.Any("response", setLoadBalancerHTTPSListenerAttributeResp)) + updateListenerResp, err := d.sdkClient.UpdateListenerWithContext(ctx, updateListenerReq) + d.logger.Debug("sdk request 'elb.UpdateListener'", slog.Any("request", updateListenerReq), slog.Any("response", updateListenerResp)) if err != nil { return fmt.Errorf("failed to execute sdk request 'elb.UpdateListener': %w", err) } diff --git a/pkg/sdk3rd-trimmed/gitlab.ecloud.com/ecloud/ecloudsdkvlb/client.go b/pkg/sdk3rd-trimmed/gitlab.ecloud.com/ecloud/ecloudsdkvlb/client.go new file mode 100644 index 000000000..6ed11f767 --- /dev/null +++ b/pkg/sdk3rd-trimmed/gitlab.ecloud.com/ecloud/ecloudsdkvlb/client.go @@ -0,0 +1,373 @@ +package ecloudsdkvlb + +import ( + "gitlab.ecloud.com/ecloud/ecloudsdkcore" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/config" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/param" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/region" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/request" + "gitlab.ecloud.com/ecloud/ecloudsdkcore/utils" + "gitlab.ecloud.com/ecloud/ecloudsdkvlb/model" +) + +type Client struct { + apiClient *ecloudsdkcore.APIClient + config *config.Config + httpRequest *request.HttpRequest + allRegions []region.Region +} + +func NewClient(config *config.Config) *Client { + httpRequest := request.DefaultHttpRequest() + httpRequest.Product = product + httpRequest.Version = version + httpRequest.SdkVersion = sdkVersion + ecloudsdkcore.InitConfig(config) + apiClient := ecloudsdkcore.DefaultApiClient(config, httpRequest) + client := &Client{ + apiClient: apiClient, + config: config, + httpRequest: httpRequest, + } + client.allRegions = client.initRegions() + client.setEndpoint(config, httpRequest) + return client +} + +const ( + product string = "vlb" + version = "v1" + sdkVersion = "1.0.7" +) + +func (c *Client) initRegions() []region.Region { + var regions []region.Region + regions = append(regions, region.Region{ + RegionId: utils.String("cn-jiangsu-1"), + PoolId: utils.String("CIDC-RP-25"), + Endpoint: utils.String("https://api-wuxi-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-guangdong-1"), + PoolId: utils.String("CIDC-RP-26"), + Endpoint: utils.String("https://api-dongguan-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-sichuan-1"), + PoolId: utils.String("CIDC-RP-27"), + Endpoint: utils.String("https://api-yaan-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-henan-1"), + PoolId: utils.String("CIDC-RP-28"), + Endpoint: utils.String("https://api-zhengzhou-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-beijing-1"), + PoolId: utils.String("CIDC-RP-29"), + Endpoint: utils.String("https://api-beijing-2.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-hunan-1"), + PoolId: utils.String("CIDC-RP-30"), + Endpoint: utils.String("https://api-zhuzhou-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-shandong-1"), + PoolId: utils.String("CIDC-RP-31"), + Endpoint: utils.String("https://api-jinan-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-shaanxi-1"), + PoolId: utils.String("CIDC-RP-32"), + Endpoint: utils.String("https://api-xian-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-shanghai-1"), + PoolId: utils.String("CIDC-RP-33"), + Endpoint: utils.String("https://api-shanghai-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-chongqing-1"), + PoolId: utils.String("CIDC-RP-34"), + Endpoint: utils.String("https://api-chongqing-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-zhejiang-1"), + PoolId: utils.String("CIDC-RP-35"), + Endpoint: utils.String("https://api-ningbo-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-tianjin-1"), + PoolId: utils.String("CIDC-RP-36"), + Endpoint: utils.String("https://api-tianjin-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-jilin-1"), + PoolId: utils.String("CIDC-RP-37"), + Endpoint: utils.String("https://api-jilin-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-hubei-1"), + PoolId: utils.String("CIDC-RP-38"), + Endpoint: utils.String("https://api-hubei-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-jiangxi-1"), + PoolId: utils.String("CIDC-RP-39"), + Endpoint: utils.String("https://api-jiangxi-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-gansu-1"), + PoolId: utils.String("CIDC-RP-40"), + Endpoint: utils.String("https://api-gansu-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-shangxi-1"), + PoolId: utils.String("CIDC-RP-41"), + Endpoint: utils.String("https://api-shanxi-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-liaoning-1"), + PoolId: utils.String("CIDC-RP-42"), + Endpoint: utils.String("https://api-liaoning-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-yunnan-1"), + PoolId: utils.String("CIDC-RP-43"), + Endpoint: utils.String("https://api-yunnan-2.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-hebei-1"), + PoolId: utils.String("CIDC-RP-44"), + Endpoint: utils.String("https://api-hebei-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-fujian-1"), + PoolId: utils.String("CIDC-RP-45"), + Endpoint: utils.String("https://api-fujian-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-guangxi-1"), + PoolId: utils.String("CIDC-RP-46"), + Endpoint: utils.String("https://api-guangxi-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-anhui-1"), + PoolId: utils.String("CIDC-RP-47"), + Endpoint: utils.String("https://api-anhui-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-neimenggu-1"), + PoolId: utils.String("CIDC-RP-48"), + Endpoint: utils.String("https://api-huhehaote-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-guzhou-1"), + PoolId: utils.String("CIDC-RP-49"), + Endpoint: utils.String("https://api-guiyang-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-hainan-1"), + PoolId: utils.String("CIDC-RP-53"), + Endpoint: utils.String("https://api-hainan-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-xinjiang-1"), + PoolId: utils.String("CIDC-RP-54"), + Endpoint: utils.String("https://api-xinjiang-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-heilongjiang-1"), + PoolId: utils.String("CIDC-RP-55"), + Endpoint: utils.String("https://api-heilongjiang-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-ningxia-1"), + PoolId: utils.String("CIDC-RP-60"), + Endpoint: utils.String("https://api-ningxia-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-qinghai-1"), + PoolId: utils.String("CIDC-RP-61"), + Endpoint: utils.String("https://api-qinghai-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-xizang-1"), + PoolId: utils.String("CIDC-RP-62"), + Endpoint: utils.String("https://api-xizang-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-hubei-2"), + PoolId: utils.String("CIDC-RP-64"), + Endpoint: utils.String("https://api-wuhan-1.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-xizang-2"), + PoolId: utils.String("CIDC-RP-68"), + Endpoint: utils.String("https://api-xizang-2.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-qinghai-2"), + PoolId: utils.String("CIDC-RP-69"), + Endpoint: utils.String("https://api-qinghai-2.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String("cn-ningxia-2"), + PoolId: utils.String("CIDC-RP-71"), + Endpoint: utils.String("https://api-ningxia-2.cmecloud.cn:8443"), + }) + regions = append(regions, region.Region{ + RegionId: utils.String(""), + PoolId: utils.String("CIDC-CORE-00"), + Endpoint: utils.String("https://ecloud.10086.cn"), + }) + return regions +} + +func (c *Client) setEndpoint(config *config.Config, httpRequest *request.HttpRequest) { + if utils.IsUnSet(config.RegionId) && utils.IsUnSet(config.PoolId) { + httpRequest.Endpoint = utils.DefaultEndpoint + } else if utils.IsSet(config.RegionId) { + c.findAndSetEndpointByRegionId(config.RegionId, httpRequest) + } else { + c.findAndSetEndpointByPoolId(config.PoolId, httpRequest) + } +} + +func (c *Client) findAndSetEndpointByRegionId(regionId *string, httpRequest *request.HttpRequest) { + for _, r := range c.allRegions { + if utils.StringValue(r.RegionId) == utils.StringValue(regionId) { + endpoint := r.Endpoint + if utils.IsUnSet(endpoint) { + httpRequest.Endpoint = utils.DefaultEndpoint + } else { + httpRequest.Endpoint = *endpoint + } + break + } + } +} + +func (c *Client) findAndSetEndpointByPoolId(poolId *string, httpRequest *request.HttpRequest) { + for _, r := range c.allRegions { + if utils.StringValue(r.PoolId) == utils.StringValue(poolId) { + endpoint := r.Endpoint + if utils.IsUnSet(endpoint) { + httpRequest.Endpoint = utils.DefaultEndpoint + } else { + httpRequest.Endpoint = *endpoint + } + break + } + } +} + +func (c *Client) CreateLoadbalanceCertification(request *model.CreateLoadbalanceCertificationRequest) (*model.CreateLoadbalanceCertificationResponse, error) { + return c.CreateLoadbalanceCertificationWithConfig(request, nil) +} + +func (c *Client) CreateLoadbalanceCertificationWithConfig(request *model.CreateLoadbalanceCertificationRequest, runtimeConfig *config.RuntimeConfig) (*model.CreateLoadbalanceCertificationResponse, error) { + params := param.NewParamsBuilder(). + Action("createLoadbalanceCertification"). + Uri("/acl/v3/certification"). + GatewayUri("/api/openapi-vlb/lb-console/acl/v3/certification"). + Protocol("http"). + ContentType("application/json"). + Method("POST"). + Request(request). + Build() + returnValue := &model.CreateLoadbalanceCertificationResponse{} + if _, err := c.apiClient.Excute(params, runtimeConfig, returnValue); err != nil { + return nil, err + } else { + return returnValue, nil + } +} + +func (c *Client) GetLoadbalanceCertificationDetailResp(request *model.GetLoadbalanceCertificationDetailRespRequest) (*model.GetLoadbalanceCertificationDetailRespResponse, error) { + return c.GetLoadbalanceCertificationDetailRespWithConfig(request, nil) +} + +func (c *Client) GetLoadbalanceCertificationDetailRespWithConfig(request *model.GetLoadbalanceCertificationDetailRespRequest, runtimeConfig *config.RuntimeConfig) (*model.GetLoadbalanceCertificationDetailRespResponse, error) { + params := param.NewParamsBuilder(). + Action("getLoadbalanceCertificationDetailResp"). + Uri("/acl/v3/certification/{containerUuid}"). + GatewayUri("/api/openapi-vlb/lb-console/acl/v3/certification/{containerUuid}"). + Protocol("http"). + ContentType("application/json"). + Method("GET"). + Request(request). + Build() + returnValue := &model.GetLoadbalanceCertificationDetailRespResponse{} + if _, err := c.apiClient.Excute(params, runtimeConfig, returnValue); err != nil { + return nil, err + } else { + return returnValue, nil + } +} + +func (c *Client) ListLoadbalanceCertificationResp() (*model.ListLoadbalanceCertificationRespResponse, error) { + return c.ListLoadbalanceCertificationRespWithConfig(nil) +} + +func (c *Client) ListLoadbalanceCertificationRespWithConfig(runtimeConfig *config.RuntimeConfig) (*model.ListLoadbalanceCertificationRespResponse, error) { + params := param.NewParamsBuilder(). + Action("listLoadbalanceCertificationResp"). + Uri("/acl/v3/certification"). + GatewayUri("/api/openapi-vlb/lb-console/acl/v3/certification"). + Protocol("http"). + ContentType("application/json"). + Method("GET"). + Build() + returnValue := &model.ListLoadbalanceCertificationRespResponse{} + if _, err := c.apiClient.Excute(params, runtimeConfig, returnValue); err != nil { + return nil, err + } else { + return returnValue, nil + } +} + +func (c *Client) ListLoadBalanceHTTPSListener(request *model.ListLoadBalanceHTTPSListenerRequest) (*model.ListLoadBalanceHTTPSListenerResponse, error) { + return c.ListLoadBalanceHTTPSListenerWithConfig(request, nil) +} + +func (c *Client) ListLoadBalanceHTTPSListenerWithConfig(request *model.ListLoadBalanceHTTPSListenerRequest, runtimeConfig *config.RuntimeConfig) (*model.ListLoadBalanceHTTPSListenerResponse, error) { + params := param.NewParamsBuilder(). + Action("listLoadBalanceHTTPSListener"). + Uri("/protocol/v3/listener/{loadBalanceId}/listeners/https"). + GatewayUri("/api/openapi-vlb/lb-console/protocol/v3/listener/{loadBalanceId}/listeners/https"). + Protocol("http"). + ContentType("application/json"). + Method("GET"). + Request(request). + Build() + returnValue := &model.ListLoadBalanceHTTPSListenerResponse{} + if _, err := c.apiClient.Excute(params, runtimeConfig, returnValue); err != nil { + return nil, err + } else { + return returnValue, nil + } +} + +func (c *Client) UpdateListener(request *model.UpdateListenerRequest) (*model.UpdateListenerResponse, error) { + return c.UpdateListenerWithConfig(request, nil) +} + +func (c *Client) UpdateListenerWithConfig(request *model.UpdateListenerRequest, runtimeConfig *config.RuntimeConfig) (*model.UpdateListenerResponse, error) { + params := param.NewParamsBuilder(). + Action("updateListener"). + Uri("/acl/v3/listener"). + GatewayUri("/api/openapi-vlb/lb-console/acl/v3/listener"). + Protocol("http"). + ContentType("application/json"). + Method("PUT"). + Request(request). + Build() + returnValue := &model.UpdateListenerResponse{} + if _, err := c.apiClient.Excute(params, runtimeConfig, returnValue); err != nil { + return nil, err + } else { + return returnValue, nil + } +} diff --git a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx index a2b50c881..c7bc9b007 100644 --- a/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx +++ b/ui/src/components/workflow/designer/forms/BizDeployNodeConfigFieldsProvider.tsx @@ -52,6 +52,7 @@ import BizDeployNodeConfigFieldsProviderBytePlusTOS from "./BizDeployNodeConfigF import BizDeployNodeConfigFieldsProviderCdnfly from "./BizDeployNodeConfigFieldsProviderCdnfly"; import BizDeployNodeConfigFieldsProviderCloudflareSSL from "./BizDeployNodeConfigFieldsProviderCloudflareSSL"; import BizDeployNodeConfigFieldsProviderCMCCCloudCDN from "./BizDeployNodeConfigFieldsProviderCMCCCloudCDN"; +import BizDeployNodeConfigFieldsProviderCMCCCloudVLB from "./BizDeployNodeConfigFieldsProviderCMCCCloudVLB"; import BizDeployNodeConfigFieldsProviderCPanel from "./BizDeployNodeConfigFieldsProviderCPanel"; import BizDeployNodeConfigFieldsProviderCTCCCloudAO from "./BizDeployNodeConfigFieldsProviderCTCCCloudAO"; import BizDeployNodeConfigFieldsProviderCTCCCloudCDN from "./BizDeployNodeConfigFieldsProviderCTCCCloudCDN"; @@ -197,6 +198,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, "deployTarget"], formInst); + + return ( + <> + } + > + + + + + + + + + + } + > + + + + + + + + + + + ); +}; + +const getInitialValues = (): Nullish>> => { + return { + poolId: "", + deployTarget: DEPLOY_TARGET_LISTENER, + }; +}; + +const getSchema = ({ i18n = getI18n() }: { i18n?: ReturnType }) => { + const { t } = i18n; + + return z + .object({ + poolId: z.string().nonempty(), + deployTarget: z.enum([DEPLOY_TARGET_LOADBALANCER, DEPLOY_TARGET_LISTENER]), + loadbalancerId: z.string().nullish(), + listenerId: z.string().nullish(), + domain: z + .string() + .nullish() + .refine((v) => { + if (!v) return true; + return isDomain(v, { allowWildcard: true }); + }, t("common.errmsg.domain_invalid")), + }) + .superRefine((values, ctx) => { + switch (values.deployTarget) { + case DEPLOY_TARGET_LOADBALANCER: + { + const scLoadbalancerId = z.string().nonempty(); + const spLoadbalancerId = scLoadbalancerId.safeParse(values.loadbalancerId); + if (!spLoadbalancerId.success) { + ctx.addIssue({ + code: "custom", + message: z.treeifyError(spLoadbalancerId.error).errors.join(), + path: ["loadbalancerId"], + }); + } + } + break; + + case DEPLOY_TARGET_LISTENER: + { + const scListenerId = z.string().nonempty(); + const spListenerId = scListenerId.safeParse(values.listenerId); + if (!spListenerId.success) { + ctx.addIssue({ + code: "custom", + message: z.treeifyError(spListenerId.error).errors.join(), + path: ["listenerId"], + }); + } + } + break; + } + }); +}; + +const _default = Object.assign(BizDeployNodeConfigFieldsProviderCMCCCloudVLB, { + getInitialValues, + getSchema, +}); + +export default _default; diff --git a/ui/src/domain/provider.ts b/ui/src/domain/provider.ts index 2ce0d6c0c..869b90551 100644 --- a/ui/src/domain/provider.ts +++ b/ui/src/domain/provider.ts @@ -644,6 +644,7 @@ export const DEPLOYMENT_PROVIDERS = Object.freeze({ CDNFLY: `${ACCESS_PROVIDERS.CDNFLY}`, CLOUDFLARE_SSL: `${ACCESS_PROVIDERS.CLOUDFLARE}-ssl`, CMCCCLOUD_CDN: `${ACCESS_PROVIDERS.CMCCCLOUD}-cdn`, + CMCCCLOUD_VLB: `${ACCESS_PROVIDERS.CMCCCLOUD}-vlb`, CPANEL: `${ACCESS_PROVIDERS.CPANEL}`, CTCCCLOUD_AO: `${ACCESS_PROVIDERS.CTCCCLOUD}-ao`, CTCCCLOUD_CDN: `${ACCESS_PROVIDERS.CTCCCLOUD}-cdn`, @@ -863,6 +864,7 @@ export const deploymentProvidersMap: Maphttps://ecloud.10086.cn/op-help-center/doc/article/54462" + }, + "cmcccloud_vlb_deploy_target": { + "option": { + "loadbalancer": { + "label": "VLB load balancer" + }, + "listener": { + "label": "VLB listener" + } + } + }, + "cmcccloud_vlb_loadbalancer_id": { + "label": "CMCC ECloud VLB load balancer ID", + "placeholder": "Please enter CMCC ECloud VLB load balancer ID", + "tooltip": "For more information, see https://ecloud.10086.cn/api/page/lb-console-manage-web/CIDC-RP-29/elb/instancelist" + }, + "cmcccloud_vlb_listener_id": { + "label": "CMCC ECloud VLB listener ID", + "placeholder": "Please enter CMCC ECloud VLB listener ID", + "tooltip": "For more information, see https://ecloud.10086.cn/api/page/lb-console-manage-web/CIDC-RP-29/elb/instancelist" + }, + "cmcccloud_vlb_snidomain": { + "label": "CMCC ECloud VLB SNI domain (Optional)", + "placeholder": "Please enter CMCC ECloud VLB SNI domain name", + "help": "Notes: Leave it blank to set the default certificate; otherwise, to set the extension one for SNI." + }, "cpanel_deploy_target": { "option": { "website": { @@ -1458,9 +1488,6 @@ }, "ctcccloud_elb_deploy_target": { "option": { - "certificate": { - "label": "ELB certificate" - }, "loadbalancer": { "label": "ELB load balancer" }, diff --git a/ui/src/i18n/resources/zh/nls.provider.json b/ui/src/i18n/resources/zh/nls.provider.json index ab0f52133..fd4434bed 100644 --- a/ui/src/i18n/resources/zh/nls.provider.json +++ b/ui/src/i18n/resources/zh/nls.provider.json @@ -110,6 +110,7 @@ "cmcccloud": "移动云", "cmcccloud_cdn": "移动云 - 内容分发网络 CDN", "cmcccloud_dns": "移动云 - 云解析 DNS", + "cmcccloud_vlb": "移动云 - 弹性负载均衡 VLB", "conohavps": "ConoHa VPS", "constellix": "Constellix", "cpanel": "cPanel", diff --git a/ui/src/i18n/resources/zh/nls.workflow.nodes.json b/ui/src/i18n/resources/zh/nls.workflow.nodes.json index 861055b16..365d22023 100644 --- a/ui/src/i18n/resources/zh/nls.workflow.nodes.json +++ b/ui/src/i18n/resources/zh/nls.workflow.nodes.json @@ -1431,6 +1431,36 @@ "label": "移动云 CDN 加速域名", "placeholder": "请输入移动云 CDN 加速域名" }, + "cmcccloud_vlb_pool_id": { + "label": "移动云资源池 ID", + "placeholder": "请输入移动云 VLB 资源池 ID(例如:CIDC-RP-29)", + "tooltip": "这是什么?请参阅 https://ecloud.10086.cn/op-help-center/doc/article/54462" + }, + "cmcccloud_vlb_deploy_target": { + "option": { + "loadbalancer": { + "label": "部署到指定负载均衡器下的全部 HTTPS 监听器" + }, + "listener": { + "label": "部署到指定 HTTPS 监听器" + } + } + }, + "cmcccloud_vlb_loadbalancer_id": { + "label": "移动云 VLB 负载均衡器 ID", + "placeholder": "请输入移动云 VLB 负载均衡器 ID", + "tooltip": "这是什么?请参阅 https://ecloud.10086.cn/api/page/lb-console-manage-web/CIDC-RP-29/elb/instancelist" + }, + "cmcccloud_vlb_listener_id": { + "label": "移动云 VLB 监听器 ID", + "placeholder": "请输入移动云 VLB 监听器 ID", + "tooltip": "这是什么?请参阅 https://ecloud.10086.cn/api/page/lb-console-manage-web/CIDC-RP-29/elb/instancelist" + }, + "cmcccloud_vlb_snidomain": { + "label": "移动云 VLB 扩展域名(可选)", + "placeholder": "请输入移动云 VLB 扩展域名", + "help": "提示:不填写时,将替换监听器的默认证书;否则,将替换扩展域名证书。" + }, "cpanel_deploy_target": { "option": { "website": {