Skip to content

Commit

Permalink
MQTT: Replace shell script by javascript functions (#1668)
Browse files Browse the repository at this point in the history
  • Loading branch information
atrovato authored Mar 24, 2023
1 parent 77f69af commit 2ea1ea9
Show file tree
Hide file tree
Showing 8 changed files with 162 additions and 45 deletions.
37 changes: 0 additions & 37 deletions server/services/mqtt/docker/eclipse-mosquitto-env.sh

This file was deleted.

54 changes: 54 additions & 0 deletions server/services/mqtt/lib/configureContainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const fs = require('fs/promises');
const { constants } = require('fs');
const os = require('os');

const logger = require('../../../utils/logger');
const { DEFAULT } = require('./constants');

const MOSQUITTO_DIRECTORY = '/var/lib/gladysassistant/mosquitto';
const MOSQUITTO_CONFIG_FILE_PATH = `${MOSQUITTO_DIRECTORY}/mosquitto.conf`;
const MOSQUITTO_PASSWORD_FILE_PATH = `${MOSQUITTO_DIRECTORY}/mosquitto.passwd`;

const MOSQUITTO_CONFIG_PORT = 'listener 1883';
const MOSQUITTO_CONFIG_CONTENT = [
'allow_anonymous false',
'connection_messages false',
`password_file ${DEFAULT.PASSWORD_FILE_PATH}`,
MOSQUITTO_CONFIG_PORT,
];

/**
* @description Configure MQTT container.
* @example
* mqtt.configureContainer();
*/
async function configureContainer() {
logger.info('MQTT broker Docker container is being configured...');

// Create configuration path (if not exists)
await fs.mkdir(MOSQUITTO_DIRECTORY, { recursive: true });

// Check if config file not already exists
try {
// eslint-disable-next-line no-bitwise
await fs.access(MOSQUITTO_CONFIG_FILE_PATH, constants.R_OK | constants.W_OK);
logger.info('eclipse-mosquitto configuration file already exists.');

// Check for breaking change
const configContent = await fs.readFile(MOSQUITTO_CONFIG_FILE_PATH);
if (!configContent.includes(MOSQUITTO_CONFIG_PORT)) {
await fs.appendFile(MOSQUITTO_CONFIG_FILE_PATH, `${os.EOL}${MOSQUITTO_CONFIG_PORT}`);
}
} catch (e) {
logger.info('Writting default eclipse-mosquitto configuration...');
await fs.writeFile(MOSQUITTO_CONFIG_FILE_PATH, MOSQUITTO_CONFIG_CONTENT.join(os.EOL));
}

// Create empty password file if not already exists
const pwdFile = await fs.open(MOSQUITTO_PASSWORD_FILE_PATH, 'w');
await pwdFile.close();
}

module.exports = {
configureContainer,
};
1 change: 1 addition & 0 deletions server/services/mqtt/lib/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const DEFAULT = {
ERROR: 'ERROR',
},
MOSQUITTO_VERSION: '3',
PASSWORD_FILE_PATH: '/mosquitto/config/mosquitto.passwd',
};

module.exports = {
Expand Down
2 changes: 2 additions & 0 deletions server/services/mqtt/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const { status } = require('./status');
const { getConfiguration } = require('./getConfiguration');
const { saveConfiguration } = require('./saveConfiguration');
const { installContainer } = require('./installContainer');
const { configureContainer } = require('./configureContainer');
const { updateContainer } = require('./updateContainer');
const { checkDockerNetwork } = require('./checkDockerNetwork');
const { setValue } = require('./setValue');
Expand Down Expand Up @@ -45,6 +46,7 @@ MqttHandler.prototype.status = status;
MqttHandler.prototype.getConfiguration = getConfiguration;
MqttHandler.prototype.saveConfiguration = saveConfiguration;
MqttHandler.prototype.installContainer = installContainer;
MqttHandler.prototype.configureContainer = configureContainer;
MqttHandler.prototype.updateContainer = updateContainer;
MqttHandler.prototype.checkDockerNetwork = checkDockerNetwork;
MqttHandler.prototype.setValue = setValue;
Expand Down
4 changes: 1 addition & 3 deletions server/services/mqtt/lib/installContainer.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const cloneDeep = require('lodash.clonedeep');
const logger = require('../../../utils/logger');
const { exec } = require('../../../utils/childProcess');
const { generate } = require('../../../utils/password');
const { EVENTS, WEBSOCKET_MESSAGE_TYPES } = require('../../../utils/constants');
const { PlatformNotCompatible } = require('../../../utils/coreErrors');
Expand Down Expand Up @@ -31,8 +30,7 @@ async function installContainer(saveConfiguration = true) {

// Prepare broker env
logger.info(`Preparing broker environment...`);
const brokerEnv = await exec('sh ./services/mqtt/docker/eclipse-mosquitto-env.sh');
logger.trace(brokerEnv);
await this.configureContainer();

logger.info(`Creating container...`);
const containerDescriptorToMutate = cloneDeep(containerDescriptor);
Expand Down
4 changes: 2 additions & 2 deletions server/services/mqtt/lib/saveConfiguration.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,14 @@ async function saveConfiguration({ mqttUrl, mqttUsername, mqttPassword, useEmbed
if (oldUser) {
// Delete old user
await this.gladys.system.exec(container.id, {
Cmd: ['mosquitto_passwd', '-D', '/mosquitto/config/mosquitto.passwd', oldUser],
Cmd: ['mosquitto_passwd', '-D', DEFAULT.PASSWORD_FILE_PATH, oldUser],
});
}

if (mqttUsername) {
// Generate password
await this.gladys.system.exec(container.id, {
Cmd: ['mosquitto_passwd', '-b', '/mosquitto/config/mosquitto.passwd', mqttUsername, mqttPassword],
Cmd: ['mosquitto_passwd', '-b', DEFAULT.PASSWORD_FILE_PATH, mqttUsername, mqttPassword],
});
}

Expand Down
96 changes: 96 additions & 0 deletions server/test/services/mqtt/lib/configureContainer.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
const sinon = require('sinon');
const os = require('os');
const { constants } = require('fs');
const proxiquire = require('proxyquire').noCallThru();

const { assert, fake } = sinon;

const gladys = {};
const mqttClient = {};
const serviceId = 'faea9c35-759a-44d5-bcc9-2af1de37b8b4';

describe('mqttHandler.configureContainer', () => {
const fsMock = {};
const configureContainer = proxiquire('../../../../services/mqtt/lib/configureContainer', {
'fs/promises': fsMock,
});
const MqttHandler = proxiquire('../../../../services/mqtt/lib', {
'./configureContainer': configureContainer,
});

beforeEach(() => {
fsMock.mkdir = fake.resolves(true);
fsMock.access = fake.resolves(true);
fsMock.readFile = fake.resolves('read');
fsMock.appendFile = fake.resolves(true);
fsMock.writeFile = fake.resolves('write');
fsMock.open = fake.resolves({ close: () => {} });
});

afterEach(() => {
sinon.reset();
});

it('should not write configuration', async () => {
fsMock.readFile = fake.resolves(`1st line${os.EOL}listener 1883`);

const mqttHandler = new MqttHandler(gladys, mqttClient, serviceId);
await mqttHandler.configureContainer();

assert.calledOnceWithExactly(fsMock.mkdir, '/var/lib/gladysassistant/mosquitto', { recursive: true });
assert.calledOnceWithExactly(
fsMock.access,
'/var/lib/gladysassistant/mosquitto/mosquitto.conf',
// eslint-disable-next-line no-bitwise
constants.R_OK | constants.W_OK,
);
assert.calledOnceWithExactly(fsMock.readFile, '/var/lib/gladysassistant/mosquitto/mosquitto.conf');
assert.calledOnceWithExactly(fsMock.open, '/var/lib/gladysassistant/mosquitto/mosquitto.passwd', 'w');
assert.notCalled(fsMock.appendFile);
assert.notCalled(fsMock.writeFile);
});

it('should write default port', async () => {
const mqttHandler = new MqttHandler(gladys, mqttClient, serviceId);
await mqttHandler.configureContainer();

assert.calledOnceWithExactly(fsMock.mkdir, '/var/lib/gladysassistant/mosquitto', { recursive: true });
assert.calledOnceWithExactly(
fsMock.access,
'/var/lib/gladysassistant/mosquitto/mosquitto.conf',
// eslint-disable-next-line no-bitwise
constants.R_OK | constants.W_OK,
);
assert.calledOnceWithExactly(fsMock.readFile, '/var/lib/gladysassistant/mosquitto/mosquitto.conf');
assert.calledOnceWithExactly(
fsMock.appendFile,
'/var/lib/gladysassistant/mosquitto/mosquitto.conf',
`${os.EOL}listener 1883`,
);
assert.calledOnceWithExactly(fsMock.open, '/var/lib/gladysassistant/mosquitto/mosquitto.passwd', 'w');
assert.notCalled(fsMock.writeFile);
});

it('should create default configuration file', async () => {
fsMock.access = fake.rejects();

const mqttHandler = new MqttHandler(gladys, mqttClient, serviceId);
await mqttHandler.configureContainer();

assert.calledOnceWithExactly(fsMock.mkdir, '/var/lib/gladysassistant/mosquitto', { recursive: true });
assert.calledOnceWithExactly(
fsMock.access,
'/var/lib/gladysassistant/mosquitto/mosquitto.conf',
// eslint-disable-next-line no-bitwise
constants.R_OK | constants.W_OK,
);
assert.notCalled(fsMock.readFile);
assert.notCalled(fsMock.appendFile);
assert.calledOnceWithExactly(
fsMock.writeFile,
'/var/lib/gladysassistant/mosquitto/mosquitto.conf',
`allow_anonymous false${os.EOL}connection_messages false${os.EOL}password_file /mosquitto/config/mosquitto.passwd${os.EOL}listener 1883`,
);
assert.calledOnceWithExactly(fsMock.open, '/var/lib/gladysassistant/mosquitto/mosquitto.passwd', 'w');
});
});
9 changes: 6 additions & 3 deletions server/test/services/mqtt/lib/installContainer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,14 @@ describe('mqttHandler.installContainer', () => {
};

const mqttHandler = new MqttHandler(gladys, MockedMqttClient, serviceId);
mqttHandler.configureContainer = fake.resolves(null);

try {
await mqttHandler.installContainer();
assert.fail('should have fail');
} catch (e) {
expect(e).to.be.eq(error);
assert.notCalled(execMock.exec);
assert.notCalled(mqttHandler.configureContainer);
assert.calledOnce(gladys.event.emit);
assert.calledWith(gladys.event.emit, EVENTS.WEBSOCKET.SEND_ALL, {
type: WEBSOCKET_MESSAGE_TYPES.MQTT.INSTALLATION_STATUS,
Expand Down Expand Up @@ -89,11 +90,12 @@ describe('mqttHandler.installContainer', () => {
};

const mqttHandler = new MqttHandler(gladys, MockedMqttClient, serviceId);
mqttHandler.configureContainer = fake.resolves(null);

await mqttHandler.installContainer();

assert.callCount(gladys.variable.setValue, 5);
assert.calledOnce(execMock.exec);
assert.calledOnce(mqttHandler.configureContainer);
assert.calledOnce(gladys.system.pull);
assert.calledOnce(gladys.system.createContainer);
assert.calledOnce(gladys.system.getNetworkMode);
Expand Down Expand Up @@ -130,11 +132,12 @@ describe('mqttHandler.installContainer', () => {
};

const mqttHandler = new MqttHandler(gladys, MockedMqttClient, serviceId);
mqttHandler.configureContainer = fake.resolves(null);

await mqttHandler.installContainer(false);

assert.notCalled(gladys.variable.setValue);
assert.calledOnce(execMock.exec);
assert.calledOnce(mqttHandler.configureContainer);
assert.calledOnce(gladys.system.pull);
assert.calledOnce(gladys.system.createContainer);
assert.calledOnce(gladys.system.getNetworkMode);
Expand Down

0 comments on commit 2ea1ea9

Please sign in to comment.