mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2024-12-19 05:37:53 +00:00
Merge pull request #2210 from balena-os/native-promises
Convert multiple bluebird uses to native promises
This commit is contained in:
commit
3c84c1e3aa
@ -28,6 +28,7 @@ import * as logger from '../logger';
|
||||
|
||||
import * as apiHelper from '../lib/api-helper';
|
||||
import { startReporting, stateReportErrors } from './report';
|
||||
import { setTimeout } from 'timers/promises';
|
||||
|
||||
interface DevicePinInfo {
|
||||
app: number;
|
||||
@ -415,7 +416,7 @@ async function reportInitialConfig(
|
||||
await reportInitialEnv(apiEndpoint, deviceId, initialName);
|
||||
} catch (err) {
|
||||
log.error('Error reporting initial configuration, will retry', err);
|
||||
await Bluebird.delay(retryDelay);
|
||||
await setTimeout(retryDelay);
|
||||
await reportInitialConfig(apiEndpoint, deviceId, retryDelay, initialName);
|
||||
}
|
||||
}
|
||||
@ -454,7 +455,7 @@ async function provisionOrRetry(retryDelay: number): Promise<void> {
|
||||
error: e,
|
||||
delay: retryDelay,
|
||||
});
|
||||
await Bluebird.delay(retryDelay);
|
||||
await setTimeout(retryDelay);
|
||||
return provisionOrRetry(retryDelay);
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import * as url from 'url';
|
||||
import * as _ from 'lodash';
|
||||
import { delay } from 'bluebird';
|
||||
import { CoreOptions } from 'request';
|
||||
import { performance } from 'perf_hooks';
|
||||
|
||||
@ -16,6 +15,7 @@ import * as eventTracker from '../event-tracker';
|
||||
import * as deviceState from '../device-state';
|
||||
|
||||
import { shallowDiff, prune, empty } from '../lib/json';
|
||||
import { setTimeout } from 'timers/promises';
|
||||
|
||||
let lastReport: DeviceState = {};
|
||||
let lastReportTime: number = -Infinity;
|
||||
@ -215,7 +215,7 @@ export async function startReporting() {
|
||||
}
|
||||
} finally {
|
||||
// Wait until we want to report again
|
||||
await delay(delayBy);
|
||||
await setTimeout(delayBy);
|
||||
// Try to report again
|
||||
await recursivelyReport(delayBy);
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import * as Bluebird from 'bluebird';
|
||||
import * as Docker from 'dockerode';
|
||||
import { EventEmitter } from 'events';
|
||||
import * as _ from 'lodash';
|
||||
@ -318,10 +317,14 @@ async function withImagesFromDockerAndDB<T>(
|
||||
cb: (dockerImages: Docker.ImageInfo[], composeImages: Image[]) => T,
|
||||
) {
|
||||
const [normalisedImages, dbImages] = await Promise.all([
|
||||
Bluebird.map(docker.listImages({ digests: true }), (image) => ({
|
||||
...image,
|
||||
RepoTag: getNormalisedTags(image),
|
||||
})),
|
||||
docker.listImages({ digests: true }).then(async (images) => {
|
||||
return await Promise.all(
|
||||
images.map((image) => ({
|
||||
...image,
|
||||
RepoTag: getNormalisedTags(image),
|
||||
})),
|
||||
);
|
||||
}),
|
||||
db.models('image').select(),
|
||||
]);
|
||||
return cb(normalisedImages, dbImages);
|
||||
|
@ -1,4 +1,3 @@
|
||||
import * as Bluebird from 'bluebird';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import * as constants from '../lib/constants';
|
||||
@ -12,14 +11,13 @@ import { Network } from './network';
|
||||
import { ResourceRecreationAttemptError } from './errors';
|
||||
|
||||
export async function getAll(): Promise<Network[]> {
|
||||
return getWithBothLabels().map(async (network: { Id: string }) => {
|
||||
return docker
|
||||
.getNetwork(network.Id)
|
||||
.inspect()
|
||||
.then((net) => {
|
||||
return Network.fromDockerNetwork(net);
|
||||
});
|
||||
});
|
||||
const networks = await getWithBothLabels();
|
||||
return await Promise.all(
|
||||
networks.map(async (network: { Id: string }) => {
|
||||
const net = await docker.getNetwork(network.Id).inspect();
|
||||
return Network.fromDockerNetwork(net);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async function get(network: {
|
||||
@ -126,8 +124,8 @@ export async function ensureSupervisorNetwork(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
function getWithBothLabels() {
|
||||
return Bluebird.join(
|
||||
async function getWithBothLabels() {
|
||||
const [legacyNetworks, currentNetworks] = await Promise.all([
|
||||
docker.listNetworks({
|
||||
filters: {
|
||||
label: ['io.resin.supervised'],
|
||||
@ -138,8 +136,6 @@ function getWithBothLabels() {
|
||||
label: ['io.balena.supervised'],
|
||||
},
|
||||
}),
|
||||
(legacyNetworks, currentNetworks) => {
|
||||
return _.unionBy(currentNetworks, legacyNetworks, 'Id');
|
||||
},
|
||||
);
|
||||
]);
|
||||
return _.unionBy(currentNetworks, legacyNetworks, 'Id');
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import * as Bluebird from 'bluebird';
|
||||
import * as Dockerode from 'dockerode';
|
||||
import { EventEmitter } from 'events';
|
||||
import { isLeft } from 'fp-ts/lib/Either';
|
||||
@ -26,6 +25,7 @@ import { serviceNetworksToDockerNetworks } from './utils';
|
||||
|
||||
import log from '../lib/supervisor-console';
|
||||
import logMonitor from '../logging/monitor';
|
||||
import { setTimeout } from 'timers/promises';
|
||||
|
||||
interface ServiceManagerEvents {
|
||||
change: void;
|
||||
@ -61,24 +61,28 @@ export const getAll = async (
|
||||
const filterLabels = ['supervised'].concat(extraLabelFilters);
|
||||
const containers = await listWithBothLabels(filterLabels);
|
||||
|
||||
const services = await Bluebird.map(containers, async (container) => {
|
||||
try {
|
||||
const serviceInspect = await docker.getContainer(container.Id).inspect();
|
||||
const service = Service.fromDockerContainer(serviceInspect);
|
||||
// We know that the containerId is set below, because `fromDockerContainer`
|
||||
// always sets it
|
||||
const vState = volatileState[service.containerId!];
|
||||
if (vState != null && vState.status != null) {
|
||||
service.status = vState.status;
|
||||
const services = await Promise.all(
|
||||
containers.map(async (container) => {
|
||||
try {
|
||||
const serviceInspect = await docker
|
||||
.getContainer(container.Id)
|
||||
.inspect();
|
||||
const service = Service.fromDockerContainer(serviceInspect);
|
||||
// We know that the containerId is set below, because `fromDockerContainer`
|
||||
// always sets it
|
||||
const vState = volatileState[service.containerId!];
|
||||
if (vState != null && vState.status != null) {
|
||||
service.status = vState.status;
|
||||
}
|
||||
return service;
|
||||
} catch (e: unknown) {
|
||||
if (isNotFoundError(e)) {
|
||||
return null;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
return service;
|
||||
} catch (e: unknown) {
|
||||
if (isNotFoundError(e)) {
|
||||
return null;
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
return services.filter((s) => s != null) as Service[];
|
||||
};
|
||||
@ -432,14 +436,17 @@ export function listenToEvents() {
|
||||
});
|
||||
};
|
||||
|
||||
Bluebird.resolve(listen())
|
||||
.catch((e) => {
|
||||
log.error('Error listening to events:', e, e.stack);
|
||||
})
|
||||
.finally(() => {
|
||||
(async () => {
|
||||
try {
|
||||
await listen();
|
||||
} catch (e) {
|
||||
log.error('Error listening to events:', e);
|
||||
} finally {
|
||||
listening = false;
|
||||
setTimeout(listenToEvents, 1000);
|
||||
});
|
||||
await setTimeout(1000);
|
||||
listenToEvents();
|
||||
}
|
||||
})();
|
||||
|
||||
return;
|
||||
}
|
||||
@ -517,77 +524,73 @@ function reportNewStatus(
|
||||
);
|
||||
}
|
||||
|
||||
function killContainer(
|
||||
async function killContainer(
|
||||
containerId: string,
|
||||
service: Partial<Service> = {},
|
||||
{ removeContainer = true, wait = false }: KillOpts = {},
|
||||
): Bluebird<void> {
|
||||
): Promise<void> {
|
||||
// To maintain compatibility of the `wait` flag, this function is not
|
||||
// async, but it feels like whether or not the promise should be waited on
|
||||
// should performed by the caller
|
||||
// TODO: Remove the need for the wait flag
|
||||
|
||||
return Bluebird.try(() => {
|
||||
logger.logSystemEvent(LogTypes.stopService, { service });
|
||||
if (service.imageId != null) {
|
||||
reportNewStatus(containerId, service, 'Stopping');
|
||||
}
|
||||
logger.logSystemEvent(LogTypes.stopService, { service });
|
||||
if (service.imageId != null) {
|
||||
reportNewStatus(containerId, service, 'Stopping');
|
||||
}
|
||||
|
||||
const containerObj = docker.getContainer(containerId);
|
||||
const killPromise = Bluebird.resolve(containerObj.stop())
|
||||
.then(() => {
|
||||
const containerObj = docker.getContainer(containerId);
|
||||
const killPromise = containerObj
|
||||
.stop()
|
||||
.then(() => {
|
||||
if (removeContainer) {
|
||||
return containerObj.remove({ v: true });
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
// Get the statusCode from the original cause and make sure it's
|
||||
// definitely an int for comparison reasons
|
||||
const maybeStatusCode = PermissiveNumber.decode(e.statusCode);
|
||||
if (isLeft(maybeStatusCode)) {
|
||||
throw new Error(`Could not parse status code from docker error: ${e}`);
|
||||
}
|
||||
const statusCode = maybeStatusCode.right;
|
||||
|
||||
// 304 means the container was already stopped, so we can just remove it
|
||||
if (statusCode === 304) {
|
||||
logger.logSystemEvent(LogTypes.stopServiceNoop, { service });
|
||||
// Why do we attempt to remove the container again?
|
||||
if (removeContainer) {
|
||||
return containerObj.remove({ v: true });
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
// Get the statusCode from the original cause and make sure it's
|
||||
// definitely an int for comparison reasons
|
||||
const maybeStatusCode = PermissiveNumber.decode(e.statusCode);
|
||||
if (isLeft(maybeStatusCode)) {
|
||||
throw new Error(
|
||||
`Could not parse status code from docker error: ${e}`,
|
||||
);
|
||||
}
|
||||
const statusCode = maybeStatusCode.right;
|
||||
|
||||
// 304 means the container was already stopped, so we can just remove it
|
||||
if (statusCode === 304) {
|
||||
logger.logSystemEvent(LogTypes.stopServiceNoop, { service });
|
||||
// Why do we attempt to remove the container again?
|
||||
if (removeContainer) {
|
||||
return containerObj.remove({ v: true });
|
||||
}
|
||||
} else if (statusCode === 404) {
|
||||
// 404 means the container doesn't exist, precisely what we want!
|
||||
logger.logSystemEvent(LogTypes.stopRemoveServiceNoop, {
|
||||
service,
|
||||
});
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
})
|
||||
.tap(() => {
|
||||
delete containerHasDied[containerId];
|
||||
logger.logSystemEvent(LogTypes.stopServiceSuccess, { service });
|
||||
})
|
||||
.catch((e) => {
|
||||
logger.logSystemEvent(LogTypes.stopServiceError, {
|
||||
} else if (statusCode === 404) {
|
||||
// 404 means the container doesn't exist, precisely what we want!
|
||||
logger.logSystemEvent(LogTypes.stopRemoveServiceNoop, {
|
||||
service,
|
||||
error: e,
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
if (service.imageId != null) {
|
||||
reportChange(containerId);
|
||||
}
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
delete containerHasDied[containerId];
|
||||
logger.logSystemEvent(LogTypes.stopServiceSuccess, { service });
|
||||
})
|
||||
.catch((e) => {
|
||||
logger.logSystemEvent(LogTypes.stopServiceError, {
|
||||
service,
|
||||
error: e,
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
if (service.imageId != null) {
|
||||
reportChange(containerId);
|
||||
}
|
||||
});
|
||||
|
||||
if (wait) {
|
||||
return killPromise;
|
||||
}
|
||||
return;
|
||||
});
|
||||
if (wait) {
|
||||
return killPromise;
|
||||
}
|
||||
}
|
||||
|
||||
async function listWithBothLabels(
|
||||
@ -630,21 +633,29 @@ function waitToKill(service: Service, timeout: number | string) {
|
||||
|
||||
const handoverCompletePaths = service.handoverCompleteFullPathsOnHost();
|
||||
|
||||
const wait = (): Bluebird<void> =>
|
||||
Bluebird.any(
|
||||
handoverCompletePaths.map((file) =>
|
||||
fs.stat(file).then(() => fs.unlink(file).catch(_.noop)),
|
||||
),
|
||||
).catch(async () => {
|
||||
const wait = async (): Promise<void> => {
|
||||
try {
|
||||
await Promise.any(
|
||||
handoverCompletePaths.map(async (file) => {
|
||||
try {
|
||||
await fs.stat(file);
|
||||
await fs.unlink(file);
|
||||
} catch {
|
||||
// noop
|
||||
}
|
||||
}),
|
||||
);
|
||||
} catch {
|
||||
if (Date.now() < deadline) {
|
||||
await Bluebird.delay(pollInterval);
|
||||
await setTimeout(pollInterval);
|
||||
return wait();
|
||||
} else {
|
||||
log.info(
|
||||
`Handover timeout has passed, assuming handover was completed for service ${service.serviceName}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
log.info(
|
||||
`Waiting for handover to be completed for service: ${service.serviceName}`,
|
||||
|
@ -1,4 +1,3 @@
|
||||
import * as Bluebird from 'bluebird';
|
||||
import * as _ from 'lodash';
|
||||
import { promises as fs } from 'fs';
|
||||
import * as path from 'path';
|
||||
@ -50,12 +49,10 @@ export class SplashImage extends ConfigBackend {
|
||||
const [
|
||||
// If no logo is found, assume the file is `balena-logo.png`
|
||||
splashFile = path.join(SplashImage.BASEPATH, 'balena-logo.png'),
|
||||
] = (
|
||||
await Bluebird.resolve(fs.readdir(SplashImage.BASEPATH))
|
||||
// Read the splash dir (will throw if the path does not exist)
|
||||
// And filter valid filenames
|
||||
.filter((filename) => SplashImage.FILENAMES.includes(filename))
|
||||
)
|
||||
] = (await fs.readdir(SplashImage.BASEPATH))
|
||||
// Read the splash dir (will throw if the path does not exist)
|
||||
// And filter valid filenames
|
||||
.filter((filename) => SplashImage.FILENAMES.includes(filename))
|
||||
// Sort by name, so in case both files are defined, balena-logo will
|
||||
// be chosen
|
||||
.sort()
|
||||
|
@ -1,4 +1,3 @@
|
||||
import * as Bluebird from 'bluebird';
|
||||
import * as express from 'express';
|
||||
import type { Response, NextFunction } from 'express';
|
||||
import * as _ from 'lodash';
|
||||
@ -156,93 +155,89 @@ router.post(
|
||||
router.get(
|
||||
'/v2/applications/state',
|
||||
async (req: AuthorizedRequest, res: Response, next: NextFunction) => {
|
||||
// It's very hacky to access the services and db via the application manager
|
||||
// refactor this code to use applicationManager.getState() instead.
|
||||
Bluebird.join(
|
||||
serviceManager.getState(),
|
||||
images.getState(),
|
||||
db.models('app').select(['appId', 'commit', 'name']),
|
||||
(
|
||||
services,
|
||||
imgs,
|
||||
apps: Array<{ appId: string; commit: string; name: string }>,
|
||||
) => {
|
||||
// Create an object which is keyed my application name
|
||||
const response: {
|
||||
[appName: string]: {
|
||||
appId: number;
|
||||
commit: string;
|
||||
services: {
|
||||
[serviceName: string]: {
|
||||
status?: string;
|
||||
releaseId: number;
|
||||
downloadProgress: number | null;
|
||||
};
|
||||
try {
|
||||
// It's very hacky to access the services and db via the application manager
|
||||
// refactor this code to use applicationManager.getState() instead.
|
||||
const [services, imgs, apps] = await Promise.all([
|
||||
serviceManager.getState(),
|
||||
images.getState(),
|
||||
db.models('app').select(['appId', 'commit', 'name']) as Promise<
|
||||
Array<{ appId: string; commit: string; name: string }>
|
||||
>,
|
||||
]);
|
||||
// Create an object which is keyed my application name
|
||||
const response: {
|
||||
[appName: string]: {
|
||||
appId: number;
|
||||
commit: string;
|
||||
services: {
|
||||
[serviceName: string]: {
|
||||
status?: string;
|
||||
releaseId: number;
|
||||
downloadProgress: number | null;
|
||||
};
|
||||
};
|
||||
} = {};
|
||||
};
|
||||
} = {};
|
||||
|
||||
const appNameById: { [id: number]: string } = {};
|
||||
const commits: string[] = [];
|
||||
const appNameById: { [id: number]: string } = {};
|
||||
const commits: string[] = [];
|
||||
|
||||
// only access scoped apps
|
||||
apps
|
||||
.filter((app) =>
|
||||
req.auth.isScoped({ apps: [parseInt(app.appId, 10)] }),
|
||||
)
|
||||
.forEach((app) => {
|
||||
const appId = parseInt(app.appId, 10);
|
||||
response[app.name] = {
|
||||
appId,
|
||||
commit: app.commit,
|
||||
services: {},
|
||||
};
|
||||
// only access scoped apps
|
||||
apps
|
||||
.filter((app) => req.auth.isScoped({ apps: [parseInt(app.appId, 10)] }))
|
||||
.forEach((app) => {
|
||||
const appId = parseInt(app.appId, 10);
|
||||
response[app.name] = {
|
||||
appId,
|
||||
commit: app.commit,
|
||||
services: {},
|
||||
};
|
||||
|
||||
appNameById[appId] = app.name;
|
||||
commits.push(app.commit);
|
||||
appNameById[appId] = app.name;
|
||||
commits.push(app.commit);
|
||||
});
|
||||
|
||||
// only access scoped images
|
||||
imgs
|
||||
.filter(
|
||||
(img) =>
|
||||
req.auth.isScoped({ apps: [img.appId] }) &&
|
||||
// Ensure we are using the apps for the target release
|
||||
commits.includes(img.commit),
|
||||
)
|
||||
.forEach((img) => {
|
||||
const appName = appNameById[img.appId];
|
||||
if (appName == null) {
|
||||
log.warn(
|
||||
`Image found for unknown application!\nImage: ${JSON.stringify(
|
||||
img,
|
||||
)}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const svc = _.find(services, (s: Service) => {
|
||||
return s.serviceName === img.serviceName && s.commit === img.commit;
|
||||
});
|
||||
|
||||
// only access scoped images
|
||||
imgs
|
||||
.filter(
|
||||
(img) =>
|
||||
req.auth.isScoped({ apps: [img.appId] }) &&
|
||||
// Ensure we are using the apps for the target release
|
||||
commits.includes(img.commit),
|
||||
)
|
||||
.forEach((img) => {
|
||||
const appName = appNameById[img.appId];
|
||||
if (appName == null) {
|
||||
log.warn(
|
||||
`Image found for unknown application!\nImage: ${JSON.stringify(
|
||||
img,
|
||||
)}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
let status: string | undefined;
|
||||
if (svc == null) {
|
||||
status = img.status;
|
||||
} else {
|
||||
status = svc.status || img.status;
|
||||
}
|
||||
response[appName].services[img.serviceName] = {
|
||||
status,
|
||||
releaseId: img.releaseId,
|
||||
downloadProgress: img.downloadProgress || null,
|
||||
};
|
||||
});
|
||||
|
||||
const svc = _.find(services, (s: Service) => {
|
||||
return (
|
||||
s.serviceName === img.serviceName && s.commit === img.commit
|
||||
);
|
||||
});
|
||||
|
||||
let status: string | undefined;
|
||||
if (svc == null) {
|
||||
status = img.status;
|
||||
} else {
|
||||
status = svc.status || img.status;
|
||||
}
|
||||
response[appName].services[img.serviceName] = {
|
||||
status,
|
||||
releaseId: img.releaseId,
|
||||
downloadProgress: img.downloadProgress || null,
|
||||
};
|
||||
});
|
||||
|
||||
res.status(200).json(response);
|
||||
},
|
||||
).catch(next);
|
||||
res.status(200).json(response);
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -45,6 +45,7 @@ import type {
|
||||
} from './compose/composition-steps';
|
||||
import * as fsUtils from './lib/fs-utils';
|
||||
import { pathOnRoot } from './lib/host-utils';
|
||||
import { setTimeout } from 'timers/promises';
|
||||
|
||||
const TARGET_STATE_CONFIG_DUMP = pathOnRoot(
|
||||
'/tmp/balena-supervisor/target-state-config',
|
||||
@ -769,7 +770,7 @@ export const applyTarget = async ({
|
||||
steps.map((s) => applyStep(s, { force, initial, skipLock })),
|
||||
);
|
||||
|
||||
await Bluebird.delay(nextDelay);
|
||||
await setTimeout(nextDelay);
|
||||
await applyTarget({
|
||||
force,
|
||||
initial,
|
||||
@ -850,8 +851,8 @@ export function triggerApplyTarget({
|
||||
}
|
||||
applyCancelled = false;
|
||||
applyInProgress = true;
|
||||
new Bluebird((resolve, reject) => {
|
||||
setTimeout(resolve, delay);
|
||||
new Promise((resolve, reject) => {
|
||||
setTimeout(delay).then(resolve);
|
||||
cancelDelay = reject;
|
||||
})
|
||||
.catch(() => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import * as url from 'url';
|
||||
import { delay } from 'bluebird';
|
||||
import { setTimeout } from 'timers/promises';
|
||||
import * as _ from 'lodash';
|
||||
import * as Bluebird from 'bluebird';
|
||||
import type StrictEventEmitter from 'strict-event-emitter-types';
|
||||
@ -171,7 +171,7 @@ const poll = async (
|
||||
// Convenience function used for delaying poll loops
|
||||
const delayedLoop = async (delayBy: number) => {
|
||||
// Wait until we want to poll again
|
||||
await delay(delayBy);
|
||||
await setTimeout(delayBy);
|
||||
// Poll again
|
||||
await poll(false, fetchErrors);
|
||||
};
|
||||
@ -232,7 +232,7 @@ export const startPoll = async (): Promise<void> => {
|
||||
appUpdatePollInterval = interval;
|
||||
} catch {
|
||||
// Delay 10 seconds and retry loading config
|
||||
await delay(10000);
|
||||
await setTimeout(10000);
|
||||
// Attempt to start poll again
|
||||
return startPoll();
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import * as Bluebird from 'bluebird';
|
||||
import * as _ from 'lodash';
|
||||
import { promises as fs } from 'fs';
|
||||
import * as path from 'path';
|
||||
@ -11,6 +10,7 @@ import { BackupError, isNotFoundError } from './errors';
|
||||
import { exec, exists, mkdirp, unlinkAll } from './fs-utils';
|
||||
import { log } from './supervisor-console';
|
||||
import { pathOnData } from './host-utils';
|
||||
import { setTimeout } from 'timers/promises';
|
||||
|
||||
export async function loadBackupFromMigration(
|
||||
targetState: TargetState,
|
||||
@ -85,7 +85,7 @@ export async function loadBackupFromMigration(
|
||||
} catch (err) {
|
||||
log.error(`Error restoring migration backup, retrying: ${err}`);
|
||||
|
||||
await Bluebird.delay(retryDelay);
|
||||
await setTimeout(retryDelay);
|
||||
return loadBackupFromMigration(targetState, retryDelay);
|
||||
}
|
||||
}
|
||||
|
@ -3,10 +3,6 @@ import * as _ from 'lodash';
|
||||
import * as memoizee from 'memoizee';
|
||||
import { promises as fs } from 'fs';
|
||||
|
||||
// TODO: remove this once we can update Node to v16 and typescript
|
||||
// to an es2021 compatible version for native Promise.any
|
||||
import { Promise } from 'bluebird';
|
||||
|
||||
import { exec } from './fs-utils';
|
||||
|
||||
export async function getCpuUsage(): Promise<number> {
|
||||
|
@ -1,4 +1,3 @@
|
||||
import * as Bluebird from 'bluebird';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import * as config from './config';
|
||||
@ -76,7 +75,7 @@ export class LocalModeManager {
|
||||
) {}
|
||||
|
||||
// Indicates that switch from or to the local mode is not complete.
|
||||
private switchInProgress: Bluebird<void> | null = null;
|
||||
private switchInProgress: Promise<void> | null = null;
|
||||
|
||||
public async init() {
|
||||
// Setup a listener to catch state changes relating to local mode
|
||||
@ -111,11 +110,11 @@ export class LocalModeManager {
|
||||
}
|
||||
|
||||
public startLocalModeChangeHandling(local: boolean) {
|
||||
this.switchInProgress = Bluebird.resolve(
|
||||
this.handleLocalModeStateChange(local),
|
||||
).finally(() => {
|
||||
this.switchInProgress = null;
|
||||
});
|
||||
this.switchInProgress = this.handleLocalModeStateChange(local).finally(
|
||||
() => {
|
||||
this.switchInProgress = null;
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Query the engine to get currently running containers and installed images.
|
||||
@ -131,7 +130,7 @@ export class LocalModeManager {
|
||||
.listNetworks()
|
||||
.then((resp) => _.map(resp, 'Id'));
|
||||
|
||||
const [containers, images, volumes, networks] = await Bluebird.all([
|
||||
const [containers, images, volumes, networks] = await Promise.all([
|
||||
containersPromise,
|
||||
imagesPromise,
|
||||
volumesPromise,
|
||||
@ -243,30 +242,38 @@ export class LocalModeManager {
|
||||
log.debug(`Going to delete the following objects: ${objects}`);
|
||||
|
||||
// Delete engine objects. We catch every deletion error, so that we can attempt other objects deletions.
|
||||
await Bluebird.map(objects.containers, (cId) => {
|
||||
return docker
|
||||
.getContainer(cId)
|
||||
.remove({ force: true })
|
||||
.catch((e) => log.error(`Unable to delete container ${cId}`, e));
|
||||
});
|
||||
await Bluebird.map(objects.images, (iId) => {
|
||||
return docker
|
||||
.getImage(iId)
|
||||
.remove({ force: true })
|
||||
.catch((e) => log.error(`Unable to delete image ${iId}`, e));
|
||||
});
|
||||
await Bluebird.map(objects.networks, (nId) => {
|
||||
return docker
|
||||
.getNetwork(nId)
|
||||
.remove()
|
||||
.catch((e) => log.error(`Unable to delete network ${nId}`, e));
|
||||
});
|
||||
await Bluebird.map(objects.volumes, (vId) => {
|
||||
return docker
|
||||
.getVolume(vId)
|
||||
.remove()
|
||||
.catch((e) => log.error(`Unable to delete volume ${vId}`, e));
|
||||
});
|
||||
await Promise.all(
|
||||
objects.containers.map((cId) => {
|
||||
return docker
|
||||
.getContainer(cId)
|
||||
.remove({ force: true })
|
||||
.catch((e) => log.error(`Unable to delete container ${cId}`, e));
|
||||
}),
|
||||
);
|
||||
await Promise.all(
|
||||
objects.images.map((iId) => {
|
||||
return docker
|
||||
.getImage(iId)
|
||||
.remove({ force: true })
|
||||
.catch((e) => log.error(`Unable to delete image ${iId}`, e));
|
||||
}),
|
||||
);
|
||||
await Promise.all(
|
||||
objects.networks.map((nId) => {
|
||||
return docker
|
||||
.getNetwork(nId)
|
||||
.remove()
|
||||
.catch((e) => log.error(`Unable to delete network ${nId}`, e));
|
||||
}),
|
||||
);
|
||||
await Promise.all(
|
||||
objects.volumes.map((vId) => {
|
||||
return docker
|
||||
.getVolume(vId)
|
||||
.remove()
|
||||
.catch((e) => log.error(`Unable to delete volume ${vId}`, e));
|
||||
}),
|
||||
);
|
||||
|
||||
// Remove any local mode state added to the database.
|
||||
await db
|
||||
|
@ -1,4 +1,3 @@
|
||||
import * as Bluebird from 'bluebird';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { Readable } from 'stream';
|
||||
@ -10,18 +9,16 @@ import log from '../lib/supervisor-console';
|
||||
export class LocalLogBackend extends LogBackend {
|
||||
private globalListeners: Readable[] = [];
|
||||
|
||||
private serviceNameResolver: (
|
||||
serviceId: number,
|
||||
) => Promise<string> | Bluebird<string>;
|
||||
private serviceNameResolver: (serviceId: number) => Promise<string>;
|
||||
|
||||
public log(message: LogMessage): void {
|
||||
public async log(message: LogMessage): Promise<void> {
|
||||
if (this.publishEnabled) {
|
||||
Bluebird.try(() => {
|
||||
try {
|
||||
if (!message.isSystem) {
|
||||
if (this.serviceNameResolver == null) {
|
||||
// This means there is no listener assigned, drop the logs
|
||||
// TODO: Store these, and resolve them when a listener is attached
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
const svcId = checkInt(message.serviceId);
|
||||
if (svcId == null) {
|
||||
@ -29,29 +26,20 @@ export class LocalLogBackend extends LogBackend {
|
||||
'Non-integer service id found in local logs:\n',
|
||||
JSON.stringify(message),
|
||||
);
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
// TODO: Can we cache this value? The service ids are reused, so
|
||||
// we would need a way of invalidating the cache
|
||||
return (this.serviceNameResolver(svcId) as Promise<string>).then(
|
||||
(serviceName: string) => {
|
||||
return _.assign({}, { serviceName }, message);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return message;
|
||||
const serviceName = await this.serviceNameResolver(svcId);
|
||||
|
||||
_.assign({}, { serviceName }, message);
|
||||
}
|
||||
})
|
||||
.then((msg: LogMessage | null) => {
|
||||
if (msg != null) {
|
||||
_.each(this.globalListeners, (listener) => {
|
||||
listener.push(`${JSON.stringify(msg)}\n`);
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
log.error('Error streaming local log output:', e);
|
||||
_.each(this.globalListeners, (listener) => {
|
||||
listener.push(`${JSON.stringify(message)}\n`);
|
||||
});
|
||||
} catch (e) {
|
||||
log.error('Error streaming local log output:', e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
import * as JSONstream from 'JSONStream';
|
||||
import { delay } from 'bluebird';
|
||||
|
||||
import * as db from '../db';
|
||||
import { spawnJournalctl, toJournalDate } from '../lib/journald';
|
||||
import log from '../lib/supervisor-console';
|
||||
import { setTimeout } from 'timers/promises';
|
||||
|
||||
export type MonitorHook = (message: {
|
||||
message: string;
|
||||
@ -91,7 +91,7 @@ class LogMonitor {
|
||||
wait / 1000
|
||||
}s`,
|
||||
);
|
||||
await delay(wait);
|
||||
await setTimeout(wait);
|
||||
return this.start();
|
||||
},
|
||||
);
|
||||
|
@ -1,4 +1,3 @@
|
||||
import * as Bluebird from 'bluebird';
|
||||
import * as _ from 'lodash';
|
||||
import { promises as fs, watch } from 'fs';
|
||||
import * as networkCheck from 'network-checker';
|
||||
@ -69,13 +68,16 @@ export const startConnectivityCheck = _.once(
|
||||
return;
|
||||
}
|
||||
|
||||
await Bluebird.resolve(fs.mkdir(constants.vpnStatusPath))
|
||||
.catch(EEXIST, () => {
|
||||
try {
|
||||
await fs.mkdir(constants.vpnStatusPath);
|
||||
} catch (err: any) {
|
||||
if (EEXIST(err)) {
|
||||
log.debug('VPN status path exists.');
|
||||
})
|
||||
.then(() => {
|
||||
watch(constants.vpnStatusPath, vpnStatusInotifyCallback);
|
||||
});
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
watch(constants.vpnStatusPath, vpnStatusInotifyCallback);
|
||||
|
||||
if (enable) {
|
||||
vpnStatusInotifyCallback();
|
||||
@ -112,9 +114,7 @@ export function enableConnectivityCheck(enable: boolean) {
|
||||
log.debug(`Connectivity check enabled: ${enable}`);
|
||||
}
|
||||
|
||||
export const connectivityCheckEnabled = Bluebird.method(
|
||||
() => isConnectivityCheckEnabled,
|
||||
);
|
||||
export const connectivityCheckEnabled = async () => isConnectivityCheckEnabled;
|
||||
|
||||
const IP_REGEX =
|
||||
/^(?:(?:balena|docker|rce|tun)[0-9]+|tun[0-9]+|resin-vpn|lo|resin-dns|supervisor0|balena-redsocks|resin-redsocks|br-[0-9a-f]{12})$/;
|
||||
|
@ -1,8 +1,8 @@
|
||||
import * as Bluebird from 'bluebird';
|
||||
import * as Docker from 'dockerode';
|
||||
import { Dockerfile } from 'livepush';
|
||||
|
||||
import * as device from './device';
|
||||
import { setTimeout } from 'timers/promises';
|
||||
|
||||
interface Opts {
|
||||
address: string;
|
||||
@ -54,7 +54,7 @@ export async function initDevice(opts: Opts) {
|
||||
true,
|
||||
);
|
||||
} catch {
|
||||
await Bluebird.delay(500);
|
||||
await setTimeout(500);
|
||||
}
|
||||
}
|
||||
return { containerId: supervisorContainer.Id, stageImages };
|
||||
|
@ -1,9 +1,10 @@
|
||||
import * as https from 'https';
|
||||
import * as stream from 'stream';
|
||||
import * as zlib from 'zlib';
|
||||
import * as Promise from 'bluebird';
|
||||
import * as Bluebird from 'bluebird';
|
||||
import { expect } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { setTimeout } from 'timers/promises';
|
||||
|
||||
import * as config from '~/src/config';
|
||||
|
||||
@ -25,7 +26,7 @@ describe('Logger', function () {
|
||||
|
||||
configStub = sinon.stub(config, 'getMany').returns(
|
||||
// @ts-expect-error this should actually work but the type system doesnt like it
|
||||
Promise.resolve({
|
||||
Bluebird.resolve({
|
||||
apiEndpoint: 'https://example.com',
|
||||
uuid: 'deadbeef',
|
||||
deviceApiKey: 'secretkey',
|
||||
@ -49,66 +50,63 @@ describe('Logger', function () {
|
||||
delete require.cache[require.resolve('~/src/logger')];
|
||||
});
|
||||
|
||||
it('waits the grace period before sending any logs', function () {
|
||||
it('waits the grace period before sending any logs', async function () {
|
||||
const clock = sinon.useFakeTimers();
|
||||
logger.log({ message: 'foobar', serviceId: 15 });
|
||||
clock.tick(4999);
|
||||
clock.restore();
|
||||
|
||||
return Promise.delay(100).then(() => {
|
||||
expect(this._req.body).to.equal('');
|
||||
});
|
||||
await setTimeout(100);
|
||||
expect(this._req.body).to.equal('');
|
||||
});
|
||||
|
||||
it('tears down the connection after inactivity', function () {
|
||||
it('tears down the connection after inactivity', async function () {
|
||||
const clock = sinon.useFakeTimers();
|
||||
logger.log({ message: 'foobar', serviceId: 15 });
|
||||
clock.tick(61000);
|
||||
clock.restore();
|
||||
|
||||
return Promise.delay(100).then(() => {
|
||||
expect(this._req.end.calledOnce).to.be.true;
|
||||
});
|
||||
await setTimeout(100);
|
||||
expect(this._req.end.calledOnce).to.be.true;
|
||||
});
|
||||
|
||||
it('sends logs as gzipped ndjson', function () {
|
||||
it('sends logs as gzipped ndjson', async function () {
|
||||
const timestamp = Date.now();
|
||||
logger.log({ message: 'foobar', serviceId: 15 });
|
||||
logger.log({ timestamp: 1337, message: 'foobar', serviceId: 15 });
|
||||
logger.log({ message: 'foobar' }); // shold be ignored
|
||||
|
||||
return Promise.delay(5500).then(() => {
|
||||
expect(this.requestStub.calledOnce).to.be.true;
|
||||
const opts = this.requestStub.firstCall.args[0];
|
||||
await setTimeout(5500);
|
||||
expect(this.requestStub.calledOnce).to.be.true;
|
||||
const opts = this.requestStub.firstCall.args[0];
|
||||
|
||||
expect(opts.href).to.equal(
|
||||
'https://example.com/device/v2/deadbeef/log-stream',
|
||||
);
|
||||
expect(opts.method).to.equal('POST');
|
||||
expect(opts.headers).to.deep.equal({
|
||||
Authorization: 'Bearer secretkey',
|
||||
'Content-Type': 'application/x-ndjson',
|
||||
'Content-Encoding': 'gzip',
|
||||
});
|
||||
expect(opts.href).to.equal(
|
||||
'https://example.com/device/v2/deadbeef/log-stream',
|
||||
);
|
||||
expect(opts.method).to.equal('POST');
|
||||
expect(opts.headers).to.deep.equal({
|
||||
Authorization: 'Bearer secretkey',
|
||||
'Content-Type': 'application/x-ndjson',
|
||||
'Content-Encoding': 'gzip',
|
||||
});
|
||||
|
||||
const lines = this._req.body.split('\n');
|
||||
expect(lines.length).to.equal(3);
|
||||
expect(lines[2]).to.equal('');
|
||||
const lines = this._req.body.split('\n');
|
||||
expect(lines.length).to.equal(3);
|
||||
expect(lines[2]).to.equal('');
|
||||
|
||||
let msg = JSON.parse(lines[0]);
|
||||
expect(msg).to.have.property('message').that.equals('foobar');
|
||||
expect(msg).to.have.property('serviceId').that.equals(15);
|
||||
expect(msg).to.have.property('timestamp').that.is.at.least(timestamp);
|
||||
msg = JSON.parse(lines[1]);
|
||||
expect(msg).to.deep.equal({
|
||||
timestamp: 1337,
|
||||
message: 'foobar',
|
||||
serviceId: 15,
|
||||
});
|
||||
let msg = JSON.parse(lines[0]);
|
||||
expect(msg).to.have.property('message').that.equals('foobar');
|
||||
expect(msg).to.have.property('serviceId').that.equals(15);
|
||||
expect(msg).to.have.property('timestamp').that.is.at.least(timestamp);
|
||||
msg = JSON.parse(lines[1]);
|
||||
expect(msg).to.deep.equal({
|
||||
timestamp: 1337,
|
||||
message: 'foobar',
|
||||
serviceId: 15,
|
||||
});
|
||||
});
|
||||
|
||||
it('allows logging system messages which are also reported to the eventTracker', function () {
|
||||
it('allows logging system messages which are also reported to the eventTracker', async function () {
|
||||
const timestamp = Date.now();
|
||||
logger.logSystemMessage(
|
||||
'Hello there!',
|
||||
@ -116,15 +114,14 @@ describe('Logger', function () {
|
||||
'Some event name',
|
||||
);
|
||||
|
||||
return Promise.delay(5500).then(() => {
|
||||
const lines = this._req.body.split('\n');
|
||||
expect(lines.length).to.equal(2);
|
||||
expect(lines[1]).to.equal('');
|
||||
await setTimeout(5500);
|
||||
const lines = this._req.body.split('\n');
|
||||
expect(lines.length).to.equal(2);
|
||||
expect(lines[1]).to.equal('');
|
||||
|
||||
const msg = JSON.parse(lines[0]);
|
||||
expect(msg).to.have.property('message').that.equals('Hello there!');
|
||||
expect(msg).to.have.property('isSystem').that.equals(true);
|
||||
expect(msg).to.have.property('timestamp').that.is.at.least(timestamp);
|
||||
});
|
||||
const msg = JSON.parse(lines[0]);
|
||||
expect(msg).to.have.property('message').that.equals('Hello there!');
|
||||
expect(msg).to.have.property('isSystem').that.equals(true);
|
||||
expect(msg).to.have.property('timestamp').that.is.at.least(timestamp);
|
||||
});
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { SinonStub, stub, spy, SinonSpy } from 'sinon';
|
||||
import { Promise } from 'bluebird';
|
||||
import * as Bluebird from 'bluebird';
|
||||
import * as _ from 'lodash';
|
||||
import rewire = require('rewire');
|
||||
import { expect } from 'chai';
|
||||
@ -30,7 +30,7 @@ const stateEndpointBody = {
|
||||
|
||||
const req = {
|
||||
getAsync: () =>
|
||||
Promise.resolve([
|
||||
Bluebird.resolve([
|
||||
{
|
||||
statusCode: 200,
|
||||
headers: { etag: 'abc' },
|
||||
@ -66,7 +66,7 @@ describe('Target state', () => {
|
||||
// new request returns 304
|
||||
const newReq = {
|
||||
getAsync: () =>
|
||||
Promise.resolve([
|
||||
Bluebird.resolve([
|
||||
{
|
||||
statusCode: 304,
|
||||
headers: {},
|
||||
@ -106,7 +106,7 @@ describe('Target state', () => {
|
||||
// new request returns 304
|
||||
const newReq = {
|
||||
getAsync: () =>
|
||||
Promise.resolve([
|
||||
Bluebird.resolve([
|
||||
{
|
||||
statusCode: 304,
|
||||
headers: {},
|
||||
@ -148,7 +148,7 @@ describe('Target state', () => {
|
||||
// new request returns 304
|
||||
const newReq = {
|
||||
getAsync: () =>
|
||||
Promise.resolve([
|
||||
Bluebird.resolve([
|
||||
{
|
||||
statusCode: 304,
|
||||
headers: {},
|
||||
@ -246,7 +246,7 @@ describe('Target state', () => {
|
||||
(request.getRequestInstance as SinonStub).restore();
|
||||
const newReq = {
|
||||
getAsync: () =>
|
||||
Promise.resolve([
|
||||
Bluebird.resolve([
|
||||
{
|
||||
statusCode: 304,
|
||||
headers: {},
|
||||
|
Loading…
Reference in New Issue
Block a user