Skip to content

Commit

Permalink
fix(dashmate): cleanup zerossl certs command (#2298)
Browse files Browse the repository at this point in the history
  • Loading branch information
shumkov authored Oct 31, 2024
1 parent 453b30c commit d1ba61d
Show file tree
Hide file tree
Showing 8 changed files with 174 additions and 7 deletions.
58 changes: 58 additions & 0 deletions packages/dashmate/src/commands/ssl/cleanup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { Listr } from 'listr2';
import { Flags } from '@oclif/core';
import ConfigBaseCommand from '../../oclif/command/ConfigBaseCommand.js';
import MuteOneLineError from '../../oclif/errors/MuteOneLineError.js';

export default class CleanupCommand extends ConfigBaseCommand {
static description = `Cleanup Zero SSL certificate
Cancel all drafted or pending validation certificates on ZeroSSL
`;

static flags = {
...ConfigBaseCommand.flags,
verbose: Flags.boolean({ char: 'v', description: 'use verbose mode for output', default: false }),
};

/**
* @param {Object} args
* @param {Object} flags
* @param {boolean} flags.verbose
* @param {Config} config
* @param {cleanupZeroSSLCertificatesTask} cleanupZeroSSLCertificatesTask
* @return {Promise<void>}
*/
async runWithDependencies(
args,
{
verbose: isVerbose,
},
config,
cleanupZeroSSLCertificatesTask,
) {
const tasks = new Listr(
[
{
title: 'Cleanup ZeroSSL certificate',
task: () => cleanupZeroSSLCertificatesTask(config),
},
],
{
renderer: isVerbose ? 'verbose' : 'default',
rendererOptions: {
showTimer: isVerbose,
clearOutput: false,
collapse: false,
showSubtasks: true,
removeEmptyLines: false,
},
},
);

try {
await tasks.run();
} catch (e) {
throw new MuteOneLineError(e);
}
}
}
2 changes: 1 addition & 1 deletion packages/dashmate/src/commands/ssl/obtain.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Certificate will be renewed if it is about to expire (see 'expiration-days' flag
[
{
title: 'Obtain ZeroSSL certificate',
task: async () => obtainZeroSSLCertificateTask(config),
task: () => obtainZeroSSLCertificateTask(config),
},
],
{
Expand Down
5 changes: 5 additions & 0 deletions packages/dashmate/src/createDIContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import analyseSystemResourcesFactory from './doctor/analyse/analyseSystemResourc
import analyseSamplesFactory from './doctor/analyseSamplesFactory.js';
import archiveSamples from './doctor/archiveSamples.js';
import unarchiveSamplesFactory from './doctor/unarchiveSamplesFactory.js';
import cleanupZeroSSLCertificatesTaskFactory
from './listr/tasks/ssl/zerossl/cleanupZeroSSLCertificatesTaskFactory.js';
import cancelCertificate from './ssl/zerossl/cancelCertificate.js';

import renderTemplateFactory from './templates/renderTemplateFactory.js';
import renderServiceTemplatesFactory from './templates/renderServiceTemplatesFactory.js';
Expand Down Expand Up @@ -206,6 +209,7 @@ export default async function createDIContainer(options = {}) {
downloadCertificate: asValue(downloadCertificate),
getCertificate: asValue(getCertificate),
listCertificates: asValue(listCertificates),
cancelCertificate: asValue(cancelCertificate),
createSelfSignedCertificate: asValue(createSelfSignedCertificate),
verificationServer: asClass(VerificationServer).singleton(),
});
Expand Down Expand Up @@ -299,6 +303,7 @@ export default async function createDIContainer(options = {}) {
enableCoreQuorumsTask: asFunction(enableCoreQuorumsTaskFactory).singleton(),
registerMasternodeGuideTask: asFunction(registerMasternodeGuideTaskFactory).singleton(),
obtainZeroSSLCertificateTask: asFunction(obtainZeroSSLCertificateTaskFactory).singleton(),
cleanupZeroSSLCertificatesTask: asFunction(cleanupZeroSSLCertificatesTaskFactory).singleton(),
obtainSelfSignedCertificateTask: asFunction(obtainSelfSignedCertificateTaskFactory).singleton(),
saveCertificateTask: asFunction(saveCertificateTaskFactory),
reindexNodeTask: asFunction(reindexNodeTaskFactory).singleton(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ export default function scheduleRenewZeroSslCertificateFactory(
}

const job = new CronJob(expiresAt, async () => {
const tasks = await obtainZeroSSLCertificateTask(config);
const tasks = obtainZeroSSLCertificateTask(config);

await tasks.run({
expirationDays: Certificate.EXPIRATION_LIMIT_DAYS,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import chalk from 'chalk';
import { Listr } from 'listr2';
import { Observable } from 'rxjs';
import wait from '../../../../util/wait.js';

/**
* @param {listCertificates} listCertificates
* @param {cancelCertificate} cancelCertificate
* @return {cleanupZeroSSLCertificatesTask}
*/
export default function cleanupZeroSSLCertificatesTaskFactory(
listCertificates,
cancelCertificate,
) {
/**
* @typedef {cleanupZeroSSLCertificatesTask}
* @param {Config} config
* @return {Listr}
*/
function cleanupZeroSSLCertificatesTask(config) {
const apiKey = config.get('platform.gateway.ssl.providerConfigs.zerossl.apiKey', true);

return new Listr([
{
title: 'Collect drafted and pending validation certificates',
// Skips the check if force flag is set
task: async (ctx, task) => {
ctx.certificates = [];

let certificatesPerRequest = [];
let page = 1;

// Fetch all certificates in draft or pending validation status
// with pagination
do {
certificatesPerRequest = await listCertificates(apiKey, ['draft', 'pending_validation'], page);

ctx.certificates = ctx.certificates.concat(certificatesPerRequest);

page += 1;

// eslint-disable-next-line no-param-reassign
task.output = `Found ${ctx.certificates.length} certificates`;
} while (certificatesPerRequest.length === 1000);

ctx.total = ctx.certificates.length;
},
},
{
title: 'Cancel certificates',
skip: (ctx) => ctx.certificates.length === 0,
task: async (ctx, task) => {
// eslint-disable-next-line no-param-reassign
task.title = `Cancel ${ctx.certificates.length} certificates`;
ctx.canceled = 0;
ctx.errored = 0;
return new Observable(async (observer) => {
for (const certificate of ctx.certificates) {
try {
await cancelCertificate(apiKey, certificate.id);

ctx.canceled += 1;
} catch (e) {
ctx.errored += 1;

if (process.env.DEBUG) {
// eslint-disable-next-line no-console
console.warn(e);
}
}

observer.next(chalk`{green ${ctx.canceled}} / {red ${ctx.errored}} / ${ctx.total}`);

await wait(100);
}

if (ctx.errored > 0) {
observer.error(new Error('Some certificates were not canceled. Please try again.'));
} else {
observer.complete();
}

return this;
});
},
options: {
persistentOutput: true,
},
},
], {
rendererOptions: {
showErrorMessage: true,
},
});
}

return cleanupZeroSSLCertificatesTask;
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ export default function obtainZeroSSLCertificateTaskFactory(
/**
* @typedef {obtainZeroSSLCertificateTask}
* @param {Config} config
* @return {Promise<Listr>}
* @return {Listr}
*/
async function obtainZeroSSLCertificateTask(config) {
function obtainZeroSSLCertificateTask(config) {
return new Listr([
{
title: 'Check if certificate already exists and not expiring soon',
Expand Down
2 changes: 1 addition & 1 deletion packages/dashmate/src/ssl/zerossl/cancelCertificate.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import requestApi from './requestApi';
import requestApi from './requestApi.js';

/**
* Get ZeroSSL certificate
Expand Down
10 changes: 8 additions & 2 deletions packages/dashmate/src/ssl/zerossl/listCertificates.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,18 @@ import Certificate from './Certificate.js';
* @param {string} apiKey
* @param {String[]} [statuses] - possible values: draft, pending_validation, issued, cancelled,
* revoked, expired.
* @param {number} [page]
* @param {string} [search]
* @return {Promise<Certificate[]>}
*/

export default async function listCertificates(apiKey, statuses = [], search = undefined) {
let url = `https://api.zerossl.com/certificates?access_key=${apiKey}&limit=1000`;
export default async function listCertificates(
apiKey,
statuses = [],
page = 1,
search = undefined,
) {
let url = `https://api.zerossl.com/certificates?access_key=${apiKey}&limit=1000&page=${page}`;

if (statuses.length > 0) {
url += `&statuses=${statuses.join(',')}`;
Expand Down

0 comments on commit d1ba61d

Please sign in to comment.