This commit is contained in:
Florian Dold 2021-08-04 22:24:06 +02:00
parent 0c6bb04356
commit 04e7315d4d
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
2 changed files with 156 additions and 38 deletions

View File

@ -870,10 +870,21 @@ const deploymentCli = walletCli.subcommand("deploymentArgs", "deployment", {
help: "Subcommands for handling GNU Taler deployments.", help: "Subcommands for handling GNU Taler deployments.",
}); });
deploymentCli.subcommand("lintExchange", "lint-exchange", { deploymentCli
help: "Run checks on the exchange deployment." .subcommand("lintExchange", "lint-exchange", {
}).action(async (args) => { help: "Run checks on the exchange deployment.",
await lintExchangeDeployment(); })
.flag("cont", ["--continue"], {
help: "Continue after errors if possible",
})
.flag("debug", ["--debug"], {
help: "Output extra debug info",
})
.action(async (args) => {
await lintExchangeDeployment(
args.lintExchange.debug,
args.lintExchange.cont,
);
}); });
deploymentCli deploymentCli

View File

@ -55,13 +55,31 @@ interface ShellResult {
status: number; status: number;
} }
interface LintContext {
/**
* Be more verbose.
*/
verbose: boolean;
/**
* Always continue even after errors.
*/
cont: boolean;
cfg: Configuration;
}
/** /**
* Run a shell command, return stdout. * Run a shell command, return stdout.
*/ */
export async function sh( export async function sh(
context: LintContext,
command: string, command: string,
env: { [index: string]: string | undefined } = process.env, env: { [index: string]: string | undefined } = process.env,
): Promise<ShellResult> { ): Promise<ShellResult> {
if (context.verbose) {
console.log("executing command:", command);
}
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const stdoutChunks: Buffer[] = []; const stdoutChunks: Buffer[] = [];
const stderrChunks: Buffer[] = []; const stderrChunks: Buffer[] = [];
@ -100,12 +118,14 @@ export async function sh(
}); });
} }
function checkBasicConf(cfg: Configuration): BasicConf { function checkBasicConf(context: LintContext): BasicConf {
const cfg = context.cfg;
const currencyEntry = cfg.getString("taler", "currency"); const currencyEntry = cfg.getString("taler", "currency");
let mainCurrency: string | undefined; let mainCurrency: string | undefined;
if (!currencyEntry.value) { if (!currencyEntry.value) {
console.log("error: currency not defined in section TALER option CURRENCY"); console.log("error: currency not defined in section TALER option CURRENCY");
console.log("Aborting further checks.");
process.exit(1); process.exit(1);
} else { } else {
mainCurrency = currencyEntry.value.toUpperCase(); mainCurrency = currencyEntry.value.toUpperCase();
@ -126,7 +146,8 @@ function checkBasicConf(cfg: Configuration): BasicConf {
return { mainCurrency }; return { mainCurrency };
} }
function checkCoinConfig(cfg: Configuration, basic: BasicConf): void { function checkCoinConfig(context: LintContext, basic: BasicConf): void {
const cfg = context.cfg;
const coinPrefix1 = "COIN_"; const coinPrefix1 = "COIN_";
const coinPrefix2 = "COIN-"; const coinPrefix2 = "COIN-";
let numCoins = 0; let numCoins = 0;
@ -147,7 +168,8 @@ function checkCoinConfig(cfg: Configuration, basic: BasicConf): void {
} }
} }
async function checkWireConfig(cfg: Configuration): Promise<void> { async function checkWireConfig(context: LintContext): Promise<void> {
const cfg = context.cfg;
const accountPrefix = "EXCHANGE-ACCOUNT-"; const accountPrefix = "EXCHANGE-ACCOUNT-";
const accountCredentialsPrefix = "EXCHANGE-ACCOUNTCREDENTIALS-"; const accountCredentialsPrefix = "EXCHANGE-ACCOUNTCREDENTIALS-";
@ -174,51 +196,126 @@ async function checkWireConfig(cfg: Configuration): Promise<void> {
} }
} }
for (const acc of accounts) {
// test debit history
{
const res = await sh( const res = await sh(
"sudo -u taler-exchange-wirewatch taler-exchange-wirewatch -t", context,
"su -l --shell /bin/sh " +
"-c 'taler-exchange-wire-gateway-client -s exchange-accountcredentials-${acc} --debit-history'" +
"taler-exchange-wire",
);
if (res.status != 0) {
console.log(res.stdout);
console.log(res.stderr);
console.log(
"error: Could not run wirewatch. Please review logs above.",
);
if (!context.cont) {
console.log("Aborting further checks.");
process.exit(1);
}
}
}
}
// TWG client
{
const res = await sh(
context,
`su -l --shell /bin/sh -c 'taler-exchange-wirewatch -t' taler-exchange-wire`,
);
if (res.status != 0) {
console.log(res.stdout);
console.log(res.stderr);
console.log("error: Could not run wirewatch. Please review logs above.");
if (!context.cont) {
console.log("Aborting further checks.");
process.exit(1);
}
}
}
// Wirewatch
{
const res = await sh(
context,
`su -l --shell /bin/sh -c 'taler-exchange-wirewatch -t' taler-exchange-wire`,
);
if (res.status != 0) {
console.log(res.stdout);
console.log(res.stderr);
console.log("error: Could not run wirewatch. Please review logs above.");
if (!context.cont) {
console.log("Aborting further checks.");
process.exit(1);
}
}
}
// Closer
{
const res = await sh(
context,
`su -l --shell /bin/sh -c 'taler-exchange-closer -t' taler-exchange-closer`,
);
if (res.status != 0) {
console.log(res.stdout);
console.log(res.stderr);
console.log("error: Could not run closer. Please review logs above.");
if (!context.cont) {
console.log("Aborting further checks.");
process.exit(1);
}
}
}
}
async function checkAggregatorConfig(context: LintContext) {
const res = await sh(
context,
"su -l --shell /bin/sh -c 'taler-exchange-aggregator -t' taler-exchange-aggregator",
); );
if (res.status != 0) { if (res.status != 0) {
console.log(res.stdout); console.log(res.stdout);
console.log(res.stderr); console.log(res.stderr);
console.log("error: Could not run aggregator. Please review logs above."); console.log("error: Could not run aggregator. Please review logs above.");
if (!context.cont) {
console.log("Aborting further checks.");
process.exit(1); process.exit(1);
} }
}
// FIXME: run wirewatch in test mode here?
// FIXME: run transfer in test mode here?
} }
async function checkAggregatorConfig(cfg: Configuration) { async function checkCloserConfig(context: LintContext) {
const res = await sh( const res = await sh(
"sudo -u taler-exchange-aggregator taler-exchange-aggregator -t", context,
`su -l --shell /bin/sh -c 'taler-exchange-closer -t' taler-exchange-closer`,
); );
if (res.status != 0) { if (res.status != 0) {
console.log(res.stdout); console.log(res.stdout);
console.log(res.stderr); console.log(res.stderr);
console.log("error: Could not run aggregator. Please review logs above."); console.log("error: Could not run closer. Please review logs above.");
if (!context.cont) {
console.log("Aborting further checks.");
process.exit(1); process.exit(1);
} }
} }
async function checkCloserConfig(cfg: Configuration) {
const res = await sh("sudo -u taler-exchange-close taler-exchange-closer -t");
if (res.status != 0) {
console.log(res.stdout);
console.log(res.stderr);
console.log("error: Could not run aggregator. Please review logs above.");
process.exit(1);
}
} }
function checkMasterPublicKeyConfig(cfg: Configuration): PubkeyConf { function checkMasterPublicKeyConfig(context: LintContext): PubkeyConf {
const cfg = context.cfg;
const pub = cfg.getString("exchange", "master_public_key"); const pub = cfg.getString("exchange", "master_public_key");
const pubDecoded = decodeCrock(pub.required()); const pubDecoded = decodeCrock(pub.required());
if (pubDecoded.length != 32) { if (pubDecoded.length != 32) {
console.log("error: invalid master public key"); console.log("error: invalid master public key");
if (!context.cont) {
console.log("Aborting further checks.");
process.exit(1); process.exit(1);
} }
}
return { return {
masterPublicKey: pub.required(), masterPublicKey: pub.required(),
@ -226,9 +323,10 @@ function checkMasterPublicKeyConfig(cfg: Configuration): PubkeyConf {
} }
export async function checkExchangeHttpd( export async function checkExchangeHttpd(
cfg: Configuration, context: LintContext,
pubConf: PubkeyConf, pubConf: PubkeyConf,
): Promise<void> { ): Promise<void> {
const cfg = context.cfg;
const baseUrlEntry = cfg.getString("exchange", "base_url"); const baseUrlEntry = cfg.getString("exchange", "base_url");
if (!baseUrlEntry.isDefined) { if (!baseUrlEntry.isDefined) {
@ -312,7 +410,10 @@ export async function checkExchangeHttpd(
/** /**
* Do some basic checks in the configuration of a Taler deployment. * Do some basic checks in the configuration of a Taler deployment.
*/ */
export async function lintExchangeDeployment(): Promise<void> { export async function lintExchangeDeployment(
verbose: boolean,
cont: boolean,
): Promise<void> {
if (process.getuid() != 0) { if (process.getuid() != 0) {
console.log( console.log(
"warning: the exchange deployment linter is designed to be run as root", "warning: the exchange deployment linter is designed to be run as root",
@ -321,17 +422,23 @@ export async function lintExchangeDeployment(): Promise<void> {
const cfg = Configuration.load(); const cfg = Configuration.load();
const basic = checkBasicConf(cfg); const context: LintContext = {
cont,
verbose,
cfg,
};
checkCoinConfig(cfg, basic); const basic = checkBasicConf(context);
await checkWireConfig(cfg); checkCoinConfig(context, basic);
await checkAggregatorConfig(cfg); await checkWireConfig(context);
await checkCloserConfig(cfg); await checkAggregatorConfig(context);
const pubConf = checkMasterPublicKeyConfig(cfg); await checkCloserConfig(context);
await checkExchangeHttpd(cfg, pubConf); const pubConf = checkMasterPublicKeyConfig(context);
await checkExchangeHttpd(context, pubConf);
} }