Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate Supervisor API v2 endpoint tests #2228

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
25 changes: 25 additions & 0 deletions src/compose/application-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1031,3 +1031,28 @@ export async function getState() {
}
return state;
}

export async function removeOrphanedVolumes(
inScope: (id: number) => boolean = () => true,
) {
const targetState = await getTargetApps();
// Get list of referenced volumes to keep
const referencedVolumes = Object.values(targetState)
// Don't include volumes out of scope
.filter((app) => inScope(app.id))
.flatMap((app) => {
const [release] = Object.values(app.releases);
// Return a list of the volume names
return Object.keys(release?.volumes ?? {}).map((volumeName) =>
Volume.generateDockerName(app.id, volumeName),
);
});

await volumeManager.removeOrphanedVolumes(referencedVolumes);
}

export async function getAllServices(
inScope: (id: number) => boolean = () => true,
) {
return (await serviceManager.getAll()).filter((svc) => inScope(svc.appId));
}
3 changes: 1 addition & 2 deletions src/config/functions.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import * as _ from 'lodash';
import * as memoizee from 'memoizee';

import supervisorVersion = require('../lib/supervisor-version');

import * as config from '.';
import * as constants from '../lib/constants';
import * as osRelease from '../lib/os-release';
import * as macAddress from '../lib/mac-address';
import * as hostUtils from '../lib/host-utils';
import log from '../lib/supervisor-console';
import { supervisorVersion } from '../lib/supervisor-version';

export const fnSchema = {
version: () => {
Expand Down
122 changes: 119 additions & 3 deletions src/device-api/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ import * as _ from 'lodash';

import { getGlobalApiKey, refreshKey } from '.';
import * as messages from './messages';
import { AuthorizedRequest } from './api-keys';
import * as eventTracker from '../event-tracker';
import * as deviceState from '../device-state';
import * as logger from '../logger';
import * as config from '../config';
import * as hostConfig from '../host-config';
import { isVPNEnabled, isVPNActive } from '../network';
import { fetchDeviceTags } from '../api-binder';
import * as applicationManager from '../compose/application-manager';
import {
CompositionStepAction,
Expand All @@ -24,6 +27,8 @@ import {
NotFoundError,
BadRequestError,
} from '../lib/errors';
import { JournalctlOpts, spawnJournalctl } from '../lib/journald';
import { supervisorVersion } from '../lib/supervisor-version';

/**
* Run an array of healthchecks, outputting whether all passed or not
Expand Down Expand Up @@ -348,7 +353,7 @@ export const getSingleContainerApp = async (appId: number) => {
/**
* Returns legacy device info, update status, and service status for a single-container application.
* Used by:
* - GET /v1/device
* - GET /v1/device
*/
export const getLegacyDeviceState = async () => {
const state = await deviceState.getLegacyState();
Expand Down Expand Up @@ -389,7 +394,7 @@ export const getLegacyDeviceState = async () => {
/**
* Get host config from the host-config module; Returns proxy config and hostname.
* Used by:
* - GET /v1/device/host-config
* - GET /v1/device/host-config
*/
export const getHostConfig = async () => {
return await hostConfig.get();
Expand All @@ -398,7 +403,7 @@ export const getHostConfig = async () => {
/**
* Patch host configs such as proxy config and hostname
* Used by:
* - PATCH /v1/device/host-config
* - PATCH /v1/device/host-config
*/
export const patchHostConfig = async (
conf: Parameters<typeof hostConfig.patch>[0],
Expand All @@ -411,3 +416,114 @@ export const patchHostConfig = async (
}
await hostConfig.patch(conf, force);
};

/**
* Get device VPN status
* Used by:
* - GET /v2/device/vpn
*/
export const getVPNStatus = async () => {
return {
enabled: await isVPNEnabled(),
connected: await isVPNActive(),
};
};

/**
* Get device name
* Used by:
* - GET /v2/device/name
*/
export const getDeviceName = async () => {
return await config.get('name');
};

/**
* Get device tags
* Used by:
* - GET /v2/device/tags
*/
export const getDeviceTags = async () => {
try {
return await fetchDeviceTags();
} catch (e: unknown) {
log.error((e as Error).message ?? e);
throw e;
}
};

/**
* Clean up orphaned volumes
* Used by:
* - GET /v2/cleanup-volumes
*/
export const cleanupVolumes = async (
withScope: AuthorizedRequest['auth']['isScoped'] = () => true,
) => {
// It's better practice to access engine functionality through application-manager
// than through volume-manager directly, as the latter should be an internal module
await applicationManager.removeOrphanedVolumes((id) =>
withScope({ apps: [id] }),
);
};

/**
* Spawn a journalctl process with the given options
* Used by:
* - POST /v2/journal-logs
*/
export const getLogStream = (opts: JournalctlOpts) => {
return spawnJournalctl(opts);
};

/**
* Get version of running Supervisor
* Used by:
* - GET /v2/version
*/
export const getSupervisorVersion = () => {
return supervisorVersion;
};

/**
* Get the containerId(s) associated with a service.
* If no serviceName is provided, get all containerIds.
* Used by:
* - GET /v2/containerId
*/
export const getContainerIds = async (
serviceName: string = '',
withScope: AuthorizedRequest['auth']['isScoped'] = () => true,
) => {
const services = await applicationManager.getAllServices((id) =>
withScope({ apps: [id] }),
);

// Return all containerIds if no serviceName is provided
if (!serviceName) {
return services.reduce(
(svcToContainerIdMap, svc) => ({
[svc.serviceName]: svc.containerId,
...svcToContainerIdMap,
}),
{},
);
}

// Otherwise, only return containerId of provided serviceNmae
const service = services.find((svc) => svc.serviceName === serviceName);
if (service != null) {
return service.containerId;
} else {
throw new Error(`Could not find service with name '${serviceName}'`);
}
};

/**
* Get device type & arch
* Used by:
* - GET /v2/local/device-info
*/
export const getDeviceInfo = async () => {
return await config.getMany(['deviceType', 'deviceArch']);
};
Loading
Loading