Skip to content

Commit

Permalink
Merge pull request #94 from RunOnFlux/minecraft
Browse files Browse the repository at this point in the history
feat(minecraft): add custom handling for minecraft applications
  • Loading branch information
TheTrunk authored Sep 18, 2023
2 parents 1333481 + 439f758 commit d417c6c
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 63 deletions.
3 changes: 2 additions & 1 deletion config/appsConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ module.exports = {
mandatoryApps: ['explorer', 'web', 'themok6', 'paoverview', 'eckodexswap', 'HavenNodeMainnet'],
ownersApps: [], // Will retrieve only apps of owners specified here
whiteListedApps: [], // If there's app in the array, blacklisting will be ignore
blackListedApps: ['Kadena', 'Kadena2', 'PresearchNode*', 'BrokerNode*'],
blackListedApps: ['Kadena', 'Kadena2', 'PresearchNode*', 'BrokerNode*', 'Folding*'],
minecraftApps: ['mcf', '*minecraft*'],
};
1 change: 1 addition & 0 deletions config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ module.exports = {
ownersApps: appsConfig.ownersApps, // Will retrieve only apps of owners specified here
whiteListedApps: appsConfig.whiteListedApps, // If there's app in the array, blacklisting will be ignore
blackListedApps: appsConfig.blackListedApps,
minecraftApps: appsConfig.minecraftApps,
appSubDomain: 'app2',
fdmAppDomain: 'fdm-lb-2-1.runonflux.io',
useSubset: false,
Expand Down
1 change: 1 addition & 0 deletions deployment/default.js.j2
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ module.exports = {
ownersApps: appsConfig.ownersApps, // Will retrieve only apps of owners specified here
whiteListedApps: appsConfig.whiteListedApps, // If there's app in the array, blacklisting will be ignore
blackListedApps: appsConfig.blackListedApps,
minecraftApps: appsConfig.minecraftApps,
appSubDomain: '{{ appSubDomain }}',
fdmAppDomain: '{{ fdmAppDomain }}',
useSubset: {{ useSubset }},
Expand Down
11 changes: 10 additions & 1 deletion src/services/domainService.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ async function generateAndReplaceMainApplicationHaproxyConfig() {
if (app.version <= 3) {
for (let i = 0; i < app.ports.length; i += 1) {
const configuredApp = {
name: app.name,
appName: `${app.name}_${app.ports[i]}`,
domain: domains[i],
port: app.ports[i],
Expand Down Expand Up @@ -269,6 +270,7 @@ async function generateAndReplaceMainApplicationHaproxyConfig() {
const domainExists = configuredApps.find((a) => a.domain === portDomain.toLowerCase());
if (!domainExists) {
const configuredAppCustom = {
name: app.name,
appName: `${app.name}_${app.ports[i]}`,
domain: portDomain,
port: app.ports[i],
Expand All @@ -282,6 +284,7 @@ async function generateAndReplaceMainApplicationHaproxyConfig() {
const domainExistsB = configuredApps.find((a) => a.domain === wwwAdjustedDomain);
if (!domainExistsB) {
const configuredAppCustom = {
name: app.name,
appName: `${app.name}_${app.ports[i]}`,
domain: wwwAdjustedDomain,
port: app.ports[i],
Expand All @@ -297,6 +300,7 @@ async function generateAndReplaceMainApplicationHaproxyConfig() {
const domainExistsB = configuredApps.find((a) => a.domain === testAdjustedDomain);
if (!domainExistsB) {
const configuredAppCustom = {
name: app.name,
appName: `${app.name}_${app.ports[i]}`,
domain: testAdjustedDomain,
port: app.ports[i],
Expand All @@ -311,6 +315,7 @@ async function generateAndReplaceMainApplicationHaproxyConfig() {
}
}
const mainApp = {
name: app.name,
appName: `${app.name}_${app.ports[0]}`,
domain: domains[domains.length - 1],
port: app.ports[0],
Expand All @@ -323,6 +328,7 @@ async function generateAndReplaceMainApplicationHaproxyConfig() {
for (const component of app.compose) {
for (let i = 0; i < component.ports.length; i += 1) {
const configuredApp = {
name: app.name,
appName: `${app.name}_${component.name}_${component.ports[i]}`,
domain: domains[j],
port: component.ports[i],
Expand Down Expand Up @@ -351,6 +357,7 @@ async function generateAndReplaceMainApplicationHaproxyConfig() {
const domainExists = configuredApps.find((a) => a.domain === portDomain.toLowerCase());
if (!domainExists) {
const configuredAppCustom = {
name: app.name,
appName: `${app.name}_${component.name}_${component.ports[i]}`,
domain: portDomain,
port: component.ports[i],
Expand All @@ -365,6 +372,7 @@ async function generateAndReplaceMainApplicationHaproxyConfig() {
const domainExistsB = configuredApps.find((a) => a.domain === wwwAdjustedDomain);
if (!domainExistsB) {
const configuredAppCustom = {
name: app.name,
appName: `${app.name}_${component.name}_${component.ports[i]}`,
domain: wwwAdjustedDomain,
port: component.ports[i],
Expand All @@ -380,6 +388,7 @@ async function generateAndReplaceMainApplicationHaproxyConfig() {
const domainExistsB = configuredApps.find((a) => a.domain === testAdjustedDomain);
if (!domainExistsB) {
const configuredAppCustom = {
name: app.name,
appName: `${app.name}_${component.name}_${component.ports[i]}`,
domain: testAdjustedDomain,
port: component.ports[i],
Expand All @@ -401,6 +410,7 @@ async function generateAndReplaceMainApplicationHaproxyConfig() {
const mainDomainExists = configuredApps.find((qw) => qw.domain === domains[domains.length - 1]);
if (!mainDomainExists) {
const mainApp = {
name: app.name,
appName: `${app.name}_${app.compose[q].name}_${app.compose[q].ports[w]}`,
domain: domains[domains.length - 1],
port: app.compose[q].ports[w],
Expand All @@ -424,7 +434,6 @@ async function generateAndReplaceMainApplicationHaproxyConfig() {
if (configuredApps.length < 10) {
throw new Error('PANIC PLEASE DEV HELP ME');
}

const hc = await haproxyTemplate.createAppsHaproxyConfig(configuredApps);
console.log(hc);
const dataToWrite = hc;
Expand Down
187 changes: 127 additions & 60 deletions src/services/haproxyTemplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const configGlobal = require('config');
const fs = require('fs').promises;
const log = require('../lib/log');
const { cmdAsync, TEMP_HAPROXY_CONFIG, HAPROXY_CONFIG } = require('./constants');
const { matchRule } = require('./serviceHelper');

const haproxyPrefix = `
global
Expand Down Expand Up @@ -103,12 +104,117 @@ function createCertificatesPaths(domains) {
return path;
}

function generateHaproxyConfig(acls, usebackends, domains, backends, redirects) {
function generateMinecraftSettings(minecraftAppsMap) {
let configs = '';
for (const port of Object.keys(minecraftAppsMap)) {
const portConf = minecraftAppsMap[port];
const tempFrontend = `
frontend minecraft_${port}
bind 0.0.0.0:${port}
tcp-request inspect-delay 5s
tcp-request content accept if { req_ssl_hello_type 1 }
mode tcp
option tcplog
option tcp-check
${portConf.acls.join('\n')}
${portConf.usebackends.join('')}
${portConf.backends.join('\n')}`;

configs = `${configs}\n\n${tempFrontend}`;
}

return configs;
}

function generateHaproxyConfig(acls, usebackends, domains, backends, redirects, minecraftAppsMap = {}) {
// eslint-disable-next-line max-len
const config = `${haproxyPrefix}\n\n${acls}\n${usebackends}\n${redirects}\n${httpsPrefix}${certificatePrefix}${createCertificatesPaths(domains)}${certificatesSuffix} ${h2Suffix}\n\n${acls}\n${usebackends}\n${redirects}\n\n${backends}\n${letsEncryptBackend}\n${cloudflareFluxBackend}\n${forbiddenBackend}`;
const minecraftConfig = generateMinecraftSettings(minecraftAppsMap);
const config = `
${haproxyPrefix}
${acls}
${usebackends}
${redirects}
${minecraftConfig}
${httpsPrefix}${certificatePrefix}${createCertificatesPaths(domains)}${certificatesSuffix} ${h2Suffix}
${acls}
${usebackends}
${redirects}
${backends}
${letsEncryptBackend}
${cloudflareFluxBackend}
${forbiddenBackend}
`;
return config;
}

function generateDomainBackend(app, mode) {
const domainUsed = app.domain.split('.').join('');
let domainBackend = `
backend ${domainUsed}backend
mode ${mode}`;
if (app.loadBalance) {
domainBackend += app.loadBalance;
} else if (mode !== 'tcp') {
domainBackend += '\n balance roundrobin';
domainBackend += '\n cookie FDMSERVERID insert indirect nocache maxlife 8h';
}
if (app.headers) {
// eslint-disable-next-line no-loop-func
app.headers.forEach((header) => {
domainBackend += `\n ${header}`;
});
}
// eslint-disable-next-line no-loop-func
app.healthcheck.forEach((hc) => {
domainBackend += `\n ${hc}`;
});
for (const ip of app.ips) {
const a = ip.split(':')[0].split('.');
if (!a) {
log.error('STRANGE IP');
log.error(ip);
// eslint-disable-next-line no-continue
continue;
}
const apiPort = ip.split(':')[1] || 16127;
const cookieConfig = app.loadBalance || mode === 'tcp' ? '' : ` cookie ${ip.split(':')[0]}:${app.port}`;
if (app.ssl) {
const h2Config = app.enableH2 ? h2Suffix : '';
domainBackend += `\n server ${ip.split(':')[0]}:${apiPort} ${ip.split(':')[0]}:${app.port} check ${app.serverConfig} ssl verify none ${h2Config}${cookieConfig}`;
} else {
domainBackend += `\n server ${ip.split(':')[0]}:${apiPort} ${ip.split(':')[0]}:${app.port} check ${app.serverConfig}${cookieConfig}`;
}
if (app.timeout) {
domainBackend += `\n timeout server ${app.timeout}`;
}
}
return domainBackend;
}

function generateMinecraftACLs(app) {
console.log(app.domain);
const aclName = app.domain.split('.').join('');
const appName = app.domain.split('.')[0];
console.log(appName);

const nameLength = appName.length + 1;
const domainLength = app.domain.length;
return [
` acl ${aclName} req.payload(4,${nameLength}) -m sub ${appName}.`,
` acl ${aclName} req.payload(5,${nameLength}) -m sub ${appName}.`,
` acl ${aclName} req.payload(7,${nameLength}) -m sub ${appName}.`,
` acl ${aclName} req.payload(8,${nameLength}) -m sub ${appName}.`,
` acl ${aclName} req.payload(1,${domainLength}) -m sub ${app.domain}`,
` acl ${aclName} req.payload(2,${domainLength}) -m sub ${app.domain}`,
` acl ${aclName} req.payload(3,${domainLength}) -m sub ${app.domain}`,
];
}

function createMainHaproxyConfig(ui, api, fluxIPs) {
const uiB = ui.split('.').join('');
let uiBackend = `backend ${uiB}backend
Expand Down Expand Up @@ -178,7 +284,7 @@ function createMainHaproxyConfig(ui, api, fluxIPs) {
const backends = `${uiBackend}\n\n${apiBackend}`;
const urls = [ui, api, 'dashboard.zel.network'];

return generateHaproxyConfig(acls, usebackends, urls, backends, redirects);
return generateHaproxyConfig(acls, usebackends, urls, backends, redirects, {});
}

// appConfig is an array of object of domain, port, ips
Expand All @@ -194,6 +300,7 @@ function createAppsHaproxyConfig(appConfig) {
// usebackends += ' use_backend forbidden-backend if forbiddenacl\n';
const domains = [];
const seenApps = {};
const minecraftAppsMap = {};
for (const app of appConfig) {
if (domains.includes(app.domain)) {
// eslint-disable-next-line no-continue
Expand All @@ -202,68 +309,28 @@ function createAppsHaproxyConfig(appConfig) {
if (app.appName in seenApps) {
domains.push(app.domain);
acls += ` acl ${seenApps[app.appName]} hdr(host) ${app.domain}\n`;
} else if (matchRule(app.name.toLowerCase(), configGlobal.minecraftApps)) {
const domainUsed = app.domain.split('.').join('');
const { port } = app;
if (!(port in minecraftAppsMap)) {
minecraftAppsMap[port] = {
acls: [],
usebackends: [],
backends: [],
};
}
const tempMinecraftACLs = generateMinecraftACLs(app);
const domainBackend = generateDomainBackend(app, 'tcp');
minecraftAppsMap[port].acls = minecraftAppsMap[port].acls.concat(tempMinecraftACLs);
minecraftAppsMap[port].usebackends.push(` use_backend ${domainUsed}backend if ${domainUsed}\n`);
minecraftAppsMap[port].backends.push(domainBackend);
} else {
const domainUsed = app.domain.split('.').join('');
if (usebackends.includes(` use_backend ${domainUsed}backend if ${domainUsed}\n`)) {
// eslint-disable-next-line no-continue
continue;
}
let domainBackend = `backend ${domainUsed}backend
mode http`;
if (app.loadBalance) {
domainBackend += app.loadBalance;
} else {
domainBackend += '\n balance roundrobin';
domainBackend += '\n cookie FDMSERVERID insert indirect nocache maxlife 8h';
}
if (app.headers) {
// eslint-disable-next-line no-loop-func
app.headers.forEach((header) => {
domainBackend += `\n ${header}`;
});
}
// eslint-disable-next-line no-loop-func
app.healthcheck.forEach((hc) => {
domainBackend += `\n ${hc}`;
});
for (const ip of app.ips) {
const a = ip.split(':')[0].split('.');
if (!a) {
log.error('STRANGE IP');
log.error(ip);
// eslint-disable-next-line no-continue
continue;
}
const apiPort = ip.split(':')[1] || 16127;
// let IpString = '';
// for (let i = 0; i < 4; i += 1) {
// if (!(a[i])) {
// log.error('STRANGE IP');
// log.error(ip);
// // eslint-disable-next-line no-continue
// continue;
// }
// if (a[i].length === 3) {
// IpString += a[i];
// }
// if (a[i].length === 2) {
// IpString = `${IpString}0${a[i]}`;
// }
// if (a[i].length === 1) {
// IpString = `${IpString}00${a[i]}`;
// }
// }
const cookieConfig = app.loadBalance ? '' : ` cookie ${ip.split(':')[0]}:${app.port}`;
if (app.ssl) {
const h2Config = app.enableH2 ? h2Suffix : '';
domainBackend += `\n server ${ip.split(':')[0]}:${apiPort} ${ip.split(':')[0]}:${app.port} check ${app.serverConfig} ssl verify none ${h2Config}${cookieConfig}`;
} else {
domainBackend += `\n server ${ip.split(':')[0]}:${apiPort} ${ip.split(':')[0]}:${app.port} check ${app.serverConfig}${cookieConfig}`;
}
if (app.timeout) {
domainBackend += `\n timeout server ${app.timeout}`;
}
}
const domainBackend = generateDomainBackend(app, 'http');
backends = `${backends + domainBackend}\n\n`;
domains.push(app.domain);
acls += ` acl ${domainUsed} hdr(host) ${app.domain}\n`;
Expand All @@ -273,7 +340,7 @@ function createAppsHaproxyConfig(appConfig) {
}
const redirects = '';

return generateHaproxyConfig(acls, usebackends, domains, backends, redirects);
return generateHaproxyConfig(acls, usebackends, domains, backends, redirects, minecraftAppsMap);
}

async function writeConfig(configName, data) {
Expand Down
2 changes: 1 addition & 1 deletion src/services/rsync/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const path = require('path');

function getHostsToRsync() {
// file generated by ansible
// eslint-disable-next-line global-require
// eslint-disable-next-line global-require, import/no-unresolved
const rsyncConfig = require('../../../deployment/rsync_config');

const hosts = ini.parse(fs.readFileSync(path.resolve(__dirname, '../../../deployment/hosts.ini'), 'utf-8'));
Expand Down

0 comments on commit d417c6c

Please sign in to comment.