origintrail-node
Version:
OriginTrail Node - Decentralized Knowledge Graph Node Library
189 lines (160 loc) • 7.02 kB
JavaScript
import { execSync } from 'child_process';
import BaseMigration from './base-migration.js';
import { NODE_ENVIRONMENTS } from '../constants/constants.js';
class RedisSetupMigration extends BaseMigration {
async executeMigration() {
if (
process.env.NODE_ENV === NODE_ENVIRONMENTS.DEVELOPMENT ||
process.env.NODE_ENV === NODE_ENVIRONMENTS.TEST
) {
this.logger.info('Skipping Redis setup in development/test environment');
return;
}
if (this.isRedisInstalledAndRunning()) {
this.logger.info('✅ Redis is already installed and running.');
// Check if configuration is correct
if (this.isRedisConfiguredCorrectly()) {
this.logger.info('✅ Redis is configured correctly. No changes needed.');
return;
}
this.logger.info('⚠️ Redis is installed but configuration needs updating.');
this.updateRedisConfiguration();
} else {
this.logger.info('🔧 Installing Redis...');
this.installRedis();
}
this.verifyRedisInstallation();
}
installRedis() {
this.run('sudo apt update', 'Updating package list');
this.run('sudo apt install -y redis-server', 'Installing Redis server');
// Backup original config before modifying
this.run(
'sudo cp /etc/redis/redis.conf /etc/redis/redis.conf.backup',
'Backing up original redis.conf',
);
this.updateRedisConfiguration();
this.run('sudo systemctl restart redis.service', 'Restarting Redis service');
this.run('sudo systemctl enable redis.service', 'Enabling Redis to start on boot');
this.run('sudo systemctl status redis.service --no-pager', 'Checking Redis service status');
}
updateRedisConfiguration() {
this.logger.info('🔧 Updating Redis configuration...');
// Ensure redis.conf uses systemd
this.modifyRedisConf('supervised', 'supervised systemd', 'Enabling systemd supervision');
// Enable AOF persistence
this.modifyRedisConf('appendonly', 'appendonly yes', 'Enabling AOF persistence');
this.modifyRedisConf(
'appendfsync',
'appendfsync everysec',
'Setting AOF fsync every second',
);
// Enforce noeviction policy
this.modifyRedisConf(
'maxmemory-policy',
'maxmemory-policy noeviction',
'Setting noeviction policy',
);
// Restart Redis to apply configuration changes
this.run(
'sudo systemctl restart redis.service',
'Restarting Redis service to apply configuration',
);
}
isRedisConfiguredCorrectly() {
try {
const configPath = '/etc/redis/redis.conf';
const configContent = execSync(`sudo cat ${configPath}`, { stdio: 'pipe' }).toString();
const checks = [
{ pattern: /supervised\s+systemd/, name: 'systemd supervision' },
{ pattern: /appendonly\s+yes/, name: 'AOF persistence' },
{ pattern: /appendfsync\s+everysec/, name: 'AOF fsync every second' },
{ pattern: /maxmemory-policy\s+noeviction/, name: 'noeviction policy' },
];
let allCorrect = true;
for (const check of checks) {
if (!check.pattern.test(configContent)) {
this.logger.warn(`⚠️ Configuration issue: ${check.name} not set correctly`);
allCorrect = false;
}
}
if (allCorrect) {
this.logger.info('✅ All Redis configuration checks passed');
} else {
this.logger.info('⚠️ Some Redis configuration settings need to be updated');
}
return allCorrect;
} catch (err) {
this.logger.warn(`⚠️ Could not read Redis configuration: ${err.message}`);
return false;
}
}
isRedisInstalledAndRunning() {
try {
execSync('which redis-server', { stdio: 'ignore' });
const serviceStatus = execSync('systemctl is-active redis.service', { stdio: 'pipe' })
.toString()
.trim();
const ping = execSync('redis-cli ping', { stdio: 'pipe' }).toString().trim();
return serviceStatus === 'active' && ping === 'PONG';
} catch {
return false;
}
}
verifyRedisInstallation() {
try {
const ping = execSync('redis-cli ping').toString().trim();
if (ping === 'PONG') {
this.logger.info('🎉 Redis is installed and responding: PONG');
// Final configuration check
if (this.isRedisConfiguredCorrectly()) {
this.logger.info(
'🎉 Redis setup completed successfully with correct configuration!',
);
} else {
this.logger.error('❌ Redis is running but configuration is still incorrect');
process.exit(1);
}
} else {
this.logger.error('❌ Redis did not respond with PONG');
process.exit(1);
}
} catch (err) {
this.logger.error('❌ Redis ping failed');
this.logger.error(err.message);
process.exit(1);
}
}
run(cmd, description) {
this.logger.info(`🔧 ${description}...`);
try {
const output = execSync(cmd, { stdio: 'inherit' });
return output?.toString().trim();
} catch (err) {
this.logger.error(`❌ Failed: ${description}`);
this.logger.error(err.message);
process.exit(1);
}
}
modifyRedisConf(pattern, replacement, description) {
const configPath = '/etc/redis/redis.conf';
// Use a simpler approach: comment out lines that contain the pattern (not commented)
const commentCommand = `sudo sed -i '/^[[:space:]]*${pattern}/s/^/# /' ${configPath}`;
this.run(commentCommand, `Commenting out existing ${description} settings`);
// Step 2: Add the new setting at the end of the file
this.run(
`echo '${replacement}' | sudo tee -a ${configPath}`,
`Adding ${replacement} to redis.conf`,
);
// Step 3: Verify the change was made
try {
const grepCheck = `grep -q "^${replacement}$" ${configPath}`;
execSync(grepCheck, { stdio: 'ignore' });
this.logger.info(`✅ Successfully updated: ${description}`);
} catch {
this.logger.error(`❌ Failed to verify: ${description}`);
throw new Error(`Failed to update ${description}`);
}
}
}
export default RedisSetupMigration;