mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-06-21 00:23:22 +00:00
Move composition types to compose/types
This reduces circular dependencies from 250 to 80 by ensuring that modules that only require types do not import the full module with all its dependencies. Change-type: patch
This commit is contained in:
@ -6,11 +6,6 @@ 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 {
|
|
||||||
CompositionStep,
|
|
||||||
CompositionStepAction,
|
|
||||||
} from './composition-steps';
|
|
||||||
import { generateStep } from './composition-steps';
|
import { generateStep } from './composition-steps';
|
||||||
import type * as targetStateCache from '../device-state/target-state-cache';
|
import type * as targetStateCache from '../device-state/target-state-cache';
|
||||||
import { getNetworkGateway } from '../lib/docker-utils';
|
import { getNetworkGateway } from '../lib/docker-utils';
|
||||||
@ -25,7 +20,16 @@ import { checkTruthy } from '../lib/validation';
|
|||||||
import type { 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';
|
||||||
import type { LocksTakenMap } from '../lib/update-lock';
|
import type {
|
||||||
|
App as AppIface,
|
||||||
|
UpdateState,
|
||||||
|
AppsToLockMap,
|
||||||
|
CompositionStep,
|
||||||
|
CompositionStepAction,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
|
// Re export the type
|
||||||
|
export type App = AppIface;
|
||||||
|
|
||||||
export interface AppConstructOpts {
|
export interface AppConstructOpts {
|
||||||
appId: number;
|
appId: number;
|
||||||
@ -40,43 +44,11 @@ export interface AppConstructOpts {
|
|||||||
networks: Network[];
|
networks: Network[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateState {
|
|
||||||
availableImages: Image[];
|
|
||||||
containerIds: Dictionary<string>;
|
|
||||||
downloading: string[];
|
|
||||||
locksTaken: LocksTakenMap;
|
|
||||||
force: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChangingPair<T> {
|
interface ChangingPair<T> {
|
||||||
current?: T;
|
current?: T;
|
||||||
target?: T;
|
target?: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AppsToLockMap {
|
|
||||||
[appId: number]: Set<string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface App {
|
|
||||||
appId: number;
|
|
||||||
appUuid?: string;
|
|
||||||
// When setting up an application from current state, these values are not available
|
|
||||||
appName?: string;
|
|
||||||
commit?: string;
|
|
||||||
source?: string;
|
|
||||||
isHost?: boolean;
|
|
||||||
// Services are stored as an array, as at any one time we could have more than one
|
|
||||||
// service for a single service ID running (for example handover)
|
|
||||||
services: Service[];
|
|
||||||
networks: Network[];
|
|
||||||
volumes: Volume[];
|
|
||||||
|
|
||||||
nextStepsForAppUpdate(state: UpdateState, target: App): CompositionStep[];
|
|
||||||
stepsToRemoveApp(
|
|
||||||
state: Omit<UpdateState, 'availableImages'> & { keepVolumes: boolean },
|
|
||||||
): CompositionStep[];
|
|
||||||
}
|
|
||||||
|
|
||||||
class AppImpl implements App {
|
class AppImpl implements App {
|
||||||
public appId: number;
|
public appId: number;
|
||||||
public appUuid?: string;
|
public appUuid?: string;
|
||||||
|
@ -21,26 +21,29 @@ import { getServicesLockedByAppId, LocksTakenMap } from '../lib/update-lock';
|
|||||||
import { checkTruthy } from '../lib/validation';
|
import { checkTruthy } from '../lib/validation';
|
||||||
|
|
||||||
import { App } from './app';
|
import { App } from './app';
|
||||||
import type { UpdateState } from './app';
|
|
||||||
import * as volumeManager from './volume-manager';
|
import * as volumeManager from './volume-manager';
|
||||||
import * as networkManager from './network-manager';
|
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 type { Service } from './service';
|
|
||||||
import type { Network } from './network';
|
|
||||||
import type { Volume } from './volume';
|
|
||||||
import { generateStep, getExecutors } from './composition-steps';
|
import { generateStep, getExecutors } from './composition-steps';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
InstancedAppState,
|
|
||||||
TargetApps,
|
TargetApps,
|
||||||
DeviceLegacyReport,
|
DeviceLegacyReport,
|
||||||
AppState,
|
AppState,
|
||||||
ServiceState,
|
ServiceState,
|
||||||
} from '../types/state';
|
} from '../types';
|
||||||
import type { Image } from './images';
|
import type {
|
||||||
import type { CompositionStep, CompositionStepT } from './composition-steps';
|
CompositionStep,
|
||||||
|
CompositionStepT,
|
||||||
|
UpdateState,
|
||||||
|
Service,
|
||||||
|
Network,
|
||||||
|
Volume,
|
||||||
|
Image,
|
||||||
|
InstancedAppState,
|
||||||
|
} from './types';
|
||||||
|
|
||||||
type ApplicationManagerEventEmitter = StrictEventEmitter<
|
type ApplicationManagerEventEmitter = StrictEventEmitter<
|
||||||
EventEmitter,
|
EventEmitter,
|
||||||
|
@ -1,104 +1,19 @@
|
|||||||
import * as config from '../config';
|
import * as config from '../config';
|
||||||
import type { Image } from './images';
|
import type { CompositionStepArgs, Image, CompositionStep } from './types';
|
||||||
import * as images from './images';
|
import * as images from './images';
|
||||||
import type { Network } from './network';
|
|
||||||
import type { Service } from './service';
|
|
||||||
import * as serviceManager from './service-manager';
|
import * as serviceManager from './service-manager';
|
||||||
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 type { Volume } from './volume';
|
|
||||||
import * as commitStore from './commit';
|
import * as commitStore from './commit';
|
||||||
import * as updateLock from '../lib/update-lock';
|
import * as updateLock from '../lib/update-lock';
|
||||||
import type { DeviceLegacyReport } from '../types/state';
|
import type { DeviceLegacyReport } from '../types/state';
|
||||||
|
import type { CompositionStepAction, CompositionStepT } from './types';
|
||||||
|
|
||||||
interface CompositionStepArgs {
|
export type {
|
||||||
stop: {
|
CompositionStep,
|
||||||
current: Service;
|
CompositionStepT,
|
||||||
options?: {
|
CompositionStepAction,
|
||||||
wait?: boolean;
|
} from './types';
|
||||||
};
|
|
||||||
};
|
|
||||||
kill: {
|
|
||||||
current: Service;
|
|
||||||
options?: {
|
|
||||||
wait?: boolean;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
remove: {
|
|
||||||
current: Service;
|
|
||||||
};
|
|
||||||
updateMetadata: {
|
|
||||||
current: Service;
|
|
||||||
target: Service;
|
|
||||||
};
|
|
||||||
restart: {
|
|
||||||
current: Service;
|
|
||||||
target: Service;
|
|
||||||
};
|
|
||||||
start: {
|
|
||||||
target: Service;
|
|
||||||
};
|
|
||||||
updateCommit: {
|
|
||||||
target: string;
|
|
||||||
appId: number;
|
|
||||||
};
|
|
||||||
handover: {
|
|
||||||
current: Service;
|
|
||||||
target: Service;
|
|
||||||
options?: {
|
|
||||||
timeout?: number;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
fetch: {
|
|
||||||
image: Image;
|
|
||||||
serviceName: string;
|
|
||||||
};
|
|
||||||
removeImage: {
|
|
||||||
image: Image;
|
|
||||||
};
|
|
||||||
saveImage: {
|
|
||||||
image: Image;
|
|
||||||
};
|
|
||||||
cleanup: object;
|
|
||||||
createNetwork: {
|
|
||||||
target: Network;
|
|
||||||
};
|
|
||||||
createVolume: {
|
|
||||||
target: Volume;
|
|
||||||
};
|
|
||||||
removeNetwork: {
|
|
||||||
current: Network;
|
|
||||||
};
|
|
||||||
removeVolume: {
|
|
||||||
current: Volume;
|
|
||||||
};
|
|
||||||
ensureSupervisorNetwork: object;
|
|
||||||
noop: object;
|
|
||||||
takeLock: {
|
|
||||||
appId: number;
|
|
||||||
services: string[];
|
|
||||||
force: boolean;
|
|
||||||
};
|
|
||||||
releaseLock: {
|
|
||||||
appId: number;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export type CompositionStepAction = keyof CompositionStepArgs;
|
|
||||||
export type CompositionStepT<T extends CompositionStepAction> = {
|
|
||||||
action: T;
|
|
||||||
} & CompositionStepArgs[T];
|
|
||||||
export type CompositionStep = CompositionStepT<CompositionStepAction>;
|
|
||||||
|
|
||||||
export function generateStep<T extends CompositionStepAction>(
|
|
||||||
action: T,
|
|
||||||
args: CompositionStepArgs[T],
|
|
||||||
): CompositionStep {
|
|
||||||
return {
|
|
||||||
action,
|
|
||||||
...args,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
type Executors<T extends CompositionStepAction> = {
|
type Executors<T extends CompositionStepAction> = {
|
||||||
[key in T]: (step: CompositionStepT<key>) => Promise<unknown>;
|
[key in T]: (step: CompositionStepT<key>) => Promise<unknown>;
|
||||||
@ -114,6 +29,16 @@ interface CompositionCallbacks {
|
|||||||
bestDeltaSource: (image: Image, available: Image[]) => string | null;
|
bestDeltaSource: (image: Image, available: Image[]) => string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function generateStep<T extends CompositionStepAction>(
|
||||||
|
action: T,
|
||||||
|
args: CompositionStepArgs[T],
|
||||||
|
): CompositionStep {
|
||||||
|
return {
|
||||||
|
action,
|
||||||
|
...args,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function getExecutors(app: { callbacks: CompositionCallbacks }) {
|
export function getExecutors(app: { callbacks: CompositionCallbacks }) {
|
||||||
const executors: Executors<CompositionStepAction> = {
|
const executors: Executors<CompositionStepAction> = {
|
||||||
stop: async (step) => {
|
stop: async (step) => {
|
||||||
|
@ -24,40 +24,13 @@ import { strict as assert } from 'assert';
|
|||||||
import log from '../lib/supervisor-console';
|
import log from '../lib/supervisor-console';
|
||||||
import { setTimeout } from 'timers/promises';
|
import { setTimeout } from 'timers/promises';
|
||||||
|
|
||||||
|
import type { Image } from './types';
|
||||||
|
export type { Image } from './types';
|
||||||
|
|
||||||
interface FetchProgressEvent {
|
interface FetchProgressEvent {
|
||||||
percentage: number;
|
percentage: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Image {
|
|
||||||
id?: number;
|
|
||||||
/**
|
|
||||||
* image [registry/]repo@digest or [registry/]repo:tag
|
|
||||||
*/
|
|
||||||
name: string;
|
|
||||||
/**
|
|
||||||
* @deprecated to be removed in target state v4
|
|
||||||
*/
|
|
||||||
appId: number;
|
|
||||||
appUuid: string;
|
|
||||||
/**
|
|
||||||
* @deprecated to be removed in target state v4
|
|
||||||
*/
|
|
||||||
serviceId: number;
|
|
||||||
serviceName: string;
|
|
||||||
/**
|
|
||||||
* @deprecated to be removed in target state v4
|
|
||||||
*/
|
|
||||||
imageId: number;
|
|
||||||
/**
|
|
||||||
* @deprecated to be removed in target state v4
|
|
||||||
*/
|
|
||||||
releaseId: number;
|
|
||||||
commit: string;
|
|
||||||
dockerImageId?: string;
|
|
||||||
status?: 'Downloading' | 'Downloaded' | 'Deleting';
|
|
||||||
downloadProgress?: number | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup an event emitter
|
// Setup an event emitter
|
||||||
interface ImageEvents {
|
interface ImageEvents {
|
||||||
change: void;
|
change: void;
|
||||||
|
@ -11,25 +11,13 @@ import type {
|
|||||||
ComposeNetworkConfig,
|
ComposeNetworkConfig,
|
||||||
NetworkConfig,
|
NetworkConfig,
|
||||||
NetworkInspectInfo,
|
NetworkInspectInfo,
|
||||||
|
Network as NetworkIface,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
import { InvalidNetworkNameError } from './errors';
|
import { InvalidNetworkNameError } from './errors';
|
||||||
import { InternalInconsistencyError } from '../lib/errors';
|
import { InternalInconsistencyError } from '../lib/errors';
|
||||||
|
|
||||||
export interface Network {
|
export type Network = NetworkIface;
|
||||||
appId: number;
|
|
||||||
appUuid?: string;
|
|
||||||
name: string;
|
|
||||||
config: NetworkConfig;
|
|
||||||
|
|
||||||
isEqualConfig(network: Network): boolean;
|
|
||||||
create(): Promise<void>;
|
|
||||||
remove(): Promise<void>;
|
|
||||||
toDockerConfig(): dockerode.NetworkCreateOptions & {
|
|
||||||
ConfigOnly: boolean;
|
|
||||||
};
|
|
||||||
toComposeObject(): ComposeNetworkConfig;
|
|
||||||
}
|
|
||||||
|
|
||||||
class NetworkImpl implements Network {
|
class NetworkImpl implements Network {
|
||||||
public appId: number;
|
public appId: number;
|
||||||
|
@ -20,8 +20,8 @@ import {
|
|||||||
} 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 type { ServiceStatus } from './service';
|
|
||||||
import { Service } from './service';
|
import { Service } from './service';
|
||||||
|
import type { ServiceStatus } from './types';
|
||||||
import { serviceNetworksToDockerNetworks } from './utils';
|
import { serviceNetworksToDockerNetworks } from './utils';
|
||||||
|
|
||||||
import log from '../lib/supervisor-console';
|
import log from '../lib/supervisor-console';
|
||||||
|
@ -23,6 +23,8 @@ import type {
|
|||||||
ConfigMap,
|
ConfigMap,
|
||||||
DeviceMetadata,
|
DeviceMetadata,
|
||||||
DockerDevice,
|
DockerDevice,
|
||||||
|
ServiceStatus,
|
||||||
|
Service as ServiceIface,
|
||||||
} from './types';
|
} from './types';
|
||||||
import {
|
import {
|
||||||
ShortMount,
|
ShortMount,
|
||||||
@ -41,60 +43,7 @@ const CONTAINER_NETWORK_MODE_REGEX = /container:\s*(.+)/;
|
|||||||
|
|
||||||
const unsupportedSecurityOpt = (opt: string) => /label=.*/.test(opt);
|
const unsupportedSecurityOpt = (opt: string) => /label=.*/.test(opt);
|
||||||
|
|
||||||
export type ServiceStatus =
|
export type Service = ServiceIface;
|
||||||
| 'Stopping'
|
|
||||||
| 'Running'
|
|
||||||
| 'Installing'
|
|
||||||
| 'Installed'
|
|
||||||
| 'Dead'
|
|
||||||
| 'paused'
|
|
||||||
| 'restarting'
|
|
||||||
| 'removing'
|
|
||||||
| 'exited';
|
|
||||||
|
|
||||||
export interface Service {
|
|
||||||
appId: number;
|
|
||||||
appUuid?: string;
|
|
||||||
imageId: number;
|
|
||||||
config: ServiceConfig;
|
|
||||||
serviceName: string;
|
|
||||||
commit: string;
|
|
||||||
releaseId: number;
|
|
||||||
serviceId: number;
|
|
||||||
imageName: string | null;
|
|
||||||
containerId: string | null;
|
|
||||||
exitErrorMessage: string | null;
|
|
||||||
|
|
||||||
dependsOn: string[] | null;
|
|
||||||
|
|
||||||
dockerImageId: string | null;
|
|
||||||
// This looks weird, and it is. The lowercase statuses come from Docker,
|
|
||||||
// except the dashboard takes these values and displays them on the dashboard.
|
|
||||||
// What we should be doin is defining these container statuses, and have the
|
|
||||||
// dashboard make these human readable instead. Until that happens we have
|
|
||||||
// this halfways state of some captalised statuses, and others coming directly
|
|
||||||
// from docker
|
|
||||||
status: ServiceStatus;
|
|
||||||
createdAt: Date | null;
|
|
||||||
|
|
||||||
hasNetwork(networkName: string): boolean;
|
|
||||||
hasVolume(volumeName: string): boolean;
|
|
||||||
isEqualExceptForRunningState(
|
|
||||||
service: Service,
|
|
||||||
currentContainerIds: Dictionary<string>,
|
|
||||||
): boolean;
|
|
||||||
isEqualConfig(
|
|
||||||
service: Service,
|
|
||||||
currentContainerIds: Dictionary<string>,
|
|
||||||
): boolean;
|
|
||||||
hasNetworkMode(networkName: string): boolean;
|
|
||||||
extraNetworksToJoin(): ServiceConfig['networks'];
|
|
||||||
toDockerContainer(opts: {
|
|
||||||
deviceName: string;
|
|
||||||
containerIds: Dictionary<string>;
|
|
||||||
}): Dockerode.ContainerCreateOptions;
|
|
||||||
handoverCompleteFullPathsOnHost(): string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
class ServiceImpl implements Service {
|
class ServiceImpl implements Service {
|
||||||
public appId: number;
|
public appId: number;
|
||||||
|
38
src/compose/types/app.ts
Normal file
38
src/compose/types/app.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import type { Network } from './network';
|
||||||
|
import type { Volume } from './volume';
|
||||||
|
import type { Service } from './service';
|
||||||
|
import type { LocksTakenMap } from '../../lib/update-lock';
|
||||||
|
import type { Image } from './image';
|
||||||
|
import type { CompositionStep } from './composition-step';
|
||||||
|
|
||||||
|
export interface UpdateState {
|
||||||
|
availableImages: Image[];
|
||||||
|
containerIds: Dictionary<string>;
|
||||||
|
downloading: string[];
|
||||||
|
locksTaken: LocksTakenMap;
|
||||||
|
force: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface App {
|
||||||
|
appId: number;
|
||||||
|
appUuid?: string;
|
||||||
|
// When setting up an application from current state, these values are not available
|
||||||
|
appName?: string;
|
||||||
|
commit?: string;
|
||||||
|
source?: string;
|
||||||
|
isHost?: boolean;
|
||||||
|
// Services are stored as an array, as at any one time we could have more than one
|
||||||
|
// service for a single service ID running (for example handover)
|
||||||
|
services: Service[];
|
||||||
|
networks: Network[];
|
||||||
|
volumes: Volume[];
|
||||||
|
|
||||||
|
nextStepsForAppUpdate(state: UpdateState, target: App): CompositionStep[];
|
||||||
|
stepsToRemoveApp(
|
||||||
|
state: Omit<UpdateState, 'availableImages'> & { keepVolumes: boolean },
|
||||||
|
): CompositionStep[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AppsToLockMap {
|
||||||
|
[appId: number]: Set<string>;
|
||||||
|
}
|
3
src/compose/types/application-manager.ts
Normal file
3
src/compose/types/application-manager.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import type { App } from './app';
|
||||||
|
|
||||||
|
export type InstancedAppState = { [appId: number]: App };
|
83
src/compose/types/composition-step.ts
Normal file
83
src/compose/types/composition-step.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
import type { Image } from './image';
|
||||||
|
import type { Service } from './service';
|
||||||
|
import type { Network } from './network';
|
||||||
|
import type { Volume } from './volume';
|
||||||
|
|
||||||
|
export interface CompositionStepArgs {
|
||||||
|
stop: {
|
||||||
|
current: Service;
|
||||||
|
options?: {
|
||||||
|
wait?: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
kill: {
|
||||||
|
current: Service;
|
||||||
|
options?: {
|
||||||
|
wait?: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
remove: {
|
||||||
|
current: Service;
|
||||||
|
};
|
||||||
|
updateMetadata: {
|
||||||
|
current: Service;
|
||||||
|
target: Service;
|
||||||
|
};
|
||||||
|
restart: {
|
||||||
|
current: Service;
|
||||||
|
target: Service;
|
||||||
|
};
|
||||||
|
start: {
|
||||||
|
target: Service;
|
||||||
|
};
|
||||||
|
updateCommit: {
|
||||||
|
target: string;
|
||||||
|
appId: number;
|
||||||
|
};
|
||||||
|
handover: {
|
||||||
|
current: Service;
|
||||||
|
target: Service;
|
||||||
|
options?: {
|
||||||
|
timeout?: number;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
fetch: {
|
||||||
|
image: Image;
|
||||||
|
serviceName: string;
|
||||||
|
};
|
||||||
|
removeImage: {
|
||||||
|
image: Image;
|
||||||
|
};
|
||||||
|
saveImage: {
|
||||||
|
image: Image;
|
||||||
|
};
|
||||||
|
cleanup: object;
|
||||||
|
createNetwork: {
|
||||||
|
target: Network;
|
||||||
|
};
|
||||||
|
createVolume: {
|
||||||
|
target: Volume;
|
||||||
|
};
|
||||||
|
removeNetwork: {
|
||||||
|
current: Network;
|
||||||
|
};
|
||||||
|
removeVolume: {
|
||||||
|
current: Volume;
|
||||||
|
};
|
||||||
|
ensureSupervisorNetwork: object;
|
||||||
|
noop: object;
|
||||||
|
takeLock: {
|
||||||
|
appId: number;
|
||||||
|
services: string[];
|
||||||
|
force: boolean;
|
||||||
|
};
|
||||||
|
releaseLock: {
|
||||||
|
appId: number;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type CompositionStepAction = keyof CompositionStepArgs;
|
||||||
|
export type CompositionStepT<T extends CompositionStepAction> = {
|
||||||
|
action: T;
|
||||||
|
} & CompositionStepArgs[T];
|
||||||
|
export type CompositionStep = CompositionStepT<CompositionStepAction>;
|
29
src/compose/types/image.ts
Normal file
29
src/compose/types/image.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
export interface Image {
|
||||||
|
id?: number;
|
||||||
|
/**
|
||||||
|
* image [registry/]repo@digest or [registry/]repo:tag
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* @deprecated to be removed in target state v4
|
||||||
|
*/
|
||||||
|
appId: number;
|
||||||
|
appUuid: string;
|
||||||
|
/**
|
||||||
|
* @deprecated to be removed in target state v4
|
||||||
|
*/
|
||||||
|
serviceId: number;
|
||||||
|
serviceName: string;
|
||||||
|
/**
|
||||||
|
* @deprecated to be removed in target state v4
|
||||||
|
*/
|
||||||
|
imageId: number;
|
||||||
|
/**
|
||||||
|
* @deprecated to be removed in target state v4
|
||||||
|
*/
|
||||||
|
releaseId: number;
|
||||||
|
commit: string;
|
||||||
|
dockerImageId?: string;
|
||||||
|
status?: 'Downloading' | 'Downloaded' | 'Deleting';
|
||||||
|
downloadProgress?: number | null;
|
||||||
|
}
|
@ -1,2 +1,7 @@
|
|||||||
export * from './service';
|
export * from './service';
|
||||||
export * from './network';
|
export * from './network';
|
||||||
|
export type * from './volume';
|
||||||
|
export type * from './image';
|
||||||
|
export type * from './composition-step';
|
||||||
|
export type * from './app';
|
||||||
|
export type * from './application-manager';
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
import type { NetworkInspectInfo as DockerNetworkInspectInfo } from 'dockerode';
|
import type {
|
||||||
|
NetworkInspectInfo as DockerNetworkInspectInfo,
|
||||||
|
NetworkCreateOptions,
|
||||||
|
} 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`.
|
||||||
@ -44,3 +47,18 @@ export interface NetworkConfig {
|
|||||||
options: { [optName: string]: string };
|
options: { [optName: string]: string };
|
||||||
configOnly: boolean;
|
configOnly: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Network {
|
||||||
|
appId: number;
|
||||||
|
appUuid?: string;
|
||||||
|
name: string;
|
||||||
|
config: NetworkConfig;
|
||||||
|
|
||||||
|
isEqualConfig(network: Network): boolean;
|
||||||
|
create(): Promise<void>;
|
||||||
|
remove(): Promise<void>;
|
||||||
|
toDockerConfig(): NetworkCreateOptions & {
|
||||||
|
ConfigOnly: boolean;
|
||||||
|
};
|
||||||
|
toComposeObject(): ComposeNetworkConfig;
|
||||||
|
}
|
||||||
|
@ -336,3 +336,58 @@ export interface DockerDevice {
|
|||||||
PathInContainer: string;
|
PathInContainer: string;
|
||||||
CgroupPermissions: string;
|
CgroupPermissions: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ServiceStatus =
|
||||||
|
| 'Stopping'
|
||||||
|
| 'Running'
|
||||||
|
| 'Installing'
|
||||||
|
| 'Installed'
|
||||||
|
| 'Dead'
|
||||||
|
| 'paused'
|
||||||
|
| 'restarting'
|
||||||
|
| 'removing'
|
||||||
|
| 'exited';
|
||||||
|
|
||||||
|
export interface Service {
|
||||||
|
appId: number;
|
||||||
|
appUuid?: string;
|
||||||
|
imageId: number;
|
||||||
|
config: ServiceConfig;
|
||||||
|
serviceName: string;
|
||||||
|
commit: string;
|
||||||
|
releaseId: number;
|
||||||
|
serviceId: number;
|
||||||
|
imageName: string | null;
|
||||||
|
containerId: string | null;
|
||||||
|
exitErrorMessage: string | null;
|
||||||
|
|
||||||
|
dependsOn: string[] | null;
|
||||||
|
|
||||||
|
dockerImageId: string | null;
|
||||||
|
// This looks weird, and it is. The lowercase statuses come from Docker,
|
||||||
|
// except the dashboard takes these values and displays them on the dashboard.
|
||||||
|
// What we should be doin is defining these container statuses, and have the
|
||||||
|
// dashboard make these human readable instead. Until that happens we have
|
||||||
|
// this halfways state of some captalised statuses, and others coming directly
|
||||||
|
// from docker
|
||||||
|
status: ServiceStatus;
|
||||||
|
createdAt: Date | null;
|
||||||
|
|
||||||
|
hasNetwork(networkName: string): boolean;
|
||||||
|
hasVolume(volumeName: string): boolean;
|
||||||
|
isEqualExceptForRunningState(
|
||||||
|
service: Service,
|
||||||
|
currentContainerIds: Dictionary<string>,
|
||||||
|
): boolean;
|
||||||
|
isEqualConfig(
|
||||||
|
service: Service,
|
||||||
|
currentContainerIds: Dictionary<string>,
|
||||||
|
): boolean;
|
||||||
|
hasNetworkMode(networkName: string): boolean;
|
||||||
|
extraNetworksToJoin(): ServiceConfig['networks'];
|
||||||
|
toDockerContainer(opts: {
|
||||||
|
deviceName: string;
|
||||||
|
containerIds: Dictionary<string>;
|
||||||
|
}): Dockerode.ContainerCreateOptions;
|
||||||
|
handoverCompleteFullPathsOnHost(): string[];
|
||||||
|
}
|
||||||
|
25
src/compose/types/volume.ts
Normal file
25
src/compose/types/volume.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import type { LabelObject } from '../../types';
|
||||||
|
import type { VolumeInspectInfo } from 'dockerode';
|
||||||
|
|
||||||
|
export interface VolumeConfig {
|
||||||
|
labels: LabelObject;
|
||||||
|
driver: string;
|
||||||
|
driverOpts: VolumeInspectInfo['Options'];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ComposeVolumeConfig {
|
||||||
|
driver: string;
|
||||||
|
driver_opts: Dictionary<string>;
|
||||||
|
labels: LabelObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Volume {
|
||||||
|
name: string;
|
||||||
|
appId: number;
|
||||||
|
appUuid: string;
|
||||||
|
config: VolumeConfig;
|
||||||
|
|
||||||
|
isEqualConfig(volume: Volume): boolean;
|
||||||
|
create(): Promise<void>;
|
||||||
|
remove(): Promise<void>;
|
||||||
|
}
|
@ -2,7 +2,7 @@ import * as imageManager from './images';
|
|||||||
import type { Service } from './service';
|
import type { Service } from './service';
|
||||||
import type { CompositionStep } from './composition-steps';
|
import type { CompositionStep } from './composition-steps';
|
||||||
import { generateStep } from './composition-steps';
|
import { generateStep } from './composition-steps';
|
||||||
import type { AppsToLockMap } from './app';
|
import type { AppsToLockMap } from './types';
|
||||||
import { InternalInconsistencyError } from '../lib/errors';
|
import { InternalInconsistencyError } from '../lib/errors';
|
||||||
import { checkString } from '../lib/validation';
|
import { checkString } from '../lib/validation';
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ 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 type { Service } from './service';
|
|
||||||
import type {
|
import type {
|
||||||
ComposeHealthcheck,
|
ComposeHealthcheck,
|
||||||
ConfigMap,
|
ConfigMap,
|
||||||
@ -16,6 +15,7 @@ import type {
|
|||||||
ServiceHealthcheck,
|
ServiceHealthcheck,
|
||||||
LongDefinition,
|
LongDefinition,
|
||||||
LongBind,
|
LongBind,
|
||||||
|
Service,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
import log from '../lib/supervisor-console';
|
import log from '../lib/supervisor-console';
|
||||||
|
@ -10,7 +10,7 @@ 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 type { VolumeConfig } from './volume';
|
import type { VolumeConfig } from './types';
|
||||||
import { Volume } from './volume';
|
import { Volume } from './volume';
|
||||||
|
|
||||||
export interface VolumeNameOpts {
|
export interface VolumeNameOpts {
|
||||||
|
@ -10,28 +10,13 @@ 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';
|
||||||
|
|
||||||
export interface VolumeConfig {
|
import type {
|
||||||
labels: LabelObject;
|
Volume as VolumeIface,
|
||||||
driver: string;
|
VolumeConfig,
|
||||||
driverOpts: Docker.VolumeInspectInfo['Options'];
|
ComposeVolumeConfig,
|
||||||
}
|
} from './types';
|
||||||
|
|
||||||
export interface ComposeVolumeConfig {
|
export type Volume = VolumeIface;
|
||||||
driver: string;
|
|
||||||
driver_opts: Dictionary<string>;
|
|
||||||
labels: LabelObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Volume {
|
|
||||||
name: string;
|
|
||||||
appId: number;
|
|
||||||
appUuid: string;
|
|
||||||
config: VolumeConfig;
|
|
||||||
|
|
||||||
isEqualConfig(volume: Volume): boolean;
|
|
||||||
create(): Promise<void>;
|
|
||||||
remove(): Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
class VolumeImpl implements Volume {
|
class VolumeImpl implements Volume {
|
||||||
private constructor(
|
private constructor(
|
||||||
|
@ -34,7 +34,6 @@ import * as commitStore from './compose/commit';
|
|||||||
|
|
||||||
import type {
|
import type {
|
||||||
DeviceLegacyState,
|
DeviceLegacyState,
|
||||||
InstancedDeviceState,
|
|
||||||
DeviceState,
|
DeviceState,
|
||||||
DeviceReport,
|
DeviceReport,
|
||||||
AppState,
|
AppState,
|
||||||
@ -47,11 +46,20 @@ import type {
|
|||||||
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';
|
||||||
import { setTimeout } from 'timers/promises';
|
import { setTimeout } from 'timers/promises';
|
||||||
|
import type { InstancedAppState } from './compose/types';
|
||||||
|
|
||||||
const TARGET_STATE_CONFIG_DUMP = pathOnRoot(
|
const TARGET_STATE_CONFIG_DUMP = pathOnRoot(
|
||||||
'/tmp/balena-supervisor/target-state-config',
|
'/tmp/balena-supervisor/target-state-config',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
interface InstancedDeviceState {
|
||||||
|
local: {
|
||||||
|
name: string;
|
||||||
|
config: Dictionary<string>;
|
||||||
|
apps: InstancedAppState;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function parseTargetState(state: unknown): TargetState {
|
function parseTargetState(state: unknown): TargetState {
|
||||||
const res = TargetState.decode(state);
|
const res = TargetState.decode(state);
|
||||||
|
|
||||||
|
@ -8,12 +8,12 @@ import { App } from '../compose/app';
|
|||||||
import * as images from '../compose/images';
|
import * as images from '../compose/images';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
InstancedAppState,
|
|
||||||
TargetApp,
|
TargetApp,
|
||||||
TargetApps,
|
TargetApps,
|
||||||
TargetRelease,
|
TargetRelease,
|
||||||
TargetService,
|
TargetService,
|
||||||
} from '../types/state';
|
} from '../types/state';
|
||||||
|
import type { InstancedAppState } from '../compose/types';
|
||||||
|
|
||||||
type InstancedApp = InstancedAppState[0];
|
type InstancedApp = InstancedAppState[0];
|
||||||
|
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
import * as t from 'io-ts';
|
import * as t from 'io-ts';
|
||||||
|
|
||||||
// TODO: move all these exported types to ../compose/types
|
|
||||||
import type { ComposeNetworkConfig } from '../compose/types';
|
|
||||||
import type { ComposeVolumeConfig } from '../compose/volume';
|
|
||||||
import type { ContractObject } from '../lib/contracts';
|
import type { ContractObject } from '../lib/contracts';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -16,7 +13,10 @@ import {
|
|||||||
nonEmptyRecord,
|
nonEmptyRecord,
|
||||||
} from './basic';
|
} from './basic';
|
||||||
|
|
||||||
import type { App } from '../compose/app';
|
import type {
|
||||||
|
ComposeVolumeConfig,
|
||||||
|
ComposeNetworkConfig,
|
||||||
|
} from '../compose/types';
|
||||||
|
|
||||||
export type DeviceLegacyReport = Partial<{
|
export type DeviceLegacyReport = Partial<{
|
||||||
api_port: number;
|
api_port: number;
|
||||||
@ -356,13 +356,3 @@ export const AppsJsonFormat = t.intersection([
|
|||||||
t.partial({ pinDevice: t.boolean }),
|
t.partial({ pinDevice: t.boolean }),
|
||||||
]);
|
]);
|
||||||
export type AppsJsonFormat = t.TypeOf<typeof AppsJsonFormat>;
|
export type AppsJsonFormat = t.TypeOf<typeof AppsJsonFormat>;
|
||||||
|
|
||||||
export type InstancedAppState = { [appId: number]: App };
|
|
||||||
|
|
||||||
export interface InstancedDeviceState {
|
|
||||||
local: {
|
|
||||||
name: string;
|
|
||||||
config: Dictionary<string>;
|
|
||||||
apps: InstancedAppState;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
@ -19,7 +19,7 @@ import {
|
|||||||
expectSteps,
|
expectSteps,
|
||||||
expectNoStep,
|
expectNoStep,
|
||||||
} from '~/test-lib/state-helper';
|
} from '~/test-lib/state-helper';
|
||||||
import type { InstancedAppState } from '~/src/types';
|
import type { InstancedAppState } from '~/src/compose/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
|
||||||
|
@ -9,7 +9,7 @@ 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 type { InstancedAppState } from '~/src/types/state';
|
import type { InstancedAppState } from '~/src/compose/types';
|
||||||
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';
|
||||||
|
@ -9,7 +9,7 @@ import type {
|
|||||||
CompositionStep,
|
CompositionStep,
|
||||||
CompositionStepAction,
|
CompositionStepAction,
|
||||||
} from '~/src/compose/composition-steps';
|
} from '~/src/compose/composition-steps';
|
||||||
import type { InstancedAppState } from '~/src/types/state';
|
import type { InstancedAppState } from '~/src/compose/types';
|
||||||
|
|
||||||
export const DEFAULT_NETWORK = Network.fromComposeObject(
|
export const DEFAULT_NETWORK = Network.fromComposeObject(
|
||||||
'default',
|
'default',
|
||||||
|
Reference in New Issue
Block a user