mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-06-18 23:38:12 +00:00
Update @balena/lint to v7
This updates balena lint to the latest version to enable eslint support and unblock Typescript updates. This is a huge number of changes as the linting rules are much more strict now, requiring modifiying most files in the codebase. This commit also bumps the test dependency `rewire` as that was interfering with the update of balena-lint Change-type: patch
This commit is contained in:
@ -2,10 +2,4 @@
|
|||||||
"*.ts": [
|
"*.ts": [
|
||||||
"balena-lint --typescript --fix",
|
"balena-lint --typescript --fix",
|
||||||
],
|
],
|
||||||
"*.js": [
|
|
||||||
"balena-lint --typescript --fix",
|
|
||||||
],
|
|
||||||
"test/**/*.ts": [
|
|
||||||
"balena-lint --typescript --no-prettier --tests"
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
8898
package-lock.json
generated
8898
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "./entry.sh",
|
"start": "./entry.sh",
|
||||||
"build": "npm run clean && npm run release && webpack",
|
"build": "npm run clean && npm run release && webpack",
|
||||||
"lint": "balena-lint -e ts -e js src/ test/ typings/ build-utils/ webpack.config.js",
|
"lint": "balena-lint -e ts -e js src/ test/ typings/ build-utils/",
|
||||||
"test:build": "tsc --noEmit && tsc --noEmit --project tsconfig.js.json",
|
"test:build": "tsc --noEmit && tsc --noEmit --project tsconfig.js.json",
|
||||||
"test:unit": "mocha --config test/unit/.mocharc.js",
|
"test:unit": "mocha --config test/unit/.mocharc.js",
|
||||||
"test:integration": "find test/integration -name *.spec.ts | xargs -n 1 -I {} sh -c 'mocha --config test/integration/.mocharc.js {} || exit 255'",
|
"test:integration": "find test/integration -name *.spec.ts | xargs -n 1 -I {} sh -c 'mocha --config test/integration/.mocharc.js {} || exit 255'",
|
||||||
@ -21,7 +21,7 @@
|
|||||||
"test:compose": "(ARCH=$(./build-utils/detect-arch.sh) docker compose -f docker-compose.yml -f docker-compose.test.yml run --build --rm sut || docker compose logs); npm run compose:down",
|
"test:compose": "(ARCH=$(./build-utils/detect-arch.sh) docker compose -f docker-compose.yml -f docker-compose.test.yml run --build --rm sut || docker compose logs); npm run compose:down",
|
||||||
"test": "npm run lint && npm run test:build && npm run test:unit",
|
"test": "npm run lint && npm run test:build && npm run test:unit",
|
||||||
"compose:down": "docker compose -f docker-compose.yml -f docker-compose.test.yml down --volumes",
|
"compose:down": "docker compose -f docker-compose.yml -f docker-compose.test.yml down --volumes",
|
||||||
"prettify": "balena-lint -e ts -e js --fix src/ test/ typings/ build-utils/ webpack.config.js",
|
"prettify": "balena-lint -e ts -e js --fix src/ test/ typings/ build-utils/",
|
||||||
"release": "tsc --project tsconfig.release.json && mv build/src/* build",
|
"release": "tsc --project tsconfig.release.json && mv build/src/* build",
|
||||||
"sync": "ts-node --files sync/sync.ts",
|
"sync": "ts-node --files sync/sync.ts",
|
||||||
"clean": "rimraf build",
|
"clean": "rimraf build",
|
||||||
@ -42,7 +42,7 @@
|
|||||||
"@balena/contrato": "^0.6.0",
|
"@balena/contrato": "^0.6.0",
|
||||||
"@balena/es-version": "^1.0.1",
|
"@balena/es-version": "^1.0.1",
|
||||||
"@balena/happy-eyeballs": "0.0.6",
|
"@balena/happy-eyeballs": "0.0.6",
|
||||||
"@balena/lint": "^6.2.0",
|
"@balena/lint": "^7.3.0",
|
||||||
"@types/bluebird": "^3.5.37",
|
"@types/bluebird": "^3.5.37",
|
||||||
"@types/chai": "^4.3.3",
|
"@types/chai": "^4.3.3",
|
||||||
"@types/chai-as-promised": "^7.1.5",
|
"@types/chai-as-promised": "^7.1.5",
|
||||||
@ -53,6 +53,7 @@
|
|||||||
"@types/dockerode": "^2.5.34",
|
"@types/dockerode": "^2.5.34",
|
||||||
"@types/event-stream": "^3.3.34",
|
"@types/event-stream": "^3.3.34",
|
||||||
"@types/express": "^4.17.14",
|
"@types/express": "^4.17.14",
|
||||||
|
"@types/json-mask": "2.0.3",
|
||||||
"@types/lodash": "^4.14.186",
|
"@types/lodash": "^4.14.186",
|
||||||
"@types/memoizee": "^0.4.8",
|
"@types/memoizee": "^0.4.8",
|
||||||
"@types/mocha": "^8.2.3",
|
"@types/mocha": "^8.2.3",
|
||||||
@ -116,7 +117,7 @@
|
|||||||
"request": "^2.88.2",
|
"request": "^2.88.2",
|
||||||
"resin-docker-build": "^1.1.6",
|
"resin-docker-build": "^1.1.6",
|
||||||
"resumable-request": "^2.0.1",
|
"resumable-request": "^2.0.1",
|
||||||
"rewire": "^5.0.0",
|
"rewire": "^6.0.0",
|
||||||
"rimraf": "^2.7.1",
|
"rimraf": "^2.7.1",
|
||||||
"rwlock": "^5.0.0",
|
"rwlock": "^5.0.0",
|
||||||
"semver": "7.5.4",
|
"semver": "7.5.4",
|
||||||
|
@ -147,7 +147,7 @@ export async function start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.debug('Starting current state report');
|
log.debug('Starting current state report');
|
||||||
await startCurrentStateReport();
|
void startCurrentStateReport();
|
||||||
|
|
||||||
// When we've provisioned, try to load the backup. We
|
// When we've provisioned, try to load the backup. We
|
||||||
// must wait for the provisioning because we need a
|
// must wait for the provisioning because we need a
|
||||||
@ -158,7 +158,7 @@ export async function start() {
|
|||||||
|
|
||||||
readyForUpdates = true;
|
readyForUpdates = true;
|
||||||
log.debug('Starting target state poll');
|
log.debug('Starting target state poll');
|
||||||
TargetState.startPoll();
|
void TargetState.startPoll();
|
||||||
// Update and apply new target state
|
// Update and apply new target state
|
||||||
TargetState.emitter.on(
|
TargetState.emitter.on(
|
||||||
'target-state-update',
|
'target-state-update',
|
||||||
@ -235,7 +235,7 @@ export function startCurrentStateReport() {
|
|||||||
'Trying to start state reporting without initializing API client',
|
'Trying to start state reporting without initializing API client',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
startReporting();
|
return startReporting();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchDeviceTags(): Promise<DeviceTag[]> {
|
export async function fetchDeviceTags(): Promise<DeviceTag[]> {
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
import * as url from 'url';
|
import * as url from 'url';
|
||||||
import * as _ from 'lodash';
|
import type { CoreOptions } from 'request';
|
||||||
import { CoreOptions } from 'request';
|
|
||||||
import { performance } from 'perf_hooks';
|
import { performance } from 'perf_hooks';
|
||||||
import { setTimeout } from 'timers/promises';
|
import { setTimeout } from 'timers/promises';
|
||||||
import { readFile } from 'fs/promises';
|
import { readFile } from 'fs/promises';
|
||||||
|
|
||||||
import { DeviceState } from '../types';
|
import { DeviceState } from '../types';
|
||||||
import * as config from '../config';
|
import * as config from '../config';
|
||||||
import { SchemaTypeKey, SchemaReturn } from '../config/schema-type';
|
import type { SchemaTypeKey, SchemaReturn } from '../config/schema-type';
|
||||||
import * as eventTracker from '../event-tracker';
|
import * as eventTracker from '../event-tracker';
|
||||||
import * as deviceState from '../device-state';
|
import * as deviceState from '../device-state';
|
||||||
|
|
||||||
import { withBackoff, OnFailureInfo } from '../lib/backoff';
|
import type { OnFailureInfo } from '../lib/backoff';
|
||||||
|
import { withBackoff } from '../lib/backoff';
|
||||||
import { log } from '../lib/supervisor-console';
|
import { log } from '../lib/supervisor-console';
|
||||||
import { InternalInconsistencyError, StatusError } from '../lib/errors';
|
import { InternalInconsistencyError, StatusError } from '../lib/errors';
|
||||||
import { getRequestInstance } from '../lib/request';
|
import { getRequestInstance } from '../lib/request';
|
||||||
|
@ -107,7 +107,9 @@ async function mdnsLookup(
|
|||||||
// Make NodeJS RFC 3484 compliant for properly handling IPv6
|
// Make NodeJS RFC 3484 compliant for properly handling IPv6
|
||||||
// See: https://github.com/nodejs/node/pull/14731
|
// See: https://github.com/nodejs/node/pull/14731
|
||||||
// https://github.com/nodejs/node/pull/17793
|
// https://github.com/nodejs/node/pull/17793
|
||||||
const dns = require('dns');
|
// We disable linting for the next line. The require call
|
||||||
|
// is necesary for monkey-patching the dns module
|
||||||
|
const dns = require('dns'); // eslint-disable-line
|
||||||
const { lookup } = dns;
|
const { lookup } = dns;
|
||||||
|
|
||||||
dns.lookup = (
|
dns.lookup = (
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import { ImageInspectInfo } from 'dockerode';
|
import type { ImageInspectInfo } from 'dockerode';
|
||||||
|
|
||||||
import Network from './network';
|
import Network from './network';
|
||||||
import Volume from './volume';
|
import Volume from './volume';
|
||||||
import Service from './service';
|
import Service from './service';
|
||||||
import * as imageManager from './images';
|
import * as imageManager from './images';
|
||||||
import type { Image } from './images';
|
import type { Image } from './images';
|
||||||
import {
|
import type {
|
||||||
CompositionStep,
|
CompositionStep,
|
||||||
generateStep,
|
|
||||||
CompositionStepAction,
|
CompositionStepAction,
|
||||||
} from './composition-steps';
|
} from './composition-steps';
|
||||||
import * as targetStateCache from '../device-state/target-state-cache';
|
import { generateStep } from './composition-steps';
|
||||||
|
import type * as targetStateCache from '../device-state/target-state-cache';
|
||||||
import { getNetworkGateway } from '../lib/docker-utils';
|
import { getNetworkGateway } from '../lib/docker-utils';
|
||||||
import * as constants from '../lib/constants';
|
import * as constants from '../lib/constants';
|
||||||
import {
|
import {
|
||||||
@ -22,7 +22,7 @@ import {
|
|||||||
import { isNotFoundError } from '../lib/errors';
|
import { isNotFoundError } from '../lib/errors';
|
||||||
import * as config from '../config';
|
import * as config from '../config';
|
||||||
import { checkTruthy } from '../lib/validation';
|
import { checkTruthy } from '../lib/validation';
|
||||||
import { ServiceComposeConfig, DeviceMetadata } from './types/service';
|
import type { ServiceComposeConfig, DeviceMetadata } from './types/service';
|
||||||
import { pathExistsOnRoot } from '../lib/host-utils';
|
import { pathExistsOnRoot } from '../lib/host-utils';
|
||||||
import { isSupervisor } from '../lib/supervisor-metadata';
|
import { isSupervisor } from '../lib/supervisor-metadata';
|
||||||
|
|
||||||
@ -64,7 +64,10 @@ export class App {
|
|||||||
public networks: Network[];
|
public networks: Network[];
|
||||||
public volumes: Volume[];
|
public volumes: Volume[];
|
||||||
|
|
||||||
public constructor(opts: AppConstructOpts, public isTargetState: boolean) {
|
public constructor(
|
||||||
|
opts: AppConstructOpts,
|
||||||
|
public isTargetState: boolean,
|
||||||
|
) {
|
||||||
this.appId = opts.appId;
|
this.appId = opts.appId;
|
||||||
this.appUuid = opts.appUuid;
|
this.appUuid = opts.appUuid;
|
||||||
this.appName = opts.appName;
|
this.appName = opts.appName;
|
||||||
@ -513,12 +516,18 @@ export class App {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const needsDownload = !context.availableImages.some(
|
const needsDownload =
|
||||||
|
target != null &&
|
||||||
|
!context.availableImages.some(
|
||||||
(image) =>
|
(image) =>
|
||||||
image.dockerImageId === target?.config.image ||
|
image.dockerImageId === target.config.image ||
|
||||||
imageManager.isSameImage(image, { name: target?.imageName! }),
|
imageManager.isSameImage(image, { name: target.imageName! }),
|
||||||
);
|
);
|
||||||
if (needsDownload && context.downloading.includes(target?.imageName!)) {
|
if (
|
||||||
|
target != null &&
|
||||||
|
needsDownload &&
|
||||||
|
context.downloading.includes(target.imageName!)
|
||||||
|
) {
|
||||||
// The image needs to be downloaded, and it's currently downloading.
|
// The image needs to be downloaded, and it's currently downloading.
|
||||||
// We simply keep the application loop alive
|
// We simply keep the application loop alive
|
||||||
return generateStep('noop', {});
|
return generateStep('noop', {});
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import StrictEventEmitter from 'strict-event-emitter-types';
|
import type StrictEventEmitter from 'strict-event-emitter-types';
|
||||||
|
|
||||||
import * as config from '../config';
|
import * as config from '../config';
|
||||||
import { transaction, Transaction } from '../db';
|
import type { Transaction } from '../db';
|
||||||
|
import { transaction } from '../db';
|
||||||
import * as logger from '../logger';
|
import * as logger from '../logger';
|
||||||
import LocalModeManager from '../local-mode';
|
import LocalModeManager from '../local-mode';
|
||||||
|
|
||||||
@ -25,9 +26,9 @@ import * as networkManager from './network-manager';
|
|||||||
import * as serviceManager from './service-manager';
|
import * as serviceManager from './service-manager';
|
||||||
import * as imageManager from './images';
|
import * as imageManager from './images';
|
||||||
import * as commitStore from './commit';
|
import * as commitStore from './commit';
|
||||||
import Service from './service';
|
import type Service from './service';
|
||||||
import Network from './network';
|
import type Network from './network';
|
||||||
import Volume from './volume';
|
import type Volume from './volume';
|
||||||
import { generateStep, getExecutors } from './composition-steps';
|
import { generateStep, getExecutors } from './composition-steps';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
@ -45,11 +46,11 @@ type ApplicationManagerEventEmitter = StrictEventEmitter<
|
|||||||
{ change: DeviceLegacyReport }
|
{ change: DeviceLegacyReport }
|
||||||
>;
|
>;
|
||||||
const events: ApplicationManagerEventEmitter = new EventEmitter();
|
const events: ApplicationManagerEventEmitter = new EventEmitter();
|
||||||
export const on: typeof events['on'] = events.on.bind(events);
|
export const on: (typeof events)['on'] = events.on.bind(events);
|
||||||
export const once: typeof events['once'] = events.once.bind(events);
|
export const once: (typeof events)['once'] = events.once.bind(events);
|
||||||
export const removeListener: typeof events['removeListener'] =
|
export const removeListener: (typeof events)['removeListener'] =
|
||||||
events.removeListener.bind(events);
|
events.removeListener.bind(events);
|
||||||
export const removeAllListeners: typeof events['removeAllListeners'] =
|
export const removeAllListeners: (typeof events)['removeAllListeners'] =
|
||||||
events.removeAllListeners.bind(events);
|
events.removeAllListeners.bind(events);
|
||||||
|
|
||||||
const localModeManager = new LocalModeManager();
|
const localModeManager = new LocalModeManager();
|
||||||
@ -611,7 +612,7 @@ export async function serviceNameFromId(serviceId: number) {
|
|||||||
(svcName) => services[svcName].id === serviceId,
|
(svcName) => services[svcName].id === serviceId,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!!serviceName) {
|
if (serviceName) {
|
||||||
return serviceName;
|
return serviceName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -771,7 +772,7 @@ function saveAndRemoveImages(
|
|||||||
: availableAndUnused.filter((image) => !deltaSources.includes(image.name));
|
: availableAndUnused.filter((image) => !deltaSources.includes(image.name));
|
||||||
|
|
||||||
return imagesToSave
|
return imagesToSave
|
||||||
.map((image) => ({ action: 'saveImage', image } as CompositionStep))
|
.map((image) => ({ action: 'saveImage', image }) as CompositionStep)
|
||||||
.concat(imagesToRemove.map((image) => ({ action: 'removeImage', image })));
|
.concat(imagesToRemove.map((image) => ({ action: 'removeImage', image })));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -948,7 +949,7 @@ export async function getState() {
|
|||||||
// We cannot report services that do not have an image as the API
|
// We cannot report services that do not have an image as the API
|
||||||
// requires passing the image name
|
// requires passing the image name
|
||||||
.filter(([, img]) => !!img)
|
.filter(([, img]) => !!img)
|
||||||
.map(([svc, img]) => ({ ...img, ...svc } as ServiceInfo))
|
.map(([svc, img]) => ({ ...img, ...svc }) as ServiceInfo)
|
||||||
.map((svc, __, serviceList) => {
|
.map((svc, __, serviceList) => {
|
||||||
// If the service is not running it cannot be a handover
|
// If the service is not running it cannot be a handover
|
||||||
if (svc.status !== 'Running') {
|
if (svc.status !== 'Running') {
|
||||||
|
@ -4,15 +4,15 @@ import * as config from '../config';
|
|||||||
|
|
||||||
import type { Image } from './images';
|
import type { Image } from './images';
|
||||||
import * as images from './images';
|
import * as images from './images';
|
||||||
import Network from './network';
|
import type Network from './network';
|
||||||
import Service from './service';
|
import type Service from './service';
|
||||||
import * as serviceManager from './service-manager';
|
import * as serviceManager from './service-manager';
|
||||||
import Volume from './volume';
|
import type Volume from './volume';
|
||||||
|
|
||||||
import { checkTruthy } from '../lib/validation';
|
import { checkTruthy } from '../lib/validation';
|
||||||
import * as networkManager from './network-manager';
|
import * as networkManager from './network-manager';
|
||||||
import * as volumeManager from './volume-manager';
|
import * as volumeManager from './volume-manager';
|
||||||
import { DeviceLegacyReport } from '../types/state';
|
import type { DeviceLegacyReport } from '../types/state';
|
||||||
import * as commitStore from './commit';
|
import * as commitStore from './commit';
|
||||||
|
|
||||||
interface BaseCompositionStepArgs {
|
interface BaseCompositionStepArgs {
|
||||||
@ -81,7 +81,7 @@ interface CompositionStepArgs {
|
|||||||
saveImage: {
|
saveImage: {
|
||||||
image: Image;
|
image: Image;
|
||||||
};
|
};
|
||||||
cleanup: {};
|
cleanup: object;
|
||||||
createNetwork: {
|
createNetwork: {
|
||||||
target: Network;
|
target: Network;
|
||||||
};
|
};
|
||||||
@ -94,8 +94,8 @@ interface CompositionStepArgs {
|
|||||||
removeVolume: {
|
removeVolume: {
|
||||||
current: Volume;
|
current: Volume;
|
||||||
};
|
};
|
||||||
ensureSupervisorNetwork: {};
|
ensureSupervisorNetwork: object;
|
||||||
noop: {};
|
noop: object;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type CompositionStepAction = keyof CompositionStepArgs;
|
export type CompositionStepAction = keyof CompositionStepArgs;
|
||||||
|
@ -10,7 +10,10 @@ export class InvalidNetworkNameError extends TypedError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class ResourceRecreationAttemptError extends TypedError {
|
export class ResourceRecreationAttemptError extends TypedError {
|
||||||
public constructor(public resource: string, public name: string) {
|
public constructor(
|
||||||
|
public resource: string,
|
||||||
|
public name: string,
|
||||||
|
) {
|
||||||
super(
|
super(
|
||||||
`Trying to create ${resource} with name: ${name}, but a ${resource} ` +
|
`Trying to create ${resource} with name: ${name}, but a ${resource} ` +
|
||||||
'with that name and a different configuration already exists',
|
'with that name and a different configuration already exists',
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import * as Docker from 'dockerode';
|
import type * as Docker from 'dockerode';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import StrictEventEmitter from 'strict-event-emitter-types';
|
import type StrictEventEmitter from 'strict-event-emitter-types';
|
||||||
|
|
||||||
import * as config from '../config';
|
import * as config from '../config';
|
||||||
import * as db from '../db';
|
import * as db from '../db';
|
||||||
import * as constants from '../lib/constants';
|
import * as constants from '../lib/constants';
|
||||||
import { DeltaFetchOptions, FetchOptions, docker } from '../lib/docker-utils';
|
import type { DeltaFetchOptions, FetchOptions } from '../lib/docker-utils';
|
||||||
|
import { docker } from '../lib/docker-utils';
|
||||||
import * as dockerUtils from '../lib/docker-utils';
|
import * as dockerUtils from '../lib/docker-utils';
|
||||||
import {
|
import {
|
||||||
DeltaStillProcessingError,
|
DeltaStillProcessingError,
|
||||||
@ -67,11 +68,11 @@ class ImageEventEmitter extends (EventEmitter as new () => StrictEventEmitter<
|
|||||||
>) {}
|
>) {}
|
||||||
const events = new ImageEventEmitter();
|
const events = new ImageEventEmitter();
|
||||||
|
|
||||||
export const on: typeof events['on'] = events.on.bind(events);
|
export const on: (typeof events)['on'] = events.on.bind(events);
|
||||||
export const once: typeof events['once'] = events.once.bind(events);
|
export const once: (typeof events)['once'] = events.once.bind(events);
|
||||||
export const removeListener: typeof events['removeListener'] =
|
export const removeListener: (typeof events)['removeListener'] =
|
||||||
events.removeListener.bind(events);
|
events.removeListener.bind(events);
|
||||||
export const removeAllListeners: typeof events['removeAllListeners'] =
|
export const removeAllListeners: (typeof events)['removeAllListeners'] =
|
||||||
events.removeAllListeners.bind(events);
|
events.removeAllListeners.bind(events);
|
||||||
|
|
||||||
const imageFetchFailures: Dictionary<number> = {};
|
const imageFetchFailures: Dictionary<number> = {};
|
||||||
@ -144,15 +145,17 @@ function reportEvent(event: 'start' | 'update' | 'finish', state: Image) {
|
|||||||
switch (event) {
|
switch (event) {
|
||||||
case 'start':
|
case 'start':
|
||||||
return true; // always report change on start
|
return true; // always report change on start
|
||||||
case 'update':
|
case 'update': {
|
||||||
const [updatedTask, changedAfterUpdate] = currentTask.update(state);
|
const [updatedTask, changedAfterUpdate] = currentTask.update(state);
|
||||||
runningTasks[imageName] = updatedTask;
|
runningTasks[imageName] = updatedTask;
|
||||||
return changedAfterUpdate; // report change only if the task context changed
|
return changedAfterUpdate; // report change only if the task context changed
|
||||||
case 'finish':
|
}
|
||||||
|
case 'finish': {
|
||||||
const [, changedAfterFinish] = currentTask.finish();
|
const [, changedAfterFinish] = currentTask.finish();
|
||||||
delete runningTasks[imageName];
|
delete runningTasks[imageName];
|
||||||
return changedAfterFinish; // report change depending on the state of the task
|
return changedAfterFinish; // report change depending on the state of the task
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
if (stateChanged) {
|
if (stateChanged) {
|
||||||
@ -551,7 +554,7 @@ const inspectByReference = async (imageName: string) => {
|
|||||||
filters: { reference: [reference] },
|
filters: { reference: [reference] },
|
||||||
})
|
})
|
||||||
.then(([img]) =>
|
.then(([img]) =>
|
||||||
!!img
|
img
|
||||||
? docker.getImage(img.Id).inspect()
|
? docker.getImage(img.Id).inspect()
|
||||||
: Promise.reject(
|
: Promise.reject(
|
||||||
new StatusError(
|
new StatusError(
|
||||||
@ -582,7 +585,7 @@ const inspectByDigest = async (imageName: string) => {
|
|||||||
// Assume that all db entries will point to the same dockerImageId, so use
|
// Assume that all db entries will point to the same dockerImageId, so use
|
||||||
// the first one. If this assumption is false, there is a bug with cleanup
|
// the first one. If this assumption is false, there is a bug with cleanup
|
||||||
.then(([img]) =>
|
.then(([img]) =>
|
||||||
!!img
|
img
|
||||||
? docker.getImage(img.dockerImageId).inspect()
|
? docker.getImage(img.dockerImageId).inspect()
|
||||||
: Promise.reject(
|
: Promise.reject(
|
||||||
new StatusError(
|
new StatusError(
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import * as dockerode from 'dockerode';
|
import type * as dockerode from 'dockerode';
|
||||||
|
|
||||||
import { docker } from '../lib/docker-utils';
|
import { docker } from '../lib/docker-utils';
|
||||||
import logTypes = require('../lib/log-types');
|
import logTypes = require('../lib/log-types');
|
||||||
@ -7,7 +7,7 @@ import * as logger from '../logger';
|
|||||||
import log from '../lib/supervisor-console';
|
import log from '../lib/supervisor-console';
|
||||||
import * as ComposeUtils from './utils';
|
import * as ComposeUtils from './utils';
|
||||||
|
|
||||||
import {
|
import type {
|
||||||
ComposeNetworkConfig,
|
ComposeNetworkConfig,
|
||||||
NetworkConfig,
|
NetworkConfig,
|
||||||
NetworkInspectInfo,
|
NetworkInspectInfo,
|
||||||
@ -22,7 +22,9 @@ export class Network {
|
|||||||
public name: string;
|
public name: string;
|
||||||
public config: NetworkConfig;
|
public config: NetworkConfig;
|
||||||
|
|
||||||
private constructor() {}
|
private constructor() {
|
||||||
|
/* do not allow instances using `new` */
|
||||||
|
}
|
||||||
|
|
||||||
private static deconstructDockerName(
|
private static deconstructDockerName(
|
||||||
name: string,
|
name: string,
|
||||||
|
@ -15,7 +15,7 @@ export interface PortBindings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface DockerPortOptions {
|
export interface DockerPortOptions {
|
||||||
exposedPorts: Dictionary<{}>;
|
exposedPorts: Dictionary<EmptyObject>;
|
||||||
portBindings: PortBindings;
|
portBindings: PortBindings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ export class PortMap {
|
|||||||
this.ports.externalEnd,
|
this.ports.externalEnd,
|
||||||
);
|
);
|
||||||
|
|
||||||
const exposedPorts: { [key: string]: {} } = {};
|
const exposedPorts: { [key: string]: EmptyObject } = {};
|
||||||
const portBindings: PortBindings = {};
|
const portBindings: PortBindings = {};
|
||||||
|
|
||||||
_.zipWith(internalRange, externalRange, (internal, external) => {
|
_.zipWith(internalRange, externalRange, (internal, external) => {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { ConfigMap, ServiceComposeConfig } from './types/service';
|
import type { ConfigMap, ServiceComposeConfig } from './types/service';
|
||||||
|
|
||||||
import log from '../lib/supervisor-console';
|
import log from '../lib/supervisor-console';
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import * as Dockerode from 'dockerode';
|
import type * as Dockerode from 'dockerode';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import { isLeft } from 'fp-ts/lib/Either';
|
import { isLeft } from 'fp-ts/lib/Either';
|
||||||
import * as JSONStream from 'JSONStream';
|
import * as JSONStream from 'JSONStream';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import StrictEventEmitter from 'strict-event-emitter-types';
|
import type StrictEventEmitter from 'strict-event-emitter-types';
|
||||||
|
|
||||||
import * as config from '../config';
|
import * as config from '../config';
|
||||||
import { docker } from '../lib/docker-utils';
|
import { docker } from '../lib/docker-utils';
|
||||||
@ -12,15 +12,16 @@ import * as logger from '../logger';
|
|||||||
|
|
||||||
import { PermissiveNumber } from '../config/types';
|
import { PermissiveNumber } from '../config/types';
|
||||||
import * as constants from '../lib/constants';
|
import * as constants from '../lib/constants';
|
||||||
|
import type { StatusCodeError } from '../lib/errors';
|
||||||
import {
|
import {
|
||||||
InternalInconsistencyError,
|
InternalInconsistencyError,
|
||||||
isNotFoundError,
|
isNotFoundError,
|
||||||
StatusCodeError,
|
|
||||||
isStatusError,
|
isStatusError,
|
||||||
} from '../lib/errors';
|
} from '../lib/errors';
|
||||||
import * as LogTypes from '../lib/log-types';
|
import * as LogTypes from '../lib/log-types';
|
||||||
import { checkInt, isValidDeviceName } from '../lib/validation';
|
import { checkInt, isValidDeviceName } from '../lib/validation';
|
||||||
import { Service, ServiceStatus } from './service';
|
import type { ServiceStatus } from './service';
|
||||||
|
import { Service } from './service';
|
||||||
import { serviceNetworksToDockerNetworks } from './utils';
|
import { serviceNetworksToDockerNetworks } from './utils';
|
||||||
|
|
||||||
import log from '../lib/supervisor-console';
|
import log from '../lib/supervisor-console';
|
||||||
@ -41,11 +42,11 @@ interface KillOpts {
|
|||||||
wait?: boolean;
|
wait?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const on: typeof events['on'] = events.on.bind(events);
|
export const on: (typeof events)['on'] = events.on.bind(events);
|
||||||
export const once: typeof events['once'] = events.once.bind(events);
|
export const once: (typeof events)['once'] = events.once.bind(events);
|
||||||
export const removeListener: typeof events['removeListener'] =
|
export const removeListener: (typeof events)['removeListener'] =
|
||||||
events.removeListener.bind(events);
|
events.removeListener.bind(events);
|
||||||
export const removeAllListeners: typeof events['removeAllListeners'] =
|
export const removeAllListeners: (typeof events)['removeAllListeners'] =
|
||||||
events.removeAllListeners.bind(events);
|
events.removeAllListeners.bind(events);
|
||||||
|
|
||||||
// Whether a container has died, indexed by ID
|
// Whether a container has died, indexed by ID
|
||||||
@ -359,7 +360,7 @@ export async function start(service: Service) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.attach(container.id, { serviceId, imageId });
|
void logger.attach(container.id, { serviceId, imageId });
|
||||||
|
|
||||||
if (!alreadyStarted) {
|
if (!alreadyStarted) {
|
||||||
logger.logSystemEvent(LogTypes.startServiceSuccess, { service });
|
logger.logSystemEvent(LogTypes.startServiceSuccess, { service });
|
||||||
@ -421,7 +422,7 @@ export function listenToEvents() {
|
|||||||
`serviceId and imageId not defined for service: ${service.serviceName} in ServiceManager.listenToEvents`,
|
`serviceId and imageId not defined for service: ${service.serviceName} in ServiceManager.listenToEvents`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
logger.attach(data.id, {
|
void logger.attach(data.id, {
|
||||||
serviceId,
|
serviceId,
|
||||||
imageId,
|
imageId,
|
||||||
});
|
});
|
||||||
@ -447,7 +448,7 @@ export function listenToEvents() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
(async () => {
|
void (async () => {
|
||||||
try {
|
try {
|
||||||
await listen();
|
await listen();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@ -479,7 +480,7 @@ export async function attachToRunning() {
|
|||||||
`containerId not defined for service: ${service.serviceName} in ServiceManager.attachToRunning`,
|
`containerId not defined for service: ${service.serviceName} in ServiceManager.attachToRunning`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
logger.attach(service.containerId, {
|
void logger.attach(service.containerId, {
|
||||||
serviceId,
|
serviceId,
|
||||||
imageId,
|
imageId,
|
||||||
});
|
});
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
import { detailedDiff as diff } from 'deep-object-diff';
|
import { detailedDiff as diff } from 'deep-object-diff';
|
||||||
import * as Dockerode from 'dockerode';
|
import type * as Dockerode from 'dockerode';
|
||||||
import Duration = require('duration-js');
|
import Duration = require('duration-js');
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { DockerPortOptions, PortMap } from './ports';
|
import type { DockerPortOptions } from './ports';
|
||||||
|
import { PortMap } from './ports';
|
||||||
import * as ComposeUtils from './utils';
|
import * as ComposeUtils from './utils';
|
||||||
import * as updateLock from '../lib/update-lock';
|
import * as updateLock from '../lib/update-lock';
|
||||||
import { sanitiseComposeConfig } from './sanitise';
|
import { sanitiseComposeConfig } from './sanitise';
|
||||||
@ -14,14 +15,16 @@ import * as conversions from '../lib/conversions';
|
|||||||
import { checkInt } from '../lib/validation';
|
import { checkInt } from '../lib/validation';
|
||||||
import { InternalInconsistencyError } from '../lib/errors';
|
import { InternalInconsistencyError } from '../lib/errors';
|
||||||
|
|
||||||
import { EnvVarObject } from '../types';
|
import type { EnvVarObject } from '../types';
|
||||||
import {
|
import type {
|
||||||
ServiceConfig,
|
ServiceConfig,
|
||||||
ServiceConfigArrayField,
|
ServiceConfigArrayField,
|
||||||
ServiceComposeConfig,
|
ServiceComposeConfig,
|
||||||
ConfigMap,
|
ConfigMap,
|
||||||
DeviceMetadata,
|
DeviceMetadata,
|
||||||
DockerDevice,
|
DockerDevice,
|
||||||
|
} from './types/service';
|
||||||
|
import {
|
||||||
ShortMount,
|
ShortMount,
|
||||||
ShortBind,
|
ShortBind,
|
||||||
ShortAnonymousVolume,
|
ShortAnonymousVolume,
|
||||||
@ -108,7 +111,9 @@ export class Service {
|
|||||||
'hostname',
|
'hostname',
|
||||||
].concat(Service.allConfigArrayFields);
|
].concat(Service.allConfigArrayFields);
|
||||||
|
|
||||||
private constructor() {}
|
private constructor() {
|
||||||
|
/* do not allow instancing a service object with `new` */
|
||||||
|
}
|
||||||
|
|
||||||
// The type here is actually ServiceComposeConfig, except that the
|
// The type here is actually ServiceComposeConfig, except that the
|
||||||
// keys must be camelCase'd first
|
// keys must be camelCase'd first
|
||||||
@ -909,11 +914,11 @@ export class Service {
|
|||||||
private getBindsMountsAndVolumes(): {
|
private getBindsMountsAndVolumes(): {
|
||||||
binds: string[];
|
binds: string[];
|
||||||
mounts: Dockerode.MountSettings[];
|
mounts: Dockerode.MountSettings[];
|
||||||
volumes: { [volName: string]: {} };
|
volumes: { [volName: string]: EmptyObject };
|
||||||
} {
|
} {
|
||||||
const binds: string[] = [];
|
const binds: string[] = [];
|
||||||
const mounts: Dockerode.MountSettings[] = [];
|
const mounts: Dockerode.MountSettings[] = [];
|
||||||
const volumes: { [volName: string]: {} } = {};
|
const volumes: { [volName: string]: EmptyObject } = {};
|
||||||
|
|
||||||
for (const volume of this.config.volumes) {
|
for (const volume of this.config.volumes) {
|
||||||
if (LongDefinition.is(volume)) {
|
if (LongDefinition.is(volume)) {
|
||||||
@ -921,7 +926,11 @@ export class Service {
|
|||||||
mounts.push(ComposeUtils.serviceMountToDockerMount(volume));
|
mounts.push(ComposeUtils.serviceMountToDockerMount(volume));
|
||||||
} else {
|
} else {
|
||||||
// Volumes with the string short syntax are acceptable as Docker configs as-is
|
// Volumes with the string short syntax are acceptable as Docker configs as-is
|
||||||
ShortMount.is(volume) ? binds.push(volume) : (volumes[volume] = {});
|
if (ShortMount.is(volume)) {
|
||||||
|
binds.push(volume);
|
||||||
|
} else {
|
||||||
|
volumes[volume] = {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { NetworkInspectInfo as DockerNetworkInspectInfo } from 'dockerode';
|
import type { NetworkInspectInfo as DockerNetworkInspectInfo } from 'dockerode';
|
||||||
|
|
||||||
// TODO: ConfigOnly is part of @types/dockerode@v3.2.0, but that version isn't
|
// TODO: ConfigOnly is part of @types/dockerode@v3.2.0, but that version isn't
|
||||||
// compatible with `resin-docker-build` which is used for `npm run sync`.
|
// compatible with `resin-docker-build` which is used for `npm run sync`.
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import * as Dockerode from 'dockerode';
|
import type * as Dockerode from 'dockerode';
|
||||||
import * as t from 'io-ts';
|
import * as t from 'io-ts';
|
||||||
import { isAbsolute } from 'path';
|
import { isAbsolute } from 'path';
|
||||||
|
|
||||||
import { PortMap } from '../ports';
|
import type { PortMap } from '../ports';
|
||||||
|
|
||||||
export interface ComposeHealthcheck {
|
export interface ComposeHealthcheck {
|
||||||
test: string | string[];
|
test: string | string[];
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import * as imageManager from './images';
|
import * as imageManager from './images';
|
||||||
import Service from './service';
|
import type Service from './service';
|
||||||
import { CompositionStep, generateStep } from './composition-steps';
|
import type { CompositionStep } from './composition-steps';
|
||||||
|
import { generateStep } from './composition-steps';
|
||||||
import { InternalInconsistencyError } from '../lib/errors';
|
import { InternalInconsistencyError } from '../lib/errors';
|
||||||
import { checkString } from '../lib/validation';
|
import { checkString } from '../lib/validation';
|
||||||
|
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import * as Dockerode from 'dockerode';
|
import type * as Dockerode from 'dockerode';
|
||||||
import Duration = require('duration-js');
|
import Duration = require('duration-js');
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { parse as parseCommand } from 'shell-quote';
|
import { parse as parseCommand } from 'shell-quote';
|
||||||
|
|
||||||
import * as constants from '../lib/constants';
|
import * as constants from '../lib/constants';
|
||||||
import { checkTruthy } from '../lib/validation';
|
import { checkTruthy } from '../lib/validation';
|
||||||
import { Service } from './service';
|
import type { Service } from './service';
|
||||||
import {
|
import type {
|
||||||
ComposeHealthcheck,
|
ComposeHealthcheck,
|
||||||
ConfigMap,
|
ConfigMap,
|
||||||
DeviceMetadata,
|
DeviceMetadata,
|
||||||
@ -209,7 +209,7 @@ function getNanoseconds(timeStr: string): number {
|
|||||||
|
|
||||||
export function composeHealthcheckToServiceHealthcheck(
|
export function composeHealthcheckToServiceHealthcheck(
|
||||||
healthcheck: ComposeHealthcheck | null | undefined,
|
healthcheck: ComposeHealthcheck | null | undefined,
|
||||||
): ServiceHealthcheck | {} {
|
): ServiceHealthcheck | EmptyObject {
|
||||||
if (healthcheck == null) {
|
if (healthcheck == null) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -351,9 +351,8 @@ export async function addFeaturesFromLabels(
|
|||||||
} as LongBind);
|
} as LongBind);
|
||||||
|
|
||||||
if (service.config.environment['DOCKER_HOST'] == null) {
|
if (service.config.environment['DOCKER_HOST'] == null) {
|
||||||
service.config.environment[
|
service.config.environment['DOCKER_HOST'] =
|
||||||
'DOCKER_HOST'
|
`unix://${constants.containerDockerSocket}`;
|
||||||
] = `unix://${constants.containerDockerSocket}`;
|
|
||||||
}
|
}
|
||||||
// We keep balena.sock for backwards compatibility
|
// We keep balena.sock for backwards compatibility
|
||||||
if (constants.dockerSocket !== '/var/run/balena.sock') {
|
if (constants.dockerSocket !== '/var/run/balena.sock') {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { VolumeInspectInfo } from 'dockerode';
|
import type { VolumeInspectInfo } from 'dockerode';
|
||||||
|
|
||||||
import { isNotFoundError, InternalInconsistencyError } from '../lib/errors';
|
import { isNotFoundError, InternalInconsistencyError } from '../lib/errors';
|
||||||
import { safeRename } from '../lib/fs-utils';
|
import { safeRename } from '../lib/fs-utils';
|
||||||
@ -10,7 +10,8 @@ import * as LogTypes from '../lib/log-types';
|
|||||||
import log from '../lib/supervisor-console';
|
import log from '../lib/supervisor-console';
|
||||||
import * as logger from '../logger';
|
import * as logger from '../logger';
|
||||||
import { ResourceRecreationAttemptError } from './errors';
|
import { ResourceRecreationAttemptError } from './errors';
|
||||||
import Volume, { VolumeConfig } from './volume';
|
import type { VolumeConfig } from './volume';
|
||||||
|
import Volume from './volume';
|
||||||
|
|
||||||
export interface VolumeNameOpts {
|
export interface VolumeNameOpts {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import * as Docker from 'dockerode';
|
import type * as Docker from 'dockerode';
|
||||||
import isEqual = require('lodash/isEqual');
|
import isEqual = require('lodash/isEqual');
|
||||||
import omitBy = require('lodash/omitBy');
|
import omitBy = require('lodash/omitBy');
|
||||||
|
|
||||||
@ -6,7 +6,7 @@ import * as constants from '../lib/constants';
|
|||||||
import { docker } from '../lib/docker-utils';
|
import { docker } from '../lib/docker-utils';
|
||||||
import { InternalInconsistencyError } from '../lib/errors';
|
import { InternalInconsistencyError } from '../lib/errors';
|
||||||
import * as LogTypes from '../lib/log-types';
|
import * as LogTypes from '../lib/log-types';
|
||||||
import { LabelObject } from '../types';
|
import type { LabelObject } from '../types';
|
||||||
import * as logger from '../logger';
|
import * as logger from '../logger';
|
||||||
import * as ComposeUtils from './utils';
|
import * as ComposeUtils from './utils';
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
export interface ConfigOptions {
|
export interface ConfigOptions {
|
||||||
[key: string]: string | string[];
|
[key: string]: string | string[];
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,8 @@ import * as _ from 'lodash';
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { ConfigOptions, ConfigBackend } from './backend';
|
import type { ConfigOptions } from './backend';
|
||||||
|
import { ConfigBackend } from './backend';
|
||||||
import { exec, exists } from '../../lib/fs-utils';
|
import { exec, exists } from '../../lib/fs-utils';
|
||||||
import * as hostUtils from '../../lib/host-utils';
|
import * as hostUtils from '../../lib/host-utils';
|
||||||
import * as constants from '../../lib/constants';
|
import * as constants from '../../lib/constants';
|
||||||
@ -49,10 +50,10 @@ export class ConfigFs extends ConfigBackend {
|
|||||||
const amlSrcPath = path.join(this.SystemAmlFiles, `${aml}.aml`);
|
const amlSrcPath = path.join(this.SystemAmlFiles, `${aml}.aml`);
|
||||||
// log to system log if the AML doesn't exist...
|
// log to system log if the AML doesn't exist...
|
||||||
if (!(await exists(amlSrcPath))) {
|
if (!(await exists(amlSrcPath))) {
|
||||||
log.error(`Missing AML for \'${aml}\'. Unable to load.`);
|
log.error(`Missing AML for '${aml}'. Unable to load.`);
|
||||||
if (logger) {
|
if (logger) {
|
||||||
logger.logSystemMessage(
|
logger.logSystemMessage(
|
||||||
`Missing AML for \'${aml}\'. Unable to load.`,
|
`Missing AML for '${aml}'. Unable to load.`,
|
||||||
{ aml, path: amlSrcPath },
|
{ aml, path: amlSrcPath },
|
||||||
'Load AML error',
|
'Load AML error',
|
||||||
false,
|
false,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { ConfigOptions, ConfigBackend } from './backend';
|
import type { ConfigOptions } from './backend';
|
||||||
|
import { ConfigBackend } from './backend';
|
||||||
import * as constants from '../../lib/constants';
|
import * as constants from '../../lib/constants';
|
||||||
import log from '../../lib/supervisor-console';
|
import log from '../../lib/supervisor-console';
|
||||||
import { exists } from '../../lib/fs-utils';
|
import { exists } from '../../lib/fs-utils';
|
||||||
@ -14,7 +15,7 @@ const ARRAY_CONFIGS = [
|
|||||||
'gpio',
|
'gpio',
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
type ArrayConfig = typeof ARRAY_CONFIGS[number];
|
type ArrayConfig = (typeof ARRAY_CONFIGS)[number];
|
||||||
|
|
||||||
// Refinement on the ConfigOptions type
|
// Refinement on the ConfigOptions type
|
||||||
// to indicate what properties are arrays
|
// to indicate what properties are arrays
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { ConfigOptions } from './backend';
|
import type { ConfigOptions } from './backend';
|
||||||
import {
|
import {
|
||||||
ExtLinuxParseError,
|
ExtLinuxParseError,
|
||||||
AppendDirectiveError,
|
AppendDirectiveError,
|
||||||
|
@ -1,13 +1,10 @@
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import * as semver from 'semver';
|
import * as semver from 'semver';
|
||||||
|
|
||||||
import { ConfigOptions, ConfigBackend } from './backend';
|
import type { ConfigOptions } from './backend';
|
||||||
import {
|
import { ConfigBackend } from './backend';
|
||||||
ExtlinuxFile,
|
import type { ExtlinuxFile, Directive } from './extlinux-file';
|
||||||
Directive,
|
import { AppendDirective, FDTDirective } from './extlinux-file';
|
||||||
AppendDirective,
|
|
||||||
FDTDirective,
|
|
||||||
} from './extlinux-file';
|
|
||||||
import * as constants from '../../lib/constants';
|
import * as constants from '../../lib/constants';
|
||||||
import log from '../../lib/supervisor-console';
|
import log from '../../lib/supervisor-console';
|
||||||
import { ExtLinuxEnvError, ExtLinuxParseError } from '../../lib/errors';
|
import { ExtLinuxEnvError, ExtLinuxParseError } from '../../lib/errors';
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { ConfigOptions, ConfigBackend } from './backend';
|
import type { ConfigOptions } from './backend';
|
||||||
|
import { ConfigBackend } from './backend';
|
||||||
import * as constants from '../../lib/constants';
|
import * as constants from '../../lib/constants';
|
||||||
import log from '../../lib/supervisor-console';
|
import log from '../../lib/supervisor-console';
|
||||||
import { ExtraUEnvError } from '../../lib/errors';
|
import { ExtraUEnvError } from '../../lib/errors';
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { Extlinux } from './extlinux';
|
import { Extlinux } from './extlinux';
|
||||||
import { ExtraUEnv } from './extra-uEnv';
|
import { ExtraUEnv } from './extra-uEnv';
|
||||||
import { ConfigTxt } from './config-txt';
|
import { ConfigTxt } from './config-txt';
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
|
|
||||||
import { ConfigOptions, ConfigBackend } from './backend';
|
import type { ConfigOptions } from './backend';
|
||||||
|
import { ConfigBackend } from './backend';
|
||||||
import * as constants from '../../lib/constants';
|
import * as constants from '../../lib/constants';
|
||||||
import log from '../../lib/supervisor-console';
|
import log from '../../lib/supervisor-console';
|
||||||
import { ODMDataError } from '../../lib/errors';
|
import { ODMDataError } from '../../lib/errors';
|
||||||
|
@ -6,7 +6,8 @@ import * as constants from '../../lib/constants';
|
|||||||
import { exists } from '../../lib/fs-utils';
|
import { exists } from '../../lib/fs-utils';
|
||||||
import * as hostUtils from '../../lib/host-utils';
|
import * as hostUtils from '../../lib/host-utils';
|
||||||
import log from '../../lib/supervisor-console';
|
import log from '../../lib/supervisor-console';
|
||||||
import { ConfigBackend, ConfigOptions } from './backend';
|
import type { ConfigOptions } from './backend';
|
||||||
|
import { ConfigBackend } from './backend';
|
||||||
|
|
||||||
export class SplashImage extends ConfigBackend {
|
export class SplashImage extends ConfigBackend {
|
||||||
private static readonly BASEPATH = hostUtils.pathOnBoot('splash');
|
private static readonly BASEPATH = hostUtils.pathOnBoot('splash');
|
||||||
@ -155,7 +156,7 @@ export class SplashImage extends ConfigBackend {
|
|||||||
return SplashImage.CONFIGS.includes(this.stripPrefix(name).toLowerCase());
|
return SplashImage.CONFIGS.includes(this.stripPrefix(name).toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async matches(_deviceType: string): Promise<boolean> {
|
public async matches(): Promise<boolean> {
|
||||||
// all device types
|
// all device types
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -191,7 +192,10 @@ export class SplashImage extends ConfigBackend {
|
|||||||
: await this.readSplashImage(SplashImage.DEFAULT);
|
: await this.readSplashImage(SplashImage.DEFAULT);
|
||||||
|
|
||||||
// If it is a data URI get only the data part
|
// If it is a data URI get only the data part
|
||||||
const [, image] = value.startsWith('data:') ? value.split(',') : [, value];
|
let image = value;
|
||||||
|
if (value.startsWith('data:')) {
|
||||||
|
[, image] = value.split(',');
|
||||||
|
}
|
||||||
|
|
||||||
// Rewrite the splash image
|
// Rewrite the splash image
|
||||||
await this.writeSplashImage(image);
|
await this.writeSplashImage(image);
|
||||||
|
@ -5,7 +5,7 @@ import * as constants from '../lib/constants';
|
|||||||
import * as hostUtils from '../lib/host-utils';
|
import * as hostUtils from '../lib/host-utils';
|
||||||
import * as osRelease from '../lib/os-release';
|
import * as osRelease from '../lib/os-release';
|
||||||
import { readLock, writeLock } from '../lib/update-lock';
|
import { readLock, writeLock } from '../lib/update-lock';
|
||||||
import * as Schema from './schema';
|
import type * as Schema from './schema';
|
||||||
|
|
||||||
export default class ConfigJsonConfigBackend {
|
export default class ConfigJsonConfigBackend {
|
||||||
private readonly readLockConfigJson: () => Bluebird.Disposer<() => void>;
|
private readonly readLockConfigJson: () => Bluebird.Disposer<() => void>;
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import type { Knex } from 'knex';
|
import type { Knex } from 'knex';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import StrictEventEmitter from 'strict-event-emitter-types';
|
import type StrictEventEmitter from 'strict-event-emitter-types';
|
||||||
import { inspect } from 'util';
|
import { inspect } from 'util';
|
||||||
import { generateUniqueKey } from '../lib/register-device';
|
import { generateUniqueKey } from '../lib/register-device';
|
||||||
|
|
||||||
import { Either, isLeft, isRight, Right } from 'fp-ts/lib/Either';
|
import type { Either, Right } from 'fp-ts/lib/Either';
|
||||||
|
import { isLeft, isRight } from 'fp-ts/lib/Either';
|
||||||
import * as t from 'io-ts';
|
import * as t from 'io-ts';
|
||||||
|
|
||||||
import ConfigJsonConfigBackend from './configJson';
|
import ConfigJsonConfigBackend from './configJson';
|
||||||
|
|
||||||
import * as FnSchema from './functions';
|
import * as FnSchema from './functions';
|
||||||
import * as Schema from './schema';
|
import * as Schema from './schema';
|
||||||
import { SchemaReturn, SchemaTypeKey, schemaTypes } from './schema-type';
|
import type { SchemaReturn, SchemaTypeKey } from './schema-type';
|
||||||
|
import { schemaTypes } from './schema-type';
|
||||||
|
|
||||||
import * as db from '../db';
|
import * as db from '../db';
|
||||||
import {
|
import {
|
||||||
@ -43,9 +45,9 @@ class ConfigEvents extends (EventEmitter as new () => ConfigEventEmitter) {}
|
|||||||
const events = new ConfigEvents();
|
const events = new ConfigEvents();
|
||||||
|
|
||||||
// Expose methods which make this module act as an EventEmitter
|
// Expose methods which make this module act as an EventEmitter
|
||||||
export const on: typeof events['on'] = events.on.bind(events);
|
export const on: (typeof events)['on'] = events.on.bind(events);
|
||||||
export const once: typeof events['once'] = events.once.bind(events);
|
export const once: (typeof events)['once'] = events.once.bind(events);
|
||||||
export const removeListener: typeof events['removeListener'] =
|
export const removeListener: (typeof events)['removeListener'] =
|
||||||
events.removeListener.bind(events);
|
events.removeListener.bind(events);
|
||||||
|
|
||||||
export async function get<T extends SchemaTypeKey>(
|
export async function get<T extends SchemaTypeKey>(
|
||||||
@ -54,7 +56,7 @@ export async function get<T extends SchemaTypeKey>(
|
|||||||
): Promise<SchemaReturn<T>> {
|
): Promise<SchemaReturn<T>> {
|
||||||
const $db = trx || db.models;
|
const $db = trx || db.models;
|
||||||
|
|
||||||
if (Schema.schema.hasOwnProperty(key)) {
|
if (Object.prototype.hasOwnProperty.call(Schema.schema, key)) {
|
||||||
const schemaKey = key as Schema.SchemaKey;
|
const schemaKey = key as Schema.SchemaKey;
|
||||||
|
|
||||||
return getSchema(schemaKey, $db).then((value) => {
|
return getSchema(schemaKey, $db).then((value) => {
|
||||||
@ -80,7 +82,7 @@ export async function get<T extends SchemaTypeKey>(
|
|||||||
// the type system happy
|
// the type system happy
|
||||||
return checkValueDecode(decoded, key, value) && decoded.right;
|
return checkValueDecode(decoded, key, value) && decoded.right;
|
||||||
});
|
});
|
||||||
} else if (FnSchema.fnSchema.hasOwnProperty(key)) {
|
} else if (Object.prototype.hasOwnProperty.call(FnSchema.fnSchema, key)) {
|
||||||
const fnKey = key as FnSchema.FnSchemaKey;
|
const fnKey = key as FnSchema.FnSchemaKey;
|
||||||
// Cast the promise as something that produces an unknown, and this means that
|
// Cast the promise as something that produces an unknown, and this means that
|
||||||
// we can validate the output of the function as well, ensuring that the type matches
|
// we can validate the output of the function as well, ensuring that the type matches
|
||||||
@ -241,10 +243,12 @@ async function getSchema<T extends Schema.SchemaKey>(
|
|||||||
value = await configJsonBackend.get(key);
|
value = await configJsonBackend.get(key);
|
||||||
break;
|
break;
|
||||||
case 'db':
|
case 'db':
|
||||||
|
{
|
||||||
const [conf] = await $db('config').select('value').where({ key });
|
const [conf] = await $db('config').select('value').where({ key });
|
||||||
if (conf != null) {
|
if (conf != null) {
|
||||||
return conf.value;
|
return conf.value;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -265,7 +269,7 @@ function validateConfigMap<T extends SchemaTypeKey>(
|
|||||||
// throw if any value fails verification
|
// throw if any value fails verification
|
||||||
return _.mapValues(configMap, (value, key) => {
|
return _.mapValues(configMap, (value, key) => {
|
||||||
if (
|
if (
|
||||||
!Schema.schema.hasOwnProperty(key) ||
|
!Object.prototype.hasOwnProperty.call(Schema.schema, key) ||
|
||||||
!Schema.schema[key as Schema.SchemaKey].mutable
|
!Schema.schema[key as Schema.SchemaKey].mutable
|
||||||
) {
|
) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
|
@ -54,12 +54,13 @@ export const PermissiveNumber = new t.Type<number, string | number>(
|
|||||||
switch (typeof v) {
|
switch (typeof v) {
|
||||||
case 'number':
|
case 'number':
|
||||||
return t.success(v);
|
return t.success(v);
|
||||||
case 'string':
|
case 'string': {
|
||||||
const i = parseInt(v, 10);
|
const i = parseInt(v, 10);
|
||||||
if (Number.isNaN(i)) {
|
if (Number.isNaN(i)) {
|
||||||
return t.failure(v, c);
|
return t.failure(v, c);
|
||||||
}
|
}
|
||||||
return t.success(i);
|
return t.success(i);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return t.failure(v, c);
|
return t.failure(v, c);
|
||||||
}
|
}
|
||||||
@ -73,7 +74,7 @@ export const PermissiveNumber = new t.Type<number, string | number>(
|
|||||||
|
|
||||||
// Define this differently, so that we can add a generic to it
|
// Define this differently, so that we can add a generic to it
|
||||||
export class StringJSON<T> extends t.Type<T, string> {
|
export class StringJSON<T> extends t.Type<T, string> {
|
||||||
public readonly _tag: 'StringJSON' = 'StringJSON';
|
public readonly _tag: 'StringJSON' = 'StringJSON' as const;
|
||||||
constructor(type: t.InterfaceType<any>) {
|
constructor(type: t.InterfaceType<any>) {
|
||||||
super(
|
super(
|
||||||
'StringJSON',
|
'StringJSON',
|
||||||
|
@ -4,9 +4,9 @@ import * as Bluebird from 'bluebird';
|
|||||||
import * as config from '../config';
|
import * as config from '../config';
|
||||||
import * as constants from '../lib/constants';
|
import * as constants from '../lib/constants';
|
||||||
import { getMetaOSRelease } from '../lib/os-release';
|
import { getMetaOSRelease } from '../lib/os-release';
|
||||||
import { EnvVarObject } from '../types';
|
import type { EnvVarObject } from '../types';
|
||||||
import { allBackends as Backends } from './backends';
|
import { allBackends as Backends } from './backends';
|
||||||
import { ConfigOptions, ConfigBackend } from './backends/backend';
|
import type { ConfigOptions, ConfigBackend } from './backends/backend';
|
||||||
|
|
||||||
export async function getSupportedBackends(): Promise<ConfigBackend[]> {
|
export async function getSupportedBackends(): Promise<ConfigBackend[]> {
|
||||||
// Get required information to find supported backends
|
// Get required information to find supported backends
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { knex, Knex } from 'knex';
|
import type { Knex } from 'knex';
|
||||||
|
import { knex } from 'knex';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
|
@ -8,10 +8,8 @@ import * as logger from '../logger';
|
|||||||
import * as config from '../config';
|
import * as config from '../config';
|
||||||
import * as hostConfig from '../host-config';
|
import * as hostConfig from '../host-config';
|
||||||
import * as applicationManager from '../compose/application-manager';
|
import * as applicationManager from '../compose/application-manager';
|
||||||
import {
|
import type { CompositionStepAction } from '../compose/composition-steps';
|
||||||
CompositionStepAction,
|
import { generateStep } from '../compose/composition-steps';
|
||||||
generateStep,
|
|
||||||
} from '../compose/composition-steps';
|
|
||||||
import * as commitStore from '../compose/commit';
|
import * as commitStore from '../compose/commit';
|
||||||
import { getApp } from '../device-state/db-format';
|
import { getApp } from '../device-state/db-format';
|
||||||
import * as TargetState from '../device-state/target-state';
|
import * as TargetState from '../device-state/target-state';
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import * as express from 'express';
|
import type * as express from 'express';
|
||||||
import * as memoizee from 'memoizee';
|
import * as memoizee from 'memoizee';
|
||||||
import { TypedError } from 'typed-error';
|
import { TypedError } from 'typed-error';
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ type ScopeCheckCollection = {
|
|||||||
* The scopes which a key can cover.
|
* The scopes which a key can cover.
|
||||||
*/
|
*/
|
||||||
type ScopeTypes = {
|
type ScopeTypes = {
|
||||||
global: {};
|
global: NonNullable<unknown>;
|
||||||
app: {
|
app: {
|
||||||
appId: number;
|
appId: number;
|
||||||
};
|
};
|
||||||
@ -222,11 +222,11 @@ async function generateKey(
|
|||||||
// remove the cached lookup for the key
|
// remove the cached lookup for the key
|
||||||
const [apiKey] = secrets;
|
const [apiKey] = secrets;
|
||||||
if (apiKey != null) {
|
if (apiKey != null) {
|
||||||
getApiKeyByKey.clear(apiKey.key);
|
await getApiKeyByKey.clear(apiKey.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove the cached value for this lookup
|
// remove the cached value for this lookup
|
||||||
getApiKeyForService.clear(appId, serviceName);
|
await getApiKeyForService.clear(appId, serviceName);
|
||||||
|
|
||||||
// return a new API key
|
// return a new API key
|
||||||
return await createNewKey(appId, serviceName, scopes);
|
return await createNewKey(appId, serviceName, scopes);
|
||||||
|
@ -100,9 +100,11 @@ export class SupervisorAPI {
|
|||||||
log.error('Failed to stop Supervisor API');
|
log.error('Failed to stop Supervisor API');
|
||||||
return reject(err);
|
return reject(err);
|
||||||
}
|
}
|
||||||
options?.errored
|
if (options?.errored) {
|
||||||
? log.error('Stopped Supervisor API')
|
log.error('Stopped Supervisor API');
|
||||||
: log.info('Stopped Supervisor API');
|
} else {
|
||||||
|
log.info('Stopped Supervisor API');
|
||||||
|
}
|
||||||
return resolve();
|
return resolve();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import * as morgan from 'morgan';
|
import * as morgan from 'morgan';
|
||||||
import { Request } from 'express';
|
import type { Request } from 'express';
|
||||||
|
|
||||||
import log from '../../lib/supervisor-console';
|
import log from '../../lib/supervisor-console';
|
||||||
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import * as express from 'express';
|
import * as express from 'express';
|
||||||
import * as _ from 'lodash';
|
|
||||||
import type { Response } from 'express';
|
import type { Response } from 'express';
|
||||||
|
|
||||||
import * as actions from './actions';
|
import * as actions from './actions';
|
||||||
import { AuthorizedRequest } from './api-keys';
|
import type { AuthorizedRequest } from './api-keys';
|
||||||
import * as eventTracker from '../event-tracker';
|
import * as eventTracker from '../event-tracker';
|
||||||
import * as deviceState from '../device-state';
|
import type * as deviceState from '../device-state';
|
||||||
|
|
||||||
import * as constants from '../lib/constants';
|
import * as constants from '../lib/constants';
|
||||||
import { checkInt, checkTruthy } from '../lib/validation';
|
import { checkInt, checkTruthy } from '../lib/validation';
|
||||||
@ -15,7 +14,7 @@ import {
|
|||||||
isBadRequestError,
|
isBadRequestError,
|
||||||
UpdatesLockedError,
|
UpdatesLockedError,
|
||||||
} from '../lib/errors';
|
} from '../lib/errors';
|
||||||
import { CompositionStepAction } from '../compose/composition-steps';
|
import type { CompositionStepAction } from '../compose/composition-steps';
|
||||||
|
|
||||||
const disallowedHostConfigPatchFields = ['local_ip', 'local_port'];
|
const disallowedHostConfigPatchFields = ['local_ip', 'local_port'];
|
||||||
|
|
||||||
|
@ -5,8 +5,8 @@ import * as _ from 'lodash';
|
|||||||
import * as deviceState from '../device-state';
|
import * as deviceState from '../device-state';
|
||||||
import * as apiBinder from '../api-binder';
|
import * as apiBinder from '../api-binder';
|
||||||
import * as applicationManager from '../compose/application-manager';
|
import * as applicationManager from '../compose/application-manager';
|
||||||
import { CompositionStepAction } from '../compose/composition-steps';
|
import type { CompositionStepAction } from '../compose/composition-steps';
|
||||||
import { Service } from '../compose/service';
|
import type { Service } from '../compose/service';
|
||||||
import Volume from '../compose/volume';
|
import Volume from '../compose/volume';
|
||||||
import * as commitStore from '../compose/commit';
|
import * as commitStore from '../compose/commit';
|
||||||
import * as config from '../config';
|
import * as config from '../config';
|
||||||
@ -26,7 +26,7 @@ import {
|
|||||||
BadRequestError,
|
BadRequestError,
|
||||||
} from '../lib/errors';
|
} from '../lib/errors';
|
||||||
import { isVPNActive } from '../network';
|
import { isVPNActive } from '../network';
|
||||||
import { AuthorizedRequest } from './api-keys';
|
import type { AuthorizedRequest } from './api-keys';
|
||||||
import { fromV2TargetState } from '../lib/legacy';
|
import { fromV2TargetState } from '../lib/legacy';
|
||||||
import * as actions from './actions';
|
import * as actions from './actions';
|
||||||
import { v2ServiceEndpointError } from './messages';
|
import { v2ServiceEndpointError } from './messages';
|
||||||
|
@ -6,14 +6,14 @@ import * as config from './config';
|
|||||||
import * as db from './db';
|
import * as db from './db';
|
||||||
import * as logger from './logger';
|
import * as logger from './logger';
|
||||||
import * as dbus from './lib/dbus';
|
import * as dbus from './lib/dbus';
|
||||||
import { EnvVarObject } from './types';
|
import type { EnvVarObject } from './types';
|
||||||
import { UnitNotLoadedError } from './lib/errors';
|
import { UnitNotLoadedError } from './lib/errors';
|
||||||
import { checkInt, checkTruthy } from './lib/validation';
|
import { checkInt, checkTruthy } from './lib/validation';
|
||||||
import log from './lib/supervisor-console';
|
import log from './lib/supervisor-console';
|
||||||
import * as configUtils from './config/utils';
|
import * as configUtils from './config/utils';
|
||||||
import { SchemaTypeKey } from './config/schema-type';
|
import type { SchemaTypeKey } from './config/schema-type';
|
||||||
import { matchesAnyBootConfig } from './config/backends';
|
import { matchesAnyBootConfig } from './config/backends';
|
||||||
import { ConfigBackend } from './config/backends/backend';
|
import type { ConfigBackend } from './config/backends/backend';
|
||||||
import { Odmdata } from './config/backends/odmdata';
|
import { Odmdata } from './config/backends/odmdata';
|
||||||
import * as fsUtils from './lib/fs-utils';
|
import * as fsUtils from './lib/fs-utils';
|
||||||
import { pathOnRoot } from './lib/host-utils';
|
import { pathOnRoot } from './lib/host-utils';
|
||||||
@ -627,6 +627,7 @@ function changeRequired(
|
|||||||
// Set changeRequired to false so we do not get stuck in a loop trying to fix this mismatch
|
// Set changeRequired to false so we do not get stuck in a loop trying to fix this mismatch
|
||||||
aChangeIsRequired = false;
|
aChangeIsRequired = false;
|
||||||
}
|
}
|
||||||
|
throw e;
|
||||||
default:
|
default:
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ import * as Bluebird from 'bluebird';
|
|||||||
import { stripIndent } from 'common-tags';
|
import { stripIndent } from 'common-tags';
|
||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import StrictEventEmitter from 'strict-event-emitter-types';
|
import type StrictEventEmitter from 'strict-event-emitter-types';
|
||||||
import { isRight } from 'fp-ts/lib/Either';
|
import { isRight } from 'fp-ts/lib/Either';
|
||||||
import Reporter from 'io-ts-reporters';
|
import Reporter from 'io-ts-reporters';
|
||||||
import prettyMs = require('pretty-ms');
|
import prettyMs = require('pretty-ms');
|
||||||
@ -31,14 +31,14 @@ import { loadTargetFromFile } from './device-state/preload';
|
|||||||
import * as applicationManager from './compose/application-manager';
|
import * as applicationManager from './compose/application-manager';
|
||||||
import * as commitStore from './compose/commit';
|
import * as commitStore from './compose/commit';
|
||||||
|
|
||||||
import {
|
import type {
|
||||||
DeviceLegacyState,
|
DeviceLegacyState,
|
||||||
InstancedDeviceState,
|
InstancedDeviceState,
|
||||||
TargetState,
|
|
||||||
DeviceState,
|
DeviceState,
|
||||||
DeviceReport,
|
DeviceReport,
|
||||||
AppState,
|
AppState,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
import { TargetState } from './types';
|
||||||
import type {
|
import type {
|
||||||
CompositionStepT,
|
CompositionStepT,
|
||||||
CompositionStepAction,
|
CompositionStepAction,
|
||||||
@ -85,11 +85,11 @@ type DeviceStateEventEmitter = StrictEventEmitter<
|
|||||||
DeviceStateEvents
|
DeviceStateEvents
|
||||||
>;
|
>;
|
||||||
const events = new EventEmitter() as DeviceStateEventEmitter;
|
const events = new EventEmitter() as DeviceStateEventEmitter;
|
||||||
export const on: typeof events['on'] = events.on.bind(events);
|
export const on: (typeof events)['on'] = events.on.bind(events);
|
||||||
export const once: typeof events['once'] = events.once.bind(events);
|
export const once: (typeof events)['once'] = events.once.bind(events);
|
||||||
export const removeListener: typeof events['removeListener'] =
|
export const removeListener: (typeof events)['removeListener'] =
|
||||||
events.removeListener.bind(events);
|
events.removeListener.bind(events);
|
||||||
export const removeAllListeners: typeof events['removeAllListeners'] =
|
export const removeAllListeners: (typeof events)['removeAllListeners'] =
|
||||||
events.removeAllListeners.bind(events);
|
events.removeAllListeners.bind(events);
|
||||||
|
|
||||||
export type DeviceStateStepTarget = 'reboot' | 'shutdown' | 'noop';
|
export type DeviceStateStepTarget = 'reboot' | 'shutdown' | 'noop';
|
||||||
@ -194,9 +194,13 @@ export async function initNetworkChecks({
|
|||||||
apiEndpoint: config.ConfigType<'apiEndpoint'>;
|
apiEndpoint: config.ConfigType<'apiEndpoint'>;
|
||||||
connectivityCheckEnabled: config.ConfigType<'connectivityCheckEnabled'>;
|
connectivityCheckEnabled: config.ConfigType<'connectivityCheckEnabled'>;
|
||||||
}) {
|
}) {
|
||||||
network.startConnectivityCheck(apiEndpoint, connectivityCheckEnabled, (c) => {
|
await network.startConnectivityCheck(
|
||||||
|
apiEndpoint,
|
||||||
|
connectivityCheckEnabled,
|
||||||
|
(c) => {
|
||||||
connected = c;
|
connected = c;
|
||||||
});
|
},
|
||||||
|
);
|
||||||
config.on('change', function (changedConfig) {
|
config.on('change', function (changedConfig) {
|
||||||
if (changedConfig.connectivityCheckEnabled != null) {
|
if (changedConfig.connectivityCheckEnabled != null) {
|
||||||
network.enableConnectivityCheck(changedConfig.connectivityCheckEnabled);
|
network.enableConnectivityCheck(changedConfig.connectivityCheckEnabled);
|
||||||
@ -240,7 +244,7 @@ export async function loadInitialState() {
|
|||||||
]);
|
]);
|
||||||
maxPollTime = conf.appUpdatePollInterval;
|
maxPollTime = conf.appUpdatePollInterval;
|
||||||
|
|
||||||
initNetworkChecks(conf);
|
await initNetworkChecks(conf);
|
||||||
|
|
||||||
if (!conf.initialConfigSaved) {
|
if (!conf.initialConfigSaved) {
|
||||||
await saveInitialConfig();
|
await saveInitialConfig();
|
||||||
@ -851,8 +855,8 @@ export function triggerApplyTarget({
|
|||||||
}
|
}
|
||||||
applyCancelled = false;
|
applyCancelled = false;
|
||||||
applyInProgress = true;
|
applyInProgress = true;
|
||||||
new Promise((resolve, reject) => {
|
void new Promise((resolve, reject) => {
|
||||||
setTimeout(delay).then(resolve);
|
void setTimeout(delay).then(resolve);
|
||||||
cancelDelay = reject;
|
cancelDelay = reject;
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import * as db from '../db';
|
import type * as db from '../db';
|
||||||
import * as targetStateCache from './target-state-cache';
|
import * as targetStateCache from './target-state-cache';
|
||||||
import { DatabaseApp, DatabaseService } from './target-state-cache';
|
import type { DatabaseApp, DatabaseService } from './target-state-cache';
|
||||||
|
|
||||||
import App from '../compose/app';
|
import App from '../compose/app';
|
||||||
import * as images from '../compose/images';
|
import * as images from '../compose/images';
|
||||||
|
|
||||||
import {
|
import type {
|
||||||
InstancedAppState,
|
InstancedAppState,
|
||||||
TargetApp,
|
TargetApp,
|
||||||
TargetApps,
|
TargetApps,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { fromV2TargetApps, TargetAppsV2 } from '../lib/legacy';
|
import type { TargetAppsV2 } from '../lib/legacy';
|
||||||
import { AppsJsonFormat, TargetApp, TargetRelease } from '../types';
|
import { fromV2TargetApps } from '../lib/legacy';
|
||||||
|
import type { AppsJsonFormat, TargetApp, TargetRelease } from '../types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a single app from single container format into
|
* Converts a single app from single container format into
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
|
|
||||||
import { Image, imageFromService } from '../compose/images';
|
import type { Image } from '../compose/images';
|
||||||
|
import { imageFromService } from '../compose/images';
|
||||||
import { NumericIdentifier } from '../types';
|
import { NumericIdentifier } from '../types';
|
||||||
import * as deviceState from '../device-state';
|
import * as deviceState from '../device-state';
|
||||||
import * as config from '../config';
|
import * as config from '../config';
|
||||||
|
@ -2,7 +2,7 @@ import * as _ from 'lodash';
|
|||||||
|
|
||||||
import * as config from '../config';
|
import * as config from '../config';
|
||||||
import * as db from '../db';
|
import * as db from '../db';
|
||||||
import { TargetAppClass } from '../types';
|
import type { TargetAppClass } from '../types';
|
||||||
|
|
||||||
// We omit the id (which does appear in the db) in this type, as we don't use it
|
// We omit the id (which does appear in the db) in this type, as we don't use it
|
||||||
// at all, and we can use the below type for both insertion and retrieval.
|
// at all, and we can use the below type for both insertion and retrieval.
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import * as url from 'url';
|
import * as url from 'url';
|
||||||
import { setTimeout } from 'timers/promises';
|
import { setTimeout } from 'timers/promises';
|
||||||
import * as _ from 'lodash';
|
|
||||||
import * as Bluebird from 'bluebird';
|
import * as Bluebird from 'bluebird';
|
||||||
import type StrictEventEmitter from 'strict-event-emitter-types';
|
import type StrictEventEmitter from 'strict-event-emitter-types';
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { EventEmitter } from 'events';
|
import { EventEmitter } from 'events';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import StrictEventEmitter from 'strict-event-emitter-types';
|
import type StrictEventEmitter from 'strict-event-emitter-types';
|
||||||
import { TargetState } from './types/state';
|
import type { TargetState } from './types/state';
|
||||||
|
|
||||||
export interface GlobalEvents {
|
export interface GlobalEvents {
|
||||||
deviceProvisioned: void;
|
deviceProvisioned: void;
|
||||||
@ -10,10 +10,6 @@ export interface GlobalEvents {
|
|||||||
|
|
||||||
type GlobalEventEmitter = StrictEventEmitter<EventEmitter, GlobalEvents>;
|
type GlobalEventEmitter = StrictEventEmitter<EventEmitter, GlobalEvents>;
|
||||||
|
|
||||||
export class GlobalEventBus extends (EventEmitter as new () => GlobalEventEmitter) {
|
export const getInstance = _.once(
|
||||||
public constructor() {
|
() => new EventEmitter() as GlobalEventEmitter,
|
||||||
super();
|
);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const getInstance = _.once(() => new GlobalEventBus());
|
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import mask = require('json-mask');
|
import mask = require('json-mask');
|
||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import log from './lib/supervisor-console';
|
import log from './lib/supervisor-console';
|
||||||
|
|
||||||
export type EventTrackProperties = Dictionary<any>;
|
export type EventTrackProperties = Dictionary<any>;
|
||||||
@ -16,7 +14,7 @@ const mixpanelMask = [
|
|||||||
'stateDiff/local(os_version,supervisor_version,ip_address,apps/*/services)',
|
'stateDiff/local(os_version,supervisor_version,ip_address,apps/*/services)',
|
||||||
].join(',');
|
].join(',');
|
||||||
|
|
||||||
export async function track(
|
export function track(
|
||||||
event: string,
|
event: string,
|
||||||
properties: EventTrackProperties | Error = {},
|
properties: EventTrackProperties | Error = {},
|
||||||
) {
|
) {
|
||||||
@ -34,6 +32,6 @@ export async function track(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Don't send potentially sensitive information, by using a whitelist
|
// Don't send potentially sensitive information, by using a whitelist
|
||||||
properties = mask(properties, mixpanelMask);
|
properties = mask(properties, mixpanelMask) || {};
|
||||||
log.event('Event:', event, JSON.stringify(properties));
|
log.event('Event:', event, JSON.stringify(properties));
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { PinejsClientRequest } from 'pinejs-client-request';
|
import type { PinejsClientRequest } from 'pinejs-client-request';
|
||||||
|
|
||||||
import * as Bluebird from 'bluebird';
|
import * as Bluebird from 'bluebird';
|
||||||
import * as config from '../config';
|
import * as config from '../config';
|
||||||
|
@ -10,7 +10,7 @@ export const initialized = _.once(async () => {
|
|||||||
|
|
||||||
config.on('change', (conf) => {
|
config.on('change', (conf) => {
|
||||||
if (conf.hostDiscoverability != null) {
|
if (conf.hostDiscoverability != null) {
|
||||||
switchDiscoverability(conf.hostDiscoverability);
|
void switchDiscoverability(conf.hostDiscoverability);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import { Blueprint, Contract, ContractObject } from '@balena/contrato';
|
|||||||
|
|
||||||
import { ContractValidationError, InternalInconsistencyError } from './errors';
|
import { ContractValidationError, InternalInconsistencyError } from './errors';
|
||||||
import { checkTruthy } from './validation';
|
import { checkTruthy } from './validation';
|
||||||
import { TargetApps } from '../types';
|
import type { TargetApps } from '../types';
|
||||||
|
|
||||||
export { ContractObject };
|
export { ContractObject };
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import { EnvVarObject } from '../types';
|
import type { EnvVarObject } from '../types';
|
||||||
|
|
||||||
import log from '../lib/supervisor-console';
|
import log from '../lib/supervisor-console';
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { DockerProgress, ProgressCallback } from 'docker-progress';
|
import type { ProgressCallback } from 'docker-progress';
|
||||||
|
import { DockerProgress } from 'docker-progress';
|
||||||
import * as Dockerode from 'dockerode';
|
import * as Dockerode from 'dockerode';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import * as memoizee from 'memoizee';
|
import * as memoizee from 'memoizee';
|
||||||
@ -6,7 +7,7 @@ import * as memoizee from 'memoizee';
|
|||||||
import { applyDelta, OutOfSyncError } from 'docker-delta';
|
import { applyDelta, OutOfSyncError } from 'docker-delta';
|
||||||
import DockerToolbelt = require('docker-toolbelt');
|
import DockerToolbelt = require('docker-toolbelt');
|
||||||
|
|
||||||
import { SchemaReturn } from '../config/schema-type';
|
import type { SchemaReturn } from '../config/schema-type';
|
||||||
import { envArrayToObject } from './conversions';
|
import { envArrayToObject } from './conversions';
|
||||||
import {
|
import {
|
||||||
DeltaStillProcessingError,
|
DeltaStillProcessingError,
|
||||||
@ -14,7 +15,7 @@ import {
|
|||||||
InvalidNetGatewayError,
|
InvalidNetGatewayError,
|
||||||
} from './errors';
|
} from './errors';
|
||||||
import * as request from './request';
|
import * as request from './request';
|
||||||
import { EnvVarObject } from '../types';
|
import type { EnvVarObject } from '../types';
|
||||||
|
|
||||||
import log from './supervisor-console';
|
import log from './supervisor-console';
|
||||||
|
|
||||||
@ -191,6 +192,8 @@ export async function fetchDeltaWithProgress(
|
|||||||
`Got ${res.statusCode} when requesting an image from delta server.`,
|
`Got ${res.statusCode} when requesting an image from delta server.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
// lexical declarations inside a case clause need to be wrapped in a block
|
||||||
const deltaUrl = res.headers['location'];
|
const deltaUrl = res.headers['location'];
|
||||||
const deltaSrc = deltaSourceId;
|
const deltaSrc = deltaSourceId;
|
||||||
const resumeOpts = {
|
const resumeOpts = {
|
||||||
@ -206,6 +209,7 @@ export async function fetchDeltaWithProgress(
|
|||||||
onProgress,
|
onProgress,
|
||||||
logFn,
|
logFn,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
if (res.statusCode !== 200) {
|
if (res.statusCode !== 200) {
|
||||||
@ -213,6 +217,8 @@ export async function fetchDeltaWithProgress(
|
|||||||
`Got ${res.statusCode} when requesting v3 delta from delta server.`,
|
`Got ${res.statusCode} when requesting v3 delta from delta server.`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
// lexical declarations inside a case clause need to be wrapped in a block
|
||||||
let name;
|
let name;
|
||||||
try {
|
try {
|
||||||
name = JSON.parse(data).name;
|
name = JSON.parse(data).name;
|
||||||
@ -222,6 +228,7 @@ export async function fetchDeltaWithProgress(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
id = await applyBalenaDelta(name, token, onProgress, logFn);
|
id = await applyBalenaDelta(name, token, onProgress, logFn);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unsupported delta version: ${deltaOpts.deltaVersion}`);
|
throw new Error(`Unsupported delta version: ${deltaOpts.deltaVersion}`);
|
||||||
@ -295,7 +302,7 @@ export async function getNetworkGateway(networkName: string): Promise<string> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyRsyncDelta(
|
async function applyRsyncDelta(
|
||||||
imgSrc: string,
|
imgSrc: string,
|
||||||
deltaUrl: string,
|
deltaUrl: string,
|
||||||
applyTimeout: number,
|
applyTimeout: number,
|
||||||
@ -305,8 +312,8 @@ function applyRsyncDelta(
|
|||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
logFn(`Applying rsync delta: ${deltaUrl}`);
|
logFn(`Applying rsync delta: ${deltaUrl}`);
|
||||||
|
|
||||||
return new Promise(async (resolve, reject) => {
|
|
||||||
const resumable = await request.getResumableRequest();
|
const resumable = await request.getResumableRequest();
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
const req = resumable(Object.assign({ url: deltaUrl }, opts));
|
const req = resumable(Object.assign({ url: deltaUrl }, opts));
|
||||||
req
|
req
|
||||||
.on('progress', onProgress)
|
.on('progress', onProgress)
|
||||||
|
@ -15,7 +15,7 @@ export const initialised = _.once(async () => {
|
|||||||
// apply firewall whenever relevant config changes occur...
|
// apply firewall whenever relevant config changes occur...
|
||||||
config.on('change', ({ firewallMode, localMode }) => {
|
config.on('change', ({ firewallMode, localMode }) => {
|
||||||
if (firewallMode || localMode != null) {
|
if (firewallMode || localMode != null) {
|
||||||
applyFirewall({ firewallMode, localMode });
|
void applyFirewall({ firewallMode, localMode });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import * as _ from 'lodash';
|
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { exec as execSync } from 'child_process';
|
import { exec as execSync } from 'child_process';
|
||||||
|
@ -5,7 +5,7 @@ import { exec, exists } from './fs-utils';
|
|||||||
|
|
||||||
function withBase(base: string) {
|
function withBase(base: string) {
|
||||||
function withPath(): string;
|
function withPath(): string;
|
||||||
function withPath(path: string): string;
|
function withPath(singlePath: string): string;
|
||||||
function withPath(...paths: string[]): string[];
|
function withPath(...paths: string[]): string[];
|
||||||
function withPath(...paths: string[]): string[] | string {
|
function withPath(...paths: string[]): string[] | string {
|
||||||
if (arguments.length === 0) {
|
if (arguments.length === 0) {
|
||||||
@ -42,7 +42,10 @@ export const pathExistsOnState = async (p: string) =>
|
|||||||
await exists(pathOnState(p));
|
await exists(pathOnState(p));
|
||||||
|
|
||||||
class CodedError extends Error {
|
class CodedError extends Error {
|
||||||
constructor(msg: string, readonly code: number | string) {
|
constructor(
|
||||||
|
msg: string,
|
||||||
|
readonly code: number | string,
|
||||||
|
) {
|
||||||
super(msg);
|
super(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,10 @@ import { exec } from './fs-utils';
|
|||||||
import log from './supervisor-console';
|
import log from './supervisor-console';
|
||||||
|
|
||||||
export class IPTablesRuleError extends TypedError {
|
export class IPTablesRuleError extends TypedError {
|
||||||
public constructor(err: string | Error, public ruleset: string) {
|
public constructor(
|
||||||
|
err: string | Error,
|
||||||
|
public ruleset: string,
|
||||||
|
) {
|
||||||
super(err);
|
super(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { ChildProcess, spawn } from 'child_process';
|
import type { ChildProcess } from 'child_process';
|
||||||
|
import { spawn } from 'child_process';
|
||||||
|
|
||||||
import log from './supervisor-console';
|
import log from './supervisor-console';
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ export function equals<T>(value: T, other: T): boolean {
|
|||||||
* Returns true if the the object equals `{}` or is an empty
|
* Returns true if the the object equals `{}` or is an empty
|
||||||
* array
|
* array
|
||||||
*/
|
*/
|
||||||
export function empty<T extends {}>(value: T): boolean {
|
export function empty<T extends object>(value: T): boolean {
|
||||||
return (Array.isArray(value) && value.length === 0) || equals(value, {});
|
return (Array.isArray(value) && value.length === 0) || equals(value, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as apiBinder from '../api-binder';
|
import * as apiBinder from '../api-binder';
|
||||||
import * as config from '../config';
|
import * as config from '../config';
|
||||||
@ -16,14 +14,14 @@ import {
|
|||||||
import { docker } from './docker-utils';
|
import { docker } from './docker-utils';
|
||||||
import { log } from './supervisor-console';
|
import { log } from './supervisor-console';
|
||||||
import { pathOnData } from './host-utils';
|
import { pathOnData } from './host-utils';
|
||||||
import Volume from '../compose/volume';
|
import type Volume from '../compose/volume';
|
||||||
import * as logger from '../logger';
|
import * as logger from '../logger';
|
||||||
import type {
|
import type {
|
||||||
DatabaseApp,
|
DatabaseApp,
|
||||||
DatabaseService,
|
DatabaseService,
|
||||||
} from '../device-state/target-state-cache';
|
} from '../device-state/target-state-cache';
|
||||||
|
|
||||||
import { TargetApp, TargetApps, TargetState } from '../types';
|
import type { TargetApp, TargetApps, TargetState } from '../types';
|
||||||
|
|
||||||
const defaultLegacyVolume = () => 'resin-data';
|
const defaultLegacyVolume = () => 'resin-data';
|
||||||
|
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
import * as _ from 'lodash';
|
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import * as volumeManager from '../compose/volume-manager';
|
import * as volumeManager from '../compose/volume-manager';
|
||||||
import * as deviceState from '../device-state';
|
import * as deviceState from '../device-state';
|
||||||
import { TargetState } from '../types';
|
import type { TargetState } from '../types';
|
||||||
import * as constants from './constants';
|
import * as constants from './constants';
|
||||||
import { BackupError, isNotFoundError } from './errors';
|
import { BackupError, isNotFoundError } from './errors';
|
||||||
import { exec, exists, mkdirp, unlinkAll } from './fs-utils';
|
import { exec, exists, mkdirp, unlinkAll } from './fs-utils';
|
||||||
@ -24,15 +23,18 @@ export async function loadBackupFromMigration(
|
|||||||
|
|
||||||
await deviceState.setTarget(targetState);
|
await deviceState.setTarget(targetState);
|
||||||
|
|
||||||
// TODO: this code is only single-app compatible
|
// Assume there is only a single device in the target state
|
||||||
const [uuid] = Object.keys(targetState.local?.apps);
|
const [localDevice] = Object.values(targetState);
|
||||||
|
|
||||||
if (!!uuid) {
|
// TODO: this code is only single-app compatible
|
||||||
|
const [uuid] = Object.keys(localDevice?.apps);
|
||||||
|
|
||||||
|
if (uuid) {
|
||||||
throw new BackupError('No apps in the target state');
|
throw new BackupError('No apps in the target state');
|
||||||
}
|
}
|
||||||
|
|
||||||
const { id: appId } = targetState.local?.apps[uuid];
|
const { id: appId } = localDevice.apps[uuid];
|
||||||
const [release] = Object.values(targetState.local?.apps[uuid].releases);
|
const [release] = Object.values(localDevice.apps[uuid].releases);
|
||||||
|
|
||||||
const volumes = release?.volumes ?? {};
|
const volumes = release?.volumes ?? {};
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ import { getRequestInstance } from './request';
|
|||||||
|
|
||||||
export const { generateUniqueKey, register } = getRegisterDevice({
|
export const { generateUniqueKey, register } = getRegisterDevice({
|
||||||
request: {
|
request: {
|
||||||
send: async (options: {}) => {
|
send: async (options: object) => {
|
||||||
const request = await getRequestInstance();
|
const request = await getRequestInstance();
|
||||||
const [response] = await request.postAsync({ ...options, json: true });
|
const [response] = await request.postAsync({ ...options, json: true });
|
||||||
return response;
|
return response;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { TransformableInfo } from 'logform';
|
import type { TransformableInfo } from 'logform';
|
||||||
import * as winston from 'winston';
|
import * as winston from 'winston';
|
||||||
|
|
||||||
const levels = {
|
const levels = {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import * as Bluebird from 'bluebird';
|
import * as Bluebird from 'bluebird';
|
||||||
import * as _ from 'lodash';
|
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as Lock from 'rwlock';
|
import * as Lock from 'rwlock';
|
||||||
@ -92,7 +91,7 @@ async function dispose(
|
|||||||
* TODO: Remove skipLock as it's not a good interface. If lock is called it should try to take the lock
|
* TODO: Remove skipLock as it's not a good interface. If lock is called it should try to take the lock
|
||||||
* without an option to skip.
|
* without an option to skip.
|
||||||
*/
|
*/
|
||||||
export async function lock<T extends unknown>(
|
export async function lock<T>(
|
||||||
appId: number | number[],
|
appId: number | number[],
|
||||||
{ force = false, skipLock = false }: { force: boolean; skipLock?: boolean },
|
{ force = false, skipLock = false }: { force: boolean; skipLock?: boolean },
|
||||||
fn: () => Resolvable<T>,
|
fn: () => Resolvable<T>,
|
||||||
|
@ -171,13 +171,13 @@ export class LocalModeManager {
|
|||||||
e.message,
|
e.message,
|
||||||
);
|
);
|
||||||
return this.collectContainerResources(fallback);
|
return this.collectContainerResources(fallback);
|
||||||
} catch (e: any) {
|
} catch (err: any) {
|
||||||
// Inspect operation fails (using legacy container name?).
|
// Inspect operation fails (using legacy container name?).
|
||||||
const fallback = SUPERVISOR_LEGACY_CONTAINER_NAME_FALLBACK;
|
const fallback = SUPERVISOR_LEGACY_CONTAINER_NAME_FALLBACK;
|
||||||
log.warn(
|
log.warn(
|
||||||
'Supervisor container resources cannot be obtained by container ID. ' +
|
'Supervisor container resources cannot be obtained by container ID. ' +
|
||||||
`Using '${fallback}' name instead.`,
|
`Using '${fallback}' name instead.`,
|
||||||
e.message,
|
err.message,
|
||||||
);
|
);
|
||||||
return this.collectContainerResources(fallback);
|
return this.collectContainerResources(fallback);
|
||||||
}
|
}
|
||||||
|
@ -4,15 +4,12 @@ import * as _ from 'lodash';
|
|||||||
import * as config from './config';
|
import * as config from './config';
|
||||||
import * as db from './db';
|
import * as db from './db';
|
||||||
import * as eventTracker from './event-tracker';
|
import * as eventTracker from './event-tracker';
|
||||||
import { LogType } from './lib/log-types';
|
import type { LogType } from './lib/log-types';
|
||||||
import { writeLock } from './lib/update-lock';
|
import { writeLock } from './lib/update-lock';
|
||||||
import {
|
import type { LogBackend, LogMessage } from './logging';
|
||||||
BalenaLogBackend,
|
import { BalenaLogBackend, LocalLogBackend } from './logging';
|
||||||
LocalLogBackend,
|
import type { MonitorHook } from './logging/monitor';
|
||||||
LogBackend,
|
import logMonitor from './logging/monitor';
|
||||||
LogMessage,
|
|
||||||
} from './logging';
|
|
||||||
import logMonitor, { MonitorHook } from './logging/monitor';
|
|
||||||
|
|
||||||
import * as globalEventBus from './event-bus';
|
import * as globalEventBus from './event-bus';
|
||||||
import superConsole from './lib/supervisor-console';
|
import superConsole from './lib/supervisor-console';
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { ClientRequest } from 'http';
|
import type { ClientRequest } from 'http';
|
||||||
import * as https from 'https';
|
import * as https from 'https';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import * as stream from 'stream';
|
import * as stream from 'stream';
|
||||||
import * as url from 'url';
|
import * as url from 'url';
|
||||||
import * as zlib from 'zlib';
|
import * as zlib from 'zlib';
|
||||||
|
|
||||||
import { LogBackend, LogMessage } from './log-backend';
|
import type { LogMessage } from './log-backend';
|
||||||
|
import { LogBackend } from './log-backend';
|
||||||
|
|
||||||
import log from '../lib/supervisor-console';
|
import log from '../lib/supervisor-console';
|
||||||
|
|
||||||
|
@ -2,7 +2,8 @@ import * as _ from 'lodash';
|
|||||||
|
|
||||||
import { Readable } from 'stream';
|
import { Readable } from 'stream';
|
||||||
import { checkInt } from '../lib/validation';
|
import { checkInt } from '../lib/validation';
|
||||||
import { LogBackend, LogMessage } from './log-backend';
|
import type { LogMessage } from './log-backend';
|
||||||
|
import { LogBackend } from './log-backend';
|
||||||
|
|
||||||
import log from '../lib/supervisor-console';
|
import log from '../lib/supervisor-console';
|
||||||
|
|
||||||
|
@ -183,7 +183,10 @@ class LogMonitor {
|
|||||||
const isStdErr = row.PRIORITY === '3';
|
const isStdErr = row.PRIORITY === '3';
|
||||||
const timestamp = Math.floor(Number(row.__REALTIME_TIMESTAMP) / 1000); // microseconds to milliseconds
|
const timestamp = Math.floor(Number(row.__REALTIME_TIMESTAMP) / 1000); // microseconds to milliseconds
|
||||||
this.updateContainerSentTimestamp(containerId, timestamp);
|
this.updateContainerSentTimestamp(containerId, timestamp);
|
||||||
this.containers[containerId].hook({ message, isStdErr, timestamp });
|
|
||||||
|
// WARNING: this could lead to a memory leak as the hook is not being awaited
|
||||||
|
// and the journal can be very verbose
|
||||||
|
void this.containers[containerId].hook({ message, isStdErr, timestamp });
|
||||||
}
|
}
|
||||||
|
|
||||||
private updateContainerSentTimestamp(
|
private updateContainerSentTimestamp(
|
||||||
|
@ -89,7 +89,7 @@ exports.up = function (knex) {
|
|||||||
addColumn('app', 'markedForDeletion', 'boolean'),
|
addColumn('app', 'markedForDeletion', 'boolean'),
|
||||||
dropColumn('app', 'containerId'),
|
dropColumn('app', 'containerId'),
|
||||||
]).then(() => {
|
]).then(() => {
|
||||||
//When updating from older supervisors, config can be null
|
// When updating from older supervisors, config can be null
|
||||||
return knex('app')
|
return knex('app')
|
||||||
.update({ config: '{}' })
|
.update({ config: '{}' })
|
||||||
.whereNull('config')
|
.whereNull('config')
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
const Bluebird = require('bluebird');
|
import * as Bluebird from 'bluebird';
|
||||||
const _ = require('lodash');
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
var tryParse = function (obj) {
|
const tryParse = function (obj) {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(obj);
|
return JSON.parse(obj);
|
||||||
} catch {
|
} catch {
|
||||||
@ -9,14 +9,14 @@ var tryParse = function (obj) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var singleToMulticontainerApp = function (app) {
|
const singleToMulticontainerApp = function (app) {
|
||||||
// From *very* old supervisors, env or config may be null
|
// From *very* old supervisors, env or config may be null
|
||||||
// so we ignore errors parsing them
|
// so we ignore errors parsing them
|
||||||
const conf = tryParse(app.config);
|
const conf = tryParse(app.config);
|
||||||
const env = tryParse(app.env);
|
const env = tryParse(app.env);
|
||||||
const environment = {};
|
const environment = {};
|
||||||
const appId = parseInt(app.appId, 10);
|
const appId = parseInt(app.appId, 10);
|
||||||
for (let key in env) {
|
for (const key in env) {
|
||||||
if (!/^RESIN_/.test(key)) {
|
if (!/^RESIN_/.test(key)) {
|
||||||
environment[key] = env[key];
|
environment[key] = env[key];
|
||||||
}
|
}
|
||||||
@ -68,7 +68,7 @@ var singleToMulticontainerApp = function (app) {
|
|||||||
return newApp;
|
return newApp;
|
||||||
};
|
};
|
||||||
|
|
||||||
var jsonifyAppFields = function (app) {
|
const jsonifyAppFields = function (app) {
|
||||||
const newApp = _.clone(app);
|
const newApp = _.clone(app);
|
||||||
newApp.services = JSON.stringify(app.services);
|
newApp.services = JSON.stringify(app.services);
|
||||||
newApp.networks = JSON.stringify(app.networks);
|
newApp.networks = JSON.stringify(app.networks);
|
||||||
@ -76,7 +76,7 @@ var jsonifyAppFields = function (app) {
|
|||||||
return newApp;
|
return newApp;
|
||||||
};
|
};
|
||||||
|
|
||||||
var imageForApp = function (app) {
|
const imageForApp = function (app) {
|
||||||
const service = app.services[0];
|
const service = app.services[0];
|
||||||
return {
|
return {
|
||||||
name: service.image,
|
name: service.image,
|
||||||
@ -89,7 +89,7 @@ var imageForApp = function (app) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
var imageForDependentApp = function (app) {
|
const imageForDependentApp = function (app) {
|
||||||
return {
|
return {
|
||||||
name: app.image,
|
name: app.image,
|
||||||
appId: app.appId,
|
appId: app.appId,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
const fs = require('fs');
|
import * as fs from 'fs';
|
||||||
const configJsonPath = process.env.CONFIG_MOUNT_POINT;
|
const configJsonPath = process.env.CONFIG_MOUNT_POINT;
|
||||||
|
|
||||||
exports.up = function (knex) {
|
exports.up = function (knex) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
const Bluebird = require('bluebird');
|
import * as Bluebird from 'bluebird';
|
||||||
const fs = require('fs');
|
import * as fs from 'fs';
|
||||||
const configJsonPath = process.env.CONFIG_MOUNT_POINT;
|
const configJsonPath = process.env.CONFIG_MOUNT_POINT;
|
||||||
|
|
||||||
exports.up = function (knex) {
|
exports.up = function (knex) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
const _ = require('lodash');
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
// We take legacy deviceConfig targets and store them without the RESIN_ prefix
|
// We take legacy deviceConfig targets and store them without the RESIN_ prefix
|
||||||
// (we also strip the BALENA_ prefix for completeness, even though no supervisors
|
// (we also strip the BALENA_ prefix for completeness, even though no supervisors
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
const fs = require('fs');
|
import * as fs from 'fs';
|
||||||
const configJsonPath = process.env.CONFIG_MOUNT_POINT;
|
const configJsonPath = process.env.CONFIG_MOUNT_POINT;
|
||||||
|
|
||||||
const { checkTruthy } = require('../lib/validation');
|
import { checkTruthy } from '../lib/validation';
|
||||||
|
|
||||||
exports.up = function (knex) {
|
exports.up = function (knex) {
|
||||||
return knex('config')
|
return knex('config')
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
const fs = require('fs');
|
import * as fs from 'fs';
|
||||||
const configJsonPath = process.env.CONFIG_MOUNT_POINT;
|
const configJsonPath = process.env.CONFIG_MOUNT_POINT;
|
||||||
|
|
||||||
const { checkTruthy } = require('../lib/validation');
|
import { checkTruthy } from '../lib/validation';
|
||||||
|
|
||||||
exports.up = function (knex) {
|
exports.up = function (knex) {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
|
@ -80,7 +80,7 @@ export const startConnectivityCheck = _.once(
|
|||||||
watch(constants.vpnStatusPath, vpnStatusInotifyCallback);
|
watch(constants.vpnStatusPath, vpnStatusInotifyCallback);
|
||||||
|
|
||||||
if (enable) {
|
if (enable) {
|
||||||
vpnStatusInotifyCallback();
|
void vpnStatusInotifyCallback();
|
||||||
}
|
}
|
||||||
|
|
||||||
const parsedUrl = url.parse(apiEndpoint);
|
const parsedUrl = url.parse(apiEndpoint);
|
||||||
|
@ -72,8 +72,8 @@ export class Supervisor {
|
|||||||
routers: [v1.router, v2.router],
|
routers: [v1.router, v2.router],
|
||||||
healthchecks: [apiBinder.healthcheck, deviceState.healthcheck],
|
healthchecks: [apiBinder.healthcheck, deviceState.healthcheck],
|
||||||
});
|
});
|
||||||
this.api.listen(conf.listenPort, conf.apiTimeout);
|
|
||||||
deviceState.on('shutdown', () => this.api.stop());
|
deviceState.on('shutdown', () => this.api.stop());
|
||||||
|
return this.api.listen(conf.listenPort, conf.apiTimeout);
|
||||||
})(),
|
})(),
|
||||||
apiBinder.start(),
|
apiBinder.start(),
|
||||||
]);
|
]);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import * as t from 'io-ts';
|
import * as t from 'io-ts';
|
||||||
import { chain, fold, isRight, left, right, Either } from 'fp-ts/lib/Either';
|
import type { Either } from 'fp-ts/lib/Either';
|
||||||
|
import { chain, fold, isRight, left, right } from 'fp-ts/lib/Either';
|
||||||
import { pipe, flow } from 'fp-ts/lib/function';
|
import { pipe, flow } from 'fp-ts/lib/function';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -154,7 +155,7 @@ export type LabelObject = t.TypeOf<typeof LabelObject>;
|
|||||||
// Valid docker container and volume name according to
|
// Valid docker container and volume name according to
|
||||||
// https://github.com/moby/moby/blob/04c6f09fbdf60c7765cc4cb78883faaa9d971fa5/daemon/daemon.go#L56
|
// https://github.com/moby/moby/blob/04c6f09fbdf60c7765cc4cb78883faaa9d971fa5/daemon/daemon.go#L56
|
||||||
// [a-zA-Z0-9][a-zA-Z0-9_.-]
|
// [a-zA-Z0-9][a-zA-Z0-9_.-]
|
||||||
const DOCKER_NAME_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9_\.\-]*$/;
|
const DOCKER_NAME_REGEX = /^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/;
|
||||||
export const DockerName = shortStringWithRegex(
|
export const DockerName = shortStringWithRegex(
|
||||||
'LabelName',
|
'LabelName',
|
||||||
DOCKER_NAME_REGEX,
|
DOCKER_NAME_REGEX,
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import * as t from 'io-ts';
|
import * as t from 'io-ts';
|
||||||
|
|
||||||
// TODO: move all these exported types to ../compose/types
|
// TODO: move all these exported types to ../compose/types
|
||||||
import { ComposeNetworkConfig } from '../compose/types/network';
|
import type { ComposeNetworkConfig } from '../compose/types/network';
|
||||||
import { ComposeVolumeConfig } from '../compose/volume';
|
import type { ComposeVolumeConfig } from '../compose/volume';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
DockerName,
|
DockerName,
|
||||||
@ -15,7 +15,7 @@ import {
|
|||||||
nonEmptyRecord,
|
nonEmptyRecord,
|
||||||
} from './basic';
|
} from './basic';
|
||||||
|
|
||||||
import App from '../compose/app';
|
import type App from '../compose/app';
|
||||||
|
|
||||||
export type DeviceLegacyReport = Partial<{
|
export type DeviceLegacyReport = Partial<{
|
||||||
api_port: number;
|
api_port: number;
|
||||||
@ -105,7 +105,7 @@ const fromType = <T extends object>(name: string) =>
|
|||||||
// Alias short string to UUID so code reads more clearly
|
// Alias short string to UUID so code reads more clearly
|
||||||
export const UUID = ShortString;
|
export const UUID = ShortString;
|
||||||
|
|
||||||
/*****************
|
/** ***************
|
||||||
* Current state *
|
* Current state *
|
||||||
*****************/
|
*****************/
|
||||||
const ServiceState = t.intersection([
|
const ServiceState = t.intersection([
|
||||||
@ -176,7 +176,7 @@ export const DeviceState = t.record(
|
|||||||
);
|
);
|
||||||
export type DeviceState = t.TypeOf<typeof DeviceState>;
|
export type DeviceState = t.TypeOf<typeof DeviceState>;
|
||||||
|
|
||||||
/****************
|
/** **************
|
||||||
* Target state *
|
* Target state *
|
||||||
****************/
|
****************/
|
||||||
/**
|
/**
|
||||||
|
@ -18,7 +18,7 @@ import {
|
|||||||
expectSteps,
|
expectSteps,
|
||||||
expectNoStep,
|
expectNoStep,
|
||||||
} from '~/test-lib/state-helper';
|
} from '~/test-lib/state-helper';
|
||||||
import { InstancedAppState } from '~/src/types';
|
import type { InstancedAppState } from '~/src/types';
|
||||||
|
|
||||||
// TODO: application manager inferNextSteps still queries some stuff from
|
// TODO: application manager inferNextSteps still queries some stuff from
|
||||||
// the engine instead of receiving that information as parameter. Refactoring
|
// the engine instead of receiving that information as parameter. Refactoring
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
import * as _ from 'lodash';
|
|
||||||
|
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
|
|
||||||
import Service from '~/src/compose/service';
|
import Service from '~/src/compose/service';
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { SinonStub, stub } from 'sinon';
|
import type { SinonStub } from 'sinon';
|
||||||
|
import { stub } from 'sinon';
|
||||||
import Volume from '~/src/compose/volume';
|
import Volume from '~/src/compose/volume';
|
||||||
import * as logTypes from '~/lib/log-types';
|
import * as logTypes from '~/lib/log-types';
|
||||||
import * as logger from '~/src/logger';
|
import * as logger from '~/src/logger';
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
import * as _ from 'lodash';
|
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import { SinonSpy, spy, stub } from 'sinon';
|
import type { SinonSpy } from 'sinon';
|
||||||
|
import { spy, stub } from 'sinon';
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { testfs, TestFs } from 'mocha-pod';
|
import type { TestFs } from 'mocha-pod';
|
||||||
|
import { testfs } from 'mocha-pod';
|
||||||
|
|
||||||
import { fnSchema } from '~/src/config/functions';
|
import { fnSchema } from '~/src/config/functions';
|
||||||
import * as hostUtils from '~/lib/host-utils';
|
import * as hostUtils from '~/lib/host-utils';
|
||||||
import { configJsonPath } from '~/lib/constants';
|
import { configJsonPath } from '~/lib/constants';
|
||||||
|
|
||||||
// Utility method to use along with `require`
|
// Utility type to use along with `require`
|
||||||
type Config = typeof import('~/src/config');
|
type Config = typeof import('~/src/config');
|
||||||
|
|
||||||
describe('config', () => {
|
describe('config', () => {
|
||||||
@ -35,6 +36,7 @@ describe('config', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('reads and exposes values from config.json', async () => {
|
it('reads and exposes values from config.json', async () => {
|
||||||
|
// eslint-disable-next-line
|
||||||
const config = require('~/src/config') as Config;
|
const config = require('~/src/config') as Config;
|
||||||
await config.initialized();
|
await config.initialized();
|
||||||
const configJson = await readConfigJson();
|
const configJson = await readConfigJson();
|
||||||
@ -43,6 +45,7 @@ describe('config', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('allows reading several values in one getMany call', async () => {
|
it('allows reading several values in one getMany call', async () => {
|
||||||
|
// eslint-disable-next-line
|
||||||
const config = require('~/src/config') as Config;
|
const config = require('~/src/config') as Config;
|
||||||
await config.initialized();
|
await config.initialized();
|
||||||
const configJson = await readConfigJson();
|
const configJson = await readConfigJson();
|
||||||
@ -55,6 +58,7 @@ describe('config', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('generates a uuid and stores it in config.json', async () => {
|
it('generates a uuid and stores it in config.json', async () => {
|
||||||
|
// eslint-disable-next-line
|
||||||
const config = require('~/src/config') as Config;
|
const config = require('~/src/config') as Config;
|
||||||
await config.initialized();
|
await config.initialized();
|
||||||
const configJson = await readConfigJson();
|
const configJson = await readConfigJson();
|
||||||
@ -65,6 +69,7 @@ describe('config', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does not allow setting an immutable field', async () => {
|
it('does not allow setting an immutable field', async () => {
|
||||||
|
// eslint-disable-next-line
|
||||||
const config = require('~/src/config') as Config;
|
const config = require('~/src/config') as Config;
|
||||||
await config.initialized();
|
await config.initialized();
|
||||||
return expect(config.set({ deviceType: 'a different device type' })).to.be
|
return expect(config.set({ deviceType: 'a different device type' })).to.be
|
||||||
@ -72,6 +77,7 @@ describe('config', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('allows setting both config.json and database fields transparently', async () => {
|
it('allows setting both config.json and database fields transparently', async () => {
|
||||||
|
// eslint-disable-next-line
|
||||||
const config = require('~/src/config') as Config;
|
const config = require('~/src/config') as Config;
|
||||||
await config.initialized();
|
await config.initialized();
|
||||||
await config.set({
|
await config.set({
|
||||||
@ -86,6 +92,7 @@ describe('config', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('allows deleting a config.json key and returns a default value if none is set', async () => {
|
it('allows deleting a config.json key and returns a default value if none is set', async () => {
|
||||||
|
// eslint-disable-next-line
|
||||||
const config = require('~/src/config') as Config;
|
const config = require('~/src/config') as Config;
|
||||||
await config.initialized();
|
await config.initialized();
|
||||||
await config.remove('appUpdatePollInterval');
|
await config.remove('appUpdatePollInterval');
|
||||||
@ -94,6 +101,7 @@ describe('config', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('allows deleting a config.json key if it is null', async () => {
|
it('allows deleting a config.json key if it is null', async () => {
|
||||||
|
// eslint-disable-next-line
|
||||||
const config = require('~/src/config') as Config;
|
const config = require('~/src/config') as Config;
|
||||||
await config.initialized();
|
await config.initialized();
|
||||||
await config.set({ apiKey: null });
|
await config.set({ apiKey: null });
|
||||||
@ -107,6 +115,7 @@ describe('config', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('does not allow modifying or removing a function value', async () => {
|
it('does not allow modifying or removing a function value', async () => {
|
||||||
|
// eslint-disable-next-line
|
||||||
const config = require('~/src/config') as Config;
|
const config = require('~/src/config') as Config;
|
||||||
await config.initialized();
|
await config.initialized();
|
||||||
// We have to cast to any below, as the type system will
|
// We have to cast to any below, as the type system will
|
||||||
@ -116,19 +125,19 @@ describe('config', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('throws when asked for an unknown key', async () => {
|
it('throws when asked for an unknown key', async () => {
|
||||||
|
// eslint-disable-next-line
|
||||||
const config = require('~/src/config') as Config;
|
const config = require('~/src/config') as Config;
|
||||||
await config.initialized();
|
await config.initialized();
|
||||||
await expect(config.get('unknownInvalidValue' as any)).to.be.rejected;
|
await expect(config.get('unknownInvalidValue' as any)).to.be.rejected;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('emits a change event when values change', async () => {
|
it('emits a change event when values change', async () => {
|
||||||
|
// eslint-disable-next-line
|
||||||
const config = require('~/src/config') as Config;
|
const config = require('~/src/config') as Config;
|
||||||
await config.initialized();
|
await config.initialized();
|
||||||
const listener = stub();
|
const listener = stub();
|
||||||
config.on('change', listener);
|
config.on('change', listener);
|
||||||
config.set({ name: 'someValue' });
|
await config.set({ name: 'someValue' });
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
||||||
|
|
||||||
expect(listener).to.have.been.calledWith({ name: 'someValue' });
|
expect(listener).to.have.been.calledWith({ name: 'someValue' });
|
||||||
});
|
});
|
||||||
@ -149,6 +158,7 @@ describe('config', () => {
|
|||||||
),
|
),
|
||||||
}).enable();
|
}).enable();
|
||||||
|
|
||||||
|
// eslint-disable-next-line
|
||||||
const config = require('~/src/config') as Config;
|
const config = require('~/src/config') as Config;
|
||||||
await config.initialized();
|
await config.initialized();
|
||||||
await config.set({ developmentMode: false });
|
await config.set({ developmentMode: false });
|
||||||
@ -160,6 +170,7 @@ describe('config', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('reads and exposes MAC addresses', async () => {
|
it('reads and exposes MAC addresses', async () => {
|
||||||
|
// eslint-disable-next-line
|
||||||
const config = require('~/src/config') as Config;
|
const config = require('~/src/config') as Config;
|
||||||
await config.initialized();
|
await config.initialized();
|
||||||
const macAddress = await config.get('macAddress');
|
const macAddress = await config.get('macAddress');
|
||||||
@ -168,12 +179,14 @@ describe('config', () => {
|
|||||||
|
|
||||||
describe('Function config providers', () => {
|
describe('Function config providers', () => {
|
||||||
it('should throw if a non-mutable function provider is set', async () => {
|
it('should throw if a non-mutable function provider is set', async () => {
|
||||||
|
// eslint-disable-next-line
|
||||||
const config = require('~/src/config') as Config;
|
const config = require('~/src/config') as Config;
|
||||||
await config.initialized();
|
await config.initialized();
|
||||||
await expect(config.set({ version: 'some-version' })).to.be.rejected;
|
await expect(config.set({ version: 'some-version' })).to.be.rejected;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw if a non-mutable function provider is removed', async () => {
|
it('should throw if a non-mutable function provider is removed', async () => {
|
||||||
|
// eslint-disable-next-line
|
||||||
const config = require('~/src/config') as Config;
|
const config = require('~/src/config') as Config;
|
||||||
await config.initialized();
|
await config.initialized();
|
||||||
await expect(config.remove('version' as any)).to.be.rejected;
|
await expect(config.remove('version' as any)).to.be.rejected;
|
||||||
@ -181,14 +194,15 @@ describe('config', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('Config data sources', () => {
|
describe('Config data sources', () => {
|
||||||
afterEach(() => {
|
afterEach(async () => {
|
||||||
// Clean up memoized values
|
// Clean up memoized values
|
||||||
fnSchema.deviceArch.clear();
|
await fnSchema.deviceArch.clear();
|
||||||
fnSchema.deviceType.clear();
|
await fnSchema.deviceType.clear();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should obtain deviceArch from device-type.json', async () => {
|
it('should obtain deviceArch from device-type.json', async () => {
|
||||||
const dtJson = await readDeviceTypeJson();
|
const dtJson = await readDeviceTypeJson();
|
||||||
|
// eslint-disable-next-line
|
||||||
const config = require('~/src/config') as Config;
|
const config = require('~/src/config') as Config;
|
||||||
await config.initialized();
|
await config.initialized();
|
||||||
|
|
||||||
@ -198,6 +212,7 @@ describe('config', () => {
|
|||||||
|
|
||||||
it('should obtain deviceType from device-type.json', async () => {
|
it('should obtain deviceType from device-type.json', async () => {
|
||||||
const dtJson = await readDeviceTypeJson();
|
const dtJson = await readDeviceTypeJson();
|
||||||
|
// eslint-disable-next-line
|
||||||
const config = require('~/src/config') as Config;
|
const config = require('~/src/config') as Config;
|
||||||
await config.initialized();
|
await config.initialized();
|
||||||
|
|
||||||
@ -206,6 +221,7 @@ describe('config', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should memoize values from device-type.json', async () => {
|
it('should memoize values from device-type.json', async () => {
|
||||||
|
// eslint-disable-next-line
|
||||||
const config = require('~/src/config') as Config;
|
const config = require('~/src/config') as Config;
|
||||||
await config.initialized();
|
await config.initialized();
|
||||||
const dtJson = await readDeviceTypeJson();
|
const dtJson = await readDeviceTypeJson();
|
||||||
@ -233,6 +249,7 @@ describe('config', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not memoize errors when reading deviceArch', async () => {
|
it('should not memoize errors when reading deviceArch', async () => {
|
||||||
|
// eslint-disable-next-line
|
||||||
const config = require('~/src/config') as Config;
|
const config = require('~/src/config') as Config;
|
||||||
await config.initialized();
|
await config.initialized();
|
||||||
|
|
||||||
@ -253,6 +270,7 @@ describe('config', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not memoize errors when reading deviceType', async () => {
|
it('should not memoize errors when reading deviceType', async () => {
|
||||||
|
// eslint-disable-next-line
|
||||||
const config = require('~/src/config') as Config;
|
const config = require('~/src/config') as Config;
|
||||||
await config.initialized();
|
await config.initialized();
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import { testfs, TestFs } from 'mocha-pod';
|
import type { TestFs } from 'mocha-pod';
|
||||||
|
import { testfs } from 'mocha-pod';
|
||||||
|
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import * as hostUtils from '~/lib/host-utils';
|
import * as hostUtils from '~/lib/host-utils';
|
||||||
|
@ -7,7 +7,7 @@ import { Extlinux } from '~/src/config/backends/extlinux';
|
|||||||
import { ConfigTxt } from '~/src/config/backends/config-txt';
|
import { ConfigTxt } from '~/src/config/backends/config-txt';
|
||||||
import { ConfigFs } from '~/src/config/backends/config-fs';
|
import { ConfigFs } from '~/src/config/backends/config-fs';
|
||||||
import { SplashImage } from '~/src/config/backends/splash-image';
|
import { SplashImage } from '~/src/config/backends/splash-image';
|
||||||
import { ConfigBackend } from '~/src/config/backends/backend';
|
import type { ConfigBackend } from '~/src/config/backends/backend';
|
||||||
|
|
||||||
import * as hostUtils from '~/lib/host-utils';
|
import * as hostUtils from '~/lib/host-utils';
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { knex, Knex } from 'knex';
|
import type { Knex } from 'knex';
|
||||||
|
import { knex } from 'knex';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
|
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
@ -56,6 +57,7 @@ describe('db', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('creates a database at the path passed on creation', async () => {
|
it('creates a database at the path passed on creation', async () => {
|
||||||
|
// eslint-disable-next-line
|
||||||
const testDb = require('~/src/db') as Db;
|
const testDb = require('~/src/db') as Db;
|
||||||
await testDb.initialized();
|
await testDb.initialized();
|
||||||
await expect(fs.access(constants.databasePath)).to.not.be.rejected;
|
await expect(fs.access(constants.databasePath)).to.not.be.rejected;
|
||||||
@ -64,6 +66,7 @@ describe('db', () => {
|
|||||||
it('migrations add new fields and removes old ones in an old database', async () => {
|
it('migrations add new fields and removes old ones in an old database', async () => {
|
||||||
// Create a database with an older schema
|
// Create a database with an older schema
|
||||||
const knexForDB = await createOldDatabase(constants.databasePath);
|
const knexForDB = await createOldDatabase(constants.databasePath);
|
||||||
|
// eslint-disable-next-line
|
||||||
const testDb = require('~/src/db') as Db;
|
const testDb = require('~/src/db') as Db;
|
||||||
await testDb.initialized();
|
await testDb.initialized();
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
@ -87,6 +90,7 @@ describe('db', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('creates a deviceConfig table with a single default value', async () => {
|
it('creates a deviceConfig table with a single default value', async () => {
|
||||||
|
// eslint-disable-next-line
|
||||||
const testDb = require('~/src/db') as Db;
|
const testDb = require('~/src/db') as Db;
|
||||||
await testDb.initialized();
|
await testDb.initialized();
|
||||||
const deviceConfig = await testDb.models('deviceConfig').select();
|
const deviceConfig = await testDb.models('deviceConfig').select();
|
||||||
@ -95,6 +99,7 @@ describe('db', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('allows performing transactions', async () => {
|
it('allows performing transactions', async () => {
|
||||||
|
// eslint-disable-next-line
|
||||||
const testDb = require('~/src/db') as Db;
|
const testDb = require('~/src/db') as Db;
|
||||||
await testDb.initialized();
|
await testDb.initialized();
|
||||||
return testDb.transaction((trx) => expect(trx.commit()).to.be.fulfilled);
|
return testDb.transaction((trx) => expect(trx.commit()).to.be.fulfilled);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { stub, SinonStub } from 'sinon';
|
import type { SinonStub } from 'sinon';
|
||||||
|
import { stub } from 'sinon';
|
||||||
import * as Docker from 'dockerode';
|
import * as Docker from 'dockerode';
|
||||||
import * as request from 'supertest';
|
import * as request from 'supertest';
|
||||||
import { setTimeout } from 'timers/promises';
|
import { setTimeout } from 'timers/promises';
|
||||||
@ -191,17 +192,21 @@ describe('manages application lifecycle', () => {
|
|||||||
// This test suite will timeout if anything goes wrong, since
|
// This test suite will timeout if anything goes wrong, since
|
||||||
// we don't have any way of knowing whether Docker has finished
|
// we don't have any way of knowing whether Docker has finished
|
||||||
// setting up containers or not.
|
// setting up containers or not.
|
||||||
while (true) {
|
let containers = await docker.listContainers({ all: true });
|
||||||
const containers = await docker.listContainers({ all: true });
|
let containerInspects = await Promise.all(
|
||||||
const containerInspects = await Promise.all(
|
|
||||||
containers.map(({ Id }) => docker.getContainer(Id).inspect()),
|
containers.map(({ Id }) => docker.getContainer(Id).inspect()),
|
||||||
);
|
);
|
||||||
if (expected === containers.length && isWaitComplete(containerInspects)) {
|
while (
|
||||||
return containerInspects;
|
expected !== containers.length ||
|
||||||
} else {
|
!isWaitComplete(containerInspects)
|
||||||
|
) {
|
||||||
await setTimeout(500);
|
await setTimeout(500);
|
||||||
|
containers = await docker.listContainers({ all: true });
|
||||||
|
containerInspects = await Promise.all(
|
||||||
|
containers.map(({ Id }) => docker.getContainer(Id).inspect()),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
return containerInspects;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get NEW container inspects. This function should be passed to waitForSetup
|
// Get NEW container inspects. This function should be passed to waitForSetup
|
||||||
@ -217,12 +222,10 @@ describe('manages application lifecycle', () => {
|
|||||||
// Images are ignored in local mode so we need to pull the base image
|
// Images are ignored in local mode so we need to pull the base image
|
||||||
await docker.pull(BASE_IMAGE);
|
await docker.pull(BASE_IMAGE);
|
||||||
// Wait for base image to finish pulling
|
// Wait for base image to finish pulling
|
||||||
while (true) {
|
let images = await docker.listImages();
|
||||||
const images = await docker.listImages();
|
while (images.length === 0) {
|
||||||
if (images.length > 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
await setTimeout(500);
|
await setTimeout(500);
|
||||||
|
images = await docker.listImages();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import * as config from '~/src/config';
|
|||||||
import * as testDb from '~/src/db';
|
import * as testDb from '~/src/db';
|
||||||
import * as deviceApi from '~/src/device-api';
|
import * as deviceApi from '~/src/device-api';
|
||||||
import * as middleware from '~/src/device-api/middleware';
|
import * as middleware from '~/src/device-api/middleware';
|
||||||
import { AuthorizedRequest } from '~/src/device-api/api-keys';
|
import type { AuthorizedRequest } from '~/src/device-api/api-keys';
|
||||||
|
|
||||||
describe('device-api/api-keys', () => {
|
describe('device-api/api-keys', () => {
|
||||||
let app: express.Application;
|
let app: express.Application;
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import * as express from 'express';
|
import type * as express from 'express';
|
||||||
import * as request from 'supertest';
|
import * as request from 'supertest';
|
||||||
|
|
||||||
import * as deviceApi from '~/src/device-api';
|
import * as deviceApi from '~/src/device-api';
|
||||||
@ -10,7 +10,7 @@ describe('device-api/index', () => {
|
|||||||
api = new deviceApi.SupervisorAPI({
|
api = new deviceApi.SupervisorAPI({
|
||||||
routers: [],
|
routers: [],
|
||||||
healthchecks: [],
|
healthchecks: [],
|
||||||
// @ts-expect-error
|
// @ts-expect-error extract private variable for testing
|
||||||
}).api;
|
}).api;
|
||||||
// Express app set in SupervisorAPI is private here
|
// Express app set in SupervisorAPI is private here
|
||||||
// but we need to access it for supertest
|
// but we need to access it for supertest
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import * as express from 'express';
|
import type * as express from 'express';
|
||||||
import { SinonStub, stub } from 'sinon';
|
import type { SinonStub } from 'sinon';
|
||||||
|
import { stub } from 'sinon';
|
||||||
import * as request from 'supertest';
|
import * as request from 'supertest';
|
||||||
|
|
||||||
import * as config from '~/src/config';
|
import * as config from '~/src/config';
|
||||||
import * as db from '~/src/db';
|
import * as db from '~/src/db';
|
||||||
import * as hostConfig from '~/src/host-config';
|
import * as hostConfig from '~/src/host-config';
|
||||||
import Service from '~/src/compose/service';
|
import type Service from '~/src/compose/service';
|
||||||
import * as deviceApi from '~/src/device-api';
|
import * as deviceApi from '~/src/device-api';
|
||||||
import * as actions from '~/src/device-api/actions';
|
import * as actions from '~/src/device-api/actions';
|
||||||
import * as v1 from '~/src/device-api/v1';
|
import * as v1 from '~/src/device-api/v1';
|
||||||
@ -32,7 +33,7 @@ describe('device-api/v1', () => {
|
|||||||
api = new deviceApi.SupervisorAPI({
|
api = new deviceApi.SupervisorAPI({
|
||||||
routers: [v1.router],
|
routers: [v1.router],
|
||||||
healthchecks: [],
|
healthchecks: [],
|
||||||
// @ts-expect-error
|
// @ts-expect-error extract private variable for testing
|
||||||
}).api;
|
}).api;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -41,7 +42,7 @@ describe('device-api/v1', () => {
|
|||||||
api = new deviceApi.SupervisorAPI({
|
api = new deviceApi.SupervisorAPI({
|
||||||
routers: [v1.router],
|
routers: [v1.router],
|
||||||
healthchecks: [],
|
healthchecks: [],
|
||||||
// @ts-expect-error
|
// @ts-expect-error extract private variable for testing
|
||||||
}).api;
|
}).api;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -49,7 +50,7 @@ describe('device-api/v1', () => {
|
|||||||
api = new deviceApi.SupervisorAPI({
|
api = new deviceApi.SupervisorAPI({
|
||||||
routers: [v1.router],
|
routers: [v1.router],
|
||||||
healthchecks: [stub().resolves(true), stub().resolves(true)],
|
healthchecks: [stub().resolves(true), stub().resolves(true)],
|
||||||
// @ts-expect-error
|
// @ts-expect-error extract private variable for testing
|
||||||
}).api;
|
}).api;
|
||||||
await request(api).get('/v1/healthy').expect(200);
|
await request(api).get('/v1/healthy').expect(200);
|
||||||
});
|
});
|
||||||
@ -58,7 +59,7 @@ describe('device-api/v1', () => {
|
|||||||
api = new deviceApi.SupervisorAPI({
|
api = new deviceApi.SupervisorAPI({
|
||||||
routers: [v1.router],
|
routers: [v1.router],
|
||||||
healthchecks: [stub().resolves(false), stub().resolves(true)],
|
healthchecks: [stub().resolves(false), stub().resolves(true)],
|
||||||
// @ts-expect-error
|
// @ts-expect-error extract private variable for testing
|
||||||
}).api;
|
}).api;
|
||||||
await request(api).get('/v1/healthy').expect(500);
|
await request(api).get('/v1/healthy').expect(500);
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import * as express from 'express';
|
import type * as express from 'express';
|
||||||
import { SinonStub, stub } from 'sinon';
|
import type { SinonStub } from 'sinon';
|
||||||
|
import { stub } from 'sinon';
|
||||||
import * as request from 'supertest';
|
import * as request from 'supertest';
|
||||||
|
|
||||||
import * as config from '~/src/config';
|
import * as config from '~/src/config';
|
||||||
@ -28,7 +29,7 @@ describe('device-api/v2', () => {
|
|||||||
api = new deviceApi.SupervisorAPI({
|
api = new deviceApi.SupervisorAPI({
|
||||||
routers: [v2.router],
|
routers: [v2.router],
|
||||||
healthchecks: [],
|
healthchecks: [],
|
||||||
// @ts-expect-error
|
// @ts-expect-error extract private variable for testing
|
||||||
}).api;
|
}).api;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import { stripIndent } from 'common-tags';
|
import { stripIndent } from 'common-tags';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { SinonStub, stub, spy, SinonSpy } from 'sinon';
|
import type { SinonStub, SinonSpy } from 'sinon';
|
||||||
|
import { stub, spy } from 'sinon';
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
|
|
||||||
import * as deviceConfig from '~/src/device-config';
|
import * as deviceConfig from '~/src/device-config';
|
||||||
|
@ -6,7 +6,7 @@ import * as updateLock from '~/lib/update-lock';
|
|||||||
import * as config from '~/src/config';
|
import * as config from '~/src/config';
|
||||||
import * as deviceState from '~/src/device-state';
|
import * as deviceState from '~/src/device-state';
|
||||||
import { appsJsonBackup, loadTargetFromFile } from '~/src/device-state/preload';
|
import { appsJsonBackup, loadTargetFromFile } from '~/src/device-state/preload';
|
||||||
import { TargetState } from '~/src/types';
|
import type { TargetState } from '~/src/types';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
import { initializeContractRequirements } from '~/lib/contracts';
|
import { initializeContractRequirements } from '~/lib/contracts';
|
||||||
|
|
||||||
@ -196,7 +196,7 @@ describe('device-state', () => {
|
|||||||
|
|
||||||
it('does not allow setting an invalid target state', () => {
|
it('does not allow setting an invalid target state', () => {
|
||||||
// v2 state should be rejected
|
// v2 state should be rejected
|
||||||
expect(
|
return expect(
|
||||||
deviceState.setTarget({
|
deviceState.setTarget({
|
||||||
local: {
|
local: {
|
||||||
name: 'aDeviceWithDifferentName',
|
name: 'aDeviceWithDifferentName',
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { testfs, TestFs } from 'mocha-pod';
|
import type { TestFs } from 'mocha-pod';
|
||||||
|
import { testfs } from 'mocha-pod';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { SinonStub, stub } from 'sinon';
|
import type { SinonStub } from 'sinon';
|
||||||
|
import { stub } from 'sinon';
|
||||||
import * as fs from 'fs/promises';
|
import * as fs from 'fs/promises';
|
||||||
|
|
||||||
import * as hostConfig from '~/src/host-config';
|
import * as hostConfig from '~/src/host-config';
|
||||||
import * as config from '~/src/config';
|
import * as config from '~/src/config';
|
||||||
import * as applicationManager from '~/src/compose/application-manager';
|
import * as applicationManager from '~/src/compose/application-manager';
|
||||||
import { InstancedAppState } from '~/src/types/state';
|
import type { InstancedAppState } from '~/src/types/state';
|
||||||
import * as updateLock from '~/lib/update-lock';
|
import * as updateLock from '~/lib/update-lock';
|
||||||
import { UpdatesLockedError } from '~/lib/errors';
|
import { UpdatesLockedError } from '~/lib/errors';
|
||||||
import * as dbus from '~/lib/dbus';
|
import * as dbus from '~/lib/dbus';
|
||||||
|
@ -10,7 +10,8 @@ import * as dbFormat from '~/src/device-state/db-format';
|
|||||||
import * as iptables from '~/lib/iptables';
|
import * as iptables from '~/lib/iptables';
|
||||||
import * as firewall from '~/lib/firewall';
|
import * as firewall from '~/lib/firewall';
|
||||||
import * as constants from '~/lib/constants';
|
import * as constants from '~/lib/constants';
|
||||||
import { RuleAction, Rule } from '~/lib/iptables';
|
import type { Rule } from '~/lib/iptables';
|
||||||
|
import { RuleAction } from '~/lib/iptables';
|
||||||
import { log } from '~/lib/supervisor-console';
|
import { log } from '~/lib/supervisor-console';
|
||||||
|
|
||||||
describe('lib/firewall', function () {
|
describe('lib/firewall', function () {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { promises as fs, mkdirSync } from 'fs';
|
import { promises as fs, mkdirSync } from 'fs';
|
||||||
import { testfs, TestFs } from 'mocha-pod';
|
import type { TestFs } from 'mocha-pod';
|
||||||
|
import { testfs } from 'mocha-pod';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { stub } from 'sinon';
|
import { stub } from 'sinon';
|
||||||
@ -174,7 +175,7 @@ describe('lib/lockfile', () => {
|
|||||||
await expect(lockfile.lock(lockOne)).to.not.be.rejected;
|
await expect(lockfile.lock(lockOne)).to.not.be.rejected;
|
||||||
await expect(lockfile.lock(lockTwo, NOBODY_UID)).to.not.be.rejected;
|
await expect(lockfile.lock(lockTwo, NOBODY_UID)).to.not.be.rejected;
|
||||||
|
|
||||||
// @ts-expect-error
|
// @ts-expect-error simulate process exit event
|
||||||
process.emit('exit');
|
process.emit('exit');
|
||||||
|
|
||||||
// Verify lockfile removal regardless of appId / appUuid
|
// Verify lockfile removal regardless of appId / appUuid
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user