Remove dependent devices content in codebase

This includes:
- proxyvisor.js
- references in docs
- references device-state, api-binder, compose modules, API
- references in tests

The commit also adds a migration to remove the 4 dependent device tables from the DB.

Change-type: minor
Signed-off-by: Christina Ying Wang <christina@balena.io>
This commit is contained in:
Christina Ying Wang 2023-01-30 15:30:17 -06:00
parent cfd18a7620
commit c4f9d72172
22 changed files with 63 additions and 1505 deletions

View File

@ -736,7 +736,6 @@ Response:
}
}
},
"dependent": {},
"commit": "7fc9c5bea8e361acd49886fe6cc1e1cd"
}
```
@ -1010,10 +1009,6 @@ Response:
"SUPERVISOR_OVERRIDE_LOCK": "false"
},
"apps": {}
},
"dependent": {
"apps": [],
"devices": []
}
}
}
@ -1080,10 +1075,6 @@ TARGET_STATE='{
"networks": {}
}
}
},
"dependent": {
"apps": [],
"devices": []
}
}
'

View File

@ -277,10 +277,6 @@ Database { open: true, filename: '/data/database.sqlite', mode: 65542 }
{ name: 'config' },
{ name: 'containerLogs' },
{ name: 'currentCommit' },
{ name: 'dependentApp' },
{ name: 'dependentAppTarget' },
{ name: 'dependentDevice' },
{ name: 'dependentDeviceTarget' },
{ name: 'deviceConfig' },
{ name: 'engineSnapshot' },
{ name: 'image' },

View File

@ -1,295 +0,0 @@
# Using the balena Supervisor to manage dependent applications
Since version 2.5.0 the balena Supervisor can act as a proxy for dependent apps.
Only Supervisors after version 2.5.0 have this functionality, and some of the endpoints appeared in later versions (we've noted it down where this is the case).
## What is a dependent application
A **dependent application** is a balena application that targets devices not capable of interacting directly with the balena API - the reasons can be several, the most common are:
- no direct Internet capabilities
- not able to run balenaOS (being a microcontroller, for example)
The **dependent application** is scoped under a balena application, which gets the definition of **gateway application**.
The **gateway application** is responsible for detecting, provisioning and managing **dependent devices** belonging to one of its **dependent applications**. This is possible leveraging a new set of endpoints exposed by the balena Supervisor.
When a new version of the dependent application is git pushed, the supervisor will download the docker image and expose the assets in one of the endpoints detailed below. It is then the gateway application (i.e. the user app that is run by the supervisor) that is responsible for ensuring those assets get deployed to the dependent devices, using the provided endpoints to perform the management.
A dependent application follows the same development cycle of a conventional balena application:
- it binds to your git workspace via the **balena remote**
- it consists in a Docker application
- it offers the same environment and configuration variables management
There are some differences:
- it does not support Dockerfile templating
- the Dockerfile must target either an `x86` or `amd64` base image
- the actual firmware/business logic must be stored in the `/assets` folder within the built docker image.
- You can either just `COPY` a pre-built artifact in that folder, or build your artifact at push time and then store it in the `/assets` folder.
- **a dependent application Docker image is only used to build, package and deliver the firmware on the dependent device via balena-supervisor - it won't be run at any point.**
## How a dependent application works
### Endpoints
The supervisor exposes a REST API to interact with the dependent applications and dependent devices models that come from the balena API - it also allows using a set of hooks to have push functionality, both documented below.
# HTTP API reference
## Applications
### GET /v1/dependent-apps
Dependent Applications List
**Example**
```bash
curl -X GET $BALENA_SUPERVISOR_ADDRESS/v1/dependent-apps?apikey=$BALENA_SUPERVISOR_API_KEY
```
**Response**
`HTTP/1.1 200 OK`
```javascript
[
{
"id": 13015,
"name": "edgeApp1",
"commit": "d43bea5e16658e653088ce4b9a91b6606c3c2a0d",
"config": {}
},
{
"id": 13016,
"name": "edgeApp2",
"commit": "d0f6624d6410fa079159fa3ebe0d3af46753d75d",
"config": {}
}
]
```
### GET /v1/dependent-apps/:appId/assets/:commit
Dependent Application Updates Registry
**Example**
```bash
curl -X GET $BALENA_SUPERVISOR_ADDRESS/v1/dependent-apps/<appId>/assets/<commit>?apikey=$BALENA_SUPERVISOR_API_KEY
```
**Response**
`HTTP/1.1 200 OK`
```none
[application/x-tar] .tar
```
## Devices
### GET /v1/devices
Dependent Devices List
**Example**
```bash
curl -X GET $BALENA_SUPERVISOR_ADDRESS/v1/devices?apikey=$BALENA_SUPERVISOR_API_KEY
```
**Response**
`HTTP/1.1 200 OK`
```javascript
[
{
"id": 1,
"uuid": "5ae8cf6e062c033ea38435498ad9b487bcc714e9eab0fed0404ee56e397790",
"appId": 13015,
"device_type": "generic-amd64",
"logs_channel": "69f961abffaad1ff66031b29f712be4fb19e1bfabf1fee7a9ebfb5fa75a1fbdb",
"deviceId": "47270",
"is_online": null,
"name": "blue-sun",
"status": "Provisioned",
"download_progress": null,
"commit": "d43bea5e16658e693088ce4b9a91b6606c3c2a0d",
"targetCommit": "d43bea5e16653e653088ce4b9a91b6606c3c2a0d",
"environment":{},
"targetEnvironment":{},
"config":{},
"targetConfig":{"RESIN_SUPERVISOR_DELTA":"1"}
},
{
"id": 3,
"uuid": "8dc608765fd32665d49d218a7eb4657bc2ab8a56db06d2c57ef7c7e9a115da",
"appId": 13015,
"device_type": "generic-amd64",
"logs_channel": "d0244a90e8cd6e9a1ab410d3d599dea7f15110a6fe37b2a8fd69bb6ee0bce043",
"deviceId": "47318",
"is_online": null,
"name": "wild-paper",
"status": "Provisioned",
"download_progress": null,
"commit": "d43bea5e16658e253088ce4b9a91b6606c3c2a0d",
"targetCommit": "d43bea5e11658e653088ce4b9a91b6606c3c2a0d",
"environment":{},
"targetEnvironment":{},
"config":{},
"targetConfig":{"RESIN_SUPERVISOR_DELTA":"1"}
}
]
```
### POST /v1/devices
Dependent Device Provision
The `device_type` parameter is optional, defaulting to `generic-amd64`. You
should only set this if you need to provision a dependent device to an
application with the deprecated `edge` device type.
**Example**
```bash
curl -H "Content-Type: application/json" -X POST --data '{"appId": <appId>,
"device_type": "edge"}' /
$BALENA_SUPERVISOR_ADDRESS/v1/devices?apikey=$BALENA_SUPERVISOR_API_KEY
```
**Response**
`HTTP/1.1 201 CREATED`
```javascript
{
"id": 47318,
"uuid": "8dc608765fd32665d49a268a7eb4657bc2ab8a56db06d2c57ef7c7e9a115da",
"name": "wild-paper",
"note": null,
"device_type": "edge"
}
```
### GET /v1/devices/:uuid
Dependent Device Information
**Example**
```bash
curl -X GET $BALENA_SUPERVISOR_ADDRESS/v1/devices/<uuid>?apikey=$BALENA_SUPERVISOR_API_KEY
```
**Response**
`HTTP/1.1 200 OK`
```javascript
{
"id": 1,
"uuid": "5ae8cf6e062c033ea57837498ad9b487bfc714e9eab0fed0404ee56e397790",
"appId": 13015,
"device_type": "generic-amd64",
"logs_channel": "69f961abffaad2ff00031b29f718be4fb19e1bfabf1fee7a9ebfb5fa75a1fbdb",
"deviceId": "47270",
"is_online": null,
"name": "blue-sun",
"status": "Provisioned",
"download_progress": null,
"commit": "d43bea5e16658e623088je4b9a91b6606c3c2a0d",
"targetCommit": "d43bea5e16658e651088ce4b9a21b6606c3c2a0d",
"env": null,
"targetEnv": null
}
```
### PUT /v1/devices/:uuid
Dependent Device Information Update
**Example**
```bash
curl -H "Content-Type: application/json" -X PUT --data /
'{"is_online":true, "status": "Updating", "commit": "339125a7529cb2c2a8c93a0bbd8af69f2d96286ab4f4552cb5cfe99b0d3ee9"}' /
$BALENA_SUPERVISOR_ADDRESS/v1/devices/<uuid>?apikey=$BALENA_SUPERVISOR_API_KEY
```
**Response**
`HTTP/1.1 200 OK`
```javascript
{
"id": 1,
"uuid": "5ae8cf6e062c033ea38437498ad9b482bcc714e9eab0fed0404ee56e397790",
"appId": 13015,
"device_type": "generic-amd64",
"logs_channel": "69f961abffaad2ff66031b29f712be4fb19e1bfabf1fee7a9ebfb5fa05a1fbdb",
"deviceId": "47270",
"is_online": true,
"name": "blue-sun",
"status": "Updating",
"download_progress": null,
"commit": "d43bea5e16658e653088ce4b9a11b6606c3c2a0d",
"targetCommit": "d43bea5e16658e653088se4b9a91b6606c3c2a0d",
"env": null,
"targetEnv": null
}
```
### POST /v1/devices/:uuid/logs
Dependent Device Log
**Example**
```bash
curl -H "Content-Type: application/json" -X POST --data '{"message":"detected movement","timestamp":1472142960}' /
$BALENA_SUPERVISOR_ADDRESS/v1/devices/<uuid>/logs?apikey=$BALENA_SUPERVISOR_API_KEY
```
**Response**
`HTTP/1.1 202 ACCEPTED`
## Hooks (the requests the balena Supervisor performs)
### Hook configuration
You can point the supervisor where to find the hook server via a configuration variable.
- `BALENA_DEPENDENT_DEVICES_HOOK_ADDRESS` _(defaults to `http://0.0.0.0:1337/v1/devices/`)_
It's worth mentioning (as described below) that the supervisor will append the dependent device uuid (`<uuid>` in the hook descriptions) to every hook request URL
### PUT /v1/devices/:uuid/restart
Dependent Device Restart Notification
**Example**
```bash
curl -H "Content-Type: application/json" -X PUT /
http://127.0.0.1:1337/v1/devices/<uuid>/restart
```
**Response**
`HTTP/1.1 200 OK`
### PUT /v1/devices/:uuid
Dependent Device Update Notification
**Example**
```bash
curl -H "Content-Type: application/json" -X PUT /
--data '{"commit":" <commit>","environment": "<environment>"}' http://127.0.0.1:1337/v1/devices/<uuid>
```
**Responses**
* `HTTP/1.1 200 OK` Acknowledgement of the notification without further trials: The Supervisor won't repeat the hook request
* `HTTP/1.1 202 ACCEPTED`Acknowledgement of the notification with validation: the Supervisor will repeat the hook request until the dependent device information gets updated via `Dependent Device Information Update` endpoint
### DELETE /v1/devices/:uuid
Dependent Device Delete Notification
**Example**
```bash
curl -X DELETE http://127.0.0.1:1337/v1/devices/<uuid>
```
**Response**
`HTTP/1.1 200 OK`

View File

@ -5,7 +5,6 @@ import * as t from 'io-ts';
import * as _ from 'lodash';
import { PinejsClientRequest } from 'pinejs-client-request';
import * as url from 'url';
import * as deviceRegister from '../lib/register-device';
import * as config from '../config';
import * as deviceConfig from '../device-config';
@ -28,7 +27,6 @@ import * as TargetState from '../device-state/target-state';
import * as logger from '../logger';
import * as apiHelper from '../lib/api-helper';
import { Device } from '../lib/api-helper';
import { startReporting, stateReportErrors } from './report';
interface DevicePinInfo {
@ -230,41 +228,6 @@ export async function patchDevice(
).timeout(conf.apiTimeout);
}
export async function provisionDependentDevice(
device: Partial<Device>,
): Promise<Device> {
const conf = await config.getMany([
'unmanaged',
'provisioned',
'apiTimeout',
'deviceId',
]);
if (conf.unmanaged) {
throw new Error('Cannot provision dependent device in unmanaged mode');
}
if (!conf.provisioned) {
throw new Error(
'Device must be provisioned to provision a dependent device',
);
}
if (balenaApi == null) {
throw new InternalInconsistencyError(
'Attempt to provision a dependent device without an API client',
);
}
_.defaults(device, {
is_managed_by__device: conf.deviceId,
uuid: deviceRegister.generateUniqueKey(),
registered_at: Math.floor(Date.now() / 1000),
});
return (await Bluebird.resolve(
balenaApi.post({ resource: 'device', body: device }),
).timeout(conf.apiTimeout)) as Device;
}
export function startCurrentStateReport() {
if (balenaApi == null) {
throw new InternalInconsistencyError(

View File

@ -6,7 +6,6 @@ import * as config from '../config';
import { transaction, Transaction } from '../db';
import * as logger from '../logger';
import LocalModeManager from '../local-mode';
import proxyvisor from '../proxyvisor';
import * as dbFormat from '../device-state/db-format';
import { validateTargetContracts } from '../lib/contracts';
@ -123,10 +122,6 @@ export const initialized = _.once(async () => {
serviceManager.on('change', reportCurrentState);
});
export function getDependentState() {
return proxyvisor.getCurrentStates();
}
function reportCurrentState(data?: Partial<InstancedAppState>) {
events.emit('change', data ?? {});
}
@ -300,16 +295,6 @@ export async function inferNextSteps(
steps.push(generateStep('noop', {}));
}
steps = steps.concat(
await proxyvisor.getRequiredSteps(
availableImages,
downloading,
currentApps,
targetApps,
steps,
),
);
return steps;
}
@ -500,10 +485,6 @@ export async function executeStep(
step: CompositionStep,
{ force = false, skipLock = false } = {},
): Promise<void> {
if (proxyvisor.validActions.includes(step.action)) {
return proxyvisor.executeStepAction(step);
}
if (!validActions.includes(step.action)) {
return Promise.reject(
new InternalInconsistencyError(
@ -645,10 +626,6 @@ export function clearTargetVolatileForServices(imageIds: number[]) {
}
}
export function getDependentTargets() {
return proxyvisor.getTarget();
}
/**
* This is only used by the API. Do not use as the use of serviceIds is getting
* deprecated
@ -684,7 +661,6 @@ export function bestDeltaSource(
image: Image,
available: Image[],
): string | null {
if (!image.dependent) {
for (const availableImage of available) {
if (
availableImage.serviceName === image.serviceName &&
@ -693,15 +669,6 @@ export function bestDeltaSource(
return availableImage.name;
}
}
} else {
// This only makes sense for dependent devices which are still
// single app.
for (const availableImage of available) {
if (availableImage.appId === image.appId) {
return availableImage.name;
}
}
}
return null;
}
@ -831,17 +798,9 @@ function saveAndRemoveImages(
.map((img) => bestDeltaSource(img, availableImages))
.filter((img) => img != null);
const proxyvisorImages = proxyvisor.imagesInUse(current, target);
const imagesToRemove = availableAndUnused.filter((image) => {
const notUsedForDelta = !deltaSources.includes(image.name);
const notUsedByProxyvisor = !proxyvisorImages.some((proxyvisorImage) =>
imageManager.isSameImage(image, {
name: proxyvisorImage,
}),
const imagesToRemove = availableAndUnused.filter(
(image) => !deltaSources.includes(image.name),
);
return notUsedForDelta && notUsedByProxyvisor;
});
return imagesToSave
.map((image) => ({ action: 'saveImage', image } as CompositionStep))
@ -897,7 +856,6 @@ export async function getLegacyState() {
]);
const apps: Dictionary<any> = {};
const dependent: Dictionary<any> = {};
let releaseId: number | boolean | null | undefined = null; // ????
const creationTimesAndReleases: Dictionary<any> = {};
// We iterate over the current running services and add them to the current state
@ -944,7 +902,6 @@ export async function getLegacyState() {
for (const image of images) {
const { appId } = image;
if (!image.dependent) {
if (apps[appId] == null) {
apps[appId] = {};
}
@ -959,22 +916,9 @@ export async function getLegacyState() {
apps[appId].services[image.imageId].download_progress =
image.downloadProgress;
}
} else if (image.imageId != null) {
if (dependent[appId] == null) {
dependent[appId] = {};
}
if (dependent[appId].images == null) {
dependent[appId].images = {};
}
dependent[appId].images[image.imageId] = _.pick(image, ['status']);
dependent[appId].images[image.imageId].download_progress =
image.downloadProgress;
} else {
log.debug('Ignoring legacy dependent image', image);
}
}
return { local: apps, dependent };
return { local: apps };
}
// TODO: this function is probably more inefficient than it needs to be, since

View File

@ -53,7 +53,6 @@ export interface Image {
*/
releaseId: number;
commit: string;
dependent: number;
dockerImageId?: string;
status?: 'Downloading' | 'Downloaded' | 'Deleting';
downloadProgress?: number | null;
@ -184,7 +183,6 @@ export function imageFromService(service: ServiceInfo): Image {
imageId: service.imageId!,
releaseId: service.releaseId!,
commit: service.commit!,
dependent: 0,
};
}
@ -756,7 +754,6 @@ function format(image: Image): Partial<Omit<Image, 'id'>> {
imageId: null,
releaseId: null,
commit: null,
dependent: 0,
dockerImageId: null,
})
.omit('id')

View File

@ -139,9 +139,6 @@ export function safeStateClone(
local: {
config: {},
},
dependent: {
config: {},
},
};
if (targetState.local != null) {
@ -151,9 +148,6 @@ export function safeStateClone(
apps: _.mapValues(targetState.local.apps, safeAppClone),
};
}
if (targetState.dependent != null) {
cloned.dependent = _.cloneDeep(targetState.dependent);
}
return cloned as InstancedDeviceState;
}

View File

@ -3,7 +3,6 @@ import * as express from 'express';
import * as middleware from './middleware';
import * as apiKeys from './api-keys';
import * as actions from './actions';
import proxyvisor from '../proxyvisor';
import log from '../lib/supervisor-console';
import type { Server } from 'http';
@ -78,8 +77,6 @@ export class SupervisorAPI {
this.api.use(router);
}
this.api.use(proxyvisor.router);
this.api.use(middleware.errors);
}

View File

@ -153,7 +153,6 @@ router.post(
},
);
// TODO: Support dependent applications when this feature is complete
router.get(
'/v2/applications/state',
async (req: AuthorizedRequest, res: Response, next: NextFunction) => {

View File

@ -364,7 +364,6 @@ export function getTarget({
config: await deviceConfig.getTarget({ initial }),
apps: await dbFormat.getApps(),
},
dependent: await applicationManager.getDependentTargets(),
};
});
}
@ -378,14 +377,12 @@ export async function getLegacyState(): Promise<DeviceLegacyState> {
const appsStatus = await applicationManager.getLegacyState();
const theState: DeepPartial<DeviceLegacyState> = {
local: {},
dependent: {},
};
theState.local = {
...theState.local,
...currentVolatile,
};
theState.local!.apps = appsStatus.local;
theState.dependent!.apps = appsStatus.dependent;
// Multi-app warning!
// If we have more than one app, simply return the first commit.
@ -503,11 +500,10 @@ export async function getCurrentForReport(
// Get the current state as object instances
export async function getCurrentState(): Promise<InstancedDeviceState> {
const [name, devConfig, apps, dependent] = await Promise.all([
const [name, devConfig, apps] = await Promise.all([
config.get('name'),
deviceConfig.getCurrent(),
applicationManager.getCurrentApps(),
applicationManager.getDependentState(),
]);
return {
@ -516,7 +512,6 @@ export async function getCurrentState(): Promise<InstancedDeviceState> {
config: devConfig,
apps,
},
dependent,
};
}

View File

@ -37,7 +37,6 @@ const constants = {
bootMountPointFromEnv,
bootMountPoint: bootMountPointFromEnv || '/boot',
configJsonPathOnHost: checkString(process.env.CONFIG_JSON_PATH),
proxyvisorHookReceiver: 'http://0.0.0.0:1337',
configJsonNonAtomicPath: '/boot/config.json',
supervisorNetworkInterface,
allowedInterfaces: [

View File

@ -177,7 +177,6 @@ export async function normaliseLegacyDatabase() {
imageId: image.id,
releaseId: release.id,
commit: app.commit,
dependent: 0,
dockerImageId: imageFromDocker.Id,
});
} else {

View File

@ -101,13 +101,6 @@ export function enable(value: boolean = true) {
}
}
export function logDependent(message: LogMessage, device: { uuid: string }) {
if (backend != null) {
message.uuid = device.uuid;
backend.log(message);
}
}
export function log(message: LogMessage) {
if (backend != null) {
backend.log(message);

26
src/migrations/M00010.js Normal file
View File

@ -0,0 +1,26 @@
// This migration removes references to dependent devices from
// the database, as we are no longer pursuing dependent devices.
export async function up(knex) {
// Remove dependent key from each image
if (await knex.schema.hasColumn('image', 'dependent')) {
await knex.schema.table('image', (t) => {
return t.dropColumn('dependent');
});
}
// Delete dependent device/app tables
const dropTable = async (table) => {
const exists = await knex.schema.hasTable(table);
if (exists) {
await knex.schema.dropTable(table);
}
};
await dropTable('dependentDeviceTarget');
await dropTable('dependentDevice');
await dropTable('dependentAppTarget');
await dropTable('dependentApp');
}
export function down() {
throw new Error('Not Implemented');
}

File diff suppressed because it is too large Load Diff

View File

@ -51,10 +51,6 @@ export interface DeviceLegacyState {
};
};
} & DeviceLegacyReport;
// TODO: Type the dependent entry correctly
dependent?: {
[key: string]: any;
};
commit?: string;
}
@ -109,14 +105,6 @@ export type DeviceReport = {
export type DeviceState = {
[deviceUuid: string]: DeviceReport & {
/**
* Used for setting dependent devices as online
*/
is_online?: boolean;
/**
* Used for setting gateway device of dependent devices
*/
parent_device?: number;
apps?: {
[appUuid: string]: AppState;
};
@ -272,22 +260,16 @@ export type TargetApps = t.TypeOf<typeof TargetApps>;
/**
* A device has a name, config and collection of apps
*/
const TargetDevice = t.intersection([
t.type({
const TargetDevice = t.type({
name: DeviceName,
config: ConfigVarObject,
apps: TargetApps,
}),
t.partial({
parent_device: UUID,
}),
]);
});
export type TargetDevice = t.TypeOf<typeof TargetDevice>;
/**
* Target state is a collection of devices one local device
* (with uuid matching the one in config.json) and zero or more dependent
* devices
* (with uuid matching the one in config.json)
*
*
* When all io-ts types are composed, the final type of the target state
@ -296,7 +278,6 @@ export type TargetDevice = t.TypeOf<typeof TargetDevice>;
* {
* [uuid: string]: {
* name: string;
* parent_device?: string;
* config?: {
* [varName: string]: string;
* };
@ -366,5 +347,4 @@ export interface InstancedDeviceState {
config: Dictionary<string>;
apps: InstancedAppState;
};
dependent: any;
}

View File

@ -14,13 +14,11 @@ function createDBImage(
appId = 1,
name = 'test-image',
serviceName = 'test',
dependent = 0,
...extra
} = {} as Partial<imageManager.Image>,
) {
return {
appId,
dependent,
name,
serviceName,
...extra,

View File

@ -1,4 +1,3 @@
import * as Bluebird from 'bluebird';
import { knex, Knex } from 'knex';
import { promises as fs } from 'fs';
@ -67,7 +66,7 @@ describe('db', () => {
const knexForDB = await createOldDatabase(constants.databasePath);
const testDb = require('~/src/db') as Db;
await testDb.initialized();
await Bluebird.all([
await Promise.all([
expect(knexForDB.schema.hasColumn('app', 'appId')).to.eventually.be.true,
expect(knexForDB.schema.hasColumn('app', 'releaseId')).to.eventually.be
.true,
@ -77,16 +76,13 @@ describe('db', () => {
.false,
expect(knexForDB.schema.hasColumn('app', 'containerId')).to.eventually.be
.false,
expect(knexForDB.schema.hasColumn('dependentApp', 'environment')).to
.eventually.be.true,
expect(knexForDB.schema.hasColumn('dependentDevice', 'markedForDeletion'))
.to.eventually.be.true,
expect(knexForDB.schema.hasColumn('dependentDevice', 'localId')).to
.eventually.be.true,
expect(knexForDB.schema.hasColumn('dependentDevice', 'is_managed_by')).to
.eventually.be.true,
expect(knexForDB.schema.hasColumn('dependentDevice', 'lock_expiry_date'))
.to.eventually.be.true,
expect(knexForDB.schema.hasTable('dependentDeviceTarget')).to.eventually
.be.false,
expect(knexForDB.schema.hasTable('dependentDevice')).to.eventually.be
.false,
expect(knexForDB.schema.hasTable('dependentAppTarget')).to.eventually.be
.false,
expect(knexForDB.schema.hasTable('dependentApp')).to.eventually.be.false,
]);
});

View File

@ -234,7 +234,6 @@ describe('device-state', () => {
},
},
},
dependent: { apps: {}, devices: {} },
} as any),
).to.be.rejected;
});

View File

@ -26,10 +26,6 @@ const stateEndpointBody = {
},
},
},
dependent: {
apps: {},
devices: {},
},
};
const req = {

View File

@ -70,7 +70,6 @@ export function createImage(
imageId: 1,
releaseId: 1,
serviceId: 1,
dependent: 0,
...extra,
} as Image;
}

View File

@ -73,7 +73,6 @@ function createImage(
{
appId = 1,
appUuid = 'appuuid',
dependent = 0,
name = 'test-image',
serviceName = 'test',
commit = 'test-commit',
@ -84,7 +83,6 @@ function createImage(
appId,
appUuid,
commit,
dependent,
name,
serviceName,
...extra,