Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(dashmate): cleanup zerossl certs command #2298

Merged
merged 3 commits into from
Oct 31, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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: async () => cleanupZeroSSLCertificatesTask(config),
},
],
{
renderer: isVerbose ? 'verbose' : 'default',
rendererOptions: {
showTimer: isVerbose,
clearOutput: false,
collapse: false,
showSubtasks: true,
removeEmptyLines: false,
},
},
);
shumkov marked this conversation as resolved.
Show resolved Hide resolved

try {
await tasks.run();
} catch (e) {
throw new MuteOneLineError(e);
}
}
}
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
@@ -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 {obtainZeroSSLCertificateTask}
shumkov marked this conversation as resolved.
Show resolved Hide resolved
* @param {Config} config
* @return {Promise<Listr>}
shumkov marked this conversation as resolved.
Show resolved Hide resolved
*/
async function cleanupZeroSSLCertificatesTask(config) {
return new Listr([
{
title: 'Collect drafted and pending validation certificates',
// Skips the check if force flag is set
task: async (ctx, task) => {
ctx.apiKey = config.get('platform.gateway.ssl.providerConfigs.zerossl.apiKey', true);

ctx.certificates = [];

let certificatesPerRequest = [];
let page = 1;

// Fetch all certificates in draft or pending validation status
// with pagination
do {
certificatesPerRequest = await listCertificates(ctx.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(ctx.apiKey, certificate.id);

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

if (process.env.DEBUG) {
// eslint-disable-next-line no-console
console.warn(e);
}
shumkov marked this conversation as resolved.
Show resolved Hide resolved
}

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

await wait(100);
shumkov marked this conversation as resolved.
Show resolved Hide resolved
}

if (ctx.errored > 0) {
observer.error(new Error('Some certificates were not canceled. Please try again.'));
} else {
observer.complete();
}
shumkov marked this conversation as resolved.
Show resolved Hide resolved

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

return cleanupZeroSSLCertificatesTask;
}
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
Loading