certd/packages/ui/certd-server/src/plugins/plugin-tencent/plugin/deploy-to-clb/index.ts

247 lines
6.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, utils } from '@certd/pipeline';
import { TencentAccess } from '../../access/index.js';
import dayjs from 'dayjs';
@IsTaskPlugin({
name: 'DeployCertToTencentCLB',
title: '部署到腾讯云CLB',
group: pluginGroups.tencent.key,
desc: '暂时只支持单向认证证书暂时只支持通用负载均衡必须开启sni',
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class DeployToClbPlugin extends AbstractTaskPlugin {
@TaskInput({
title: '大区',
component: {
name: 'a-auto-complete',
vModel: 'value',
options: [
{ value: 'ap-guangzhou' },
{ value: 'ap-beijing' },
{ value: 'ap-chengdu' },
{ value: 'ap-chongqing' },
{ value: 'ap-hongkong' },
{ value: 'ap-jakarta' },
{ value: 'ap-mumbai' },
{ value: 'ap-nanjing' },
{ value: 'ap-seoul' },
{ value: 'ap-shanghai' },
{ value: 'ap-shanghai-fsi' },
{ value: 'ap-shenzhen-fsi' },
{ value: 'ap-singapore' },
{ value: 'ap-tokyo' },
{ value: 'eu-frankfurt' },
{ value: 'na-ashburn' },
{ value: 'na-siliconvalley' },
{ value: 'na-toronto' },
{ value: 'sa-saopaulo' },
],
},
required: true,
})
region!: string;
@TaskInput({
title: '证书名称前缀',
})
certName!: string;
@TaskInput({
title: '负载均衡ID',
helper: '如果没有配置则根据域名匹配负载均衡下的监听器根据域名匹配时暂时只支持前100个',
required: true,
})
loadBalancerId!: string;
@TaskInput({
title: '监听器ID',
helper: '如果没有配置则根据域名或负载均衡id匹配监听器',
})
listenerId!: string;
@TaskInput({
title: '域名',
required: true,
helper: '要更新的支持https的负载均衡的域名',
})
domain!: string;
@TaskInput({
title: '域名证书',
helper: '请选择前置任务输出的域名证书',
component: {
name: 'pi-output-selector',
from: ['CertApply', 'CertApplyLego'],
},
required: true,
})
cert!: any;
@TaskInput({
title: 'Access提供者',
helper: 'access授权',
component: {
name: 'pi-access-selector',
type: 'tencent',
},
required: true,
})
accessId!: string;
client: any;
async onInstance() {
this.client = await this.getClient();
}
async getClient() {
const sdk = await import('tencentcloud-sdk-nodejs/tencentcloud/services/clb/v20180317/index.js');
const ClbClient = sdk.v20180317.Client;
const accessProvider = (await this.accessService.getById(this.accessId)) as TencentAccess;
const region = this.region;
const clientConfig = {
credential: {
secretId: accessProvider.secretId,
secretKey: accessProvider.secretKey,
},
region: region,
profile: {
httpProfile: {
endpoint: 'clb.tencentcloudapi.com',
},
},
};
return new ClbClient(clientConfig);
}
async execute(): Promise<void> {
const client = this.client;
const lastCertId = await this.getCertIdFromProps(client);
if (!this.domain) {
await this.updateListener(client);
} else {
await this.updateByDomainAttr(client);
}
try {
await utils.sleep(2000);
let newCertId = await this.getCertIdFromProps(client);
if ((lastCertId && newCertId === lastCertId) || (!lastCertId && !newCertId)) {
await utils.sleep(2000);
newCertId = await this.getCertIdFromProps(client);
}
if (newCertId === lastCertId) {
return;
}
this.logger.info('腾讯云证书ID:', newCertId);
} catch (e) {
this.logger.warn('查询腾讯云证书失败', e);
}
return;
}
async getCertIdFromProps(client: any) {
const listenerRet = await this.getListenerList(client, this.loadBalancerId, [this.listenerId]);
return this.getCertIdFromListener(listenerRet[0], this.domain);
}
getCertIdFromListener(listener: any, domain: string) {
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 updateListener(client: any) {
const params = this.buildProps();
const ret = await client.ModifyListener(params);
this.checkRet(ret);
this.logger.info('设置腾讯云CLB证书成功:', ret.RequestId, '->loadBalancerId:', this.loadBalancerId, 'listenerId', this.listenerId);
return ret;
}
async updateByDomainAttr(client: any) {
const params: any = this.buildProps();
params.Domain = this.domain;
const ret = await client.ModifyDomainAttributes(params);
this.checkRet(ret);
this.logger.info(
'设置腾讯云CLB证书(sni)成功:',
ret.RequestId,
'->loadBalancerId:',
this.loadBalancerId,
'listenerId',
this.listenerId,
'domain:',
this.domain
);
return ret;
}
appendTimeSuffix(name: string) {
if (name == null) {
name = 'certd';
}
return name + '-' + dayjs().format('YYYYMMDD-HHmmss');
}
buildProps() {
return {
Certificate: {
SSLMode: 'UNIDIRECTIONAL', // 单向认证
CertName: this.appendTimeSuffix(this.certName || this.cert.domain),
CertKey: this.cert.key,
CertContent: this.cert.crt,
},
LoadBalancerId: this.loadBalancerId,
ListenerId: this.listenerId,
};
}
async getCLBList(client: any) {
const params = {
Limit: 100, // 最大暂时只支持100个暂时没做翻页
OrderBy: 'CreateTime',
OrderType: 0,
// ...this.DescribeLoadBalancers,
};
const ret = await client.DescribeLoadBalancers(params);
this.checkRet(ret);
return ret.LoadBalancerSet;
}
async getListenerList(client: any, balancerId: any, listenerIds: any) {
// HTTPS
const params = {
LoadBalancerId: balancerId,
Protocol: 'HTTPS',
ListenerIds: listenerIds,
};
const ret = await client.DescribeListeners(params);
this.checkRet(ret);
return ret.Listeners;
}
checkRet(ret: any) {
if (!ret || ret.Error) {
throw new Error('执行失败:' + ret.Error.Code + ',' + ret.Error.Message);
}
}
}