mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-02-11 05:31:29 +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:
parent
8750951521
commit
988a1c9e9a
@ -2,10 +2,4 @@
|
||||
"*.ts": [
|
||||
"balena-lint --typescript --fix",
|
||||
],
|
||||
"*.js": [
|
||||
"balena-lint --typescript --fix",
|
||||
],
|
||||
"test/**/*.ts": [
|
||||
"balena-lint --typescript --no-prettier --tests"
|
||||
],
|
||||
}
|
||||
|
8916
package-lock.json
generated
8916
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,7 @@
|
||||
"scripts": {
|
||||
"start": "./entry.sh",
|
||||
"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: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'",
|
||||
@ -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": "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",
|
||||
"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",
|
||||
"sync": "ts-node --files sync/sync.ts",
|
||||
"clean": "rimraf build",
|
||||
@ -42,7 +42,7 @@
|
||||
"@balena/contrato": "^0.6.0",
|
||||
"@balena/es-version": "^1.0.1",
|
||||
"@balena/happy-eyeballs": "0.0.6",
|
||||
"@balena/lint": "^6.2.0",
|
||||
"@balena/lint": "^7.3.0",
|
||||
"@types/bluebird": "^3.5.37",
|
||||
"@types/chai": "^4.3.3",
|
||||
"@types/chai-as-promised": "^7.1.5",
|
||||
@ -53,6 +53,7 @@
|
||||
"@types/dockerode": "^2.5.34",
|
||||
"@types/event-stream": "^3.3.34",
|
||||
"@types/express": "^4.17.14",
|
||||
"@types/json-mask": "2.0.3",
|
||||
"@types/lodash": "^4.14.186",
|
||||
"@types/memoizee": "^0.4.8",
|
||||
"@types/mocha": "^8.2.3",
|
||||
@ -116,7 +117,7 @@
|
||||
"request": "^2.88.2",
|
||||
"resin-docker-build": "^1.1.6",
|
||||
"resumable-request": "^2.0.1",
|
||||
"rewire": "^5.0.0",
|
||||
"rewire": "^6.0.0",
|
||||
"rimraf": "^2.7.1",
|
||||
"rwlock": "^5.0.0",
|
||||
"semver": "7.5.4",
|
||||
|
@ -147,7 +147,7 @@ export async function start() {
|
||||
}
|
||||
|
||||
log.debug('Starting current state report');
|
||||
await startCurrentStateReport();
|
||||
void startCurrentStateReport();
|
||||
|
||||
// When we've provisioned, try to load the backup. We
|
||||
// must wait for the provisioning because we need a
|
||||
@ -158,7 +158,7 @@ export async function start() {
|
||||
|
||||
readyForUpdates = true;
|
||||
log.debug('Starting target state poll');
|
||||
TargetState.startPoll();
|
||||
void TargetState.startPoll();
|
||||
// Update and apply new target state
|
||||
TargetState.emitter.on(
|
||||
'target-state-update',
|
||||
@ -235,7 +235,7 @@ export function startCurrentStateReport() {
|
||||
'Trying to start state reporting without initializing API client',
|
||||
);
|
||||
}
|
||||
startReporting();
|
||||
return startReporting();
|
||||
}
|
||||
|
||||
export async function fetchDeviceTags(): Promise<DeviceTag[]> {
|
||||
|
@ -1,17 +1,17 @@
|
||||
import * as url from 'url';
|
||||
import * as _ from 'lodash';
|
||||
import { CoreOptions } from 'request';
|
||||
import type { CoreOptions } from 'request';
|
||||
import { performance } from 'perf_hooks';
|
||||
import { setTimeout } from 'timers/promises';
|
||||
import { readFile } from 'fs/promises';
|
||||
|
||||
import { DeviceState } from '../types';
|
||||
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 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 { InternalInconsistencyError, StatusError } from '../lib/errors';
|
||||
import { getRequestInstance } from '../lib/request';
|
||||
|
@ -107,7 +107,9 @@ async function mdnsLookup(
|
||||
// Make NodeJS RFC 3484 compliant for properly handling IPv6
|
||||
// See: https://github.com/nodejs/node/pull/14731
|
||||
// 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;
|
||||
|
||||
dns.lookup = (
|
||||
|
@ -1,18 +1,18 @@
|
||||
import * as _ from 'lodash';
|
||||
import { promises as fs } from 'fs';
|
||||
import { ImageInspectInfo } from 'dockerode';
|
||||
import type { ImageInspectInfo } from 'dockerode';
|
||||
|
||||
import Network from './network';
|
||||
import Volume from './volume';
|
||||
import Service from './service';
|
||||
import * as imageManager from './images';
|
||||
import type { Image } from './images';
|
||||
import {
|
||||
import type {
|
||||
CompositionStep,
|
||||
generateStep,
|
||||
CompositionStepAction,
|
||||
} 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 * as constants from '../lib/constants';
|
||||
import {
|
||||
@ -22,7 +22,7 @@ import {
|
||||
import { isNotFoundError } from '../lib/errors';
|
||||
import * as config from '../config';
|
||||
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 { isSupervisor } from '../lib/supervisor-metadata';
|
||||
|
||||
@ -64,7 +64,10 @@ export class App {
|
||||
public networks: Network[];
|
||||
public volumes: Volume[];
|
||||
|
||||
public constructor(opts: AppConstructOpts, public isTargetState: boolean) {
|
||||
public constructor(
|
||||
opts: AppConstructOpts,
|
||||
public isTargetState: boolean,
|
||||
) {
|
||||
this.appId = opts.appId;
|
||||
this.appUuid = opts.appUuid;
|
||||
this.appName = opts.appName;
|
||||
@ -513,12 +516,18 @@ export class App {
|
||||
return;
|
||||
}
|
||||
|
||||
const needsDownload = !context.availableImages.some(
|
||||
(image) =>
|
||||
image.dockerImageId === target?.config.image ||
|
||||
imageManager.isSameImage(image, { name: target?.imageName! }),
|
||||
);
|
||||
if (needsDownload && context.downloading.includes(target?.imageName!)) {
|
||||
const needsDownload =
|
||||
target != null &&
|
||||
!context.availableImages.some(
|
||||
(image) =>
|
||||
image.dockerImageId === target.config.image ||
|
||||
imageManager.isSameImage(image, { name: target.imageName! }),
|
||||
);
|
||||
if (
|
||||
target != null &&
|
||||
needsDownload &&
|
||||
context.downloading.includes(target.imageName!)
|
||||
) {
|
||||
// The image needs to be downloaded, and it's currently downloading.
|
||||
// We simply keep the application loop alive
|
||||
return generateStep('noop', {});
|
||||
|
@ -1,9 +1,10 @@
|
||||
import * as _ from 'lodash';
|
||||
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 { transaction, Transaction } from '../db';
|
||||
import type { Transaction } from '../db';
|
||||
import { transaction } from '../db';
|
||||
import * as logger from '../logger';
|
||||
import LocalModeManager from '../local-mode';
|
||||
|
||||
@ -25,9 +26,9 @@ import * as networkManager from './network-manager';
|
||||
import * as serviceManager from './service-manager';
|
||||
import * as imageManager from './images';
|
||||
import * as commitStore from './commit';
|
||||
import Service from './service';
|
||||
import Network from './network';
|
||||
import Volume from './volume';
|
||||
import type Service from './service';
|
||||
import type Network from './network';
|
||||
import type Volume from './volume';
|
||||
import { generateStep, getExecutors } from './composition-steps';
|
||||
|
||||
import type {
|
||||
@ -45,11 +46,11 @@ type ApplicationManagerEventEmitter = StrictEventEmitter<
|
||||
{ change: DeviceLegacyReport }
|
||||
>;
|
||||
const events: ApplicationManagerEventEmitter = new EventEmitter();
|
||||
export const on: typeof events['on'] = events.on.bind(events);
|
||||
export const once: typeof events['once'] = events.once.bind(events);
|
||||
export const removeListener: typeof events['removeListener'] =
|
||||
export const on: (typeof events)['on'] = events.on.bind(events);
|
||||
export const once: (typeof events)['once'] = events.once.bind(events);
|
||||
export const removeListener: (typeof events)['removeListener'] =
|
||||
events.removeListener.bind(events);
|
||||
export const removeAllListeners: typeof events['removeAllListeners'] =
|
||||
export const removeAllListeners: (typeof events)['removeAllListeners'] =
|
||||
events.removeAllListeners.bind(events);
|
||||
|
||||
const localModeManager = new LocalModeManager();
|
||||
@ -611,7 +612,7 @@ export async function serviceNameFromId(serviceId: number) {
|
||||
(svcName) => services[svcName].id === serviceId,
|
||||
);
|
||||
|
||||
if (!!serviceName) {
|
||||
if (serviceName) {
|
||||
return serviceName;
|
||||
}
|
||||
}
|
||||
@ -771,7 +772,7 @@ function saveAndRemoveImages(
|
||||
: availableAndUnused.filter((image) => !deltaSources.includes(image.name));
|
||||
|
||||
return imagesToSave
|
||||
.map((image) => ({ action: 'saveImage', image } as CompositionStep))
|
||||
.map((image) => ({ action: 'saveImage', image }) as CompositionStep)
|
||||
.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
|
||||
// requires passing the image name
|
||||
.filter(([, img]) => !!img)
|
||||
.map(([svc, img]) => ({ ...img, ...svc } as ServiceInfo))
|
||||
.map(([svc, img]) => ({ ...img, ...svc }) as ServiceInfo)
|
||||
.map((svc, __, serviceList) => {
|
||||
// If the service is not running it cannot be a handover
|
||||
if (svc.status !== 'Running') {
|
||||
|
@ -4,15 +4,15 @@ import * as config from '../config';
|
||||
|
||||
import type { Image } from './images';
|
||||
import * as images from './images';
|
||||
import Network from './network';
|
||||
import Service from './service';
|
||||
import type Network from './network';
|
||||
import type Service from './service';
|
||||
import * as serviceManager from './service-manager';
|
||||
import Volume from './volume';
|
||||
import type Volume from './volume';
|
||||
|
||||
import { checkTruthy } from '../lib/validation';
|
||||
import * as networkManager from './network-manager';
|
||||
import * as volumeManager from './volume-manager';
|
||||
import { DeviceLegacyReport } from '../types/state';
|
||||
import type { DeviceLegacyReport } from '../types/state';
|
||||
import * as commitStore from './commit';
|
||||
|
||||
interface BaseCompositionStepArgs {
|
||||
@ -81,7 +81,7 @@ interface CompositionStepArgs {
|
||||
saveImage: {
|
||||
image: Image;
|
||||
};
|
||||
cleanup: {};
|
||||
cleanup: object;
|
||||
createNetwork: {
|
||||
target: Network;
|
||||
};
|
||||
@ -94,8 +94,8 @@ interface CompositionStepArgs {
|
||||
removeVolume: {
|
||||
current: Volume;
|
||||
};
|
||||
ensureSupervisorNetwork: {};
|
||||
noop: {};
|
||||
ensureSupervisorNetwork: object;
|
||||
noop: object;
|
||||
}
|
||||
|
||||
export type CompositionStepAction = keyof CompositionStepArgs;
|
||||
|
@ -10,7 +10,10 @@ export class InvalidNetworkNameError extends TypedError {
|
||||
}
|
||||
|
||||
export class ResourceRecreationAttemptError extends TypedError {
|
||||
public constructor(public resource: string, public name: string) {
|
||||
public constructor(
|
||||
public resource: string,
|
||||
public name: string,
|
||||
) {
|
||||
super(
|
||||
`Trying to create ${resource} with name: ${name}, but a ${resource} ` +
|
||||
'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 * 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 db from '../db';
|
||||
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 {
|
||||
DeltaStillProcessingError,
|
||||
@ -67,11 +68,11 @@ class ImageEventEmitter extends (EventEmitter as new () => StrictEventEmitter<
|
||||
>) {}
|
||||
const events = new ImageEventEmitter();
|
||||
|
||||
export const on: typeof events['on'] = events.on.bind(events);
|
||||
export const once: typeof events['once'] = events.once.bind(events);
|
||||
export const removeListener: typeof events['removeListener'] =
|
||||
export const on: (typeof events)['on'] = events.on.bind(events);
|
||||
export const once: (typeof events)['once'] = events.once.bind(events);
|
||||
export const removeListener: (typeof events)['removeListener'] =
|
||||
events.removeListener.bind(events);
|
||||
export const removeAllListeners: typeof events['removeAllListeners'] =
|
||||
export const removeAllListeners: (typeof events)['removeAllListeners'] =
|
||||
events.removeAllListeners.bind(events);
|
||||
|
||||
const imageFetchFailures: Dictionary<number> = {};
|
||||
@ -144,14 +145,16 @@ function reportEvent(event: 'start' | 'update' | 'finish', state: Image) {
|
||||
switch (event) {
|
||||
case 'start':
|
||||
return true; // always report change on start
|
||||
case 'update':
|
||||
case 'update': {
|
||||
const [updatedTask, changedAfterUpdate] = currentTask.update(state);
|
||||
runningTasks[imageName] = updatedTask;
|
||||
return changedAfterUpdate; // report change only if the task context changed
|
||||
case 'finish':
|
||||
}
|
||||
case 'finish': {
|
||||
const [, changedAfterFinish] = currentTask.finish();
|
||||
delete runningTasks[imageName];
|
||||
return changedAfterFinish; // report change depending on the state of the task
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
@ -551,14 +554,14 @@ const inspectByReference = async (imageName: string) => {
|
||||
filters: { reference: [reference] },
|
||||
})
|
||||
.then(([img]) =>
|
||||
!!img
|
||||
img
|
||||
? docker.getImage(img.Id).inspect()
|
||||
: Promise.reject(
|
||||
new StatusError(
|
||||
404,
|
||||
`Failed to find an image matching ${imageName}`,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
@ -582,14 +585,14 @@ const inspectByDigest = async (imageName: string) => {
|
||||
// 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
|
||||
.then(([img]) =>
|
||||
!!img
|
||||
img
|
||||
? docker.getImage(img.dockerImageId).inspect()
|
||||
: Promise.reject(
|
||||
new StatusError(
|
||||
404,
|
||||
`Failed to find an image matching ${imageName}`,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as _ from 'lodash';
|
||||
import * as dockerode from 'dockerode';
|
||||
import type * as dockerode from 'dockerode';
|
||||
|
||||
import { docker } from '../lib/docker-utils';
|
||||
import logTypes = require('../lib/log-types');
|
||||
@ -7,7 +7,7 @@ import * as logger from '../logger';
|
||||
import log from '../lib/supervisor-console';
|
||||
import * as ComposeUtils from './utils';
|
||||
|
||||
import {
|
||||
import type {
|
||||
ComposeNetworkConfig,
|
||||
NetworkConfig,
|
||||
NetworkInspectInfo,
|
||||
@ -22,7 +22,9 @@ export class Network {
|
||||
public name: string;
|
||||
public config: NetworkConfig;
|
||||
|
||||
private constructor() {}
|
||||
private constructor() {
|
||||
/* do not allow instances using `new` */
|
||||
}
|
||||
|
||||
private static deconstructDockerName(
|
||||
name: string,
|
||||
|
@ -15,7 +15,7 @@ export interface PortBindings {
|
||||
}
|
||||
|
||||
export interface DockerPortOptions {
|
||||
exposedPorts: Dictionary<{}>;
|
||||
exposedPorts: Dictionary<EmptyObject>;
|
||||
portBindings: PortBindings;
|
||||
}
|
||||
|
||||
@ -49,7 +49,7 @@ export class PortMap {
|
||||
this.ports.externalEnd,
|
||||
);
|
||||
|
||||
const exposedPorts: { [key: string]: {} } = {};
|
||||
const exposedPorts: { [key: string]: EmptyObject } = {};
|
||||
const portBindings: PortBindings = {};
|
||||
|
||||
_.zipWith(internalRange, externalRange, (internal, external) => {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { ConfigMap, ServiceComposeConfig } from './types/service';
|
||||
import type { ConfigMap, ServiceComposeConfig } from './types/service';
|
||||
|
||||
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 { isLeft } from 'fp-ts/lib/Either';
|
||||
import * as JSONStream from 'JSONStream';
|
||||
import * as _ from 'lodash';
|
||||
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 { docker } from '../lib/docker-utils';
|
||||
@ -12,15 +12,16 @@ import * as logger from '../logger';
|
||||
|
||||
import { PermissiveNumber } from '../config/types';
|
||||
import * as constants from '../lib/constants';
|
||||
import type { StatusCodeError } from '../lib/errors';
|
||||
import {
|
||||
InternalInconsistencyError,
|
||||
isNotFoundError,
|
||||
StatusCodeError,
|
||||
isStatusError,
|
||||
} from '../lib/errors';
|
||||
import * as LogTypes from '../lib/log-types';
|
||||
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 log from '../lib/supervisor-console';
|
||||
@ -41,11 +42,11 @@ interface KillOpts {
|
||||
wait?: boolean;
|
||||
}
|
||||
|
||||
export const on: typeof events['on'] = events.on.bind(events);
|
||||
export const once: typeof events['once'] = events.once.bind(events);
|
||||
export const removeListener: typeof events['removeListener'] =
|
||||
export const on: (typeof events)['on'] = events.on.bind(events);
|
||||
export const once: (typeof events)['once'] = events.once.bind(events);
|
||||
export const removeListener: (typeof events)['removeListener'] =
|
||||
events.removeListener.bind(events);
|
||||
export const removeAllListeners: typeof events['removeAllListeners'] =
|
||||
export const removeAllListeners: (typeof events)['removeAllListeners'] =
|
||||
events.removeAllListeners.bind(events);
|
||||
|
||||
// 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) {
|
||||
logger.logSystemEvent(LogTypes.startServiceSuccess, { service });
|
||||
@ -421,7 +422,7 @@ export function listenToEvents() {
|
||||
`serviceId and imageId not defined for service: ${service.serviceName} in ServiceManager.listenToEvents`,
|
||||
);
|
||||
}
|
||||
logger.attach(data.id, {
|
||||
void logger.attach(data.id, {
|
||||
serviceId,
|
||||
imageId,
|
||||
});
|
||||
@ -447,7 +448,7 @@ export function listenToEvents() {
|
||||
});
|
||||
};
|
||||
|
||||
(async () => {
|
||||
void (async () => {
|
||||
try {
|
||||
await listen();
|
||||
} catch (e) {
|
||||
@ -479,7 +480,7 @@ export async function attachToRunning() {
|
||||
`containerId not defined for service: ${service.serviceName} in ServiceManager.attachToRunning`,
|
||||
);
|
||||
}
|
||||
logger.attach(service.containerId, {
|
||||
void logger.attach(service.containerId, {
|
||||
serviceId,
|
||||
imageId,
|
||||
});
|
||||
|
@ -1,10 +1,11 @@
|
||||
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 * as _ from 'lodash';
|
||||
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 updateLock from '../lib/update-lock';
|
||||
import { sanitiseComposeConfig } from './sanitise';
|
||||
@ -14,14 +15,16 @@ import * as conversions from '../lib/conversions';
|
||||
import { checkInt } from '../lib/validation';
|
||||
import { InternalInconsistencyError } from '../lib/errors';
|
||||
|
||||
import { EnvVarObject } from '../types';
|
||||
import {
|
||||
import type { EnvVarObject } from '../types';
|
||||
import type {
|
||||
ServiceConfig,
|
||||
ServiceConfigArrayField,
|
||||
ServiceComposeConfig,
|
||||
ConfigMap,
|
||||
DeviceMetadata,
|
||||
DockerDevice,
|
||||
} from './types/service';
|
||||
import {
|
||||
ShortMount,
|
||||
ShortBind,
|
||||
ShortAnonymousVolume,
|
||||
@ -108,7 +111,9 @@ export class Service {
|
||||
'hostname',
|
||||
].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
|
||||
// keys must be camelCase'd first
|
||||
@ -909,11 +914,11 @@ export class Service {
|
||||
private getBindsMountsAndVolumes(): {
|
||||
binds: string[];
|
||||
mounts: Dockerode.MountSettings[];
|
||||
volumes: { [volName: string]: {} };
|
||||
volumes: { [volName: string]: EmptyObject };
|
||||
} {
|
||||
const binds: string[] = [];
|
||||
const mounts: Dockerode.MountSettings[] = [];
|
||||
const volumes: { [volName: string]: {} } = {};
|
||||
const volumes: { [volName: string]: EmptyObject } = {};
|
||||
|
||||
for (const volume of this.config.volumes) {
|
||||
if (LongDefinition.is(volume)) {
|
||||
@ -921,7 +926,11 @@ export class Service {
|
||||
mounts.push(ComposeUtils.serviceMountToDockerMount(volume));
|
||||
} else {
|
||||
// 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
|
||||
// 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 { isAbsolute } from 'path';
|
||||
|
||||
import { PortMap } from '../ports';
|
||||
import type { PortMap } from '../ports';
|
||||
|
||||
export interface ComposeHealthcheck {
|
||||
test: string | string[];
|
||||
|
@ -1,6 +1,7 @@
|
||||
import * as imageManager from './images';
|
||||
import Service from './service';
|
||||
import { CompositionStep, generateStep } from './composition-steps';
|
||||
import type Service from './service';
|
||||
import type { CompositionStep } from './composition-steps';
|
||||
import { generateStep } from './composition-steps';
|
||||
import { InternalInconsistencyError } from '../lib/errors';
|
||||
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 * as _ from 'lodash';
|
||||
import { parse as parseCommand } from 'shell-quote';
|
||||
|
||||
import * as constants from '../lib/constants';
|
||||
import { checkTruthy } from '../lib/validation';
|
||||
import { Service } from './service';
|
||||
import {
|
||||
import type { Service } from './service';
|
||||
import type {
|
||||
ComposeHealthcheck,
|
||||
ConfigMap,
|
||||
DeviceMetadata,
|
||||
@ -209,7 +209,7 @@ function getNanoseconds(timeStr: string): number {
|
||||
|
||||
export function composeHealthcheckToServiceHealthcheck(
|
||||
healthcheck: ComposeHealthcheck | null | undefined,
|
||||
): ServiceHealthcheck | {} {
|
||||
): ServiceHealthcheck | EmptyObject {
|
||||
if (healthcheck == null) {
|
||||
return {};
|
||||
}
|
||||
@ -351,9 +351,8 @@ export async function addFeaturesFromLabels(
|
||||
} as LongBind);
|
||||
|
||||
if (service.config.environment['DOCKER_HOST'] == null) {
|
||||
service.config.environment[
|
||||
'DOCKER_HOST'
|
||||
] = `unix://${constants.containerDockerSocket}`;
|
||||
service.config.environment['DOCKER_HOST'] =
|
||||
`unix://${constants.containerDockerSocket}`;
|
||||
}
|
||||
// We keep balena.sock for backwards compatibility
|
||||
if (constants.dockerSocket !== '/var/run/balena.sock') {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as _ from 'lodash';
|
||||
import * as path from 'path';
|
||||
import { VolumeInspectInfo } from 'dockerode';
|
||||
import type { VolumeInspectInfo } from 'dockerode';
|
||||
|
||||
import { isNotFoundError, InternalInconsistencyError } from '../lib/errors';
|
||||
import { safeRename } from '../lib/fs-utils';
|
||||
@ -10,7 +10,8 @@ import * as LogTypes from '../lib/log-types';
|
||||
import log from '../lib/supervisor-console';
|
||||
import * as logger from '../logger';
|
||||
import { ResourceRecreationAttemptError } from './errors';
|
||||
import Volume, { VolumeConfig } from './volume';
|
||||
import type { VolumeConfig } from './volume';
|
||||
import Volume from './volume';
|
||||
|
||||
export interface VolumeNameOpts {
|
||||
name: string;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import * as Docker from 'dockerode';
|
||||
import type * as Docker from 'dockerode';
|
||||
import isEqual = require('lodash/isEqual');
|
||||
import omitBy = require('lodash/omitBy');
|
||||
|
||||
@ -6,7 +6,7 @@ import * as constants from '../lib/constants';
|
||||
import { docker } from '../lib/docker-utils';
|
||||
import { InternalInconsistencyError } from '../lib/errors';
|
||||
import * as LogTypes from '../lib/log-types';
|
||||
import { LabelObject } from '../types';
|
||||
import type { LabelObject } from '../types';
|
||||
import * as logger from '../logger';
|
||||
import * as ComposeUtils from './utils';
|
||||
|
||||
|
@ -1,5 +1,3 @@
|
||||
import * as _ from 'lodash';
|
||||
|
||||
export interface ConfigOptions {
|
||||
[key: string]: string | string[];
|
||||
}
|
||||
|
@ -2,7 +2,8 @@ import * as _ from 'lodash';
|
||||
import { promises as fs } from 'fs';
|
||||
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 * as hostUtils from '../../lib/host-utils';
|
||||
import * as constants from '../../lib/constants';
|
||||
@ -49,10 +50,10 @@ export class ConfigFs extends ConfigBackend {
|
||||
const amlSrcPath = path.join(this.SystemAmlFiles, `${aml}.aml`);
|
||||
// log to system log if the AML doesn't exist...
|
||||
if (!(await exists(amlSrcPath))) {
|
||||
log.error(`Missing AML for \'${aml}\'. Unable to load.`);
|
||||
log.error(`Missing AML for '${aml}'. Unable to load.`);
|
||||
if (logger) {
|
||||
logger.logSystemMessage(
|
||||
`Missing AML for \'${aml}\'. Unable to load.`,
|
||||
`Missing AML for '${aml}'. Unable to load.`,
|
||||
{ aml, path: amlSrcPath },
|
||||
'Load AML error',
|
||||
false,
|
||||
|
@ -1,6 +1,7 @@
|
||||
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 log from '../../lib/supervisor-console';
|
||||
import { exists } from '../../lib/fs-utils';
|
||||
@ -14,7 +15,7 @@ const ARRAY_CONFIGS = [
|
||||
'gpio',
|
||||
] as const;
|
||||
|
||||
type ArrayConfig = typeof ARRAY_CONFIGS[number];
|
||||
type ArrayConfig = (typeof ARRAY_CONFIGS)[number];
|
||||
|
||||
// Refinement on the ConfigOptions type
|
||||
// to indicate what properties are arrays
|
||||
|
@ -1,6 +1,6 @@
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { ConfigOptions } from './backend';
|
||||
import type { ConfigOptions } from './backend';
|
||||
import {
|
||||
ExtLinuxParseError,
|
||||
AppendDirectiveError,
|
||||
|
@ -1,13 +1,10 @@
|
||||
import * as _ from 'lodash';
|
||||
import * as semver from 'semver';
|
||||
|
||||
import { ConfigOptions, ConfigBackend } from './backend';
|
||||
import {
|
||||
ExtlinuxFile,
|
||||
Directive,
|
||||
AppendDirective,
|
||||
FDTDirective,
|
||||
} from './extlinux-file';
|
||||
import type { ConfigOptions } from './backend';
|
||||
import { ConfigBackend } from './backend';
|
||||
import type { ExtlinuxFile, Directive } from './extlinux-file';
|
||||
import { AppendDirective, FDTDirective } from './extlinux-file';
|
||||
import * as constants from '../../lib/constants';
|
||||
import log from '../../lib/supervisor-console';
|
||||
import { ExtLinuxEnvError, ExtLinuxParseError } from '../../lib/errors';
|
||||
|
@ -1,6 +1,7 @@
|
||||
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 log from '../../lib/supervisor-console';
|
||||
import { ExtraUEnvError } from '../../lib/errors';
|
||||
|
@ -1,5 +1,3 @@
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { Extlinux } from './extlinux';
|
||||
import { ExtraUEnv } from './extra-uEnv';
|
||||
import { ConfigTxt } from './config-txt';
|
||||
|
@ -1,7 +1,8 @@
|
||||
import * as _ from 'lodash';
|
||||
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 log from '../../lib/supervisor-console';
|
||||
import { ODMDataError } from '../../lib/errors';
|
||||
|
@ -6,7 +6,8 @@ import * as constants from '../../lib/constants';
|
||||
import { exists } from '../../lib/fs-utils';
|
||||
import * as hostUtils from '../../lib/host-utils';
|
||||
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 {
|
||||
private static readonly BASEPATH = hostUtils.pathOnBoot('splash');
|
||||
@ -155,7 +156,7 @@ export class SplashImage extends ConfigBackend {
|
||||
return SplashImage.CONFIGS.includes(this.stripPrefix(name).toLowerCase());
|
||||
}
|
||||
|
||||
public async matches(_deviceType: string): Promise<boolean> {
|
||||
public async matches(): Promise<boolean> {
|
||||
// all device types
|
||||
return true;
|
||||
}
|
||||
@ -191,7 +192,10 @@ export class SplashImage extends ConfigBackend {
|
||||
: await this.readSplashImage(SplashImage.DEFAULT);
|
||||
|
||||
// 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
|
||||
await this.writeSplashImage(image);
|
||||
|
@ -5,7 +5,7 @@ import * as constants from '../lib/constants';
|
||||
import * as hostUtils from '../lib/host-utils';
|
||||
import * as osRelease from '../lib/os-release';
|
||||
import { readLock, writeLock } from '../lib/update-lock';
|
||||
import * as Schema from './schema';
|
||||
import type * as Schema from './schema';
|
||||
|
||||
export default class ConfigJsonConfigBackend {
|
||||
private readonly readLockConfigJson: () => Bluebird.Disposer<() => void>;
|
||||
|
@ -1,18 +1,20 @@
|
||||
import { EventEmitter } from 'events';
|
||||
import type { Knex } from 'knex';
|
||||
import * as _ from 'lodash';
|
||||
import StrictEventEmitter from 'strict-event-emitter-types';
|
||||
import type StrictEventEmitter from 'strict-event-emitter-types';
|
||||
import { inspect } from 'util';
|
||||
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 ConfigJsonConfigBackend from './configJson';
|
||||
|
||||
import * as FnSchema from './functions';
|
||||
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 {
|
||||
@ -43,9 +45,9 @@ class ConfigEvents extends (EventEmitter as new () => ConfigEventEmitter) {}
|
||||
const events = new ConfigEvents();
|
||||
|
||||
// Expose methods which make this module act as an EventEmitter
|
||||
export const on: typeof events['on'] = events.on.bind(events);
|
||||
export const once: typeof events['once'] = events.once.bind(events);
|
||||
export const removeListener: typeof events['removeListener'] =
|
||||
export const on: (typeof events)['on'] = events.on.bind(events);
|
||||
export const once: (typeof events)['once'] = events.once.bind(events);
|
||||
export const removeListener: (typeof events)['removeListener'] =
|
||||
events.removeListener.bind(events);
|
||||
|
||||
export async function get<T extends SchemaTypeKey>(
|
||||
@ -54,7 +56,7 @@ export async function get<T extends SchemaTypeKey>(
|
||||
): Promise<SchemaReturn<T>> {
|
||||
const $db = trx || db.models;
|
||||
|
||||
if (Schema.schema.hasOwnProperty(key)) {
|
||||
if (Object.prototype.hasOwnProperty.call(Schema.schema, key)) {
|
||||
const schemaKey = key as Schema.SchemaKey;
|
||||
|
||||
return getSchema(schemaKey, $db).then((value) => {
|
||||
@ -80,7 +82,7 @@ export async function get<T extends SchemaTypeKey>(
|
||||
// the type system happy
|
||||
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;
|
||||
// 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
|
||||
@ -241,9 +243,11 @@ async function getSchema<T extends Schema.SchemaKey>(
|
||||
value = await configJsonBackend.get(key);
|
||||
break;
|
||||
case 'db':
|
||||
const [conf] = await $db('config').select('value').where({ key });
|
||||
if (conf != null) {
|
||||
return conf.value;
|
||||
{
|
||||
const [conf] = await $db('config').select('value').where({ key });
|
||||
if (conf != null) {
|
||||
return conf.value;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -265,7 +269,7 @@ function validateConfigMap<T extends SchemaTypeKey>(
|
||||
// throw if any value fails verification
|
||||
return _.mapValues(configMap, (value, key) => {
|
||||
if (
|
||||
!Schema.schema.hasOwnProperty(key) ||
|
||||
!Object.prototype.hasOwnProperty.call(Schema.schema, key) ||
|
||||
!Schema.schema[key as Schema.SchemaKey].mutable
|
||||
) {
|
||||
throw new Error(
|
||||
|
@ -54,12 +54,13 @@ export const PermissiveNumber = new t.Type<number, string | number>(
|
||||
switch (typeof v) {
|
||||
case 'number':
|
||||
return t.success(v);
|
||||
case 'string':
|
||||
case 'string': {
|
||||
const i = parseInt(v, 10);
|
||||
if (Number.isNaN(i)) {
|
||||
return t.failure(v, c);
|
||||
}
|
||||
return t.success(i);
|
||||
}
|
||||
default:
|
||||
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
|
||||
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>) {
|
||||
super(
|
||||
'StringJSON',
|
||||
|
@ -4,9 +4,9 @@ import * as Bluebird from 'bluebird';
|
||||
import * as config from '../config';
|
||||
import * as constants from '../lib/constants';
|
||||
import { getMetaOSRelease } from '../lib/os-release';
|
||||
import { EnvVarObject } from '../types';
|
||||
import type { EnvVarObject } from '../types';
|
||||
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[]> {
|
||||
// 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 _ from 'lodash';
|
||||
|
||||
|
@ -8,10 +8,8 @@ import * as logger from '../logger';
|
||||
import * as config from '../config';
|
||||
import * as hostConfig from '../host-config';
|
||||
import * as applicationManager from '../compose/application-manager';
|
||||
import {
|
||||
CompositionStepAction,
|
||||
generateStep,
|
||||
} from '../compose/composition-steps';
|
||||
import type { CompositionStepAction } from '../compose/composition-steps';
|
||||
import { generateStep } from '../compose/composition-steps';
|
||||
import * as commitStore from '../compose/commit';
|
||||
import { getApp } from '../device-state/db-format';
|
||||
import * as TargetState from '../device-state/target-state';
|
||||
@ -265,7 +263,7 @@ export const executeServiceAction = async ({
|
||||
? currentApp.services[0]
|
||||
: currentApp.services.find(
|
||||
(s) => s.imageId === imageId || s.serviceName === serviceName,
|
||||
);
|
||||
);
|
||||
if (currentService == null) {
|
||||
// Legacy (v1) throws 400 while v2 throws 404, and we have to keep the interface consistent.
|
||||
throw new (isLegacy ? BadRequestError : NotFoundError)(
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as _ from 'lodash';
|
||||
import * as express from 'express';
|
||||
import type * as express from 'express';
|
||||
import * as memoizee from 'memoizee';
|
||||
import { TypedError } from 'typed-error';
|
||||
|
||||
@ -37,7 +37,7 @@ type ScopeCheckCollection = {
|
||||
* The scopes which a key can cover.
|
||||
*/
|
||||
type ScopeTypes = {
|
||||
global: {};
|
||||
global: NonNullable<unknown>;
|
||||
app: {
|
||||
appId: number;
|
||||
};
|
||||
@ -222,11 +222,11 @@ async function generateKey(
|
||||
// remove the cached lookup for the key
|
||||
const [apiKey] = secrets;
|
||||
if (apiKey != null) {
|
||||
getApiKeyByKey.clear(apiKey.key);
|
||||
await getApiKeyByKey.clear(apiKey.key);
|
||||
}
|
||||
|
||||
// remove the cached value for this lookup
|
||||
getApiKeyForService.clear(appId, serviceName);
|
||||
await getApiKeyForService.clear(appId, serviceName);
|
||||
|
||||
// return a new API key
|
||||
return await createNewKey(appId, serviceName, scopes);
|
||||
|
@ -100,9 +100,11 @@ export class SupervisorAPI {
|
||||
log.error('Failed to stop Supervisor API');
|
||||
return reject(err);
|
||||
}
|
||||
options?.errored
|
||||
? log.error('Stopped Supervisor API')
|
||||
: log.info('Stopped Supervisor API');
|
||||
if (options?.errored) {
|
||||
log.error('Stopped Supervisor API');
|
||||
} else {
|
||||
log.info('Stopped Supervisor API');
|
||||
}
|
||||
return resolve();
|
||||
});
|
||||
});
|
||||
|