Skip to content

Commit

Permalink
use signaling for reloading config
Browse files Browse the repository at this point in the history
  • Loading branch information
TheKhanj committed Dec 28, 2023
1 parent 802aec0 commit 8047093
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 22 deletions.
4 changes: 2 additions & 2 deletions config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"ttl": {
"type": "number"
"pidFile": {
"type": "string"
},
"proxy": {
"type": "object",
Expand Down
18 changes: 17 additions & 1 deletion src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#!/usr/bin/env node
import { z } from "zod";
import minimist from "minimist";
import { kill } from "node:process";
import * as fsp from "node:fs/promises";
import { NestFactory } from "@nestjs/core";
import path, { dirname } from "node:path";
import { DynamicModule, Module } from "@nestjs/common";
Expand Down Expand Up @@ -37,6 +39,14 @@ async function runDaemon(config: string) {
await daemon.init();
}

async function sendSignal(config: string, signal: "reload") {
const c = await ConfigService.readConfig(config);

const content = await fsp.readFile(c.pidFile);
const pid = +content.toString();
kill(pid, "SIGHUP");
}

export async function main() {
const argv = minimist(process.argv, {
boolean: ["version", "v"],
Expand All @@ -46,11 +56,16 @@ export async function main() {

const schema = z.object({
config: z.string().default("moxy.json"),
signal: z.literal("reload").optional(),
});

const parsed = parseSchema(schema, {
config: argv.config ?? argv.c,
signal: argv.signal ?? argv.s,
});

if (parsed.signal) return sendSignal(parsed.config, parsed.signal);

await runDaemon(parsed.config);
}

Expand Down Expand Up @@ -81,8 +96,9 @@ function help(): never {
moxy - Distributed transparent proxy with traffic control facilities
USAGE:
[-c moxy.json]`
[-c moxy.json] [-s reload]`
);

process.exit(1);
}

Expand Down
2 changes: 1 addition & 1 deletion src/config.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ const DatabaseConfigSchema = z
.default({});

export const ConfigSchema = z.object({
ttl: z.number().default(5 * 60_000),
pidFile: z.string().default("moxy.pid"),
database: DatabaseConfigSchema,
users: z.record(UserConfigSchema),
});
35 changes: 17 additions & 18 deletions src/config.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import {
DynamicModule,
Inject,
Injectable,
Logger,
Module,
OnApplicationBootstrap,
} from "@nestjs/common";
import * as fsp from "node:fs/promises";
import * as assert from "node:assert";
import { readFile } from "node:fs";
import { promisify } from "node:util";

import { ConfigSchema } from "./config.schema";
import { EventModule, MoxyEventEmitter } from "./event";
import { UserNotFoundError } from "./errors";
import { withErrorLogging } from "./utils";

export type ExpirationDate = "unlimit" | string;

Expand Down Expand Up @@ -68,7 +69,7 @@ export type ProxyConfig = {
};

export type Config = {
ttl: number;
pidFile: string;
database: DatabaseConfig;
users: Record<string, UserConfig>;
};
Expand All @@ -82,7 +83,12 @@ export class ConfigService {
public constructor(
private readonly file: string,
private readonly eventEmitter: MoxyEventEmitter
) {}
) {
process.on("SIGHUP", () => {
this.logger.log("Reloading config");
this.reloadCache().catch((err) => this.logger.error(err));
});
}

public static async readConfig(file: string): Promise<Config> {
const content = await promisify(readFile)(file);
Expand All @@ -109,16 +115,16 @@ export class ConfigService {
public async getConfig(): Promise<Config> {
if (this.cache) return this.cache;

return this.refreshCache();
return this.reloadCache();
}

public async refreshCache(): Promise<Config> {
private async reloadCache(): Promise<Config> {
this.oldCache = this.cache;
this.cache = await ConfigService.readConfig(this.file);

await this.emitChangeEvents(this.cache, this.oldCache);

this.logger.log("Refreshed config cache");
this.logger.log("Reloaded config");

return this.cache;
}
Expand Down Expand Up @@ -167,27 +173,20 @@ export class ConfigService {
export class ConfigModule implements OnApplicationBootstrap {
private readonly logger = new Logger("ConfigModule");

public constructor(
private readonly configService: ConfigService,
@Inject("CacheTtl")
private readonly ttl: number
) {}
public constructor(private readonly configService: ConfigService) {}

onApplicationBootstrap() {
this.configService.refreshCache().catch((err) => this.logger.error(err));
withErrorLogging(async () => {
const config = await this.configService.getConfig();

setInterval(() => {
this.configService.refreshCache().catch((err) => this.logger.error(err));
}, this.ttl);
await fsp.writeFile(config.pidFile, String(process.pid));
}, this.logger);
}

public static async register(file: string): Promise<DynamicModule> {
const config = await ConfigService.readConfig(file);

return {
module: ConfigModule,
providers: [
{ provide: "CacheTtl", useValue: config.ttl },
{
provide: ConfigService,
inject: [MoxyEventEmitter],
Expand Down

0 comments on commit 8047093

Please sign in to comment.