mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2024-12-19 21:57:54 +00:00
Convert network module to typescript
Change-type: patch Signed-off-by: Cameron Diver <cameron@balena.io>
This commit is contained in:
parent
4bcdc83850
commit
6317b16138
@ -1,107 +0,0 @@
|
||||
Promise = require 'bluebird'
|
||||
_ = require 'lodash'
|
||||
url = require 'url'
|
||||
networkCheck = require 'network-checker'
|
||||
os = require 'os'
|
||||
fs = Promise.promisifyAll(require('fs'))
|
||||
|
||||
constants = require './lib/constants'
|
||||
{ checkTruthy } = require './lib/validation'
|
||||
blink = require './lib/blink'
|
||||
{ EEXIST } = require './lib/errors'
|
||||
|
||||
networkPattern =
|
||||
blinks: 4
|
||||
pause: 1000
|
||||
|
||||
pauseConnectivityCheck = false
|
||||
enableConnectivityCheck = true
|
||||
|
||||
# options: An object of net.connect options, with the addition of:
|
||||
# timeout: 10s
|
||||
checkHost = (options) ->
|
||||
if !enableConnectivityCheck or pauseConnectivityCheck
|
||||
return true
|
||||
else
|
||||
return networkCheck.checkHost(options)
|
||||
|
||||
# Custom monitor that uses checkHost function above.
|
||||
customMonitor = (options, fn) ->
|
||||
networkCheck.monitor(checkHost, options, fn)
|
||||
|
||||
# enable: A Boolean to enable/disable the connectivity checks
|
||||
exports.enableCheck = enableCheck = (enable) ->
|
||||
enableConnectivityCheck = enable
|
||||
|
||||
# Call back for inotify triggered when the VPN status is changed.
|
||||
vpnStatusInotifyCallback = ->
|
||||
fs.lstatAsync(constants.vpnStatusPath + '/active')
|
||||
.then ->
|
||||
pauseConnectivityCheck = true
|
||||
.catch ->
|
||||
pauseConnectivityCheck = false
|
||||
|
||||
exports.startConnectivityCheck = _.once (apiEndpoint, enable, onChangeCallback) ->
|
||||
exports.enableConnectivityCheck(enable)
|
||||
if !apiEndpoint?
|
||||
console.log('No API endpoint specified, skipping connectivity check')
|
||||
return
|
||||
parsedUrl = url.parse(apiEndpoint)
|
||||
fs.mkdirAsync(constants.vpnStatusPath)
|
||||
.catch EEXIST, (err) ->
|
||||
console.log('VPN status path exists.')
|
||||
.then ->
|
||||
fs.watch(constants.vpnStatusPath, vpnStatusInotifyCallback)
|
||||
|
||||
# Manually trigger the call back to detect cases when VPN was switched on before the supervisor starts.
|
||||
vpnStatusInotifyCallback() if enable
|
||||
customMonitor
|
||||
host: parsedUrl.hostname
|
||||
port: parsedUrl.port ? (if parsedUrl.protocol is 'https:' then 443 else 80)
|
||||
interval: 10 * 1000
|
||||
(connected) ->
|
||||
onChangeCallback?(connected)
|
||||
if connected
|
||||
console.log('Internet Connectivity: OK')
|
||||
blink.pattern.stop()
|
||||
else
|
||||
console.log('Waiting for connectivity...')
|
||||
blink.pattern.start(networkPattern)
|
||||
|
||||
# Callback function to enable/disable tcp pings
|
||||
exports.enableConnectivityCheck = (val) ->
|
||||
enabled = checkTruthy(val) ? true
|
||||
enableCheck(enabled)
|
||||
console.log("Connectivity check enabled: #{enabled}")
|
||||
|
||||
exports.connectivityCheckEnabled = Promise.method ->
|
||||
return enableConnectivityCheck
|
||||
|
||||
exports.getIPAddresses = ->
|
||||
# We get IP addresses but ignore:
|
||||
# - docker and balena bridges (docker0, docker1, balena0, etc)
|
||||
# - legacy rce bridges (rce0, etc)
|
||||
# - tun interfaces like the legacy vpn
|
||||
# - the resin VPN interface (resin-vpn)
|
||||
# - loopback interface (lo)
|
||||
# - the bridge for dnsmasq (resin-dns)
|
||||
# - the docker network for the supervisor API (supervisor0)
|
||||
# - custom docker network bridges (br- + 12 hex characters)
|
||||
_.flatten(_.map(_.omitBy(os.networkInterfaces(), (interfaceFields, interfaceName) ->
|
||||
/^(?:balena|docker|rce|tun)[0-9]+|tun[0-9]+|resin-vpn|lo|resin-dns|supervisor0|balena-redsocks|resin-redsocks|br-[0-9a-f]{12}$/.test(interfaceName))
|
||||
, (validInterfaces) ->
|
||||
_.map(_.pickBy(validInterfaces, family: 'IPv4'), 'address'))
|
||||
)
|
||||
|
||||
exports.startIPAddressUpdate = do ->
|
||||
_lastIPValues = null
|
||||
return (callback, interval) ->
|
||||
getAndReportIP = ->
|
||||
ips = exports.getIPAddresses()
|
||||
if !_.isEmpty(_.xor(ips , _lastIPValues))
|
||||
_lastIPValues = ips
|
||||
callback(ips)
|
||||
setInterval( ->
|
||||
getAndReportIP()
|
||||
, interval)
|
||||
getAndReportIP()
|
156
src/network.ts
Normal file
156
src/network.ts
Normal file
@ -0,0 +1,156 @@
|
||||
import * as Bluebird from 'bluebird';
|
||||
import * as _ from 'lodash';
|
||||
import { fs } from 'mz';
|
||||
import * as networkCheck from 'network-checker';
|
||||
import * as os from 'os';
|
||||
import * as url from 'url';
|
||||
|
||||
import * as constants from './lib/constants';
|
||||
import { EEXIST } from './lib/errors';
|
||||
import { checkTruthy } from './lib/validation';
|
||||
|
||||
import blink = require('./lib/blink');
|
||||
|
||||
const networkPattern = {
|
||||
blinks: 4,
|
||||
pause: 1000,
|
||||
};
|
||||
|
||||
let isConnectivityCheckPaused = false;
|
||||
let isConnectivityCheckEnabled = true;
|
||||
|
||||
function checkHost(
|
||||
opts: networkCheck.ConnectOptions,
|
||||
): boolean | PromiseLike<boolean> {
|
||||
return (
|
||||
!isConnectivityCheckEnabled ||
|
||||
isConnectivityCheckPaused ||
|
||||
networkCheck.checkHost(opts)
|
||||
);
|
||||
}
|
||||
|
||||
function customMonitor(
|
||||
options: networkCheck.ConnectOptions,
|
||||
fn: networkCheck.MonitorChangeFunction,
|
||||
) {
|
||||
return networkCheck.monitor(checkHost, options, fn);
|
||||
}
|
||||
|
||||
export function enableCheck(enable: boolean) {
|
||||
isConnectivityCheckEnabled = enable;
|
||||
}
|
||||
|
||||
async function vpnStatusInotifyCallback(): Promise<void> {
|
||||
try {
|
||||
await fs.lstat(constants.vpnStatusPath);
|
||||
isConnectivityCheckPaused = true;
|
||||
} catch {
|
||||
isConnectivityCheckPaused = false;
|
||||
}
|
||||
}
|
||||
|
||||
export const startConnectivityCheck = _.once(
|
||||
async (
|
||||
apiEndpoint: string,
|
||||
enable: boolean,
|
||||
onChangeCallback?: networkCheck.MonitorChangeFunction,
|
||||
) => {
|
||||
enableConnectivityCheck(enable);
|
||||
if (!apiEndpoint) {
|
||||
console.log('No API endpoint specified, skipping connectivity check');
|
||||
return;
|
||||
}
|
||||
|
||||
await Bluebird.resolve(fs.mkdir(constants.vpnStatusPath))
|
||||
.catch(EEXIST, () => {
|
||||
console.log('VPN status path exists.');
|
||||
})
|
||||
.then(() => {
|
||||
fs.watch(constants.vpnStatusPath, vpnStatusInotifyCallback);
|
||||
});
|
||||
|
||||
if (enable) {
|
||||
vpnStatusInotifyCallback();
|
||||
}
|
||||
|
||||
const parsedUrl = url.parse(apiEndpoint);
|
||||
const port = parseInt(parsedUrl.port!, 10);
|
||||
|
||||
customMonitor(
|
||||
{
|
||||
host: parsedUrl.hostname,
|
||||
port: port || (parsedUrl.protocol === 'https' ? 443 : 80),
|
||||
path: parsedUrl.path || '/',
|
||||
interval: 10 * 1000,
|
||||
},
|
||||
connected => {
|
||||
if (_.isFunction(onChangeCallback)) {
|
||||
onChangeCallback(connected);
|
||||
}
|
||||
if (connected) {
|
||||
console.log('Internet Connectivity: OK');
|
||||
blink.pattern.stop();
|
||||
} else {
|
||||
console.log('Waiting for connectivity...');
|
||||
blink.pattern.start(networkPattern);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
export function enableConnectivityCheck(enable: boolean) {
|
||||
const boolEnable = checkTruthy(enable);
|
||||
enable = boolEnable != null ? boolEnable : true;
|
||||
enableCheck(enable);
|
||||
console.log(`Connectivity check enabled: ${enable}`);
|
||||
}
|
||||
|
||||
export const connectivityCheckEnabled = Bluebird.method(
|
||||
() => isConnectivityCheckEnabled,
|
||||
);
|
||||
|
||||
const IP_REGEX = /^(?:balena|docker|rce|tun)[0-9]+|tun[0-9]+|resin-vpn|lo|resin-dns|supervisor0|balena-redsocks|resin-redsocks|br-[0-9a-f]{12}$/;
|
||||
export function getIPAddresses(): string[] {
|
||||
// We get IP addresses but ignore:
|
||||
// - docker and balena bridges (docker0, docker1, balena0, etc)
|
||||
// - legacy rce bridges (rce0, etc)
|
||||
// - tun interfaces like the legacy vpn
|
||||
// - the resin VPN interface (resin-vpn)
|
||||
// - loopback interface (lo)
|
||||
// - the bridge for dnsmasq (resin-dns)
|
||||
// - the docker network for the supervisor API (supervisor0)
|
||||
// - custom docker network bridges (br- + 12 hex characters)
|
||||
return _(os.networkInterfaces())
|
||||
.omitBy((_interfaceFields, interfaceName) => IP_REGEX.test(interfaceName))
|
||||
.flatMap(validInterfaces => {
|
||||
return _(validInterfaces)
|
||||
.pickBy({ family: 'IPv4' })
|
||||
.map('address')
|
||||
.value();
|
||||
})
|
||||
.value();
|
||||
}
|
||||
|
||||
export function startIPAddressUpdate(): (
|
||||
callback: (ips: string[]) => void,
|
||||
interval: number,
|
||||
) => void {
|
||||
let lastIPValues: string[] | null = null;
|
||||
return (cb, interval) => {
|
||||
const getAndReportIP = () => {
|
||||
const ips = getIPAddresses();
|
||||
if (
|
||||
!_(ips)
|
||||
.xor(lastIPValues)
|
||||
.isEmpty()
|
||||
) {
|
||||
lastIPValues = ips;
|
||||
cb(ips);
|
||||
}
|
||||
};
|
||||
|
||||
setInterval(getAndReportIP, interval);
|
||||
getAndReportIP();
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user