import { AbstractTencentPlugin } from '../../tencent/abstract-tencent.js' import tencentcloud from 'tencentcloud-sdk-nodejs' export class DeployCertToTencentCLB extends AbstractTencentPlugin { /** * 插件定义 * 名称 * 入参 * 出参 */ static define () { return { name: 'deployCertToTencentCLB', label: '部署到腾讯云CLB', desc: '暂时只支持单向认证证书,暂时只支持通用负载均衡', input: { region: { label: '大区', default: 'ap-guangzhou', required: true }, domain: { label: '域名', type: [String, Array], required: true, desc: '要更新的支持https的负载均衡的域名' }, loadBalancerId: { label: '负载均衡ID', desc: '如果没有配置,则根据域名匹配负载均衡下的监听器(根据域名匹配时暂时只支持前100个)', required: true }, listenerId: { label: '监听器ID', desc: '如果没有配置,则根据域名或负载均衡id匹配监听器' }, certName: { label: '证书名称', desc: '如无uploadCertToTencent作为前置,则此项需要设置,默认为域名' }, accessProvider: { label: 'Access提供者', type: [String, Object], desc: 'access授权', component: { name: 'access-provider-selector', filter: 'tencent' }, required: true } }, output: { } } } async execute ({ cert, props, context }) { const accessProvider = this.getAccessProvider(props.accessProvider) const { region } = props const client = this.getClient(accessProvider, region) const lastCertId = await this.getCertIdFromProps(client, props) if (!props.domain) { await this.updateListener(client, cert, props, context) } else { await this.updateByDomainAttr(client, cert, props, context) } try { await this.sleep(2000) let newCertId = await this.getCertIdFromProps(client, props) if ((lastCertId && newCertId === lastCertId) || (!lastCertId && !newCertId)) { await this.sleep(2000) newCertId = await this.getCertIdFromProps(client, props) } if (newCertId === lastCertId) { return {} } this.logger.info('腾讯云证书ID:', newCertId) if (!context.tencentCertId) { context.tencentCertId = newCertId } return { tencentCertId: newCertId } } catch (e) { this.logger.warn('查询腾讯云证书失败', e) } } async getCertIdFromProps (client, props) { const listenerRet = await this.getListenerList(client, props.loadBalancerId, [props.listenerId]) return this.getCertIdFromListener(listenerRet[0], props.domain) } getCertIdFromListener (listener, domain) { let certId if (!domain) { certId = listener.Certificate.CertId } else { if (listener.Rules && listener.Rules.length > 0) { for (const rule of listener.Rules) { if (rule.Domain === domain) { if (rule.Certificate != null) { certId = rule.Certificate.CertId } break } } } } return certId } async rollback ({ cert, props, context }) { this.logger.warn('未实现rollback') } async updateListener (client, cert, props, context) { const params = this.buildProps(props, context, cert) const ret = await client.ModifyListener(params) this.checkRet(ret) this.logger.info('设置腾讯云CLB证书成功:', ret.RequestId, '->loadBalancerId:', props.loadBalancerId, 'listenerId', props.listenerId) return ret } async updateByDomainAttr (client, cert, props, context) { const params = this.buildProps(props, context, cert) params.Domain = props.domain const ret = await client.ModifyDomainAttributes(params) this.checkRet(ret) this.logger.info('设置腾讯云CLB证书(sni)成功:', ret.RequestId, '->loadBalancerId:', props.loadBalancerId, 'listenerId', props.listenerId, 'domain:', props.domain) return ret } buildProps (props, context, cert) { const { certName } = props const { tencentCertId } = context this.logger.info('部署腾讯云证书ID:', tencentCertId) const params = { Certificate: { SSLMode: 'UNIDIRECTIONAL', // 单向认证 CertId: tencentCertId }, LoadBalancerId: props.loadBalancerId, ListenerId: props.listenerId } if (tencentCertId == null) { params.Certificate.CertName = this.appendTimeSuffix(certName || cert.domain) params.Certificate.CertKey = cert.key params.Certificate.CertContent = cert.crt } return params } async getCLBList (client, props) { const params = { Limit: 100, // 最大暂时只支持100个,暂时没做翻页 OrderBy: 'CreateTime', OrderType: 0, ...props.DescribeLoadBalancers } const ret = await client.DescribeLoadBalancers(params) this.checkRet(ret) return ret.LoadBalancerSet } async getListenerList (client, balancerId, listenerIds) { // HTTPS const params = { LoadBalancerId: balancerId, Protocol: 'HTTPS', ListenerIds: listenerIds } const ret = await client.DescribeListeners(params) this.checkRet(ret) return ret.Listeners } getClient (accessProvider, region) { const ClbClient = tencentcloud.clb.v20180317.Client const clientConfig = { credential: { secretId: accessProvider.secretId, secretKey: accessProvider.secretKey }, region: region, profile: { httpProfile: { endpoint: 'clb.tencentcloudapi.com' } } } return new ClbClient(clientConfig) } }