From 9c3c8cc9d180f3317217fb176b93a39a2b134a8c Mon Sep 17 00:00:00 2001 From: Girish Ramakrishnan Date: Fri, 27 Mar 2026 11:39:38 +0100 Subject: [PATCH] rename promise-retry to retry --- src/acme2.js | 12 ++++++------ src/appstore.js | 4 ++-- src/apptask.js | 4 ++-- src/backupformat/rsync.js | 6 +++--- src/backupformat/tgz.js | 6 +++--- src/community.js | 4 ++-- src/dns.js | 6 +++--- src/dns/hetznercloud.js | 4 ++-- src/dns/waitfordns.js | 4 ++-- src/docker.js | 6 +++--- src/locks.js | 10 +++++----- src/{promise-retry.js => retry.js} | 4 ++-- src/services.js | 4 ++-- src/test/{promise-retry-test.js => retry-test.js} | 12 ++++++------ src/updater.js | 4 ++-- 15 files changed, 45 insertions(+), 45 deletions(-) rename src/{promise-retry.js => retry.js} (90%) rename src/test/{promise-retry-test.js => retry-test.js} (63%) diff --git a/src/acme2.js b/src/acme2.js index d55e10279..8b2d3c28e 100644 --- a/src/acme2.js +++ b/src/acme2.js @@ -7,7 +7,7 @@ import dns from './dns.js'; import openssl from './openssl.js'; import path from 'node:path'; import paths from './paths.js'; -import promiseRetry from './promise-retry.js'; +import retry from './retry.js'; import safe from 'safetydance'; import superagent from '@cloudron/superagent'; import users from './users.js'; @@ -244,7 +244,7 @@ Acme2.prototype.waitForOrder = async function (orderUrl) { log(`waitForOrder: ${orderUrl}`); - return await promiseRetry({ times: 15, interval: 20000, log }, async () => { + return await retry({ times: 15, interval: 20000, log }, async () => { log('waitForOrder: getting status'); const result = await this.postAsGet(orderUrl); @@ -297,7 +297,7 @@ Acme2.prototype.waitForChallenge = async function (challenge) { log(`waitingForChallenge: ${JSON.stringify(challenge)}`); - await promiseRetry({ times: 15, interval: 20000, log }, async () => { + await retry({ times: 15, interval: 20000, log }, async () => { log('waitingForChallenge: getting status'); const result = await this.postAsGet(challenge.url); @@ -335,7 +335,7 @@ Acme2.prototype.signCertificate = async function (finalizationUrl, csrPem) { Acme2.prototype.downloadCertificate = async function (certUrl) { assert.strictEqual(typeof certUrl, 'string'); - return await promiseRetry({ times: 5, interval: 20000, log }, async () => { + return await retry({ times: 5, interval: 20000, log }, async () => { log(`downloadCertificate: downloading certificate of ${this.cn}`); const result = await this.postAsGet(certUrl); @@ -485,7 +485,7 @@ Acme2.prototype.acmeFlow = async function () { }; Acme2.prototype.loadDirectory = async function () { - await promiseRetry({ times: 3, interval: 20000, log }, async () => { + await retry({ times: 3, interval: 20000, log }, async () => { const response = await superagent.get(this.caDirectory).timeout(30000).ok(() => true); if (response.status !== 200) throw new BoxError(BoxError.ACME_ERROR, `Invalid response code when fetching directory : ${response.status}`); @@ -522,7 +522,7 @@ async function getCertificate(fqdn, domainObject, key) { const owner = await users.getOwner(); const email = owner?.email || 'webmaster@cloudron.io'; // can error if not activated yet - return await promiseRetry({ times: 3, interval: 0, log }, async function () { + return await retry({ times: 3, interval: 0, log }, async function () { log(`getCertificate: for fqdn ${fqdn} and domain ${domainObject.domain}`); const acme = new Acme2(fqdn, domainObject, email, key, { /* profile: 'shortlived' */ }); diff --git a/src/appstore.js b/src/appstore.js index ed2b90126..7d5a70813 100644 --- a/src/appstore.js +++ b/src/appstore.js @@ -14,7 +14,7 @@ import mail from './mail.js'; import manifestFormat from '@cloudron/manifest-format'; import oidcClients from './oidcclients.js'; import paths from './paths.js'; -import promiseRetry from './promise-retry.js'; +import retry from './retry.js'; import safe from 'safetydance'; import semver from 'semver'; import settings from './settings.js'; @@ -388,7 +388,7 @@ async function getApp(appId) { async function downloadIcon(appStoreId, version) { const iconUrl = `${await getApiServerOrigin()}/api/v1/apps/${appStoreId}/versions/${version}/icon`; - return await promiseRetry({ times: 10, interval: 5000, log }, async function () { + return await retry({ times: 10, interval: 5000, log }, async function () { const [networkError, response] = await safe(superagent.get(iconUrl) .timeout(60 * 1000) .ok(() => true)); diff --git a/src/apptask.js b/src/apptask.js index 318c6a7b0..110e5a44b 100644 --- a/src/apptask.js +++ b/src/apptask.js @@ -21,7 +21,7 @@ import manifestFormat from '@cloudron/manifest-format'; import os from 'node:os'; import path from 'node:path'; import paths from './paths.js'; -import promiseRetry from './promise-retry.js'; +import retry from './retry.js'; import reverseProxy from './reverseproxy.js'; import safe from 'safetydance'; import services from './services.js'; @@ -63,7 +63,7 @@ async function allocateContainerIp(app) { if (app.manifest.id === constants.PROXY_APP_APPSTORE_ID) return; - await promiseRetry({ times: 10, interval: 0, log }, async function () { + await retry({ times: 10, interval: 0, log }, async function () { const iprange = iputils.intFromIp(constants.APPS_IPv4_END) - iputils.intFromIp(constants.APPS_IPv4_START); const rnd = Math.floor(Math.random() * iprange); const containerIp = iputils.ipFromInt(iputils.intFromIp(constants.APPS_IPv4_START) + rnd); diff --git a/src/backupformat/rsync.js b/src/backupformat/rsync.js index c7c3d611e..08f7f1bc9 100644 --- a/src/backupformat/rsync.js +++ b/src/backupformat/rsync.js @@ -12,7 +12,7 @@ import path from 'node:path'; import paths from '../paths.js'; import { pipeline } from 'node:stream/promises'; import ProgressStream from '../progress-stream.js'; -import promiseRetry from '../promise-retry.js'; +import retry from '../retry.js'; import safe from 'safetydance'; import shellModule from '../shell.js'; import syncer from '../syncer.js'; @@ -193,7 +193,7 @@ async function sync(backupSite, remotePath, dataLayout, progressCallback) { } else if (change.operation === 'remove') { await backupSites.storageApi(backupSite).remove(backupSite.config, fullPath); } else if (change.operation === 'add') { - await promiseRetry({ times: 5, interval: 20000, log }, async (retryCount) => { + await retry({ times: 5, interval: 20000, log }, async (retryCount) => { reportUploadProgress(`Current: ${change.path}` + (retryCount > 1 ? ` (Try ${retryCount})` : '')); if (retryCount > 1) log(`sync: retrying ${change.path} position ${change.position} try ${retryCount}`); const uploader = await backupSites.storageApi(backupSite).upload(backupSite.config, backupSite.limits, fullPath); @@ -259,7 +259,7 @@ async function downloadDir(backupSite, remotePath, dataLayout, progressCallback) const [mkdirError] = await safe(fs.promises.mkdir(path.dirname(destFilePath), { recursive: true })); if (mkdirError) throw new BoxError(BoxError.FS_ERROR, mkdirError.message); - await promiseRetry({ times: 3, interval: 20000 }, async function () { + await retry({ times: 3, interval: 20000 }, async function () { reportProgress(`Current: ${relativePath}`); const [downloadError, sourceStream] = await safe(backupSites.storageApi(backupSite).download(backupSite.config, entry.path)); diff --git a/src/backupformat/tgz.js b/src/backupformat/tgz.js index 62e3619b1..4fdbda1a1 100644 --- a/src/backupformat/tgz.js +++ b/src/backupformat/tgz.js @@ -9,7 +9,7 @@ import fs from 'node:fs'; import HashStream from '../hash-stream.js'; import path from 'node:path'; import ProgressStream from '../progress-stream.js'; -import promiseRetry from '../promise-retry.js'; +import retry from '../retry.js'; import safe from 'safetydance'; import stream from 'stream/promises'; import { Transform } from 'node:stream'; @@ -253,7 +253,7 @@ async function download(backupSite, remotePath, dataLayout, progressCallback) { log(`download: Downloading ${remotePath} to ${dataLayout.toString()}`); - await promiseRetry({ times: 3, interval: 20000, log }, async () => { + await retry({ times: 3, interval: 20000, log }, async () => { progressCallback({ message: `Downloading backup ${remotePath}` }); const sourceStream = await backupSites.storageApi(backupSite).download(backupSite.config, remotePath); @@ -269,7 +269,7 @@ async function upload(backupSite, remotePath, dataLayout, progressCallback) { log(`upload: uploading to site ${backupSite.id} path ${remotePath} (encrypted: ${!!backupSite.encryption}) dataLayout ${dataLayout.toString()}`); - return await promiseRetry({ times: 5, interval: 20000, log }, async () => { + return await retry({ times: 5, interval: 20000, log }, async () => { progressCallback({ message: `Uploading backup ${remotePath}` }); const uploader = await backupSites.storageApi(backupSite).upload(backupSite.config, backupSite.limits, remotePath); diff --git a/src/community.js b/src/community.js index cfd8a59ce..a6455f6ab 100644 --- a/src/community.js +++ b/src/community.js @@ -2,7 +2,7 @@ import assert from 'node:assert'; import BoxError from './boxerror.js'; import logger from './logger.js'; import manifestFormat from '@cloudron/manifest-format'; -import promiseRetry from './promise-retry.js'; +import retry from './retry.js'; import safe from 'safetydance'; import superagent from '@cloudron/superagent'; @@ -170,7 +170,7 @@ async function getAppUpdate(app, options) { } async function downloadIcon(manifest) { - return await promiseRetry({ times: 10, interval: 5000, log }, async function () { + return await retry({ times: 10, interval: 5000, log }, async function () { const [networkError, response] = await safe(superagent.get(manifest.iconUrl) .timeout(60 * 1000) .ok(() => true)); diff --git a/src/dns.js b/src/dns.js index 23900a185..e5b7ccfb6 100644 --- a/src/dns.js +++ b/src/dns.js @@ -9,7 +9,7 @@ import ipaddr from './ipaddr.js'; import mail from './mail.js'; import mailServer from './mailserver.js'; import network from './network.js'; -import promiseRetry from './promise-retry.js'; +import retry from './retry.js'; import safe from 'safetydance'; import tasks from './tasks.js'; import tld from 'tldjs'; @@ -250,7 +250,7 @@ async function registerLocations(locations, options, progressCallback) { for (const location of locations) { progressCallback({ message: `Registering location ${fqdn(location.subdomain, location.domain)}` }); - await promiseRetry({ times: 200, interval: 5000, log, retry: (error) => error.retryable }, async function () { + await retry({ times: 200, interval: 5000, log, retry: (error) => error.retryable }, async function () { // cname records cannot co-exist with other records const [getError, values] = await safe(getDnsRecords(location.subdomain, location.domain, 'CNAME')); if (!getError && values.length === 1) { @@ -300,7 +300,7 @@ async function unregisterLocations(locations, progressCallback) { for (const location of locations) { progressCallback({ message: `Unregistering location: ${location.subdomain ? (location.subdomain + '.') : ''}${location.domain}` }); - await promiseRetry({ times: 30, interval: 5000, log, retry: (error) => error.retryable }, async function () { + await retry({ times: 30, interval: 5000, log, retry: (error) => error.retryable }, async function () { if (ipv4) await unregisterLocation(location, 'A', ipv4); if (ipv6) await unregisterLocation(location, 'AAAA', ipv6); }); diff --git a/src/dns/hetznercloud.js b/src/dns/hetznercloud.js index 46d4845df..f93962e1c 100644 --- a/src/dns/hetznercloud.js +++ b/src/dns/hetznercloud.js @@ -4,7 +4,7 @@ import constants from '../constants.js'; import logger from '../logger.js'; import dig from '../dig.js'; import dns from '../dns.js'; -import promiseRetry from '../promise-retry.js'; +import retry from '../retry.js'; import safe from 'safetydance'; import superagent from '@cloudron/superagent'; import waitForDns from './waitfordns.js'; @@ -88,7 +88,7 @@ async function waitForAction(domainConfig, id) { assert.strictEqual(typeof domainConfig, 'object'); assert.strictEqual(typeof id, 'number'); - await promiseRetry({ times: 100, interval: 1000, log }, async () => { + await retry({ times: 100, interval: 1000, log }, async () => { const [error, response] = await safe(superagent.get(`${ENDPOINT}/actions/${id}`) .set('Authorization', `Bearer ${domainConfig.token}`) .timeout(30 * 1000) diff --git a/src/dns/waitfordns.js b/src/dns/waitfordns.js index 56d63fd77..8db78932c 100644 --- a/src/dns/waitfordns.js +++ b/src/dns/waitfordns.js @@ -3,7 +3,7 @@ import BoxError from '../boxerror.js'; import logger from '../logger.js'; import dig from '../dig.js'; import dns from 'node:dns'; -import promiseRetry from '../promise-retry.js'; +import retry from '../retry.js'; import safe from 'safetydance'; import _ from '../underscore.js'; @@ -89,7 +89,7 @@ async function waitForDns(hostname, zoneName, type, value, options) { log(`waitForDns: waiting for ${hostname} to be ${value} in zone ${zoneName}`); - await promiseRetry(Object.assign({ log }, options), async function () { + await retry(Object.assign({ log }, options), async function () { const nameservers = await dig.resolve(zoneName, 'NS', { timeout: 5000 }); if (!nameservers) throw new BoxError(BoxError.EXTERNAL_ERROR, 'Unable to get nameservers'); log(`waitForDns: nameservers are ${JSON.stringify(nameservers)}`); diff --git a/src/docker.js b/src/docker.js index 345bbfa9e..d382cbe0a 100644 --- a/src/docker.js +++ b/src/docker.js @@ -10,7 +10,7 @@ import fs from 'node:fs'; import mailServer from './mailserver.js'; import os from 'node:os'; import paths from './paths.js'; -import promiseRetry from './promise-retry.js'; +import retry from './retry.js'; import services from './services.js'; import shellModule from './shell.js'; import safe from 'safetydance'; @@ -459,7 +459,7 @@ async function downloadImage(manifest) { const parsedManifestRef = parseImageRef(manifest.dockerImage); - await promiseRetry({ times: 10, interval: 5000, log, retry: (pullError) => pullError.reason !== BoxError.FS_ERROR }, async () => { + await retry({ times: 10, interval: 5000, log, retry: (pullError) => pullError.reason !== BoxError.FS_ERROR }, async () => { // custom (non appstore) image if (parsedManifestRef.registry !== null || !parsedManifestRef.fullRepositoryName.startsWith('cloudron/')) return await pullImage(manifest.dockerImage); @@ -763,7 +763,7 @@ async function createSubcontainer(app, name, cmd, options) { const mergedOptions = Object.assign({}, containerOptions, options); // attempt a few times in case ephemeral ports got allocated during container recreation - const [createError, container] = await safe(promiseRetry({ times: 3, interval: 30000, log, retry: (err) => err.statusCode === 500 && /address already in use/.test(err.message) }, async function () { + const [createError, container] = await safe(retry({ times: 3, interval: 30000, log, retry: (err) => err.statusCode === 500 && /address already in use/.test(err.message) }, async function () { return await gConnection.createContainer(mergedOptions); })); if (createError && createError.statusCode === 409) throw new BoxError(BoxError.ALREADY_EXISTS, createError); diff --git a/src/locks.js b/src/locks.js index 3defdb6a2..5283f3b80 100644 --- a/src/locks.js +++ b/src/locks.js @@ -2,7 +2,7 @@ import assert from 'node:assert'; import BoxError from './boxerror.js'; import database from './database.js'; import logger from './logger.js'; -import promiseRetry from './promise-retry.js'; +import retry from './retry.js'; const { log } = logger('locks'); @@ -59,7 +59,7 @@ function canAcquire(data, type) { async function acquire(type) { assert.strictEqual(typeof type, 'string'); - await promiseRetry({ times: Number.MAX_SAFE_INTEGER, interval: 100, log, retry: (error) => error.reason === BoxError.CONFLICT }, async () => { + await retry({ times: Number.MAX_SAFE_INTEGER, interval: 100, log, retry: (error) => error.reason === BoxError.CONFLICT }, async () => { const { version, data } = await read(); const error = canAcquire(data, type); if (error) throw error; @@ -72,13 +72,13 @@ async function acquire(type) { async function wait(type) { assert.strictEqual(typeof type, 'string'); - await promiseRetry({ times: Number.MAX_SAFE_INTEGER, interval: 10000, log }, async () => await acquire(type)); + await retry({ times: Number.MAX_SAFE_INTEGER, interval: 10000, log }, async () => await acquire(type)); } async function release(type) { assert.strictEqual(typeof type, 'string'); - await promiseRetry({ times: Number.MAX_SAFE_INTEGER, interval: 100, log, retry: (error) => error.reason === BoxError.CONFLICT }, async () => { + await retry({ times: Number.MAX_SAFE_INTEGER, interval: 100, log, retry: (error) => error.reason === BoxError.CONFLICT }, async () => { const { version, data } = await read(); if (!(type in data)) throw new BoxError(BoxError.BAD_STATE, `Lock ${type} was never acquired`); if (data[type] !== gTaskId) throw new BoxError(BoxError.BAD_STATE, `Task ${gTaskId} attempted to release lock ${type} acquired by ${data[type]}`); @@ -98,7 +98,7 @@ async function releaseAll() { async function releaseByTaskId(taskId) { assert.strictEqual(typeof taskId, 'string'); - await promiseRetry({ times: Number.MAX_SAFE_INTEGER, interval: 100, log, retry: (error) => error.reason === BoxError.CONFLICT }, async () => { + await retry({ times: Number.MAX_SAFE_INTEGER, interval: 100, log, retry: (error) => error.reason === BoxError.CONFLICT }, async () => { const { version, data } = await read(); for (const type of Object.keys(data)) { diff --git a/src/promise-retry.js b/src/retry.js similarity index 90% rename from src/promise-retry.js rename to src/retry.js index e2825f217..1ae2d68f1 100644 --- a/src/promise-retry.js +++ b/src/retry.js @@ -2,7 +2,7 @@ import assert from 'node:assert'; import timers from 'timers/promises'; import util from 'node:util'; -async function promiseRetry(options, asyncFunction) { +async function retry(options, asyncFunction) { assert.strictEqual(typeof options, 'object'); assert(util.types.isAsyncFunction(asyncFunction)); @@ -20,4 +20,4 @@ async function promiseRetry(options, asyncFunction) { } } -export default promiseRetry; +export default retry; diff --git a/src/services.js b/src/services.js index 3e2bf2b59..16c25f0d9 100644 --- a/src/services.js +++ b/src/services.js @@ -23,7 +23,7 @@ import os from 'node:os'; import path from 'node:path'; import paths from './paths.js'; import { pipeFileToRequest, pipeRequestToFile } from '@cloudron/pipework'; -import promiseRetry from './promise-retry.js'; +import retry from './retry.js'; import safe from 'safetydance'; import semver from 'semver'; import settings from './settings.js'; @@ -194,7 +194,7 @@ async function waitForContainer(containerName, tokenEnvName) { const result = await getContainerDetails(containerName, tokenEnvName); - await promiseRetry({ times: 20, interval: 15000, log }, async () => { + await retry({ times: 20, interval: 15000, log }, async () => { const [networkError, response] = await safe(superagent.get(`http://${result.ip}:3000/healthcheck?access_token=${result.token}`) .timeout(20000) .ok(() => true)); diff --git a/src/test/promise-retry-test.js b/src/test/retry-test.js similarity index 63% rename from src/test/promise-retry-test.js rename to src/test/retry-test.js index 111ef2c4d..4ea548ea0 100644 --- a/src/test/promise-retry-test.js +++ b/src/test/retry-test.js @@ -1,12 +1,12 @@ import { describe, it } from 'mocha'; import assert from 'node:assert/strict'; -import promiseRetry from '../promise-retry.js'; +import retry from '../retry.js'; import safe from 'safetydance'; -describe('promiseRetry', function () { +describe('retry', function () { it('normal return', async function () { - const result = await promiseRetry({ times: 5, interval: 1000 }, async () => { + const result = await retry({ times: 5, interval: 1000 }, async () => { return 42; }); @@ -14,7 +14,7 @@ describe('promiseRetry', function () { }); it('throws error', async function () { - const [error] = await safe(promiseRetry({ times: 5, interval: 1000 }, async () => { + const [error] = await safe(retry({ times: 5, interval: 1000 }, async () => { throw new Error('42'); })); @@ -23,7 +23,7 @@ describe('promiseRetry', function () { it('3 tries', async function () { let tryCount = 0; - const result = await promiseRetry({ times: 5, interval: 1000 }, async () => { + const result = await retry({ times: 5, interval: 1000 }, async () => { if (++tryCount == 3) return 42; else throw new Error('42'); }); @@ -32,7 +32,7 @@ describe('promiseRetry', function () { it('can abort with 1 try only', async function () { let tryCount = 0; - const [error] = await safe(promiseRetry({ times: 5, interval: 1000, retry: () => false }, async () => { + const [error] = await safe(retry({ times: 5, interval: 1000, retry: () => false }, async () => { ++tryCount; throw tryCount; })); diff --git a/src/updater.js b/src/updater.js index fd3f1c90a..55bcaa668 100644 --- a/src/updater.js +++ b/src/updater.js @@ -19,7 +19,7 @@ import notifications from './notifications.js'; import os from 'node:os'; import path from 'node:path'; import paths from './paths.js'; -import promiseRetry from './promise-retry.js'; +import retry from './retry.js'; import safe from 'safetydance'; import semver from 'semver'; import settings from './settings.js'; @@ -72,7 +72,7 @@ async function downloadBoxUrl(url, file) { safe.fs.unlinkSync(file); - await promiseRetry({ times: 10, interval: 5000, log }, async function () { + await retry({ times: 10, interval: 5000, log }, async function () { log(`downloadBoxUrl: downloading ${url} to ${file}`); const [error] = await safe(shell.spawn('curl', ['-s', '--fail', url, '-o', file], { encoding: 'utf8' })); if (error) throw new BoxError(BoxError.NETWORK_ERROR, `Failed to download ${url}: ${error.message}`);