perf: 新增部署到火山引擎ALB/CLB、上传到证书中心

pull/409/head
xiaojunnuo 2025-04-20 23:53:27 +08:00
parent 8387708901
commit c9a3e3d9d2
6 changed files with 677 additions and 1 deletions

View File

@ -1 +1,4 @@
export * from './plugin-deploy-to-cdn.js' export * from './plugin-deploy-to-cdn.js'
export * from './plugin-deploy-to-clb.js'
export * from './plugin-upload-to-cert-center.js'
export * from './plugin-deploy-to-alb.js'

View File

@ -0,0 +1,220 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
import { VolcengineAccess } from "../access.js";
import { VolcengineClient } from "../ve-client.js";
@IsTaskPlugin({
name: "VolcengineDeployToALB",
title: "火山引擎-部署证书至ALB",
icon: "svg:icon-volcengine",
group: pluginGroups.volcengine.key,
desc: "部署至火山引擎应用负载均衡",
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed
}
}
})
export class VolcengineDeployToALB extends AbstractTaskPlugin {
@TaskInput({
title: "域名证书",
helper: "请选择前置任务输出的域名证书",
component: {
name: "output-selector",
from: [...CertApplyPluginNames, "VolcengineUploadToCertCenter"]
},
required: true
})
cert!: CertInfo | string;
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
certDomains!: string[];
@TaskInput({
title: "Access授权",
helper: "火山引擎AccessKeyId、AccessKeySecret",
component: {
name: "access-selector",
type: "volcengine"
},
required: true
})
accessId!: string;
@TaskInput({
title: "Region",
helper: "地区选择",
component: {
name: "a-select",
options: [
/**
*
* 2
*
* cn-beijing
* 4
* Acn-beijing-a
* Bcn-beijing-b
* Ccn-beijing-c
* Dcn-beijing-d
*
* 2
*
* cn-shanghai
* 4
* Acn-shanghai-a
* Bcn-shanghai-b
* Ccn-shanghai-c
* Ecn-shanghai-e
*
* 1
* 广
* cn-guangzhou
* 3
* Acn-guangzhou-a
* Bcn-guangzhou-b
* Ccn-guangzhou-c
*
*
*
* cn-hongkong
* 2
* Acn-hongkong-a
* Bcn-hongkong-b
*
*
*
*
* ap-southeast-1
* 2
* Aap-southeast-1a
* Bap-southeast-1b
*
*
* ap-southeast-3
* 2
* Aap-southeast-3a
* Bap-southeast-3b
*
*/
{ label: "北京", value: "cn-beijing" },
{ label: "上海", value: "cn-shanghai" },
{ label: "广州", value: "cn-guangzhou" },
{ label: "香港", value: "cn-hongkong" },
{ label: "柔佛", value: "ap-southeast-1" },
{ label: "雅加达", value: "ap-southeast-3" }
]
},
value: "cn-beijing",
required: true
})
regionId!: string;
@TaskInput(
createRemoteSelectInputDefine({
title: "监听器列表",
helper: "选择要部署证书的监听器\n需要在监听器中选择证书中心进行跨服务访问授权",
action: VolcengineDeployToALB.prototype.onGetListenerList.name,
watches: ["certDomains", "accessId", "regionId"],
required: true
})
)
listenerList!: string | string[];
async onInstance() {
}
async execute(): Promise<void> {
this.logger.info("开始部署证书到火山引擎ALB");
const access = await this.getAccess<VolcengineAccess>(this.accessId);
const certService = await this.getCertService(access);
let certId = this.cert;
if (typeof certId !== "string") {
const certInfo = this.cert as CertInfo;
this.logger.info(`开始上传证书`);
certId = await certService.ImportCertificate({
certName:this.appendTimeSuffix("certd"),
cert:certInfo
});
this.logger.info(`上传证书成功:${certId}`);
} else {
this.logger.info(`使用已有证书ID:${certId}`);
}
const service = await this.getAlbService();
for (const listener of this.listenerList) {
this.logger.info(`开始部署监听器${listener}证书`);
await service.request({
action: "ModifyListenerAttributes",
query: {
ListenerId: listener,
CertificateSource: "cert_center",
CertCenterCertificateId: certId
}
});
this.logger.info(`部署监听器${listener}证书成功`);
}
this.logger.info("部署完成");
}
private async getCertService(access: VolcengineAccess) {
const client = new VolcengineClient({
logger: this.logger,
access,
http: this.http
});
return await client.getCertCenterService();
}
private async getAlbService() {
const access = await this.getAccess<VolcengineAccess>(this.accessId);
const client = new VolcengineClient({
logger: this.logger,
access,
http: this.http
});
const service = await client.getAlbService({
region: this.regionId
});
return service;
}
async onGetListenerList(data: any) {
if (!this.accessId) {
throw new Error("请选择Access授权");
}
const service = await this.getAlbService();
const res = await service.request({
action: "DescribeListeners",
method: "GET",
query: {
PageSize: 100,
Protocol: "HTTPS"
},
});
const list = res.Result.Listeners;
if (!list || list.length === 0) {
throw new Error("找不到HTTPS类型的负载均衡监听器您也可以手动输入监听器ID");
}
return list.map((item: any) => {
return {
value: item.ListenerId,
label: `${item.ListenerName}<${item.Description}:${item.ListenerId}>`
};
});
}
}
new VolcengineDeployToALB();

View File

@ -23,7 +23,7 @@ export class VolcengineDeployToCDN extends AbstractTaskPlugin {
helper: '请选择前置任务输出的域名证书', helper: '请选择前置任务输出的域名证书',
component: { component: {
name: 'output-selector', name: 'output-selector',
from: [...CertApplyPluginNames, 'VolcengineUploadCert'], from: [...CertApplyPluginNames, 'VolcengineUploadToCertCenter'],
}, },
required: true, required: true,
}) })
@ -82,11 +82,17 @@ export class VolcengineDeployToCDN extends AbstractTaskPlugin {
const client = await this.getClient(access) const client = await this.getClient(access)
const service = await client.getCdnClient() const service = await client.getCdnClient()
if (!this.cert) {
throw new Error('你还未选择证书');
}
let certId = this.cert let certId = this.cert
if (typeof certId !== 'string') { if (typeof certId !== 'string') {
const certInfo = this.cert as CertInfo const certInfo = this.cert as CertInfo
this.logger.info(`开始上传证书`) this.logger.info(`开始上传证书`)
certId = await client.uploadCert(certInfo, this.appendTimeSuffix('certd')) certId = await client.uploadCert(certInfo, this.appendTimeSuffix('certd'))
this.logger.info(`上传证书成功:${certId}`);
}else{
this.logger.info(`使用已有证书ID${certId}`);
} }
for (const domain of this.domainName) { for (const domain of this.domainName) {

View File

@ -0,0 +1,248 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput } from "@certd/pipeline";
import { createCertDomainGetterInputDefine, createRemoteSelectInputDefine } from "@certd/plugin-lib";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
import { VolcengineAccess } from "../access.js";
import { VolcengineClient } from "../ve-client.js";
@IsTaskPlugin({
name: "VolcengineDeployToCLB",
title: "火山引擎-部署证书至CLB",
icon: "svg:icon-volcengine",
group: pluginGroups.volcengine.key,
desc: "部署至火山引擎负载均衡",
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed
}
}
})
export class VolcengineDeployToCLB extends AbstractTaskPlugin {
@TaskInput({
title: "域名证书",
helper: "请选择前置任务输出的域名证书",
component: {
name: "output-selector",
from: [...CertApplyPluginNames, "VolcengineUploadToCertCenter"]
},
required: true
})
cert!: CertInfo | string;
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
certDomains!: string[];
@TaskInput({
title: "Access授权",
helper: "火山引擎AccessKeyId、AccessKeySecret",
component: {
name: "access-selector",
type: "volcengine"
},
required: true
})
accessId!: string;
@TaskInput({
title: "Region",
helper: "地区选择",
component: {
name: "a-select",
options: [
/**
*
* 2
*
* cn-beijing
* 4
* Acn-beijing-a
* Bcn-beijing-b
* Ccn-beijing-c
* Dcn-beijing-d
*
* 2
*
* cn-shanghai
* 4
* Acn-shanghai-a
* Bcn-shanghai-b
* Ccn-shanghai-c
* Ecn-shanghai-e
*
* 1
* 广
* cn-guangzhou
* 3
* Acn-guangzhou-a
* Bcn-guangzhou-b
* Ccn-guangzhou-c
*
*
*
* cn-hongkong
* 2
* Acn-hongkong-a
* Bcn-hongkong-b
*
*
*
*
* ap-southeast-1
* 2
* Aap-southeast-1a
* Bap-southeast-1b
*
*
* ap-southeast-3
* 2
* Aap-southeast-3a
* Bap-southeast-3b
*
*/
{ label: "北京", value: "cn-beijing" },
{ label: "上海", value: "cn-shanghai" },
{ label: "广州", value: "cn-guangzhou" },
{ label: "深圳", value: "cn-shenzhen" },
{ label: "杭州", value: "cn-hangzhou" },
{ label: "南京", value: "cn-north-1" },
{ label: "青岛", value: "cn-qingdao" },
{ label: "重庆", value: "cn-chengdu" },
{ label: "香港", value: "cn-hongkong" },
{ label: "柔佛", value: "ap-southeast-1" },
{ label: "雅加达", value: "ap-southeast-3" }
]
},
value: "cn-beijing",
required: true
})
regionId!: string;
@TaskInput(
createRemoteSelectInputDefine({
title: "监听器列表",
helper: "选择要部署证书的监听器\n需要在监听器中选择证书中心进行跨服务访问授权",
action: VolcengineDeployToCLB.prototype.onGetListenerList.name,
watches: ["certDomains", "accessId", "regionId"],
required: true
})
)
listenerList!: string | string[];
async onInstance() {
}
async execute(): Promise<void> {
this.logger.info("开始部署证书到火山引擎CLB");
const access = await this.getAccess<VolcengineAccess>(this.accessId);
const certService = await this.getCertService(access);
let certId = this.cert;
if (typeof certId !== "string") {
const certInfo = this.cert as CertInfo;
this.logger.info(`开始上传证书`);
certId = await certService.ImportCertificate({
certName:this.appendTimeSuffix("certd"),
cert:certInfo
});
this.logger.info(`上传证书成功:${certId}`);
} else {
this.logger.info(`使用已有证书ID:${certId}`);
}
const service = await this.getClbService();
for (const listener of this.listenerList) {
this.logger.info(`开始部署监听器${listener}证书`);
await service.request({
action: "ModifyListenerAttributes",
query: {
ListenerId: listener,
CertificateSource: "cert_center",
CertCenterCertificateId: certId
}
});
this.logger.info(`部署监听器${listener}证书成功`);
}
this.logger.info("部署完成");
}
private async getCertService(access: VolcengineAccess) {
const client = new VolcengineClient({
logger: this.logger,
access,
http: this.http
});
return await client.getCertCenterService();
}
async onGetClbList(data: any) {
if (!this.accessId) {
throw new Error("请选择Access授权");
}
const service = await this.getClbService();
const res = await service.request({
action: "DescribeLoadBalancers",
method: "GET",
query: {
PageSize: 100
},
});
const list = res.Result.LoadBalancers;
return list.map((item: any) => {
return {
value: item.LoadBalancerId,
label: `${item.LoadBalancerName}<${item.Description}>`
};
});
}
private async getClbService() {
const access = await this.getAccess<VolcengineAccess>(this.accessId);
const client = new VolcengineClient({
logger: this.logger,
access,
http: this.http
});
const service = await client.getClbService({
region: this.regionId
});
return service;
}
async onGetListenerList(data: any) {
if (!this.accessId) {
throw new Error("请选择Access授权");
}
const service = await this.getClbService();
const res = await service.request({
action: "DescribeListeners",
method: "GET",
query: {
PageSize: 100,
Protocol: "HTTPS"
},
});
const list = res.Result.Listeners;
if (!list || list.length === 0) {
throw new Error("找不到HTTPS类型的负载均衡监听器您也可以手动输入监听器ID");
}
return list.map((item: any) => {
return {
value: item.ListenerId,
label: `${item.ListenerName}<${item.Description}:${item.ListenerId}>`
};
});
}
}
new VolcengineDeployToCLB();

View File

@ -0,0 +1,77 @@
import { AbstractTaskPlugin, IsTaskPlugin, pluginGroups, RunStrategy, TaskInput, TaskOutput } from "@certd/pipeline";
import { createCertDomainGetterInputDefine } from "@certd/plugin-lib";
import { CertApplyPluginNames, CertInfo } from "@certd/plugin-cert";
import { VolcengineAccess } from "../access.js";
import { VolcengineClient } from "../ve-client.js";
@IsTaskPlugin({
name: 'VolcengineUploadToCertCenter',
title: '火山引擎-上传证书至证书中心',
icon: 'svg:icon-volcengine',
group: pluginGroups.volcengine.key,
desc: '上传证书至火山引擎证书中心',
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class VolcengineUploadToCertCenter extends AbstractTaskPlugin {
@TaskInput({
title: '域名证书',
helper: '请选择前置任务输出的域名证书',
component: {
name: 'output-selector',
from: [...CertApplyPluginNames],
},
required: true,
})
cert!: CertInfo;
@TaskInput(createCertDomainGetterInputDefine({ props: { required: false } }))
certDomains!: string[];
@TaskInput({
title: 'Access授权',
helper: '火山引擎AccessKeyId、AccessKeySecret',
component: {
name: 'access-selector',
type: 'volcengine',
},
required: true,
})
accessId!: string;
@TaskOutput({
title: '上传成功后的火山引擎证书Id',
})
volcengineCertId?: string;
async onInstance() {}
async execute(): Promise<void> {
this.logger.info('开始上传证书到证书中心');
const access = await this.getAccess<VolcengineAccess>(this.accessId);
const client = await this.getClient(access)
const service = await client.getCertCenterService()
const certInfo = this.cert
this.logger.info(`开始上传证书`)
this.volcengineCertId = await service.ImportCertificate({
certName: this.appendTimeSuffix('certd'),
cert: certInfo
})
this.logger.info(`上传完成:${this.volcengineCertId}`);
}
async getClient(access: VolcengineAccess) {
return new VolcengineClient({
logger: this.logger,
access,
http:this.http
})
}
}
new VolcengineUploadToCertCenter();

View File

@ -0,0 +1,122 @@
import { VolcengineAccess } from "./access.js";
import { HttpClient, ILogger } from "@certd/basic";
export type VolcengineOpts = {
access: VolcengineAccess
logger: ILogger
http: HttpClient
}
export class VolcengineClient {
opts: VolcengineOpts;
CommonService: any;
constructor(opts: VolcengineOpts) {
this.opts = opts;
}
async getCertCenterService() {
const CommonService = await this.getServiceCls();
const service = new CommonService({
serviceName: "certificate_service",
defaultVersion: "2024-10-01"
});
service.setAccessKeyId(this.opts.access.accessKeyId);
service.setSecretKey(this.opts.access.secretAccessKey);
service.setRegion("cn-beijing");
service.ImportCertificate = async (body: { certName: string, cert: any }) => {
const { certName, cert } = body;
const res = await service.request({
action: "ImportCertificate",
method: "POST",
body: {
Tag: certName,
Repeatable: false,
CertificateInfo: {
CertificateChain: cert.crt,
PrivateKey: cert.key
}
}
});
return res.Result.InstanceId || res.Result.RepeatId
};
return service;
}
async getClbService(opts: { region?: string }) {
const CommonService = await this.getServiceCls();
const service = new CommonService({
serviceName: "clb",
defaultVersion: "2020-04-01"
});
service.setAccessKeyId(this.opts.access.accessKeyId);
service.setSecretKey(this.opts.access.secretAccessKey);
service.setRegion(opts.region);
return service;
}
async getAlbService(opts: { region?: string }) {
const CommonService = await this.getServiceCls();
const service = new CommonService({
serviceName: "alb",
defaultVersion: "2020-04-01"
});
service.setAccessKeyId(this.opts.access.accessKeyId);
service.setSecretKey(this.opts.access.secretAccessKey);
service.setRegion(opts.region);
return service;
}
async getServiceCls() {
if (this.CommonService) {
return this.CommonService;
}
const { Service } = await import("@volcengine/openapi");
class CommonService extends Service {
Generic: any;
constructor(options: {
serviceName: string;
defaultVersion: string;
}) {
super(Object.assign({ host: "open.volcengineapi.com" }, options));
this.Generic = async (req: { action: string, body?: any, method?: string, query?: any }) => {
const { action, method, body, query } = req;
return await this.fetchOpenAPI({
Action: action,
Version: options.defaultVersion,
method: method as any,
headers: {
"content-type": "application/json"
},
query: query || {},
data: body
});
};
}
async request(req: { action: string, body?: any, method?: string, query?: any }) {
const res = await this.Generic(req);
if (res.errorcode) {
throw new Error(`${res.errorcode}:${res.message}`);
}
if (res.ResponseMetadata?.Error) {
throw new Error(res.ResponseMetadata?.Error?.Message);
}
return res;
}
}
this.CommonService = CommonService;
return CommonService;
}
}