mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2024-12-23 15:32:24 +00:00
Merge pull request #1262 from balena-io/1049-no-dbus-native
Move from dbus-native to dbus
This commit is contained in:
commit
96a54af21c
@ -5,7 +5,7 @@
|
||||
"*.ts": [
|
||||
"balena-lint --typescript --fix",
|
||||
],
|
||||
"*!(*webpack.config).js": [
|
||||
"*.js": [
|
||||
"balena-lint --typescript --fix",
|
||||
],
|
||||
"test/**/*.coffee": [
|
||||
|
@ -32,7 +32,8 @@ RUN apk add --no-cache \
|
||||
libstdc++ \
|
||||
libuv \
|
||||
sqlite-libs \
|
||||
sqlite-dev
|
||||
sqlite-dev \
|
||||
dbus-dev
|
||||
|
||||
COPY build-conf/node-sums.txt .
|
||||
|
||||
|
@ -58,9 +58,14 @@ version: 2
|
||||
jobs:
|
||||
generic:
|
||||
docker:
|
||||
- image: library/node:10
|
||||
- image: balenalib/amd64-alpine-node:10
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: |
|
||||
apk add dbus-dev python make \
|
||||
gcc libgcc libc-dev g++
|
||||
- run:
|
||||
name: Run tests
|
||||
command: |
|
||||
|
848
package-lock.json
generated
848
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -30,6 +30,7 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"dbus": "^1.0.7",
|
||||
"sqlite3": "^4.1.1"
|
||||
},
|
||||
"engines": {
|
||||
@ -42,6 +43,7 @@
|
||||
"@types/chai": "^4.2.11",
|
||||
"@types/chai-as-promised": "^7.1.2",
|
||||
"@types/common-tags": "^1.8.0",
|
||||
"@types/dbus": "^1.0.0",
|
||||
"@types/dockerode": "^2.5.25",
|
||||
"@types/event-stream": "^3.3.34",
|
||||
"@types/express": "^4.17.3",
|
||||
|
@ -8,8 +8,8 @@ import Logger from './logger';
|
||||
|
||||
import { ConfigOptions, DeviceConfigBackend } from './config/backend';
|
||||
import * as configUtils from './config/utils';
|
||||
import * as dbus from './lib/dbus';
|
||||
import { UnitNotLoadedError } from './lib/errors';
|
||||
import * as systemd from './lib/systemd';
|
||||
import { EnvVarObject } from './lib/types';
|
||||
import { checkInt, checkTruthy } from './lib/validation';
|
||||
import { DeviceStatus } from './types/state';
|
||||
@ -573,7 +573,7 @@ export class DeviceConfig {
|
||||
|
||||
private async getVPNEnabled(): Promise<boolean> {
|
||||
try {
|
||||
const activeState = await systemd.serviceActiveState(vpnServiceName);
|
||||
const activeState = await dbus.serviceActiveState(vpnServiceName);
|
||||
return !_.includes(['inactive', 'deactivating'], activeState);
|
||||
} catch (e) {
|
||||
if (UnitNotLoadedError(e)) {
|
||||
@ -588,9 +588,9 @@ export class DeviceConfig {
|
||||
const enable = v != null ? v : true;
|
||||
|
||||
if (enable) {
|
||||
await systemd.startService(vpnServiceName);
|
||||
await dbus.startService(vpnServiceName);
|
||||
} else {
|
||||
await systemd.stopService(vpnServiceName);
|
||||
await dbus.stopService(vpnServiceName);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,8 +20,8 @@ import { loadTargetFromFile } from './device-state/preload';
|
||||
import * as globalEventBus from './event-bus';
|
||||
import * as hostConfig from './host-config';
|
||||
import constants = require('./lib/constants');
|
||||
import * as dbus from './lib/dbus';
|
||||
import { InternalInconsistencyError, UpdatesLockedError } from './lib/errors';
|
||||
import * as systemd from './lib/systemd';
|
||||
import * as updateLock from './lib/update-lock';
|
||||
import * as validation from './lib/validation';
|
||||
import * as network from './network';
|
||||
@ -555,7 +555,7 @@ export class DeviceState extends (EventEmitter as new () => DeviceStateEventEmit
|
||||
private async reboot(force?: boolean, skipLock?: boolean) {
|
||||
await this.applications.stopAll({ force, skipLock });
|
||||
this.logger.logSystemMessage('Rebooting', {}, 'Reboot');
|
||||
const reboot = await systemd.reboot();
|
||||
const reboot = await dbus.reboot();
|
||||
this.shuttingDown = true;
|
||||
this.emitAsync('shutdown', undefined);
|
||||
return reboot;
|
||||
@ -564,7 +564,7 @@ export class DeviceState extends (EventEmitter as new () => DeviceStateEventEmit
|
||||
private async shutdown(force?: boolean, skipLock?: boolean) {
|
||||
await this.applications.stopAll({ force, skipLock });
|
||||
this.logger.logSystemMessage('Shutting down', {}, 'Shutdown');
|
||||
const shutdown = await systemd.shutdown();
|
||||
const shutdown = await dbus.shutdown();
|
||||
this.shuttingDown = true;
|
||||
this.emitAsync('shutdown', undefined);
|
||||
return shutdown;
|
||||
|
@ -7,9 +7,9 @@ import * as path from 'path';
|
||||
|
||||
import Config from './config';
|
||||
import * as constants from './lib/constants';
|
||||
import * as dbus from './lib/dbus';
|
||||
import { ENOENT } from './lib/errors';
|
||||
import { writeFileAtomic } from './lib/fs-utils';
|
||||
import * as systemd from './lib/systemd';
|
||||
|
||||
const mkdirp = Bluebird.promisify(mkdirCb) as (
|
||||
path: string,
|
||||
@ -140,8 +140,8 @@ async function setProxy(maybeConf: ProxyConfig | null): Promise<void> {
|
||||
await writeFileAtomic(redsocksConfPath, redsocksConf);
|
||||
}
|
||||
|
||||
await systemd.restartService('resin-proxy-config');
|
||||
await systemd.restartService('redsocks');
|
||||
await dbus.restartService('resin-proxy-config');
|
||||
await dbus.restartService('redsocks');
|
||||
}
|
||||
|
||||
const hostnamePath = path.join(constants.rootMountPoint, '/etc/hostname');
|
||||
@ -152,7 +152,7 @@ async function readHostname() {
|
||||
|
||||
async function setHostname(val: string, configModel: Config) {
|
||||
await configModel.set({ hostname: val });
|
||||
await systemd.restartService('resin-hostname');
|
||||
await dbus.restartService('resin-hostname');
|
||||
}
|
||||
|
||||
// Don't use async/await here to maintain the bluebird
|
||||
|
132
src/lib/dbus.ts
Normal file
132
src/lib/dbus.ts
Normal file
@ -0,0 +1,132 @@
|
||||
import * as Bluebird from 'bluebird';
|
||||
import * as dbus from 'dbus';
|
||||
import TypedError = require('typed-error');
|
||||
|
||||
import log from './supervisor-console';
|
||||
|
||||
export class DbusError extends TypedError {}
|
||||
|
||||
const bus = dbus.getBus('system');
|
||||
const getInterfaceAsync = Bluebird.promisify(bus.getInterface, {
|
||||
context: bus,
|
||||
});
|
||||
|
||||
async function getSystemdInterface() {
|
||||
try {
|
||||
return await getInterfaceAsync(
|
||||
'org.freedesktop.systemd1',
|
||||
'/org/freedesktop/systemd1',
|
||||
'org.freedesktop.systemd1.Manager',
|
||||
);
|
||||
} catch (e) {
|
||||
throw new DbusError(e);
|
||||
}
|
||||
}
|
||||
|
||||
export async function getLoginManagerInterface() {
|
||||
try {
|
||||
return await getInterfaceAsync(
|
||||
'org.freedesktop.login1',
|
||||
'/org/freedesktop/login1',
|
||||
'org.freedesktop.login1.Manager',
|
||||
);
|
||||
} catch (e) {
|
||||
throw new DbusError(e);
|
||||
}
|
||||
}
|
||||
|
||||
export async function restartService(serviceName: string) {
|
||||
const systemd = await getSystemdInterface();
|
||||
try {
|
||||
systemd.RestartUnit(`${serviceName}.service`, 'fail');
|
||||
} catch (e) {
|
||||
throw new DbusError(e);
|
||||
}
|
||||
}
|
||||
|
||||
export async function startService(serviceName: string) {
|
||||
const systemd = await getSystemdInterface();
|
||||
try {
|
||||
systemd.StartUnit(`${serviceName}.service`, 'fail');
|
||||
} catch (e) {
|
||||
throw new DbusError(e);
|
||||
}
|
||||
}
|
||||
|
||||
export async function stopService(serviceName: string) {
|
||||
const systemd = await getSystemdInterface();
|
||||
try {
|
||||
systemd.StopUnit(`${serviceName}.service`, 'fail');
|
||||
} catch (e) {
|
||||
throw new DbusError(e);
|
||||
}
|
||||
}
|
||||
|
||||
export async function enableService(serviceName: string) {
|
||||
const systemd = await getSystemdInterface();
|
||||
try {
|
||||
systemd.EnableUnitFiles([`${serviceName}.service`], false, false);
|
||||
} catch (e) {
|
||||
throw new DbusError(e);
|
||||
}
|
||||
}
|
||||
|
||||
export async function disableService(serviceName: string) {
|
||||
const systemd = await getSystemdInterface();
|
||||
try {
|
||||
systemd.DisableUnitFiles([`${serviceName}.service`], false);
|
||||
} catch (e) {
|
||||
throw new DbusError(e);
|
||||
}
|
||||
}
|
||||
|
||||
export const reboot = async () =>
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
const logind = await getLoginManagerInterface();
|
||||
logind.Reboot(false);
|
||||
} catch (e) {
|
||||
log.error(`Unable to reboot: ${e}`);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
export const shutdown = async () =>
|
||||
setTimeout(async () => {
|
||||
try {
|
||||
const logind = await getLoginManagerInterface();
|
||||
logind.PowerOff(false);
|
||||
} catch (e) {
|
||||
log.error(`Unable to shutdown: ${e}`);
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
async function getUnitProperty(unitName: string, property: string) {
|
||||
const systemd = await getSystemdInterface();
|
||||
return new Promise((resolve, reject) => {
|
||||
systemd.GetUnit(unitName, async (err: Error, unitPath: string) => {
|
||||
if (err) {
|
||||
return reject(err);
|
||||
}
|
||||
const iface = await getInterfaceAsync(
|
||||
'org.freedesktop.systemd1',
|
||||
unitPath,
|
||||
'org.freedesktop.DBus.Properties',
|
||||
);
|
||||
|
||||
iface.Get(
|
||||
'org.freedesktop.systemd1.Unit',
|
||||
property,
|
||||
(e: Error, value: unknown) => {
|
||||
if (e) {
|
||||
return reject(new DbusError(e));
|
||||
}
|
||||
resolve(value);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function serviceActiveState(serviceName: string) {
|
||||
return getUnitProperty(`${serviceName}.service`, 'ActiveState');
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
import * as Bluebird from 'bluebird';
|
||||
import * as dbus from 'dbus-native';
|
||||
|
||||
const bus = dbus.systemBus();
|
||||
const invokeAsync = Bluebird.promisify(bus.invoke).bind(bus);
|
||||
|
||||
const defaultPathInfo = {
|
||||
path: '/org/freedesktop/systemd1',
|
||||
destination: 'org.freedesktop.systemd1',
|
||||
interface: 'org.freedesktop.systemd1.Manager',
|
||||
};
|
||||
|
||||
const systemdManagerMethodCall = (
|
||||
method: string,
|
||||
signature?: string,
|
||||
body?: any[],
|
||||
pathInfo: {
|
||||
path: string;
|
||||
destination: string;
|
||||
interface: string;
|
||||
} = defaultPathInfo,
|
||||
) => {
|
||||
if (signature == null) {
|
||||
signature = '';
|
||||
}
|
||||
if (body == null) {
|
||||
body = [];
|
||||
}
|
||||
return invokeAsync({
|
||||
...pathInfo,
|
||||
member: method,
|
||||
signature,
|
||||
body,
|
||||
});
|
||||
};
|
||||
|
||||
export function restartService(serviceName: string) {
|
||||
return systemdManagerMethodCall('RestartUnit', 'ss', [
|
||||
`${serviceName}.service`,
|
||||
'fail',
|
||||
]);
|
||||
}
|
||||
|
||||
export function startService(serviceName: string) {
|
||||
return systemdManagerMethodCall('StartUnit', 'ss', [
|
||||
`${serviceName}.service`,
|
||||
'fail',
|
||||
]);
|
||||
}
|
||||
|
||||
export function stopService(serviceName: string) {
|
||||
return systemdManagerMethodCall('StopUnit', 'ss', [
|
||||
`${serviceName}.service`,
|
||||
'fail',
|
||||
]);
|
||||
}
|
||||
|
||||
export function enableService(serviceName: string) {
|
||||
return systemdManagerMethodCall('EnableUnitFiles', 'asbb', [
|
||||
[`${serviceName}.service`],
|
||||
false,
|
||||
false,
|
||||
]);
|
||||
}
|
||||
|
||||
export function disableService(serviceName: string) {
|
||||
return systemdManagerMethodCall('DisableUnitFiles', 'asb', [
|
||||
[`${serviceName}.service`],
|
||||
false,
|
||||
]);
|
||||
}
|
||||
|
||||
export const reboot = Bluebird.method(() =>
|
||||
setTimeout(
|
||||
() =>
|
||||
systemdManagerMethodCall('Reboot', 'b', [false], {
|
||||
path: '/org/freedesktop/login1',
|
||||
destination: 'org.freedesktop.login1',
|
||||
interface: 'org.freedesktop.login1.Manager',
|
||||
}),
|
||||
1000,
|
||||
),
|
||||
);
|
||||
|
||||
export const shutdown = Bluebird.method(() =>
|
||||
setTimeout(
|
||||
() =>
|
||||
systemdManagerMethodCall('PowerOff', 'b', [false], {
|
||||
path: '/org/freedesktop/login1',
|
||||
destination: 'org.freedesktop.login1',
|
||||
interface: 'org.freedesktop.login1.Manager',
|
||||
}),
|
||||
1000,
|
||||
),
|
||||
);
|
||||
|
||||
function getUnitProperty(unitName: string, property: string) {
|
||||
return systemdManagerMethodCall('GetUnit', 's', [unitName])
|
||||
.then((unitPath: string) =>
|
||||
invokeAsync({
|
||||
path: unitPath,
|
||||
destination: 'org.freedesktop.systemd1',
|
||||
interface: 'org.freedesktop.DBus.Properties',
|
||||
member: 'Get',
|
||||
signature: 'ss',
|
||||
body: ['org.freedesktop.systemd1.Unit', property],
|
||||
}),
|
||||
)
|
||||
.get(1)
|
||||
.get(0);
|
||||
}
|
||||
|
||||
export function serviceActiveState(serviceName: string) {
|
||||
return getUnitProperty(`${serviceName}.service`, 'ActiveState');
|
||||
}
|
@ -8,11 +8,28 @@ process.env.LED_FILE = './test/data/led_file';
|
||||
|
||||
import { stub } from 'sinon';
|
||||
|
||||
import dbus = require('dbus-native');
|
||||
import * as dbus from 'dbus';
|
||||
|
||||
stub(dbus, 'systemBus').returns(({
|
||||
invoke(obj: unknown, cb: () => void) {
|
||||
console.log(obj);
|
||||
return cb();
|
||||
// Stub the dbus objects to dynamically generate the methods
|
||||
// on request
|
||||
stub(dbus, 'getBus').returns({
|
||||
getInterface: (
|
||||
_obj: unknown,
|
||||
cb: (err: Error | undefined, iface: dbus.DBusInterface) => void,
|
||||
) => {
|
||||
return cb(
|
||||
undefined,
|
||||
new Proxy(
|
||||
{},
|
||||
{
|
||||
get(_target, name) {
|
||||
console.log(`Dbus method ${String(name)} requested`);
|
||||
return () => {
|
||||
/* noop */
|
||||
};
|
||||
},
|
||||
},
|
||||
) as any,
|
||||
);
|
||||
},
|
||||
} as unknown) as dbus.MessageBus);
|
||||
} as any);
|
||||
|
@ -19,6 +19,7 @@ var externalModules = [
|
||||
'oracledb',
|
||||
'pg-query-stream',
|
||||
'tedious',
|
||||
'dbus',
|
||||
/mssql\/.*/,
|
||||
];
|
||||
|
||||
@ -35,19 +36,19 @@ lookForOptionalDeps = function(sourceDir) {
|
||||
}
|
||||
try {
|
||||
packageJson = JSON.parse(
|
||||
fs.readFileSync(path.join(sourceDir, dir, '/package.json'))
|
||||
fs.readFileSync(path.join(sourceDir, dir, '/package.json')),
|
||||
);
|
||||
} catch (e) {
|
||||
continue;
|
||||
}
|
||||
if (packageJson.optionalDependencies != null) {
|
||||
maybeOptionalModules = maybeOptionalModules.concat(
|
||||
_.keys(packageJson.optionalDependencies)
|
||||
_.keys(packageJson.optionalDependencies),
|
||||
);
|
||||
}
|
||||
if (packageJson.dependencies != null) {
|
||||
requiredModules = requiredModules.concat(
|
||||
_.keys(packageJson.dependencies)
|
||||
_.keys(packageJson.dependencies),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -60,8 +61,8 @@ externalModules.push(
|
||||
_.reject(maybeOptionalModules, requiredModules)
|
||||
.map(_.escapeRegExp)
|
||||
.join('|') +
|
||||
')(/.*)?$'
|
||||
)
|
||||
')(/.*)?$',
|
||||
),
|
||||
);
|
||||
|
||||
console.log('Using the following dependencies as external:', externalModules);
|
||||
@ -88,23 +89,23 @@ module.exports = function(env) {
|
||||
new TerserWebpackPlugin({
|
||||
terserOptions: {
|
||||
mangle: false,
|
||||
keep_classnames: true
|
||||
}
|
||||
})
|
||||
]
|
||||
keep_classnames: true,
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: new RegExp(
|
||||
_.escapeRegExp(path.join('knex', 'lib', 'migrate', 'index.js')) +
|
||||
'$'
|
||||
'$',
|
||||
),
|
||||
use: require.resolve('./hardcode-migrations'),
|
||||
},
|
||||
{
|
||||
test: new RegExp(
|
||||
_.escapeRegExp(path.join('JSONStream', 'index.js')) + '$'
|
||||
_.escapeRegExp(path.join('JSONStream', 'index.js')) + '$',
|
||||
),
|
||||
use: require.resolve('./fix-jsonstream'),
|
||||
},
|
||||
@ -152,7 +153,7 @@ module.exports = function(env) {
|
||||
]),
|
||||
new webpack.ContextReplacementPlugin(
|
||||
/\.\/migrations/,
|
||||
path.resolve(__dirname, 'build/migrations')
|
||||
path.resolve(__dirname, 'build/migrations'),
|
||||
),
|
||||
],
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user