Skip to content

Commit

Permalink
Merge pull request #61 from syfxlin/develop
Browse files Browse the repository at this point in the history
Merge develop to master
  • Loading branch information
syfxlin authored Feb 1, 2024
2 parents 6e3aedb + 80e3b1f commit c65d7f7
Show file tree
Hide file tree
Showing 12 changed files with 449 additions and 81 deletions.
63 changes: 30 additions & 33 deletions src/depker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ import { fs, path } from "./deps.ts";
import { Dax, createDax } from "./services/dax/index.ts";
import { DockerNode } from "./services/run/index.ts";
import { ProxyModule } from "./modules/proxy/proxy.module.ts";
import { MinioModule } from "./modules/minio/minio.module.ts";
import { ServiceModule } from "./modules/service/service.module.ts";
import { CliModule } from "./services/cli/index.ts";
import { CfgModule } from "./services/cfg/index.ts";
import { OpsModule } from "./services/ops/index.ts";
import { EvsModule } from "./services/evs/index.ts";
import { LogModule } from "./services/log/index.ts";
import { DepkerMaster, DepkerRunner } from "./services/run/types.ts";
import { MongoModule } from "./modules/mongo/mongo.module.ts";

export type DepkerCallback<T> = T | ((depker: DepkerApp) => T);
export type DepkerRegister<T> = (depker: DepkerApp) => T;

export interface DepkerModule {
name: string;
init?: () => Promise<void> | void;
destroy?: () => Promise<void> | void;
}
Expand All @@ -36,7 +37,7 @@ export class Depker {
// runner
private _master: DepkerMaster;
private _runner: DepkerRunner;
private _modules: Array<DepkerModule>;
private _modules: Record<string, DepkerModule>;
// service
public readonly dax: Dax;
public readonly cli: CliModule;
Expand All @@ -61,9 +62,12 @@ export class Depker {
this._master = new DockerNode(this);
this._runner = this._master;
// module
this._modules = [];
this._modules.push(new ProxyModule(this));
this._modules.push(new ServiceModule(this));
this._modules = {
proxy: new ProxyModule(this),
minio: new MinioModule(this),
mongo: new MongoModule(this),
service: new ServiceModule(this),
};
}

public static create(): DepkerApp {
Expand Down Expand Up @@ -128,8 +132,8 @@ export class Depker {
}

public master(): DepkerMaster;
public master(node: DepkerCallback<DepkerMaster>): DepkerApp;
public master(node?: DepkerCallback<DepkerMaster>): DepkerMaster | DepkerApp {
public master(node: DepkerRegister<DepkerMaster>): DepkerApp;
public master(node?: DepkerRegister<DepkerMaster>): DepkerMaster | DepkerApp {
if (node) {
this._master = typeof node === "function" ? node(this as unknown as DepkerApp) : node;
return this as unknown as DepkerApp;
Expand All @@ -139,8 +143,8 @@ export class Depker {
}

public runner(): DepkerRunner;
public runner(node: DepkerCallback<DepkerRunner>): DepkerApp;
public runner(node?: DepkerCallback<DepkerRunner>): DepkerRunner | DepkerApp {
public runner(node: DepkerRegister<DepkerRunner>): DepkerApp;
public runner(node?: DepkerRegister<DepkerRunner>): DepkerRunner | DepkerApp {
if (node) {
this._runner = typeof node === "function" ? node(this as unknown as DepkerApp) : node;
return this as unknown as DepkerApp;
Expand All @@ -149,32 +153,25 @@ export class Depker {
}
}

public module<M extends DepkerModule = DepkerModule>(name: string): M {
const module = this._modules.find(i => i.name === name);
if (!module) {
throw new Error(`Not found module ${name}`);
}
return module as M;
}

public use(module: DepkerCallback<DepkerModule>): DepkerApp {
if (typeof module === "function") {
this._modules.push(module(this as unknown as DepkerApp));
} else {
this._modules.push(module);
}
public inject(name: string, register: DepkerRegister<any>): DepkerApp {
// @ts-expect-error
this[name] = register(this);
return this as unknown as DepkerApp;
}

public inject(name: string, builder: (depker: Depker) => any): DepkerApp {
// @ts-expect-error
this[name] = builder(this);
return this as unknown as DepkerApp;
public module<M extends DepkerModule = DepkerModule>(name: string): M {
if (!this._modules[name]) {
throw new Error(`Not found module ${name}.`);
}
return this._modules[name] as M;
}

public dependency(name: string, builder: (depker: Depker) => DepkerModule): DepkerApp {
if (!this._modules.find(i => i.name === name)) {
this.use(builder(this));
public register(module: DepkerRegister<DepkerModule>): DepkerApp {
if (!module.name) {
throw new Error(`Unnamed module are not allowed to be loaded.`);
}
if (!this._modules[module.name]) {
this._modules[module.name] = module(this as unknown as DepkerApp);
}
return this as unknown as DepkerApp;
}
Expand Down Expand Up @@ -226,7 +223,7 @@ export class Depker {

private async _init_module(): Promise<void> {
await this.emit("depker:modules:before-init", this._modules);
for (const module of this._modules) {
for (const module of Object.values(this._modules)) {
await this.emit("depker:module:before-init", module);
await module?.init?.();
await this.emit("depker:module:after-init", module);
Expand All @@ -236,7 +233,7 @@ export class Depker {

private async _destroy_module(): Promise<void> {
await this.emit("depker:modules:before-destroy", this._modules);
for (const module of this._modules) {
for (const module of Object.values(this._modules)) {
await this.emit("depker:module:before-destroy", module);
await module?.destroy?.();
await this.emit("depker:module:after-destroy", module);
Expand Down
1 change: 1 addition & 0 deletions src/deps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export { fs, dax, path, yaml, hash, date, ansi, table, event, dotenv, prompt, co
export { isSubdir } from "https://deno.land/[email protected]/fs/_is_subdir.ts";
export { toPathString } from "https://deno.land/[email protected]/fs/_to_path_string.ts";
export { getFileInfoType } from "https://deno.land/[email protected]/fs/_get_file_info_type.ts";
export { cryptoRandomString } from "https://deno.land/x/[email protected]/mod.ts";

declare module "https://deno.land/x/[email protected]/mod.ts" {
// @ts-expect-error
Expand Down
191 changes: 191 additions & 0 deletions src/modules/minio/minio.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import { Depker, DepkerModule } from "../../depker.ts";
import { command, cryptoRandomString, dax } from "../../deps.ts";
import { MinioConfig, SavedMinioConfig } from "./minio.type.ts";

export class MinioModule implements DepkerModule {
public static readonly NAME = "minio";
public static readonly IMAGE = "minio/minio:latest";

constructor(private readonly depker: Depker) {}

public async init(): Promise<void> {
const minio = new command.Command().description("Manage minio");

minio
.command("reload", "Reload a new minio service")
.action(async () => {
this.depker.log.step(`Reloading minio service started.`);
try {
await this.reload();
this.depker.log.done(`Reloading minio service successfully.`);
} catch (e) {
this.depker.log.error(`Reloading minio service failed.`, e);
}
});
minio
.command("client [...args]", "Use the Minio Client to operate the minio service")
.alias("mc")
.useRawArgs()
.action(async (_options, ...args) => {
await this.exec(true, ...args)
.stdin("inherit")
.stdout("inherit")
.stderr("inherit")
.spawn();
});
minio
.command("list", "List minio buckets")
.alias("ls")
.option("--json", "Pretty-print using json")
.option("--yaml", "Pretty-print using yaml")
.action(async (options) => {
try {
const buckets = await this.list();
if (options.json) {
this.depker.log.json(buckets);
} else if (options.yaml) {
this.depker.log.yaml(buckets);
} else {
this.depker.log.table(["Bucket"], buckets.map(b => [b]));
}
} catch (e) {
this.depker.log.error(`Listing buckets failed.`, e);
}
});
minio
.command("add <bucket...:string>", "Add minio buckets")
.action(async (_options, ...buckets) => {
this.depker.log.step(`Adding buckets started.`);
try {
for (let i = 0; i < buckets.length; i++) {
const data = await this.create(buckets[i]);
if (i !== 0) {
this.depker.log.raw(`---`);
}
this.depker.log.yaml(data);
}
this.depker.log.done(`Adding buckets successfully.`);
} catch (e) {
this.depker.log.error(`Adding buckets failed.`, e);
}
});
minio
.command("del <bucket...:string>", "Remove minio buckets")
.action(async (_options, ...buckets) => {
this.depker.log.step(`Removing buckets started.`);
try {
for (const bucket of buckets) {
await this.remove(bucket);
}
this.depker.log.done(`Removing buckets successfully.`);
} catch (e) {
this.depker.log.error(`Removing buckets failed.`, e);
}
});

this.depker.cli.command("minio", minio);
}

public async list() {
const lines = await this.depker.ops.container.exec(MinioModule.NAME, [`mc`, `ls`, `minio`]).lines();
return lines.map(i => i.replace(/^.+\s+(\w+)\/$/, "$1"));
}

public async create(name: string) {
const user = `${name}`;
const pass = cryptoRandomString({ length: 16, type: "alphanumeric" });
const policy = JSON.stringify({
Version: "2012-10-17",
Statement: [
{
Effect: "Allow",
Action: [
"s3:*",
],
Resource: [
`arn:aws:s3:::${name}/*`,
],
},
],
});
const commands = [
`mc mb --ignore-existing minio/${name}`,
`echo '${policy}' > /tmp/minio-policy-${user}.json`,
`mc admin user add minio ${user} ${pass}`,
`mc admin policy create minio ${user} /tmp/minio-policy-${user}.json`,
`mc admin policy attach minio ${user} --user ${user}`,
];
await this.depker.ops.container.exec(MinioModule.NAME, [`sh`, `-c`, commands.join(" && ")]);
return { bucket: name, username: name, password: pass };
}

public async remove(name: string) {
const commands = [
`(mc admin policy detach minio ${name} --user ${name} 2>/dev/null || true)`,
`(mc admin policy rm minio ${name} 2>/dev/null || true)`,
`(mc rb --force minio/${name} 2>/dev/null || true)`,
];
await this.depker.ops.container.exec(MinioModule.NAME, [`sh`, `-c`, commands.join(" && ")]);
}

public exec(...commands: string[]): dax.CommandBuilder;
public exec(tty: boolean, ...commands: string[]): dax.CommandBuilder;
public exec(tty: string | boolean, ...commands: string[]): dax.CommandBuilder {
if (typeof tty !== "boolean") {
commands.unshift(tty);
}
return this.depker.ops.container.exec(
MinioModule.NAME,
[`mc`, ...commands],
{ Interactive: true, Tty: typeof tty === "boolean" ? tty : false },
);
}

public async reload(config?: Omit<MinioConfig, "username" | "password">) {
await this.depker.emit("minio:before-reload", this);
this.depker.log.debug(`Minio reloading started.`);

const saved: SavedMinioConfig = { ...await this.depker.cfg.config<SavedMinioConfig>(MinioModule.NAME), ...config };
if (config || !saved.username || !saved.password) {
saved.username = saved.username ?? "root";
saved.password = saved.password ?? cryptoRandomString({ length: 16, type: "alphanumeric" });
await this.depker.cfg.config(MinioModule.NAME, saved);
}

try {
await this.depker.ops.container.remove([MinioModule.NAME], { Force: true });
} catch (e) {
// ignore
}

await this.depker.ops.container.run(MinioModule.NAME, MinioModule.IMAGE, {
Detach: true,
Pull: "always",
Restart: "always",
Labels: saved.labels,
Networks: [await this.depker.ops.network.default()],
Commands: [`minio`, `server`, `--console-address`, `:9001`],
Envs: {
...saved.envs,
MINIO_VOLUMES: "/mnt/data",
MINIO_ALIAS: "minio",
MINIO_ROOT_USER: saved.username,
MINIO_ROOT_PASSWORD: saved.password,
},
Ports: [
...(saved.port ? [`${saved.port}:9000`] : []),
...(saved.ports ?? []),
],
Volumes: [
`/var/depker/minio:/mnt/data`,
...(saved.volumes ?? []),
],
});
await this.depker.ops.container.exec(MinioModule.NAME, [`mc`, `alias`, `set`, `minio`, `http://localhost:9000`, saved.username, saved.password], {
Detach: true,
});

await this.depker.emit("minio:after-reload", this);
this.depker.log.debug(`Minio reloading successfully.`);
}
}
14 changes: 14 additions & 0 deletions src/modules/minio/minio.type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export interface MinioConfig {
readonly username: string;
readonly password: string;
port?: number;
envs?: Record<string, string>;
labels?: Record<string, string>;
ports?: string[];
volumes?: string[];
}

export interface SavedMinioConfig extends MinioConfig {
username: string;
password: string;
}
Loading

0 comments on commit c65d7f7

Please sign in to comment.