refactor: huawei

pull/21/merge
xiaojunnuo 2023-05-09 13:52:25 +08:00
parent 9747d40734
commit d2897cefaa
8 changed files with 128 additions and 77 deletions

View File

@ -116,7 +116,7 @@ module.exports = async function(client, userOpts) {
const keyAuthorization = await client.getChallengeKeyAuthorization(challenge);
try {
await opts.challengeCreateFn(authz, challenge, keyAuthorization);
const recordItem = await opts.challengeCreateFn(authz, challenge, keyAuthorization);
/* Challenge verification */
if (opts.skipChallengeVerification === true) {
@ -139,7 +139,7 @@ module.exports = async function(client, userOpts) {
log(`[auto] [${d}] Trigger challengeRemoveFn()`);
try {
await opts.challengeRemoveFn(authz, challenge, keyAuthorization);
await opts.challengeRemoveFn(authz, challenge, keyAuthorization, recordItem);
}
catch (e) {
log(`[auto] [${d}] challengeRemoveFn threw error: ${e.message}`);

View File

@ -49,11 +49,22 @@ export class Executor {
this.runtime.start(runnable);
await this.onChanged(this.runtime);
const contextKey = `status.${runnable.id}`;
const inputKey = `input.${runnable.id}`;
if (runnable.strategy?.runStrategy === RunStrategy.SkipWhenSucceed) {
//如果是成功后跳过策略
const lastResult = await this.pipelineContext.get(contextKey);
if (lastResult != null && lastResult === ResultType.success) {
const lastInput = await this.pipelineContext.get(inputKey);
let inputChanged = false;
//TODO 参数不变
if (runnableType === "step") {
const step = runnable as Step;
const input = JSON.stringify(step.input);
if (input != null && lastInput !== input) {
inputChanged = true;
}
}
if (lastResult != null && lastResult === ResultType.success && !inputChanged) {
this.runtime.skip(runnable);
await this.onChanged(this.runtime);
return ResultType.skip;

View File

@ -7,7 +7,7 @@ function generateId() {
}
export const pipeline: Pipeline = {
version: 1,
id: generateId(),
id: "3",
title: "华为管道测试",
userId: 1,
triggers: [],
@ -27,10 +27,13 @@ export const pipeline: Pipeline = {
title: "申请证书",
type: "CertApply",
input: {
domains: ["*.powerleader.chat"],
domains: ["powerleader.chat", "*.powerleader.chat", "*.test.powerleader.chat", "*.ai.powerleader.chat"],
email: "xiaojunnuo@qq.com",
dnsProviderType: "huawei",
accessId: "111",
accessId: "333",
},
strategy: {
runStrategy: RunStrategy.SkipWhenSucceed,
},
},
],

View File

@ -13,7 +13,7 @@ describe("pipeline-hauwei-test", function () {
}
const executor = new Executor({ userId: "test", pipeline, onChanged, accessService: new AccessServiceTest(), storage: new FileStorage() });
await executor.run(1, "user");
await executor.run(2, "user");
// expect(define.name).eq("EchoPlugin");
});
});

View File

@ -123,12 +123,17 @@ export class AcmeService {
/* Replace this */
this.logger.info(`Would remove TXT record "${dnsRecord}" with value "${recordValue}"`);
await dnsProvider.removeRecord({
fullRecord: dnsRecord,
type: "TXT",
value: keyAuthorization,
record: recordItem,
});
try {
await dnsProvider.removeRecord({
fullRecord: dnsRecord,
type: "TXT",
value: keyAuthorization,
record: recordItem,
});
} catch (e) {
this.logger.error("删除解析记录出错:", e);
throw e;
}
}
}

View File

@ -181,6 +181,17 @@ export class CertApplyPlugin implements ITaskPlugin {
if (this.forceUpdate) {
return null;
}
let inputChanged = false;
const inputCacheKey = "input.cert";
const oldInputStr = await this.pipelineContext.get(inputCacheKey);
await this.pipelineContext.set(inputCacheKey, this.cert);
const oldInput = JSON.stringify(oldInputStr);
const thisInput = JSON.stringify(this.cert);
if (oldInput !== thisInput) {
inputChanged = true;
}
let oldCert;
try {
oldCert = await this.readCurrentCert();
@ -192,6 +203,11 @@ export class CertApplyPlugin implements ITaskPlugin {
return null;
}
if (inputChanged) {
this.logger.info("输入参数变更,申请新证书");
return null;
}
const ret = this.isWillExpire(oldCert.expires, this.renewDays);
if (!ret.isWillExpire) {
this.logger.info(`证书还未过期:过期时间${dayjs(oldCert.expires).format("YYYY-MM-DD HH:mm:ss")},剩余${ret.leftDays}`);
@ -261,6 +277,9 @@ export class CertApplyPlugin implements ITaskPlugin {
csr: this.formatCert(cert.csr),
};
await this.pipelineContext.set("cert", newCert);
await this.pipelineContext.set("cert.crt", newCert.crt);
await this.pipelineContext.set("cert.key", newCert.key);
await this.pipelineContext.set("cert.csr", newCert.csr);
}
async readCurrentCert() {

View File

@ -2,7 +2,11 @@ import _ from "lodash";
import { CreateRecordOptions, IDnsProvider, IsDnsProvider, RemoveRecordOptions } from "@certd/plugin-cert";
import { Autowire, ILogger } from "@certd/pipeline";
import { HuaweiAccess } from "../access";
import { HuaweiYunClient } from "../lib/client";
import { ApiRequestOptions, HuaweiYunClient } from "../lib/client";
export type SearchRecordOptions = {
zoneId: string;
} & CreateRecordOptions;
@IsDnsProvider({
name: "huawei",
@ -11,85 +15,85 @@ import { HuaweiYunClient } from "../lib/client";
accessType: "huawei",
})
export class HuaweiDnsProvider implements IDnsProvider {
client: any;
client!: HuaweiYunClient;
@Autowire()
access!: HuaweiAccess;
@Autowire()
logger!: ILogger;
endpoint = "https://domains-external.myhuaweicloud.com";
domainEndpoint = "https://domains-external.myhuaweicloud.com";
dnsEndpoint = "https://dns.cn-south-1.myhuaweicloud.com";
async onInstance() {
const access: any = this.access;
this.client = new HuaweiYunClient(access);
}
async getDomainList() {
const url = `${this.endpoint}/v2/domains`;
const url = `${this.dnsEndpoint}/v2/zones`;
const ret = await this.client.request({
url,
method: "GET",
});
return ret.domains;
return ret.zones;
}
async matchDomain(dnsRecord: string) {
const list = await this.getDomainList();
let domain = null;
for (const item of list) {
if (_.endsWith(dnsRecord, item.DomainName)) {
domain = item.DomainName;
const zoneList = await this.getDomainList();
let zoneRecord = null;
for (const item of zoneList) {
if (_.endsWith(dnsRecord + ".", item.name)) {
zoneRecord = item;
break;
}
}
if (!domain) {
if (!zoneRecord) {
throw new Error("can not find Domain ," + dnsRecord);
}
return domain;
return zoneRecord;
}
async getRecords(domain: string, rr: string, value: string) {
const params: any = {
RegionId: "cn-hangzhou",
DomainName: domain,
RRKeyWord: rr,
ValueKeyWord: undefined,
async searchRecord(options: SearchRecordOptions): Promise<any> {
const req: ApiRequestOptions = {
url: `${this.dnsEndpoint}/v2/zones/${options.zoneId}/recordsets?name=${options.fullRecord}.`,
method: "GET",
};
if (value) {
params.ValueKeyWord = value;
}
const requestOption = {
method: "POST",
};
const ret = await this.client.request("DescribeDomainRecords", params, requestOption);
return ret.DomainRecords.Record;
const ret = await this.client.request(req);
return ret.recordsets;
}
async createRecord(options: CreateRecordOptions): Promise<any> {
const { fullRecord, value, type } = options;
this.logger.info("添加域名解析:", fullRecord, value);
const domain = await this.matchDomain(fullRecord);
const rr = fullRecord.replace("." + domain, "");
const zoneRecord = await this.matchDomain(fullRecord);
const zoneId = zoneRecord.id;
const params = {
RegionId: "cn-hangzhou",
DomainName: domain,
RR: rr,
Type: type,
Value: value,
// Line: 'oversea' // 海外
};
const requestOption = {
method: "POST",
};
const records: any = await this.searchRecord({
zoneId,
...options,
});
if (records && records.length > 0) {
for (const record of records) {
await this.removeRecord({
record: records[0],
...options,
});
}
}
try {
const ret = await this.client.request("AddDomainRecord", params, requestOption);
this.logger.info("添加域名解析成功:", value, value, ret.RecordId);
return ret.RecordId;
const req: ApiRequestOptions = {
url: `${this.dnsEndpoint}/v2/zones/${zoneId}/recordsets`,
method: "POST",
data: {
name: fullRecord + ".",
type,
records: [`"${value}"`],
},
};
const ret = await this.client.request(req);
this.logger.info("添加域名解析成功:", value, ret);
return ret;
} catch (e: any) {
if (e.code === "DomainRecordDuplicate") {
if (e.code === "DNS.0312") {
return;
}
this.logger.info("添加域名解析出错", e);
@ -98,16 +102,12 @@ export class HuaweiDnsProvider implements IDnsProvider {
}
async removeRecord(options: RemoveRecordOptions): Promise<any> {
const { fullRecord, value, record } = options;
const params = {
RegionId: "cn-hangzhou",
RecordId: record,
const req: ApiRequestOptions = {
url: `${this.dnsEndpoint}/v2/zones/${record.zone_id}/recordsets/${record.id}`,
method: "DELETE",
};
const requestOption = {
method: "POST",
};
const ret = await this.client.request("DeleteDomainRecord", params, requestOption);
const ret = await this.client.request(req);
this.logger.info("删除域名解析成功:", fullRecord, value, ret.RecordId);
return ret.RecordId;
}

View File

@ -3,12 +3,13 @@ import signer from "./signer";
import https from "https";
import { HuaweiAccess } from "../access";
import { axios } from "@certd/acme-client";
import { logger } from "@certd/pipeline";
export type ApiRequestOptions = {
method: string;
url: string;
headers: any;
body: any;
headers?: any;
data?: any;
};
export class HuaweiYunClient {
access: HuaweiAccess;
@ -26,17 +27,29 @@ export class HuaweiYunClient {
//Set request host.
//Set request URI.
//Set parameters for the request URL.
const r = new signer.HttpRequest(options.method, options.url, options.headers, options.body);
let body = undefined;
if (options.data) {
body = JSON.stringify(options.data);
}
const r = new signer.HttpRequest(options.method, options.url, options.headers, body);
//Add header parameters, for example, x-domain-id for invoking a global service and x-project-id for invoking a project-level service.
r.headers = { "Content-Type": "application/json" };
//Add a body if you have specified the PUT or POST method. Special characters, such as the double quotation mark ("), contained in the body must be escaped.
r.body = "";
// r.body = option;
const opt = sig.Sign(r);
console.log("opt", opt);
console.log(opt.headers["X-Sdk-Date"]);
console.log(opt.headers["Authorization"]);
const res = await axios.request(opt);
return res;
try {
const res = await axios.request({
url: options.url,
method: options.method,
headers: opt.headers,
data: body,
});
return res.data;
} catch (e: any) {
logger.error("华为云接口请求出错:", e?.response?.data);
const error: any = new Error(e?.response?.data.message);
error.code = e?.response?.code;
throw error;
}
}
}