certd/packages/plugins/plugin-tencent/src/plugin/deploy-to-tke-ingress/index.ts

236 lines
6.8 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, IAccessService, IsTaskPlugin, RunStrategy, TaskInput, utils } from "@certd/pipeline";
import tencentcloud from "tencentcloud-sdk-nodejs/index";
import { K8sClient } from "@certd/plugin-util";
import dayjs from "dayjs";
import { Logger } from "log4js";
@IsTaskPlugin({
name: "DeployCertToTencentTKEIngress",
title: "部署到腾讯云TKE-ingress",
desc: "需要【上传到腾讯云】作为前置任务",
default: {
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
})
export class DeployCertToTencentTKEIngressPlugin extends AbstractTaskPlugin {
@TaskInput({ title: "大区", value: "ap-guangzhou", required: true })
region!: string;
@TaskInput({ title: "集群ID", required: true, desc: "例如cls-6lbj1vee", request: true })
clusterId!: string;
@TaskInput({ title: "集群namespace", value: "default", required: true })
namespace!: string;
@TaskInput({ title: "证书的secret名称", required: true })
secretName!: string | string[];
@TaskInput({ title: "ingress名称", required: true })
ingressName!: string | string[];
@TaskInput({
title: "ingress类型",
component: {
name: "a-select",
options: [{ value: "qcloud" }, { value: "nginx" }],
},
helper: "可选 qcloud / nginx",
})
ingressClass!: string;
@TaskInput({ title: "集群内网ip", helper: "如果开启了外网的话,无需设置" })
clusterIp!: string;
@TaskInput({ title: "集群域名", helper: "可不填,默认为:[clusterId].ccs.tencent-cloud.com" })
clusterDomain!: string;
@TaskInput({
title: "腾讯云证书id",
helper: "请选择“上传证书到腾讯云”前置任务的输出",
component: {
name: "pi-output-selector",
from: "UploadCertToTencent",
},
required: true,
})
tencentCertId!: string;
/**
* AccessProvider的key,或者一个包含access的具体的对象
*/
@TaskInput({
title: "Access授权",
helper: "access授权",
component: {
name: "pi-access-selector",
type: "tencent",
},
required: true,
})
accessId!: string;
@TaskInput({
title: "域名证书",
helper: "请选择前置任务输出的域名证书",
component: {
name: "pi-output-selector",
},
required: true,
})
cert!: any;
logger!: Logger;
accessService!: IAccessService;
async onInstance() {
this.accessService = this.ctx.accessService;
this.logger = this.ctx.logger;
}
async execute(): Promise<void> {
const accessProvider = this.accessService.getById(this.accessId);
const tkeClient = this.getTkeClient(accessProvider, this.region);
const kubeConfigStr = await this.getTkeKubeConfig(tkeClient, this.clusterId);
this.logger.info("kubeconfig已成功获取");
const k8sClient = new K8sClient(kubeConfigStr);
if (this.clusterIp != null) {
if (!this.clusterDomain) {
this.clusterDomain = `${this.clusterId}.ccs.tencent-cloud.com`;
}
// 修改内网解析ip地址
k8sClient.setLookup({ [this.clusterDomain]: { ip: this.clusterIp } });
}
const ingressType = this.ingressClass || "qcloud";
if (ingressType === "qcloud") {
await this.patchQcloudCertSecret({ k8sClient });
} else {
await this.patchNginxCertSecret({ k8sClient });
}
await utils.sleep(2000); // 停留2秒等待secret部署完成
await this.restartIngress({ k8sClient });
}
getTkeClient(accessProvider: any, region = "ap-guangzhou") {
const TkeClient = tencentcloud.tke.v20180525.Client;
const clientConfig = {
credential: {
secretId: accessProvider.secretId,
secretKey: accessProvider.secretKey,
},
region,
profile: {
httpProfile: {
endpoint: "tke.tencentcloudapi.com",
},
},
};
return new TkeClient(clientConfig);
}
async getTkeKubeConfig(client: any, clusterId: string) {
// Depends on tencentcloud-sdk-nodejs version 4.0.3 or higher
const params = {
ClusterId: clusterId,
};
const ret = await client.DescribeClusterKubeconfig(params);
this.checkRet(ret);
this.logger.info("注意:后续操作需要在【集群->基本信息】中开启外网或内网访问,https://console.cloud.tencent.com/tke2/cluster");
return ret.Kubeconfig;
}
appendTimeSuffix(name: string) {
if (name == null) {
name = "certd";
}
return name + "-" + dayjs().format("YYYYMMDD-HHmmss");
}
async patchQcloudCertSecret(options: { k8sClient: any }) {
if (this.tencentCertId == null) {
throw new Error("请先将【上传证书到腾讯云】作为前置任务");
}
this.logger.info("腾讯云证书ID:", this.tencentCertId);
const certIdBase64 = Buffer.from(this.tencentCertId).toString("base64");
const { namespace, secretName } = this;
const body = {
data: {
qcloud_cert_id: certIdBase64,
},
metadata: {
labels: {
certd: this.appendTimeSuffix("certd"),
},
},
};
let secretNames: any = secretName;
if (typeof secretName === "string") {
secretNames = [secretName];
}
for (const secret of secretNames) {
await options.k8sClient.patchSecret({ namespace, secretName: secret, body });
this.logger.info(`CertSecret已更新:${secret}`);
}
}
async patchNginxCertSecret(options: { k8sClient: any }) {
const { k8sClient } = options;
const { cert } = this;
const crt = cert.crt;
const key = cert.key;
const crtBase64 = Buffer.from(crt).toString("base64");
const keyBase64 = Buffer.from(key).toString("base64");
const { namespace, secretName } = this;
const body = {
data: {
"tls.crt": crtBase64,
"tls.key": keyBase64,
},
metadata: {
labels: {
certd: this.appendTimeSuffix("certd"),
},
},
};
let secretNames = secretName;
if (typeof secretName === "string") {
secretNames = [secretName];
}
for (const secret of secretNames) {
await k8sClient.patchSecret({ namespace, secretName: secret, body });
this.logger.info(`CertSecret已更新:${secret}`);
}
}
async restartIngress(options: { k8sClient: any }) {
const { k8sClient } = options;
const { namespace, ingressName } = this;
const body = {
metadata: {
labels: {
certd: this.appendTimeSuffix("certd"),
},
},
};
let ingressNames = this.ingressName;
if (typeof ingressName === "string") {
ingressNames = [ingressName];
}
for (const ingress of ingressNames) {
await k8sClient.patchIngress({ namespace, ingressName: ingress, body });
this.logger.info(`ingress已重启:${ingress}`);
}
}
checkRet(ret: any) {
if (!ret || ret.Error) {
throw new Error("执行失败:" + ret.Error.Code + "," + ret.Error.Message);
}
}
}