mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2024-12-21 14:37:49 +00:00
Split compose types into interface and implementation
This splits `App`, `Network`, `Service` and `Volume` which used to be defined as classes into an interface and a class implementation that is not exported. This will allow to work with just the types in some cases and prevent circular dependencies when importing. Change-type: patch
This commit is contained in:
parent
435a363716
commit
94de4006a0
@ -2,9 +2,9 @@ import _ from 'lodash';
|
||||
import { promises as fs } from 'fs';
|
||||
import type { ImageInspectInfo } from 'dockerode';
|
||||
|
||||
import Network from './network';
|
||||
import Volume from './volume';
|
||||
import Service from './service';
|
||||
import { Network } from './network';
|
||||
import { Volume } from './volume';
|
||||
import { Service } from './service';
|
||||
import * as imageManager from './images';
|
||||
import type { Image } from './images';
|
||||
import type {
|
||||
@ -57,7 +57,27 @@ export interface AppsToLockMap {
|
||||
[appId: number]: Set<string>;
|
||||
}
|
||||
|
||||
export class App {
|
||||
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 {
|
||||
public appId: number;
|
||||
public appUuid?: string;
|
||||
// When setting up an application from current state, these values are not available
|
||||
@ -1027,7 +1047,7 @@ export class App {
|
||||
}),
|
||||
);
|
||||
|
||||
return new App(
|
||||
return new AppImpl(
|
||||
{
|
||||
appId: app.appId,
|
||||
appUuid: app.uuid,
|
||||
@ -1044,4 +1064,4 @@ export class App {
|
||||
}
|
||||
}
|
||||
|
||||
export default App;
|
||||
export const App = AppImpl;
|
||||
|
@ -20,16 +20,16 @@ import {
|
||||
import { getServicesLockedByAppId, LocksTakenMap } from '../lib/update-lock';
|
||||
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 networkManager from './network-manager';
|
||||
import * as serviceManager from './service-manager';
|
||||
import * as imageManager from './images';
|
||||
import * as commitStore from './commit';
|
||||
import type Service from './service';
|
||||
import type Network from './network';
|
||||
import type 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 {
|
||||
|
@ -1,12 +1,12 @@
|
||||
import * as config from '../config';
|
||||
import type { Image } from './images';
|
||||
import * as images from './images';
|
||||
import type Network from './network';
|
||||
import type Service from './service';
|
||||
import type { Network } from './network';
|
||||
import type { Service } from './service';
|
||||
import * as serviceManager from './service-manager';
|
||||
import * as networkManager from './network-manager';
|
||||
import * as volumeManager from './volume-manager';
|
||||
import type Volume from './volume';
|
||||
import type { Volume } from './volume';
|
||||
import * as commitStore from './commit';
|
||||
import * as updateLock from '../lib/update-lock';
|
||||
import type { DeviceLegacyReport } from '../types/state';
|
||||
|
@ -11,12 +11,27 @@ import type {
|
||||
ComposeNetworkConfig,
|
||||
NetworkConfig,
|
||||
NetworkInspectInfo,
|
||||
} from './types/network';
|
||||
} from './types';
|
||||
|
||||
import { InvalidNetworkNameError } from './errors';
|
||||
import { InternalInconsistencyError } from '../lib/errors';
|
||||
|
||||
export class Network {
|
||||
export interface Network {
|
||||
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 {
|
||||
public appId: number;
|
||||
public appUuid?: string;
|
||||
public name: string;
|
||||
@ -303,4 +318,4 @@ export class Network {
|
||||
}
|
||||
}
|
||||
|
||||
export default Network;
|
||||
export const Network = NetworkImpl;
|
||||
|
@ -23,7 +23,7 @@ import type {
|
||||
ConfigMap,
|
||||
DeviceMetadata,
|
||||
DockerDevice,
|
||||
} from './types/service';
|
||||
} from './types';
|
||||
import {
|
||||
ShortMount,
|
||||
ShortBind,
|
||||
@ -34,7 +34,7 @@ import {
|
||||
LongBind,
|
||||
LongAnonymousVolume,
|
||||
LongNamedVolume,
|
||||
} from './types/service';
|
||||
} from './types';
|
||||
|
||||
const SERVICE_NETWORK_MODE_REGEX = /service:\s*(.+)/;
|
||||
const CONTAINER_NETWORK_MODE_REGEX = /container:\s*(.+)/;
|
||||
@ -52,7 +52,51 @@ export type ServiceStatus =
|
||||
| 'removing'
|
||||
| 'exited';
|
||||
|
||||
export class Service {
|
||||
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 {
|
||||
public appId: number;
|
||||
public appUuid?: string;
|
||||
public imageId: number;
|
||||
@ -64,17 +108,8 @@ export class Service {
|
||||
public imageName: string | null;
|
||||
public containerId: string | null;
|
||||
public exitErrorMessage: string | null;
|
||||
|
||||
public dependsOn: string[] | null;
|
||||
|
||||
public 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
|
||||
public status: ServiceStatus;
|
||||
public createdAt: Date | null;
|
||||
|
||||
@ -95,7 +130,7 @@ export class Service {
|
||||
'dnsSearch',
|
||||
];
|
||||
public static allConfigArrayFields: ServiceConfigArrayField[] =
|
||||
Service.configArrayFields.concat(Service.orderedConfigArrayFields);
|
||||
ServiceImpl.configArrayFields.concat(ServiceImpl.orderedConfigArrayFields);
|
||||
|
||||
// A list of fields to ignore when comparing container configuration
|
||||
private static omitFields = [
|
||||
@ -109,7 +144,7 @@ export class Service {
|
||||
// These fields are special case, due to network_mode:service:<service>
|
||||
'networkMode',
|
||||
'hostname',
|
||||
].concat(Service.allConfigArrayFields);
|
||||
].concat(ServiceImpl.allConfigArrayFields);
|
||||
|
||||
private constructor() {
|
||||
/* do not allow instancing a service object with `new` */
|
||||
@ -1170,4 +1205,4 @@ export class Service {
|
||||
}
|
||||
}
|
||||
|
||||
export default Service;
|
||||
export const Service = ServiceImpl;
|
||||
|
2
src/compose/types/index.ts
Normal file
2
src/compose/types/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './service';
|
||||
export * from './network';
|
@ -1,5 +1,5 @@
|
||||
import * as imageManager from './images';
|
||||
import type Service from './service';
|
||||
import type { Service } from './service';
|
||||
import type { CompositionStep } from './composition-steps';
|
||||
import { generateStep } from './composition-steps';
|
||||
import type { AppsToLockMap } from './app';
|
||||
|
@ -16,7 +16,7 @@ import type {
|
||||
ServiceHealthcheck,
|
||||
LongDefinition,
|
||||
LongBind,
|
||||
} from './types/service';
|
||||
} from './types';
|
||||
|
||||
import log from '../lib/supervisor-console';
|
||||
|
||||
|
@ -11,7 +11,7 @@ import log from '../lib/supervisor-console';
|
||||
import * as logger from '../logger';
|
||||
import { ResourceRecreationAttemptError } from './errors';
|
||||
import type { VolumeConfig } from './volume';
|
||||
import Volume from './volume';
|
||||
import { Volume } from './volume';
|
||||
|
||||
export interface VolumeNameOpts {
|
||||
name: string;
|
||||
|
@ -22,7 +22,18 @@ export interface ComposeVolumeConfig {
|
||||
labels: LabelObject;
|
||||
}
|
||||
|
||||
export class Volume {
|
||||
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 {
|
||||
private constructor(
|
||||
public name: string,
|
||||
public appId: number,
|
||||
@ -162,4 +173,4 @@ export class Volume {
|
||||
}
|
||||
}
|
||||
|
||||
export default Volume;
|
||||
export const Volume = VolumeImpl;
|
||||
|
@ -11,7 +11,7 @@ import * as applicationManager from '../compose/application-manager';
|
||||
import type { CompositionStepAction } from '../compose/composition-steps';
|
||||
import { generateStep } from '../compose/composition-steps';
|
||||
import * as commitStore from '../compose/commit';
|
||||
import type Service from '../compose/service';
|
||||
import type { Service } from '../compose/service';
|
||||
import { getApp } from '../device-state/db-format';
|
||||
import * as TargetState from '../device-state/target-state';
|
||||
import log from '../lib/supervisor-console';
|
||||
|
@ -7,7 +7,7 @@ import * as apiBinder from '../api-binder';
|
||||
import * as applicationManager from '../compose/application-manager';
|
||||
import type { CompositionStepAction } from '../compose/composition-steps';
|
||||
import type { Service } from '../compose/service';
|
||||
import Volume from '../compose/volume';
|
||||
import { Volume } from '../compose/volume';
|
||||
import * as commitStore from '../compose/commit';
|
||||
import * as config from '../config';
|
||||
import * as db from '../db';
|
||||
|
@ -4,7 +4,7 @@ import type * as db from '../db';
|
||||
import * as targetStateCache 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 type {
|
||||
|
@ -14,7 +14,7 @@ import {
|
||||
import { docker } from './docker-utils';
|
||||
import { log } from './supervisor-console';
|
||||
import { pathOnData } from './host-utils';
|
||||
import type Volume from '../compose/volume';
|
||||
import type { Volume } from '../compose/volume';
|
||||
import * as logger from '../logger';
|
||||
import type {
|
||||
DatabaseApp,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import * as t from 'io-ts';
|
||||
|
||||
// TODO: move all these exported types to ../compose/types
|
||||
import type { ComposeNetworkConfig } from '../compose/types/network';
|
||||
import type { ComposeNetworkConfig } from '../compose/types';
|
||||
import type { ComposeVolumeConfig } from '../compose/volume';
|
||||
import type { ContractObject } from '../lib/contracts';
|
||||
|
||||
@ -16,7 +16,7 @@ import {
|
||||
nonEmptyRecord,
|
||||
} from './basic';
|
||||
|
||||
import type App from '../compose/app';
|
||||
import type { App } from '../compose/app';
|
||||
|
||||
export type DeviceLegacyReport = Partial<{
|
||||
api_port: number;
|
||||
|
@ -4,9 +4,9 @@ import Docker from 'dockerode';
|
||||
import * as applicationManager from '~/src/compose/application-manager';
|
||||
import * as imageManager from '~/src/compose/images';
|
||||
import * as serviceManager from '~/src/compose/service-manager';
|
||||
import Network from '~/src/compose/network';
|
||||
import { Network } from '~/src/compose/network';
|
||||
import * as networkManager from '~/src/compose/network-manager';
|
||||
import Volume from '~/src/compose/volume';
|
||||
import { Volume } from '~/src/compose/volume';
|
||||
import * as config from '~/src/config';
|
||||
import { LocksTakenMap } from '~/lib/update-lock';
|
||||
import { createDockerImage } from '~/test-lib/docker-helper';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { expect } from 'chai';
|
||||
|
||||
import Service from '~/src/compose/service';
|
||||
import { Service } from '~/src/compose/service';
|
||||
import * as deviceApi from '~/src/device-api';
|
||||
|
||||
describe('compose/service: integration tests', () => {
|
||||
|
@ -2,7 +2,7 @@ import { expect } from 'chai';
|
||||
|
||||
import * as sinon from 'sinon';
|
||||
import * as volumeManager from '~/src/compose/volume-manager';
|
||||
import Volume from '~/src/compose/volume';
|
||||
import { Volume } from '~/src/compose/volume';
|
||||
import { createDockerImage } from '~/test-lib/docker-helper';
|
||||
|
||||
import Docker from 'dockerode';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { expect } from 'chai';
|
||||
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 logger from '~/src/logger';
|
||||
|
||||
|
@ -7,7 +7,7 @@ import request from 'supertest';
|
||||
import * as config from '~/src/config';
|
||||
import * as db from '~/src/db';
|
||||
import * as hostConfig from '~/src/host-config';
|
||||
import type Service from '~/src/compose/service';
|
||||
import type { Service } from '~/src/compose/service';
|
||||
import * as deviceApi from '~/src/device-api';
|
||||
import * as actions from '~/src/device-api/actions';
|
||||
import * as v1 from '~/src/device-api/v1';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { expect } from 'chai';
|
||||
import { isRight } from 'fp-ts/lib/Either';
|
||||
import App from '~/src/compose/app';
|
||||
import Network from '~/src/compose/network';
|
||||
import { App } from '~/src/compose/app';
|
||||
import { Network } from '~/src/compose/network';
|
||||
import * as config from '~/src/config';
|
||||
import * as testDb from '~/src/db';
|
||||
import * as dbFormat from '~/src/device-state/db-format';
|
||||
|
@ -1,10 +1,10 @@
|
||||
import App from '~/src/compose/app';
|
||||
import { App } from '~/src/compose/app';
|
||||
import * as imageManager from '~/src/compose/images';
|
||||
import type { Image } from '~/src/compose/images';
|
||||
import Network from '~/src/compose/network';
|
||||
import Service from '~/src/compose/service';
|
||||
import { Network } from '~/src/compose/network';
|
||||
import { Service } from '~/src/compose/service';
|
||||
import type { ServiceComposeConfig } from '~/src/compose/types/service';
|
||||
import type Volume from '~/src/compose/volume';
|
||||
import type { Volume } from '~/src/compose/volume';
|
||||
import type {
|
||||
CompositionStep,
|
||||
CompositionStepAction,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { expect } from 'chai';
|
||||
import type { Image } from '~/src/compose/images';
|
||||
import Network from '~/src/compose/network';
|
||||
import Volume from '~/src/compose/volume';
|
||||
import { Network } from '~/src/compose/network';
|
||||
import { Volume } from '~/src/compose/volume';
|
||||
import { LocksTakenMap } from '~/lib/update-lock';
|
||||
|
||||
import {
|
||||
|
@ -2,7 +2,7 @@ import { expect } from 'chai';
|
||||
import type * as sinon from 'sinon';
|
||||
|
||||
import { Network } from '~/src/compose/network';
|
||||
import type { NetworkInspectInfo } from '~/src/compose/types/network';
|
||||
import type { NetworkInspectInfo } from '~/src/compose/types';
|
||||
|
||||
import { log } from '~/lib/supervisor-console';
|
||||
|
||||
|
@ -3,8 +3,8 @@ import * as _ from 'lodash';
|
||||
import { expect } from 'chai';
|
||||
import { createContainer } from '~/test-lib/mockerode';
|
||||
|
||||
import Service from '~/src/compose/service';
|
||||
import Volume from '~/src/compose/volume';
|
||||
import { Service } from '~/src/compose/service';
|
||||
import { Volume } from '~/src/compose/volume';
|
||||
import * as ServiceT from '~/src/compose/types/service';
|
||||
import * as constants from '~/lib/constants';
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { expect } from 'chai';
|
||||
import Volume from '~/src/compose/volume';
|
||||
import { Volume } from '~/src/compose/volume';
|
||||
|
||||
describe('compose/volume: unit tests', () => {
|
||||
describe('creating a volume from a compose object', () => {
|
||||
|
Loading…
Reference in New Issue
Block a user