mirror of https://github.com/certd/certd
refactor: pipeline run log
parent
25d5a99b3d
commit
3dfdd3c31c
|
@ -1 +1 @@
|
||||||
Subproject commit a78bb43f6b65877b4f0aad25995d7cbf3215a3bc
|
Subproject commit 1c0e55a5339ef0ac92f96726c4c61e6918908730
|
|
@ -39,7 +39,7 @@
|
||||||
"log4js": "^6.3.0",
|
"log4js": "^6.3.0",
|
||||||
"mocha": "^10.1.0",
|
"mocha": "^10.1.0",
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"typescript": "^4.6.4",
|
"typescript": "^4.8.4",
|
||||||
"vite": "^3.1.0"
|
"vite": "^3.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,94 +1,136 @@
|
||||||
import { ConcurrencyStrategy, Pipeline, Runnable, Stage, Step, Task } from "../d.ts/pipeline";
|
import { ConcurrencyStrategy, HistoryResult, HistoryResultGroup, Pipeline, ResultType, Runnable, RunStrategy, Stage, Step, Task } from "../d.ts";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { RunHistory } from "./run-history";
|
import { RunHistory } from "./run-history";
|
||||||
import { pluginRegistry, TaskPlugin } from "../plugin";
|
import { pluginRegistry, TaskPlugin } from "../plugin";
|
||||||
import { IAccessService } from "../access/access-service";
|
import { IAccessService } from "../access/access-service";
|
||||||
import { ContextFactory } from "./context";
|
import { ContextFactory, IContext } from "./context";
|
||||||
import { IStorage } from "./storage";
|
import { IStorage } from "./storage";
|
||||||
import { logger } from "../utils/util.log";
|
import { logger } from "../utils/util.log";
|
||||||
|
import { Logger } from "log4js";
|
||||||
|
|
||||||
export class Executor {
|
export class Executor {
|
||||||
userId: any;
|
userId: any;
|
||||||
pipeline: Pipeline;
|
pipeline: Pipeline;
|
||||||
runtime: RunHistory = new RunHistory();
|
runtime!: RunHistory;
|
||||||
lastSuccessHistory: RunHistory;
|
|
||||||
accessService: IAccessService;
|
accessService: IAccessService;
|
||||||
contextFactory: ContextFactory;
|
contextFactory: ContextFactory;
|
||||||
|
logger: Logger;
|
||||||
|
pipelineContext: IContext;
|
||||||
onChanged: (history: RunHistory) => void;
|
onChanged: (history: RunHistory) => void;
|
||||||
constructor(options: {
|
constructor(options: { userId: any; pipeline: Pipeline; storage: IStorage; onChanged: (history: RunHistory) => void; accessService: IAccessService }) {
|
||||||
userId: any;
|
|
||||||
pipeline: Pipeline;
|
|
||||||
storage: IStorage;
|
|
||||||
onChanged: (history: RunHistory) => void;
|
|
||||||
lastSuccessHistory?: RunHistory;
|
|
||||||
accessService: IAccessService;
|
|
||||||
}) {
|
|
||||||
this.pipeline = options.pipeline;
|
this.pipeline = options.pipeline;
|
||||||
this.lastSuccessHistory = options.lastSuccessHistory ?? new RunHistory();
|
|
||||||
this.onChanged = options.onChanged;
|
this.onChanged = options.onChanged;
|
||||||
this.accessService = options.accessService;
|
this.accessService = options.accessService;
|
||||||
this.userId = options.userId;
|
this.userId = options.userId;
|
||||||
|
|
||||||
this.contextFactory = new ContextFactory(options.storage);
|
this.contextFactory = new ContextFactory(options.storage);
|
||||||
|
this.logger = logger;
|
||||||
|
this.pipelineContext = this.contextFactory.getContext("pipeline", this.pipeline.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
async run() {
|
async run(runtimeId: any = 0) {
|
||||||
await this.runWithHistory(this.pipeline, async () => {
|
|
||||||
return await this.runStages();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async runWithHistory(runnable: Runnable, run: () => Promise<any>) {
|
|
||||||
this.runtime.start(runnable);
|
|
||||||
this.onChanged(this.runtime);
|
|
||||||
try {
|
try {
|
||||||
await run();
|
this.runtime = new RunHistory(runtimeId);
|
||||||
|
this.logger.info(`pipeline.${this.pipeline.id} start`);
|
||||||
|
await this.runWithHistory(this.pipeline, "pipeline", async () => {
|
||||||
|
return await this.runStages();
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
this.logger.info(`pipeline.${this.pipeline.id} end`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async runWithHistory(runnable: Runnable, runnableType: string, run: (result: HistoryResult) => Promise<any>) {
|
||||||
|
const result = this.runtime.start(runnable, runnableType);
|
||||||
|
this.onChanged(this.runtime);
|
||||||
|
const contextKey = `status.${runnable.id}`;
|
||||||
|
if (runnable.strategy?.runStrategy === RunStrategy.SkipWhenSucceed) {
|
||||||
|
const lastResult = await this.pipelineContext.get(contextKey);
|
||||||
|
if (lastResult != null && lastResult === ResultType.success) {
|
||||||
|
this.runtime.log(runnable, result, "跳过");
|
||||||
|
return ResultType.skip;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await run(result);
|
||||||
this.runtime.success(runnable);
|
this.runtime.success(runnable);
|
||||||
this.onChanged(this.runtime);
|
this.onChanged(this.runtime);
|
||||||
|
await this.pipelineContext.set(contextKey, ResultType.success);
|
||||||
|
return ResultType.success;
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
logger.error(e);
|
this.logger.error(e);
|
||||||
this.runtime.error(runnable, e);
|
this.runtime.error(runnable, e);
|
||||||
this.onChanged(this.runtime);
|
this.onChanged(this.runtime);
|
||||||
|
await this.pipelineContext.set(contextKey, ResultType.error);
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
this.runtime.finally(runnable);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async runStages() {
|
private async runStages() {
|
||||||
|
const resList: ResultType[] = [];
|
||||||
for (const stage of this.pipeline.stages) {
|
for (const stage of this.pipeline.stages) {
|
||||||
await this.runWithHistory(stage, async () => {
|
const res: ResultType = await this.runWithHistory(stage, "stage", async (result) => {
|
||||||
return await this.runStage(stage);
|
return await this.runStage(stage, result);
|
||||||
});
|
});
|
||||||
|
resList.push(res);
|
||||||
}
|
}
|
||||||
|
return this.compositionResultType(resList);
|
||||||
}
|
}
|
||||||
|
|
||||||
async runStage(stage: Stage) {
|
async runStage(stage: Stage, result: HistoryResult) {
|
||||||
const runnerList = [];
|
const runnerList = [];
|
||||||
for (const task of stage.tasks) {
|
for (const task of stage.tasks) {
|
||||||
const runner = this.runWithHistory(task, async () => {
|
const runner = this.runWithHistory(task, "task", async (result) => {
|
||||||
return await this.runTask(task);
|
return await this.runTask(task, result);
|
||||||
});
|
});
|
||||||
runnerList.push(runner);
|
runnerList.push(runner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let resList: ResultType[] = [];
|
||||||
if (stage.concurrency === ConcurrencyStrategy.Parallel) {
|
if (stage.concurrency === ConcurrencyStrategy.Parallel) {
|
||||||
await Promise.all(runnerList);
|
resList = await Promise.all(runnerList);
|
||||||
} else {
|
} else {
|
||||||
for (const runner of runnerList) {
|
for (let i = 0; i < runnerList.length; i++) {
|
||||||
await runner;
|
const runner = runnerList[i];
|
||||||
|
resList[i] = await runner;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return this.compositionResultType(resList);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async runTask(task: Task) {
|
compositionResultType(resList: ResultType[]) {
|
||||||
for (const step of task.steps) {
|
let hasSuccess = false;
|
||||||
await this.runWithHistory(step, async () => {
|
for (const type of resList) {
|
||||||
return await this.runStep(step);
|
if (type === ResultType.error) {
|
||||||
});
|
return ResultType.error;
|
||||||
|
}
|
||||||
|
if (type === ResultType.success) {
|
||||||
|
hasSuccess = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (hasSuccess) {
|
||||||
|
return ResultType.success;
|
||||||
|
}
|
||||||
|
return ResultType.error;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async runStep(step: Step) {
|
private async runTask(task: Task, result: HistoryResult) {
|
||||||
//执行任务
|
const resList: ResultType[] = [];
|
||||||
const taskPlugin: TaskPlugin = await this.getPlugin(step.type);
|
for (const step of task.steps) {
|
||||||
|
const res: ResultType = await this.runWithHistory(step, "step", async (result) => {
|
||||||
|
await this.runStep(step, result);
|
||||||
|
});
|
||||||
|
resList.push(res);
|
||||||
|
}
|
||||||
|
return this.compositionResultType(resList);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async runStep(step: Step, result: HistoryResult) {
|
||||||
|
//执行任务
|
||||||
|
const taskPlugin: TaskPlugin = await this.getPlugin(step.type, result.logger);
|
||||||
|
// @ts-ignore
|
||||||
|
delete taskPlugin.define;
|
||||||
const define = taskPlugin.getDefine();
|
const define = taskPlugin.getDefine();
|
||||||
//从outputContext读取输入参数
|
//从outputContext读取输入参数
|
||||||
_.forEach(define.input, (item, key) => {
|
_.forEach(define.input, (item, key) => {
|
||||||
|
@ -109,14 +151,15 @@ export class Executor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getPlugin(type: string): Promise<TaskPlugin> {
|
private async getPlugin(type: string, logger: Logger): Promise<TaskPlugin> {
|
||||||
const pluginClass = pluginRegistry.get(type);
|
const pluginClass = pluginRegistry.get(type);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const plugin = new pluginClass();
|
const plugin = new pluginClass();
|
||||||
await plugin.doInit({
|
await plugin.doInit({
|
||||||
accessService: this.accessService,
|
accessService: this.accessService,
|
||||||
pipelineContext: this.contextFactory.getContext("pipeline", this.pipeline.id),
|
pipelineContext: this.pipelineContext,
|
||||||
userContext: this.contextFactory.getContext("user", this.userId),
|
userContext: this.contextFactory.getContext("user", this.userId),
|
||||||
|
logger,
|
||||||
});
|
});
|
||||||
return plugin;
|
return plugin;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,62 +1,74 @@
|
||||||
import { Context, HistoryResult, Log, Runnable } from "../d.ts/pipeline";
|
import { Context, HistoryResult, Log, Runnable } from "../d.ts";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
|
import { buildLogger, logger } from "../utils/util.log";
|
||||||
|
|
||||||
export class RunHistory {
|
export class RunHistory {
|
||||||
|
constructor(runtimeId: any) {
|
||||||
|
this.id = runtimeId;
|
||||||
|
}
|
||||||
|
|
||||||
id: any;
|
id: any;
|
||||||
logs: Log[] = [];
|
logs: Log[] = [];
|
||||||
context: Context = {};
|
context: Context = {};
|
||||||
results: {
|
results: {
|
||||||
[key: string]: HistoryResult;
|
[key: string]: HistoryResult;
|
||||||
} = {};
|
} = {};
|
||||||
|
logger = logger;
|
||||||
|
|
||||||
start(runnable: Runnable) {
|
start(runnable: Runnable, runnableType: string) {
|
||||||
const status = "ing";
|
const status = "start";
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
_.merge(runnable, { status, lastTime: now });
|
_.merge(runnable, { status, lastTime: now });
|
||||||
this.results[runnable.id] = {
|
const result: HistoryResult = {
|
||||||
|
type: runnableType,
|
||||||
startTime: new Date().getTime(),
|
startTime: new Date().getTime(),
|
||||||
title: runnable.title,
|
title: runnable.title,
|
||||||
status,
|
status,
|
||||||
|
logs: [],
|
||||||
|
logger: buildLogger((text) => {
|
||||||
|
result.logs.push(text);
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
this.log(runnable, `${runnable.title}<${runnable.id}> 开始执行`);
|
this.results[runnable.id] = result;
|
||||||
|
this.log(runnable, result, `${runnable.title}<id:${runnable.id}> 开始执行`);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
success(runnable: Runnable, result?: any) {
|
success(runnable: Runnable, res?: any) {
|
||||||
const status = "success";
|
const status = "success";
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
_.merge(runnable, { status, lastTime: now });
|
_.merge(runnable, { status, lastTime: now });
|
||||||
_.merge(this.results[runnable.id], { status, endTime: now }, result);
|
const result = this.results[runnable.id];
|
||||||
this.log(
|
_.merge(result, { status, endTime: now }, res);
|
||||||
runnable,
|
this.log(runnable, result, `${result.title}<id:${runnable.id}> 执行成功`);
|
||||||
`${this.results[runnable.id].title}<${runnable.id}> 执行成功`
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
error(runnable: Runnable, e: Error) {
|
error(runnable: Runnable, e: Error) {
|
||||||
const status = "error";
|
const status = "error";
|
||||||
const now = new Date().getTime();
|
const now = new Date().getTime();
|
||||||
_.merge(runnable, { status, lastTime: now });
|
_.merge(runnable, { status, lastTime: now });
|
||||||
_.merge(this.results[runnable.id], {
|
const result = this.results[runnable.id];
|
||||||
|
_.merge(result, {
|
||||||
status,
|
status,
|
||||||
endTime: now,
|
endTime: now,
|
||||||
errorMessage: e.message,
|
errorMessage: e.message,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.log(
|
this.log(runnable, result, `${result.title}<id:${runnable.id}> 执行异常:${e.message}`, status);
|
||||||
runnable,
|
|
||||||
`${this.results[runnable.id].title}<${runnable.id}> 执行异常:${
|
|
||||||
e.message
|
|
||||||
}`,
|
|
||||||
status
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
log(runnable: Runnable, text: string, level = "info") {
|
log(runnable: Runnable, result: HistoryResult, text: string, level = "info") {
|
||||||
|
const now = new Date().getTime();
|
||||||
|
result.logger.info(`[${runnable.title}] [${result.type}]`, text);
|
||||||
this.logs.push({
|
this.logs.push({
|
||||||
time: new Date().getTime(),
|
time: now,
|
||||||
level,
|
level,
|
||||||
title: runnable.title,
|
title: runnable.title,
|
||||||
text,
|
text,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
finally(runnable: Runnable) {
|
||||||
|
//
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,11 +7,6 @@ export interface IStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class FileStorage implements IStorage {
|
export class FileStorage implements IStorage {
|
||||||
/**
|
|
||||||
* 范围: user / pipeline / runtime / task
|
|
||||||
*/
|
|
||||||
scope: any;
|
|
||||||
namespace: any;
|
|
||||||
root: string;
|
root: string;
|
||||||
constructor(rootDir?: string) {
|
constructor(rootDir?: string) {
|
||||||
if (rootDir == null) {
|
if (rootDir == null) {
|
||||||
|
@ -42,17 +37,17 @@ export class FileStorage implements IStorage {
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(scope: string, namespace: string, key: string): Promise<string | null> {
|
async get(scope: string, namespace: string, key: string): Promise<string | null> {
|
||||||
const path = `${this.root}/${this.scope}/${namespace}/${key}`;
|
const path = this.buildPath(scope, namespace, key);
|
||||||
return this.readFile(path);
|
return this.readFile(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
async set(scope: string, namespace: string, key: string, value: string): Promise<void> {
|
async set(scope: string, namespace: string, key: string, value: string): Promise<void> {
|
||||||
const path = this.buildPath(namespace, key);
|
const path = this.buildPath(scope, namespace, key);
|
||||||
this.writeFile(path, value);
|
this.writeFile(path, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private buildPath(namespace: string, key: string) {
|
private buildPath(scope: string, namespace: string, key: string) {
|
||||||
return `${this.root}/${this.scope}/${namespace}/${key}`;
|
return `${this.root}/${scope}/${namespace}/${key}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { Logger } from "log4js";
|
||||||
|
|
||||||
export enum RunStrategy {
|
export enum RunStrategy {
|
||||||
AlwaysRun,
|
AlwaysRun,
|
||||||
SkipWhenSucceed,
|
SkipWhenSucceed,
|
||||||
|
@ -27,9 +29,9 @@ export type EventHandler = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RunnableStrategy = {
|
export type RunnableStrategy = {
|
||||||
runStrategy: RunStrategy;
|
runStrategy?: RunStrategy;
|
||||||
onSuccess: EventHandler[];
|
onSuccess?: EventHandler[];
|
||||||
onError: EventHandler[];
|
onError?: EventHandler[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Step = Runnable & {
|
export type Step = Runnable & {
|
||||||
|
@ -79,7 +81,20 @@ export type Log = {
|
||||||
text: string;
|
text: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export enum ResultType {
|
||||||
|
success,
|
||||||
|
error,
|
||||||
|
skip,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type HistoryResultGroup = {
|
||||||
|
[key: string]: {
|
||||||
|
runnable: Runnable;
|
||||||
|
res: HistoryResult;
|
||||||
|
};
|
||||||
|
};
|
||||||
export type HistoryResult = {
|
export type HistoryResult = {
|
||||||
|
type: string;
|
||||||
title: string;
|
title: string;
|
||||||
/**
|
/**
|
||||||
* 任务状态
|
* 任务状态
|
||||||
|
@ -92,4 +107,6 @@ export type HistoryResult = {
|
||||||
*/
|
*/
|
||||||
result?: string;
|
result?: string;
|
||||||
errorMessage?: string;
|
errorMessage?: string;
|
||||||
|
logs: string[];
|
||||||
|
logger: Logger;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import { AbstractRegistrable } from "../registry";
|
import { AbstractRegistrable } from "../registry";
|
||||||
import { CreateRecordOptions, IDnsProvider, DnsProviderDefine, RemoveRecordOptions } from "./api";
|
import { CreateRecordOptions, IDnsProvider, DnsProviderDefine, RemoveRecordOptions } from "./api";
|
||||||
import { AbstractAccess } from "../access";
|
import { AbstractAccess } from "../access";
|
||||||
|
import { Logger } from "log4js";
|
||||||
export abstract class AbstractDnsProvider extends AbstractRegistrable<DnsProviderDefine> implements IDnsProvider {
|
export abstract class AbstractDnsProvider extends AbstractRegistrable<DnsProviderDefine> implements IDnsProvider {
|
||||||
// @ts-ignore
|
access!: AbstractAccess;
|
||||||
access: AbstractAccess;
|
logger!: Logger;
|
||||||
|
doInit(options: { access: AbstractAccess; logger: Logger }) {
|
||||||
doInit(options: { access: AbstractAccess }) {
|
|
||||||
this.access = options.access;
|
this.access = options.access;
|
||||||
|
this.logger = options.logger;
|
||||||
this.onInit();
|
this.onInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Registrable } from "../registry";
|
import { Registrable } from "../registry";
|
||||||
import { dnsProviderRegistry } from "./registry";
|
import { dnsProviderRegistry } from "./registry";
|
||||||
|
import { AbstractDnsProvider } from "./abstract-dns-provider";
|
||||||
|
|
||||||
export type DnsProviderDefine = Registrable & {
|
export type DnsProviderDefine = Registrable & {
|
||||||
accessType: string;
|
accessType: string;
|
||||||
|
@ -21,9 +22,13 @@ export interface IDnsProvider {
|
||||||
removeRecord(options: RemoveRecordOptions): Promise<any>;
|
removeRecord(options: RemoveRecordOptions): Promise<any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function IsDnsProvider(define: DnsProviderDefine) {
|
export function IsDnsProvider(define: (() => DnsProviderDefine) | DnsProviderDefine) {
|
||||||
return function (target: any) {
|
return function (target: typeof AbstractDnsProvider) {
|
||||||
target.prototype.define = define;
|
if (define instanceof Function) {
|
||||||
|
target.prototype.define = define();
|
||||||
|
} else {
|
||||||
|
target.prototype.define = define;
|
||||||
|
}
|
||||||
dnsProviderRegistry.install(target);
|
dnsProviderRegistry.install(target);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,6 @@ export class AliyunDnsProvider extends AbstractDnsProvider implements IDnsProvid
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
async onInit() {
|
async onInit() {
|
||||||
const access: any = this.access;
|
const access: any = this.access;
|
||||||
this.client = new Core({
|
this.client = new Core({
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import { AbstractRegistrable } from "../registry";
|
import { AbstractRegistrable } from "../registry";
|
||||||
import { Logger } from "log4js";
|
import { Logger } from "log4js";
|
||||||
import { logger } from "../utils/util.log";
|
|
||||||
import { IAccessService } from "../access/access-service";
|
import { IAccessService } from "../access/access-service";
|
||||||
import { IContext } from "../core/context";
|
import { IContext } from "../core/context";
|
||||||
import { PluginDefine, TaskInput, TaskOutput, TaskPlugin } from "./api";
|
import { PluginDefine, TaskInput, TaskOutput, TaskPlugin } from "./api";
|
||||||
|
|
||||||
export abstract class AbstractPlugin extends AbstractRegistrable<PluginDefine> implements TaskPlugin {
|
export abstract class AbstractPlugin extends AbstractRegistrable<PluginDefine> implements TaskPlugin {
|
||||||
logger: Logger = logger;
|
logger!: Logger;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
accessService: IAccessService;
|
accessService: IAccessService;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -14,10 +13,11 @@ export abstract class AbstractPlugin extends AbstractRegistrable<PluginDefine> i
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
userContext: IContext;
|
userContext: IContext;
|
||||||
|
|
||||||
async doInit(options: { accessService: IAccessService; pipelineContext: IContext; userContext: IContext }) {
|
async doInit(options: { accessService: IAccessService; pipelineContext: IContext; userContext: IContext; logger: Logger }) {
|
||||||
this.accessService = options.accessService;
|
this.accessService = options.accessService;
|
||||||
this.pipelineContext = options.pipelineContext;
|
this.pipelineContext = options.pipelineContext;
|
||||||
this.userContext = options.userContext;
|
this.userContext = options.userContext;
|
||||||
|
this.logger = options.logger;
|
||||||
await this.onInit();
|
await this.onInit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import * as acme from "@certd/acme-client";
|
import * as acme from "@certd/acme-client";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { logger } from "../../../utils/util.log";
|
|
||||||
import { AbstractDnsProvider } from "../../../dns-provider/abstract-dns-provider";
|
import { AbstractDnsProvider } from "../../../dns-provider/abstract-dns-provider";
|
||||||
import { IContext } from "../../../core/context";
|
import { IContext } from "../../../core/context";
|
||||||
import { IDnsProvider } from "../../../dns-provider";
|
import { IDnsProvider } from "../../../dns-provider";
|
||||||
import { Challenge } from "@certd/acme-client/types/rfc8555";
|
import { Challenge } from "@certd/acme-client/types/rfc8555";
|
||||||
console.log("acme", acme);
|
import { Logger } from "log4js";
|
||||||
export class AcmeService {
|
export class AcmeService {
|
||||||
userContext: IContext;
|
userContext: IContext;
|
||||||
constructor(options: { userContext: IContext }) {
|
logger: Logger;
|
||||||
|
constructor(options: { userContext: IContext; logger: Logger }) {
|
||||||
this.userContext = options.userContext;
|
this.userContext = options.userContext;
|
||||||
|
this.logger = options.logger;
|
||||||
acme.setLogger((text: string) => {
|
acme.setLogger((text: string) => {
|
||||||
logger.info(text);
|
this.logger.info(text);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,27 +65,27 @@ export class AcmeService {
|
||||||
}
|
}
|
||||||
|
|
||||||
async challengeCreateFn(authz: any, challenge: any, keyAuthorization: string, dnsProvider: IDnsProvider) {
|
async challengeCreateFn(authz: any, challenge: any, keyAuthorization: string, dnsProvider: IDnsProvider) {
|
||||||
logger.info("Triggered challengeCreateFn()");
|
this.logger.info("Triggered challengeCreateFn()");
|
||||||
|
|
||||||
/* http-01 */
|
/* http-01 */
|
||||||
if (challenge.type === "http-01") {
|
if (challenge.type === "http-01") {
|
||||||
const filePath = `/var/www/html/.well-known/acme-challenge/${challenge.token}`;
|
const filePath = `/var/www/html/.well-known/acme-challenge/${challenge.token}`;
|
||||||
const fileContents = keyAuthorization;
|
const fileContents = keyAuthorization;
|
||||||
|
|
||||||
logger.info(`Creating challenge response for ${authz.identifier.value} at path: ${filePath}`);
|
this.logger.info(`Creating challenge response for ${authz.identifier.value} at path: ${filePath}`);
|
||||||
|
|
||||||
/* Replace this */
|
/* Replace this */
|
||||||
logger.info(`Would write "${fileContents}" to path "${filePath}"`);
|
this.logger.info(`Would write "${fileContents}" to path "${filePath}"`);
|
||||||
// await fs.writeFileAsync(filePath, fileContents);
|
// await fs.writeFileAsync(filePath, fileContents);
|
||||||
} else if (challenge.type === "dns-01") {
|
} else if (challenge.type === "dns-01") {
|
||||||
/* dns-01 */
|
/* dns-01 */
|
||||||
const dnsRecord = `_acme-challenge.${authz.identifier.value}`;
|
const dnsRecord = `_acme-challenge.${authz.identifier.value}`;
|
||||||
const recordValue = keyAuthorization;
|
const recordValue = keyAuthorization;
|
||||||
|
|
||||||
logger.info(`Creating TXT record for ${authz.identifier.value}: ${dnsRecord}`);
|
this.logger.info(`Creating TXT record for ${authz.identifier.value}: ${dnsRecord}`);
|
||||||
|
|
||||||
/* Replace this */
|
/* Replace this */
|
||||||
logger.info(`Would create TXT record "${dnsRecord}" with value "${recordValue}"`);
|
this.logger.info(`Would create TXT record "${dnsRecord}" with value "${recordValue}"`);
|
||||||
|
|
||||||
return await dnsProvider.createRecord({
|
return await dnsProvider.createRecord({
|
||||||
fullRecord: dnsRecord,
|
fullRecord: dnsRecord,
|
||||||
|
@ -106,25 +107,25 @@ export class AcmeService {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
async challengeRemoveFn(authz: any, challenge: any, keyAuthorization: string, recordItem: any, dnsProvider: IDnsProvider) {
|
async challengeRemoveFn(authz: any, challenge: any, keyAuthorization: string, recordItem: any, dnsProvider: IDnsProvider) {
|
||||||
logger.info("Triggered challengeRemoveFn()");
|
this.logger.info("Triggered challengeRemoveFn()");
|
||||||
|
|
||||||
/* http-01 */
|
/* http-01 */
|
||||||
if (challenge.type === "http-01") {
|
if (challenge.type === "http-01") {
|
||||||
const filePath = `/var/www/html/.well-known/acme-challenge/${challenge.token}`;
|
const filePath = `/var/www/html/.well-known/acme-challenge/${challenge.token}`;
|
||||||
|
|
||||||
logger.info(`Removing challenge response for ${authz.identifier.value} at path: ${filePath}`);
|
this.logger.info(`Removing challenge response for ${authz.identifier.value} at path: ${filePath}`);
|
||||||
|
|
||||||
/* Replace this */
|
/* Replace this */
|
||||||
logger.info(`Would remove file on path "${filePath}"`);
|
this.logger.info(`Would remove file on path "${filePath}"`);
|
||||||
// await fs.unlinkAsync(filePath);
|
// await fs.unlinkAsync(filePath);
|
||||||
} else if (challenge.type === "dns-01") {
|
} else if (challenge.type === "dns-01") {
|
||||||
const dnsRecord = `_acme-challenge.${authz.identifier.value}`;
|
const dnsRecord = `_acme-challenge.${authz.identifier.value}`;
|
||||||
const recordValue = keyAuthorization;
|
const recordValue = keyAuthorization;
|
||||||
|
|
||||||
logger.info(`Removing TXT record for ${authz.identifier.value}: ${dnsRecord}`);
|
this.logger.info(`Removing TXT record for ${authz.identifier.value}: ${dnsRecord}`);
|
||||||
|
|
||||||
/* Replace this */
|
/* Replace this */
|
||||||
logger.info(`Would remove TXT record "${dnsRecord}" with value "${recordValue}"`);
|
this.logger.info(`Would remove TXT record "${dnsRecord}" with value "${recordValue}"`);
|
||||||
await dnsProvider.removeRecord({
|
await dnsProvider.removeRecord({
|
||||||
fullRecord: dnsRecord,
|
fullRecord: dnsRecord,
|
||||||
type: "TXT",
|
type: "TXT",
|
||||||
|
@ -169,9 +170,9 @@ export class AcmeService {
|
||||||
csr: csr.toString(),
|
csr: csr.toString(),
|
||||||
};
|
};
|
||||||
/* Done */
|
/* Done */
|
||||||
logger.debug(`CSR:\n${cert.csr}`);
|
this.logger.debug(`CSR:\n${cert.csr}`);
|
||||||
logger.debug(`Certificate:\n${cert.crt}`);
|
this.logger.debug(`Certificate:\n${cert.crt}`);
|
||||||
logger.info("证书申请成功");
|
this.logger.info("证书申请成功");
|
||||||
return cert;
|
return cert;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { AbstractPlugin } from "../../abstract-plugin";
|
import { AbstractPlugin } from "../../abstract-plugin";
|
||||||
import forge from "node-forge";
|
import forge from "node-forge";
|
||||||
import { ContextScope, IsTask, TaskInput, TaskOutput, TaskPlugin } from "../../api";
|
import { IsTask, TaskInput, TaskOutput, TaskPlugin } from "../../api";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import { dnsProviderRegistry } from "../../../dns-provider";
|
import { dnsProviderRegistry } from "../../../dns-provider";
|
||||||
import { AbstractDnsProvider } from "../../../dns-provider/abstract-dns-provider";
|
import { AbstractDnsProvider } from "../../../dns-provider/abstract-dns-provider";
|
||||||
|
@ -39,53 +39,49 @@ export type CertInfo = {
|
||||||
dnsProviderType: {
|
dnsProviderType: {
|
||||||
title: "DNS提供商",
|
title: "DNS提供商",
|
||||||
component: {
|
component: {
|
||||||
name: "a-select",
|
name: "pi-dns-provider-selector",
|
||||||
},
|
},
|
||||||
helper: "请选择dns解析提供商",
|
helper: "请选择dns解析提供商",
|
||||||
},
|
},
|
||||||
dnsProviderAccess: {
|
dnsProviderAccess: {
|
||||||
title: "DNS解析授权",
|
title: "DNS解析授权",
|
||||||
component: {
|
component: {
|
||||||
name: "access-selector",
|
name: "pi-access-selector",
|
||||||
},
|
},
|
||||||
helper: "请选择dns解析提供商授权",
|
helper: "请选择dns解析提供商授权",
|
||||||
},
|
},
|
||||||
renewDays: {
|
renewDays: {
|
||||||
title: "更新天数",
|
title: "更新天数",
|
||||||
|
value: 20,
|
||||||
component: {
|
component: {
|
||||||
name: "a-input-number",
|
name: "a-input-number",
|
||||||
value: 20,
|
|
||||||
},
|
},
|
||||||
helper: "到期前多少天后更新证书",
|
helper: "到期前多少天后更新证书",
|
||||||
},
|
},
|
||||||
forceUpdate: {
|
forceUpdate: {
|
||||||
title: "强制更新",
|
title: "强制更新",
|
||||||
|
value: false,
|
||||||
component: {
|
component: {
|
||||||
name: "a-switch",
|
name: "a-switch",
|
||||||
vModel: "checked",
|
vModel: "checked",
|
||||||
value: false,
|
|
||||||
},
|
},
|
||||||
helper: "强制重新申请证书",
|
helper: "是否强制重新申请证书",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
cert: {
|
cert: {
|
||||||
key: "cert",
|
key: "cert",
|
||||||
type: "CertInfo",
|
type: "CertInfo",
|
||||||
title: "证书",
|
title: "域名证书",
|
||||||
scope: ContextScope.pipeline,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
export class CertPlugin extends AbstractPlugin implements TaskPlugin {
|
export class CertApplyPlugin extends AbstractPlugin implements TaskPlugin {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
acme: AcmeService;
|
acme: AcmeService;
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
protected async onInit() {
|
protected async onInit() {
|
||||||
this.acme = new AcmeService({ userContext: this.userContext });
|
this.acme = new AcmeService({ userContext: this.userContext, logger: this.logger });
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(input: TaskInput): Promise<TaskOutput> {
|
async execute(input: TaskInput): Promise<TaskOutput> {
|
||||||
|
@ -139,7 +135,7 @@ export class CertPlugin extends AbstractPlugin implements TaskPlugin {
|
||||||
const access = await this.accessService.getById(dnsProviderAccessId);
|
const access = await this.accessService.getById(dnsProviderAccessId);
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const dnsProvider: AbstractDnsProvider = new dnsProviderClass();
|
const dnsProvider: AbstractDnsProvider = new dnsProviderClass();
|
||||||
dnsProvider.doInit({ access });
|
dnsProvider.doInit({ access, logger: this.logger });
|
||||||
|
|
||||||
const cert = await this.acme.order({
|
const cert = await this.acme.order({
|
||||||
email,
|
email,
|
||||||
|
|
|
@ -10,33 +10,34 @@ import { CertInfo } from "../cert-plugin";
|
||||||
return {
|
return {
|
||||||
name: "DeployCertToAliyunCDN",
|
name: "DeployCertToAliyunCDN",
|
||||||
title: "部署证书至阿里云CDN",
|
title: "部署证书至阿里云CDN",
|
||||||
|
desc: "依赖证书申请前置任务,自动部署域名证书至阿里云CDN",
|
||||||
input: {
|
input: {
|
||||||
domainName: {
|
domainName: {
|
||||||
title: "cdn加速域名",
|
title: "CDN加速域名",
|
||||||
component: {
|
component: {
|
||||||
placeholder: "cdn加速域名",
|
placeholder: "你在阿里云上配置的CDN加速域名,比如certd.docmirror.cn",
|
||||||
},
|
},
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
certName: {
|
certName: {
|
||||||
title: "证书名称",
|
title: "证书名称",
|
||||||
component: {
|
component: {
|
||||||
placeholder: "上传后将以此名称作为前缀",
|
placeholder: "上传后将以此名称作为前缀备注",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
cert: {
|
cert: {
|
||||||
title: "域名证书",
|
title: "域名证书",
|
||||||
helper: "请选择前置任务输出的域名证书",
|
helper: "请选择前置任务输出的域名证书",
|
||||||
component: {
|
component: {
|
||||||
name: "output-selector",
|
name: "pi-output-selector",
|
||||||
},
|
},
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
accessId: {
|
accessId: {
|
||||||
title: "Access提供者",
|
title: "Access授权",
|
||||||
helper: "access授权",
|
helper: "阿里云授权AccessKeyId、AccessKeySecret",
|
||||||
component: {
|
component: {
|
||||||
name: "access-selector",
|
name: "pi-access-selector",
|
||||||
type: "aliyun",
|
type: "aliyun",
|
||||||
},
|
},
|
||||||
required: true,
|
required: true,
|
||||||
|
@ -46,16 +47,13 @@ import { CertInfo } from "../cert-plugin";
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
export class DeployCertToAliyunCDN extends AbstractPlugin implements TaskPlugin {
|
export class DeployCertToAliyunCDN extends AbstractPlugin implements TaskPlugin {
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
async execute(input: TaskInput): Promise<TaskOutput> {
|
async execute(input: TaskInput): Promise<TaskOutput> {
|
||||||
console.log("开始部署证书到阿里云cdn");
|
console.log("开始部署证书到阿里云cdn");
|
||||||
const access = (await this.accessService.getById(input.accessId)) as AliyunAccess;
|
const access = (await this.accessService.getById(input.accessId)) as AliyunAccess;
|
||||||
const client = this.getClient(access);
|
const client = this.getClient(access);
|
||||||
const params = await this.buildParams(input);
|
const params = await this.buildParams(input);
|
||||||
await this.doRequest(client, params);
|
await this.doRequest(client, params);
|
||||||
|
console.log("部署完成");
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,7 +68,7 @@ export class DeployCertToAliyunCDN extends AbstractPlugin implements TaskPlugin
|
||||||
|
|
||||||
async buildParams(input: TaskInput) {
|
async buildParams(input: TaskInput) {
|
||||||
const { certName, domainName } = input;
|
const { certName, domainName } = input;
|
||||||
const CertName = certName + "-" + dayjs().format("YYYYMMDDHHmmss");
|
const CertName = (certName ?? "certd") + "-" + dayjs().format("YYYYMMDDHHmmss");
|
||||||
const cert = input.cert as CertInfo;
|
const cert = input.cert as CertInfo;
|
||||||
return {
|
return {
|
||||||
RegionId: "cn-hangzhou",
|
RegionId: "cn-hangzhou",
|
||||||
|
|
|
@ -4,12 +4,12 @@ import { IsTask, TaskInput, TaskOutput, TaskPlugin } from "../api";
|
||||||
@IsTask(() => {
|
@IsTask(() => {
|
||||||
return {
|
return {
|
||||||
name: "EchoPlugin",
|
name: "EchoPlugin",
|
||||||
title: "测试插件回声",
|
title: "测试插件【echo】",
|
||||||
input: {
|
input: {
|
||||||
cert: {
|
cert: {
|
||||||
title: "cert",
|
title: "cert",
|
||||||
component: {
|
component: {
|
||||||
name: "output-selector",
|
name: "pi-output-selector",
|
||||||
},
|
},
|
||||||
helper: "输出选择",
|
helper: "输出选择",
|
||||||
},
|
},
|
||||||
|
@ -20,7 +20,7 @@ import { IsTask, TaskInput, TaskOutput, TaskPlugin } from "../api";
|
||||||
export class EchoPlugin extends AbstractPlugin implements TaskPlugin {
|
export class EchoPlugin extends AbstractPlugin implements TaskPlugin {
|
||||||
async execute(input: TaskInput): Promise<TaskOutput> {
|
async execute(input: TaskInput): Promise<TaskOutput> {
|
||||||
for (const key in input) {
|
for (const key in input) {
|
||||||
console.log("input :", key, input[key]);
|
this.logger.info("input :", key, input[key]);
|
||||||
}
|
}
|
||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { Logger } from "log4js";
|
import { Logger } from "log4js";
|
||||||
import { logger } from "../utils/util.log";
|
|
||||||
|
|
||||||
export type Registrable = {
|
export type Registrable = {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -8,7 +7,6 @@ export type Registrable = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export abstract class AbstractRegistrable<T extends Registrable> {
|
export abstract class AbstractRegistrable<T extends Registrable> {
|
||||||
logger: Logger = logger;
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
define: T;
|
define: T;
|
||||||
|
|
||||||
|
@ -21,18 +19,20 @@ export class Registry<T extends typeof AbstractRegistrable> {
|
||||||
[key: string]: T;
|
[key: string]: T;
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
install(target: T) {
|
install(pluginClass: T) {
|
||||||
if (target == null) {
|
if (pluginClass == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const define = new target().define;
|
const plugin = new pluginClass();
|
||||||
|
delete plugin.define;
|
||||||
|
const define = plugin.getDefine();
|
||||||
let defineName = define.name;
|
let defineName = define.name;
|
||||||
if (defineName == null) {
|
if (defineName == null) {
|
||||||
defineName = target.name;
|
defineName = plugin.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.register(defineName, target);
|
this.register(defineName, pluginClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
register(key: string, value: T) {
|
register(key: string, value: T) {
|
||||||
|
@ -57,4 +57,15 @@ export class Registry<T extends typeof AbstractRegistrable> {
|
||||||
getStorage() {
|
getStorage() {
|
||||||
return this.storage;
|
return this.storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDefineList() {
|
||||||
|
const list = [];
|
||||||
|
for (const key in this.storage) {
|
||||||
|
const PluginClass = this.storage[key];
|
||||||
|
// @ts-ignore
|
||||||
|
const plugin = new PluginClass();
|
||||||
|
list.push({ ...plugin.define, key });
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,34 @@
|
||||||
import log4js from "log4js";
|
import log4js, { Appender, Logger, LoggingEvent } from "log4js";
|
||||||
|
|
||||||
|
const OutputAppender = {
|
||||||
|
configure: (config: any, layouts: any, findAppender: any, levels: any) => {
|
||||||
|
let layout = layouts.colouredLayout;
|
||||||
|
if (config.layout) {
|
||||||
|
layout = layouts.layout(config.layout.type, config.layout);
|
||||||
|
}
|
||||||
|
function customAppender(layout: any, timezoneOffset: any) {
|
||||||
|
return (loggingEvent: LoggingEvent) => {
|
||||||
|
if (loggingEvent.context.outputHandler?.write) {
|
||||||
|
const text = `${layout(loggingEvent, timezoneOffset)}\n`;
|
||||||
|
loggingEvent.context.outputHandler.write(text);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return customAppender(layout, config.timezoneOffset);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// @ts-ignore
|
||||||
log4js.configure({
|
log4js.configure({
|
||||||
appenders: { std: { type: "stdout" } },
|
appenders: { std: { type: "stdout" }, output: { type: OutputAppender } },
|
||||||
categories: { default: { appenders: ["std"], level: "info" } },
|
categories: { default: { appenders: ["std"], level: "info" }, pipeline: { appenders: ["std", "output"], level: "info" } },
|
||||||
});
|
});
|
||||||
export const logger = log4js.getLogger("pipeline");
|
export const logger = log4js.getLogger("default");
|
||||||
|
|
||||||
|
export function buildLogger(write: (text: string) => void) {
|
||||||
|
const logger = log4js.getLogger("pipeline");
|
||||||
|
logger.addContext("outputHandler", {
|
||||||
|
write,
|
||||||
|
});
|
||||||
|
return logger;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
export const fakeCrt = `-----BEGIN CERTIFICATE-----
|
||||||
|
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
|
||||||
|
AHxTaXliTVvS83dogw==
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIEqzCCApOgAwIBAgIRAIvhKg5ZRO08VGQx8JdhT+UwDQYJKoZIhvcNAQELBQAw
|
||||||
|
GjEYMBYGA1UEAwwPRmFrZSBMRSBSb290IFgxMB4XDTE2MDUyMzIyMDc1OVoXDTM2
|
||||||
|
MDUyMzIyMDc1OVowIjEgMB4GA1UEAwwXRmFrZSBMRSBJbnRlcm1lZGlhdGUgWDEw
|
||||||
|
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtWKySDn7rWZc5ggjz3ZB0
|
||||||
|
8jO4xti3uzINfD5sQ7Lj7hzetUT+wQob+iXSZkhnvx+IvdbXF5/yt8aWPpUKnPym
|
||||||
|
oLxsYiI5gQBLxNDzIec0OIaflWqAr29m7J8+NNtApEN8nZFnf3bhehZW7AxmS1m0
|
||||||
|
ZnSsdHw0Fw+bgixPg2MQ9k9oefFeqa+7Kqdlz5bbrUYV2volxhDFtnI4Mh8BiWCN
|
||||||
|
xDH1Hizq+GKCcHsinDZWurCqder/afJBnQs+SBSL6MVApHt+d35zjBD92fO2Je56
|
||||||
|
dhMfzCgOKXeJ340WhW3TjD1zqLZXeaCyUNRnfOmWZV8nEhtHOFbUCU7r/KkjMZO9
|
||||||
|
AgMBAAGjgeMwgeAwDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAw
|
||||||
|
HQYDVR0OBBYEFMDMA0a5WCDMXHJw8+EuyyCm9Wg6MHoGCCsGAQUFBwEBBG4wbDA0
|
||||||
|
BggrBgEFBQcwAYYoaHR0cDovL29jc3Auc3RnLXJvb3QteDEubGV0c2VuY3J5cHQu
|
||||||
|
b3JnLzA0BggrBgEFBQcwAoYoaHR0cDovL2NlcnQuc3RnLXJvb3QteDEubGV0c2Vu
|
||||||
|
Y3J5cHQub3JnLzAfBgNVHSMEGDAWgBTBJnSkikSg5vogKNhcI5pFiBh54DANBgkq
|
||||||
|
hkiG9w0BAQsFAAOCAgEABYSu4Il+fI0MYU42OTmEj+1HqQ5DvyAeyCA6sGuZdwjF
|
||||||
|
UGeVOv3NnLyfofuUOjEbY5irFCDtnv+0ckukUZN9lz4Q2YjWGUpW4TTu3ieTsaC9
|
||||||
|
AFvCSgNHJyWSVtWvB5XDxsqawl1KzHzzwr132bF2rtGtazSqVqK9E07sGHMCf+zp
|
||||||
|
DQVDVVGtqZPHwX3KqUtefE621b8RI6VCl4oD30Olf8pjuzG4JKBFRFclzLRjo/h7
|
||||||
|
IkkfjZ8wDa7faOjVXx6n+eUQ29cIMCzr8/rNWHS9pYGGQKJiY2xmVC9h12H99Xyf
|
||||||
|
zWE9vb5zKP3MVG6neX1hSdo7PEAb9fqRhHkqVsqUvJlIRmvXvVKTwNCP3eCjRCCI
|
||||||
|
PTAvjV+4ni786iXwwFYNz8l3PmPLCyQXWGohnJ8iBm+5nk7O2ynaPVW0U2W+pt2w
|
||||||
|
SVuvdDM5zGv2f9ltNWUiYZHJ1mmO97jSY/6YfdOUH66iRtQtDkHBRdkNBsMbD+Em
|
||||||
|
2TgBldtHNSJBfB3pm9FblgOcJ0FSWcUDWJ7vO0+NTXlgrRofRT6pVywzxVo6dND0
|
||||||
|
WzYlTWeUVsO40xJqhgUQRER9YLOLxJ0O6C8i0xFxAMKOtSdodMB3RIwt7RFQ0uyt
|
||||||
|
n5Z5MqkYhlMI3J1tPRTp1nEt9fyGspBOO05gi148Qasp+3N+svqKomoQglNoAxU=
|
||||||
|
-----END CERTIFICATE-----`;
|
|
@ -2,7 +2,7 @@ import { IAccessService } from "../../src/access/access-service";
|
||||||
import { AbstractAccess, AliyunAccess } from "../../src";
|
import { AbstractAccess, AliyunAccess } from "../../src";
|
||||||
import { aliyunSecret } from "../user.secret";
|
import { aliyunSecret } from "../user.secret";
|
||||||
export class AccessServiceTest implements IAccessService {
|
export class AccessServiceTest implements IAccessService {
|
||||||
getById(id: any): AbstractAccess {
|
async getById(id: any): Promise<AbstractAccess> {
|
||||||
return {
|
return {
|
||||||
...aliyunSecret,
|
...aliyunSecret,
|
||||||
} as AliyunAccess;
|
} as AliyunAccess;
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { ContextFactory } from "../../src/core/context";
|
||||||
|
import { FileStorage } from "../../src/core/storage";
|
||||||
|
import { AccessServiceTest } from "./access-service-test";
|
||||||
|
import { logger } from "../../src/utils/util.log";
|
||||||
|
|
||||||
|
const contextFactory = new ContextFactory(new FileStorage());
|
||||||
|
|
||||||
|
const userContext = contextFactory.getContext("user", "test");
|
||||||
|
const pipelineContext = contextFactory.getContext("pipeline", "test");
|
||||||
|
export const pluginInitProps = {
|
||||||
|
accessService: new AccessServiceTest(),
|
||||||
|
pipelineContext: pipelineContext,
|
||||||
|
userContext: userContext,
|
||||||
|
logger: logger,
|
||||||
|
};
|
|
@ -1,4 +1,4 @@
|
||||||
import { ConcurrencyStrategy, NextStrategy, Pipeline } from "../../src";
|
import { ConcurrencyStrategy, NextStrategy, Pipeline, RunStrategy } from "../../src";
|
||||||
|
|
||||||
let idIndex = 0;
|
let idIndex = 0;
|
||||||
function generateId() {
|
function generateId() {
|
||||||
|
@ -44,15 +44,18 @@ export const pipeline: Pipeline = {
|
||||||
tasks: [
|
tasks: [
|
||||||
{
|
{
|
||||||
id: generateId(),
|
id: generateId(),
|
||||||
title: "测试输出参数",
|
title: "测试输出参数任务",
|
||||||
steps: [
|
steps: [
|
||||||
{
|
{
|
||||||
id: generateId(),
|
id: generateId(),
|
||||||
title: "输出参数",
|
title: "输出参数(echo插件)",
|
||||||
type: "EchoPlugin",
|
type: "EchoPlugin",
|
||||||
input: {
|
input: {
|
||||||
cert: "cert",
|
cert: "cert",
|
||||||
},
|
},
|
||||||
|
strategy: {
|
||||||
|
runStrategy: RunStrategy.SkipWhenSucceed,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { expect } from "chai";
|
//import { expect } from "chai";
|
||||||
import "mocha";
|
import "mocha";
|
||||||
import { Executor, RunHistory } from "../../src";
|
import { Executor, RunHistory } from "../../src";
|
||||||
import { pipeline } from "./pipeline.define";
|
import { pipeline } from "./pipeline.define";
|
||||||
|
@ -11,7 +11,7 @@ describe("pipeline", function () {
|
||||||
console.log("changed:");
|
console.log("changed:");
|
||||||
}
|
}
|
||||||
|
|
||||||
const executor = new Executor({ userId: 1, pipeline, onChanged, accessService: new AccessServiceTest(), storage: new FileStorage() });
|
const executor = new Executor({ userId: "test", pipeline, onChanged, accessService: new AccessServiceTest(), storage: new FileStorage() });
|
||||||
await executor.run();
|
await executor.run();
|
||||||
// expect(define.name).eq("EchoPlugin");
|
// expect(define.name).eq("EchoPlugin");
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { expect } from "chai";
|
||||||
|
import "mocha";
|
||||||
|
import { CertApplyPlugin } from "../../../src/plugin/plugins";
|
||||||
|
import { pluginInitProps } from "../init.test";
|
||||||
|
describe("CertApply", function () {
|
||||||
|
it("#execute", async function () {
|
||||||
|
this.timeout(120000);
|
||||||
|
const plugin = new CertApplyPlugin();
|
||||||
|
// @ts-ignore
|
||||||
|
delete plugin.define;
|
||||||
|
await plugin.doInit(pluginInitProps);
|
||||||
|
const output = await plugin.execute({
|
||||||
|
domains: ["*.docmirror.cn", "docmirror.cn"],
|
||||||
|
email: "xiaojunnuo@qq.com",
|
||||||
|
dnsProviderType: "aliyun",
|
||||||
|
accessId: "111",
|
||||||
|
forceUpdate: true,
|
||||||
|
});
|
||||||
|
const cert = output.cert;
|
||||||
|
expect(plugin.getDefine().name).eq("CertApply");
|
||||||
|
expect(cert.crt != null).eq(true);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { expect } from "chai";
|
||||||
|
import "mocha";
|
||||||
|
import { DeployCertToAliyunCDN } from "../../../src/plugin/plugins";
|
||||||
|
import { pluginInitProps } from "../init.test";
|
||||||
|
|
||||||
|
describe("DeployToAliyunCDN", function () {
|
||||||
|
it("#execute", async function () {
|
||||||
|
this.timeout(120000);
|
||||||
|
const plugin = new DeployCertToAliyunCDN();
|
||||||
|
// @ts-ignore
|
||||||
|
delete plugin.define;
|
||||||
|
|
||||||
|
await plugin.doInit(pluginInitProps);
|
||||||
|
|
||||||
|
const cert = await pluginInitProps.pipelineContext.get("cert");
|
||||||
|
|
||||||
|
await plugin.execute({
|
||||||
|
cert,
|
||||||
|
domainName: "certd-cdn-upload.docmirror.cn",
|
||||||
|
});
|
||||||
|
expect(plugin.getDefine().name).eq("DeployCertToAliyunCDN");
|
||||||
|
});
|
||||||
|
});
|
|
@ -1 +1 @@
|
||||||
Subproject commit ae0676e9e3e32a9359195a1f0ab4b83c941698c1
|
Subproject commit 51dd77563b83ccc541067037e1125dcd8e46f7da
|
Loading…
Reference in New Issue