mirror of
https://github.com/balena-os/balena-supervisor.git
synced 2025-03-23 20:45:38 +00:00
Add support for redsocks dnsu2t config
Users may specify dnsu2t config by including a `dns` field in the `proxy` section of PATCH /v1/device/host-config's body: ``` { network: { proxy: { dns: '1.1.1.1:53', } } } ``` If `dns` is a string, ADDRESS and PORT are required and should be in the format `ADDRESS:PORT`. The endpoint with error with code 400 if either ADDRESS or PORT are missing. `dns` may also be a boolean. If true, defaults will be configured. If false, the dns configuration will be removed. If `proxy` is patched to empty, `dns` will be removed regardless of its current or input configs, as `dns` depends on an active redsocks proxy to function. Change-type: minor Signed-off-by: Christina Ying Wang <christina@balena.io>
This commit is contained in:
parent
8bf346a6fd
commit
eaa07e97a9
@ -51,9 +51,15 @@ export function parse(
|
||||
const legacyDecoded = LegacyHostConfiguration.decode(conf);
|
||||
if (isRight(legacyDecoded)) {
|
||||
return legacyDecoded.right;
|
||||
} else {
|
||||
const formattedErrorMessage = [
|
||||
'Could not parse host config input to a valid format:',
|
||||
]
|
||||
.concat(Reporter.report(legacyDecoded))
|
||||
.join('\n');
|
||||
throw new Error(formattedErrorMessage);
|
||||
}
|
||||
}
|
||||
throw new Error('Could not parse host config input to a valid format');
|
||||
}
|
||||
|
||||
function patchProxy(
|
||||
|
@ -82,7 +82,14 @@ export type HostConfiguration = t.TypeOf<typeof HostConfiguration>;
|
||||
export const LegacyHostConfiguration = t.type({
|
||||
network: t.exact(
|
||||
t.partial({
|
||||
proxy: t.record(t.string, t.any),
|
||||
proxy: t.intersection([
|
||||
t.record(t.string, t.any),
|
||||
// Dns was added after the initial API endpoint was introduced,
|
||||
// so we can be more strict with its type.
|
||||
t.partial({
|
||||
dns: DnsInput,
|
||||
}),
|
||||
]),
|
||||
hostname: t.string,
|
||||
}),
|
||||
),
|
||||
|
@ -15,3 +15,10 @@ redsocks {
|
||||
local_ip = 127.0.0.1;
|
||||
local_port = 12345;
|
||||
}
|
||||
|
||||
dnsu2t {
|
||||
remote_ip = 1.2.3.4;
|
||||
remote_port = 54;
|
||||
local_ip = 127.0.0.1;
|
||||
local_port = 53;
|
||||
}
|
||||
|
@ -31,6 +31,18 @@ describe('host-config', () => {
|
||||
const noProxy = path.join(proxyBase, 'no_proxy');
|
||||
const hostname = pathOnRoot('/etc/hostname');
|
||||
const appLockDir = pathOnRoot(updateLock.lockPath(APP_ID));
|
||||
const defaultConf = {
|
||||
proxy: {
|
||||
ip: 'example.org',
|
||||
port: 1080,
|
||||
type: 'socks5',
|
||||
login: 'foo',
|
||||
password: 'bar',
|
||||
dns: '1.2.3.4:54',
|
||||
noProxy: ['152.10.30.4', '253.1.1.0/16'],
|
||||
},
|
||||
hostname: 'deadbeef',
|
||||
};
|
||||
|
||||
before(async () => {
|
||||
await config.initialized();
|
||||
@ -56,7 +68,7 @@ describe('host-config', () => {
|
||||
'test/data/mnt/boot/system-proxy/redsocks.conf',
|
||||
),
|
||||
[noProxy]: testfs.from('test/data/mnt/boot/system-proxy/no_proxy'),
|
||||
[hostname]: 'deadbeef',
|
||||
[hostname]: defaultConf.hostname,
|
||||
// Create a lock. This won't prevent host config patch unless
|
||||
// there are current apps present, in which case an updates locked
|
||||
// error will be thrown.
|
||||
@ -70,6 +82,7 @@ describe('host-config', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await tFs.enable();
|
||||
await config.set({ hostname: defaultConf.hostname });
|
||||
// Stub external dependencies
|
||||
stub(dbus, 'servicePartOf').resolves([]);
|
||||
stub(dbus, 'restartService').resolves();
|
||||
@ -85,17 +98,7 @@ describe('host-config', () => {
|
||||
|
||||
it('reads proxy configs and hostname', async () => {
|
||||
const { network } = await get();
|
||||
expect(network).to.have.property('hostname', 'deadbeef');
|
||||
expect(network).to.have.property('proxy');
|
||||
expect(network.proxy).to.have.property('ip', 'example.org');
|
||||
expect(network.proxy).to.have.property('port', 1080);
|
||||
expect(network.proxy).to.have.property('type', 'socks5');
|
||||
expect(network.proxy).to.have.property('login', 'foo');
|
||||
expect(network.proxy).to.have.property('password', 'bar');
|
||||
expect(network.proxy).to.have.deep.property('noProxy', [
|
||||
'152.10.30.4',
|
||||
'253.1.1.0/16',
|
||||
]);
|
||||
expect(network).to.deep.equal(defaultConf);
|
||||
});
|
||||
|
||||
it('prevents patch if update locks are present', async () => {
|
||||
@ -117,7 +120,13 @@ describe('host-config', () => {
|
||||
} catch (e: unknown) {
|
||||
expect.fail(`Expected hostConfig.patch to not throw, but got ${e}`);
|
||||
}
|
||||
expect(await get()).to.deep.equal({ network: { hostname: 'deadbeef' } });
|
||||
// Proxy should have been deleted as proxy was patched to empty,
|
||||
// hostname should remain unchanged
|
||||
const { network } = await get();
|
||||
expect(network).to.deep.equal({
|
||||
hostname: defaultConf.hostname,
|
||||
});
|
||||
expect(await config.get('hostname')).to.equal(defaultConf.hostname);
|
||||
});
|
||||
|
||||
it('patches hostname regardless of update locks', async () => {
|
||||
@ -125,44 +134,43 @@ describe('host-config', () => {
|
||||
|
||||
try {
|
||||
await patch({ network: { hostname: 'test' } });
|
||||
// /etc/hostname isn't changed until the balena-hostname service
|
||||
// is restarted by the OS.
|
||||
expect(await config.get('hostname')).to.equal('test');
|
||||
} catch (e: unknown) {
|
||||
expect.fail(`Expected hostConfig.patch to not throw, but got ${e}`);
|
||||
}
|
||||
});
|
||||
|
||||
it('patches hostname', async () => {
|
||||
it('patches hostname without modifying other fields', async () => {
|
||||
await patch({ network: { hostname: 'test' } });
|
||||
// /etc/hostname isn't changed until the balena-hostname service
|
||||
// is restarted by the OS.
|
||||
expect(await config.get('hostname')).to.equal('test');
|
||||
// Proxy should remain unchanged as patch didn't include it
|
||||
const { network } = await get();
|
||||
expect(network.proxy).to.deep.equal(defaultConf.proxy);
|
||||
});
|
||||
|
||||
it('patches proxy', async () => {
|
||||
const newConf = {
|
||||
network: {
|
||||
proxy: {
|
||||
ip: 'example2.org',
|
||||
port: 1090,
|
||||
type: 'http-relay',
|
||||
login: 'bar',
|
||||
password: 'foo',
|
||||
noProxy: ['balena.io', '222.22.2.2'],
|
||||
},
|
||||
},
|
||||
it('patches proxy without modifying other fields', async () => {
|
||||
const newProxy = {
|
||||
ip: 'example2.org',
|
||||
port: 1090,
|
||||
type: 'http-relay',
|
||||
login: 'bar',
|
||||
password: 'foo',
|
||||
dns: '2.2.2.2:52',
|
||||
noProxy: ['balena.io', '222.22.2.2'],
|
||||
};
|
||||
await patch(newConf);
|
||||
await patch({ network: { proxy: newProxy } });
|
||||
const { network } = await get();
|
||||
expect(network).to.have.property('proxy');
|
||||
expect(network.proxy).to.have.property('ip', 'example2.org');
|
||||
expect(network.proxy).to.have.property('port', 1090);
|
||||
expect(network.proxy).to.have.property('type', 'http-relay');
|
||||
expect(network.proxy).to.have.property('login', 'bar');
|
||||
expect(network.proxy).to.have.property('password', 'foo');
|
||||
expect(network.proxy).to.have.deep.property('noProxy', [
|
||||
'balena.io',
|
||||
'222.22.2.2',
|
||||
]);
|
||||
expect(network).to.deep.equal({
|
||||
proxy: {
|
||||
...defaultConf.proxy,
|
||||
...newProxy,
|
||||
},
|
||||
hostname: defaultConf.hostname,
|
||||
});
|
||||
|
||||
await expect(fs.readFile(redsocksConf, 'utf-8')).to.eventually.equal(
|
||||
stripIndent`
|
||||
@ -182,6 +190,13 @@ describe('host-config', () => {
|
||||
password = "foo";
|
||||
local_ip = 127.0.0.1;
|
||||
local_port = 12345;
|
||||
}
|
||||
|
||||
dnsu2t {
|
||||
remote_ip = 2.2.2.2;
|
||||
remote_port = 52;
|
||||
local_ip = 127.0.0.1;
|
||||
local_port = 53;
|
||||
}` + '\n',
|
||||
);
|
||||
|
||||
@ -192,25 +207,23 @@ describe('host-config', () => {
|
||||
});
|
||||
|
||||
it('patches proxy fields specified while leaving unspecified fields unchanged', async () => {
|
||||
const newProxyFields = {
|
||||
ip: 'example2.org',
|
||||
port: 1090,
|
||||
};
|
||||
await patch({
|
||||
network: {
|
||||
proxy: {
|
||||
ip: 'example2.org',
|
||||
port: 1090,
|
||||
},
|
||||
proxy: newProxyFields,
|
||||
},
|
||||
});
|
||||
const { network } = await get();
|
||||
expect(network).to.have.property('proxy');
|
||||
expect(network.proxy).to.have.property('ip', 'example2.org');
|
||||
expect(network.proxy).to.have.property('port', 1090);
|
||||
expect(network.proxy).to.have.property('type', 'socks5');
|
||||
expect(network.proxy).to.have.property('login', 'foo');
|
||||
expect(network.proxy).to.have.property('password', 'bar');
|
||||
expect(network.proxy).to.have.deep.property('noProxy', [
|
||||
'152.10.30.4',
|
||||
'253.1.1.0/16',
|
||||
]);
|
||||
expect(network).to.deep.equal({
|
||||
proxy: {
|
||||
...defaultConf.proxy,
|
||||
...newProxyFields,
|
||||
},
|
||||
hostname: defaultConf.hostname,
|
||||
});
|
||||
|
||||
await expect(fs.readFile(redsocksConf, 'utf-8')).to.eventually.equal(
|
||||
stripIndent`
|
||||
@ -230,6 +243,13 @@ describe('host-config', () => {
|
||||
password = "bar";
|
||||
local_ip = 127.0.0.1;
|
||||
local_port = 12345;
|
||||
}
|
||||
|
||||
dnsu2t {
|
||||
remote_ip = 1.2.3.4;
|
||||
remote_port = 54;
|
||||
local_ip = 127.0.0.1;
|
||||
local_port = 53;
|
||||
}` + '\n',
|
||||
);
|
||||
|
||||
@ -244,22 +264,15 @@ describe('host-config', () => {
|
||||
it('patches proxy to empty if input is empty', async () => {
|
||||
await patch({ network: { proxy: {} } });
|
||||
const { network } = await get();
|
||||
expect(network).to.not.have.property('proxy');
|
||||
expect(network).to.deep.equal({
|
||||
hostname: defaultConf.hostname,
|
||||
});
|
||||
});
|
||||
|
||||
it('keeps current proxy if input is invalid', async () => {
|
||||
await patch({ network: { proxy: null as any } });
|
||||
const { network } = await get();
|
||||
expect(network).to.have.property('proxy');
|
||||
expect(network.proxy).to.have.property('ip', 'example.org');
|
||||
expect(network.proxy).to.have.property('port', 1080);
|
||||
expect(network.proxy).to.have.property('type', 'socks5');
|
||||
expect(network.proxy).to.have.property('login', 'foo');
|
||||
expect(network.proxy).to.have.property('password', 'bar');
|
||||
expect(network.proxy).to.have.deep.property('noProxy', [
|
||||
'152.10.30.4',
|
||||
'253.1.1.0/16',
|
||||
]);
|
||||
expect(network).to.deep.equal(defaultConf);
|
||||
});
|
||||
|
||||
it('ignores unsupported fields when patching proxy', async () => {
|
||||
@ -352,43 +365,41 @@ describe('host-config', () => {
|
||||
|
||||
it('skips restarting proxy services when part of redsocks-conf.target', async () => {
|
||||
(dbus.servicePartOf as SinonStub).resolves(['redsocks-conf.target']);
|
||||
const newProxy = {
|
||||
ip: 'example2.org',
|
||||
port: 1090,
|
||||
type: 'http-relay',
|
||||
login: 'bar',
|
||||
password: 'foo',
|
||||
dns: '4.3.2.1:52',
|
||||
noProxy: ['balena.io', '222.22.2.2'],
|
||||
};
|
||||
await patch({
|
||||
network: {
|
||||
proxy: {
|
||||
ip: 'example2.org',
|
||||
port: 1090,
|
||||
type: 'http-relay',
|
||||
login: 'bar',
|
||||
password: 'foo',
|
||||
noProxy: ['balena.io', '222.22.2.2'],
|
||||
},
|
||||
proxy: newProxy,
|
||||
},
|
||||
});
|
||||
expect(dbus.restartService as SinonStub).to.not.have.been.called;
|
||||
const { network } = await get();
|
||||
expect(network).to.have.property('proxy');
|
||||
expect(network.proxy).to.have.property('ip', 'example2.org');
|
||||
expect(network.proxy).to.have.property('port', 1090);
|
||||
expect(network.proxy).to.have.property('type', 'http-relay');
|
||||
expect(network.proxy).to.have.property('login', 'bar');
|
||||
expect(network.proxy).to.have.property('password', 'foo');
|
||||
expect(network.proxy).to.have.deep.property('noProxy', [
|
||||
'balena.io',
|
||||
'222.22.2.2',
|
||||
]);
|
||||
expect(network.proxy).to.deep.equal({
|
||||
...defaultConf.proxy,
|
||||
...newProxy,
|
||||
});
|
||||
});
|
||||
|
||||
it('patches redsocks.conf to be empty if prompted', async () => {
|
||||
it('patches redsocks.conf to be empty', async () => {
|
||||
await patch({ network: { proxy: {} } });
|
||||
const { network } = await get();
|
||||
expect(network).to.not.have.property('proxy');
|
||||
expect(network).to.deep.equal({
|
||||
hostname: defaultConf.hostname,
|
||||
});
|
||||
expect(await fs.readdir(proxyBase)).to.not.have.members([
|
||||
'redsocks.conf',
|
||||
'no_proxy',
|
||||
]);
|
||||
});
|
||||
|
||||
it('patches no_proxy to be empty if prompted', async () => {
|
||||
it('patches no_proxy to be empty', async () => {
|
||||
await patch({
|
||||
network: {
|
||||
proxy: {
|
||||
@ -398,13 +409,41 @@ describe('host-config', () => {
|
||||
});
|
||||
const { network } = await get();
|
||||
// If only noProxy is patched, redsocks.conf should remain unchanged
|
||||
expect(network).to.have.property('proxy').that.deep.includes({
|
||||
expect(network).to.have.property('proxy').that.deep.equals({
|
||||
ip: 'example.org',
|
||||
port: 1080,
|
||||
type: 'socks5',
|
||||
login: 'foo',
|
||||
password: 'bar',
|
||||
dns: '1.2.3.4:54',
|
||||
});
|
||||
await expect(fs.readFile(redsocksConf, 'utf-8')).to.eventually.equal(
|
||||
stripIndent`
|
||||
base {
|
||||
log_debug = off;
|
||||
log_info = on;
|
||||
log = stderr;
|
||||
daemon = off;
|
||||
redirector = iptables;
|
||||
}
|
||||
|
||||
redsocks {
|
||||
ip = example.org;
|
||||
port = 1080;
|
||||
type = socks5;
|
||||
login = "foo";
|
||||
password = "bar";
|
||||
local_ip = 127.0.0.1;
|
||||
local_port = 12345;
|
||||
}
|
||||
|
||||
dnsu2t {
|
||||
remote_ip = 1.2.3.4;
|
||||
remote_port = 54;
|
||||
local_ip = 127.0.0.1;
|
||||
local_port = 53;
|
||||
}` + '\n',
|
||||
);
|
||||
expect(network.proxy).to.not.have.property('noProxy');
|
||||
expect(await fs.readdir(proxyBase)).to.not.have.members(['no_proxy']);
|
||||
});
|
||||
@ -415,5 +454,217 @@ describe('host-config', () => {
|
||||
const { network: newNetwork } = await get();
|
||||
expect(network.hostname).to.equal(newNetwork.hostname);
|
||||
expect(network.proxy).to.deep.equal(newNetwork.proxy);
|
||||
await expect(fs.readFile(redsocksConf, 'utf-8')).to.eventually.equal(
|
||||
stripIndent`
|
||||
base {
|
||||
log_debug = off;
|
||||
log_info = on;
|
||||
log = stderr;
|
||||
daemon = off;
|
||||
redirector = iptables;
|
||||
}
|
||||
|
||||
redsocks {
|
||||
ip = example.org;
|
||||
port = 1080;
|
||||
type = socks5;
|
||||
login = "foo";
|
||||
password = "bar";
|
||||
local_ip = 127.0.0.1;
|
||||
local_port = 12345;
|
||||
}
|
||||
|
||||
dnsu2t {
|
||||
remote_ip = 1.2.3.4;
|
||||
remote_port = 54;
|
||||
local_ip = 127.0.0.1;
|
||||
local_port = 53;
|
||||
}` + '\n',
|
||||
);
|
||||
});
|
||||
|
||||
it('patches dnsu2t config to default without modifying other fields', async () => {
|
||||
await patch({
|
||||
network: {
|
||||
proxy: {
|
||||
dns: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
const { network } = await get();
|
||||
expect(network.proxy).to.deep.equal({
|
||||
...defaultConf.proxy,
|
||||
dns: '8.8.8.8:53',
|
||||
});
|
||||
expect(await config.get('hostname')).to.equal(defaultConf.hostname);
|
||||
await expect(fs.readFile(redsocksConf, 'utf-8')).to.eventually.equal(
|
||||
stripIndent`
|
||||
base {
|
||||
log_debug = off;
|
||||
log_info = on;
|
||||
log = stderr;
|
||||
daemon = off;
|
||||
redirector = iptables;
|
||||
}
|
||||
|
||||
redsocks {
|
||||
ip = example.org;
|
||||
port = 1080;
|
||||
type = socks5;
|
||||
login = "foo";
|
||||
password = "bar";
|
||||
local_ip = 127.0.0.1;
|
||||
local_port = 12345;
|
||||
}
|
||||
|
||||
dnsu2t {
|
||||
remote_ip = 8.8.8.8;
|
||||
remote_port = 53;
|
||||
local_ip = 127.0.0.1;
|
||||
local_port = 53;
|
||||
}` + '\n',
|
||||
);
|
||||
});
|
||||
|
||||
it('patches dnsu2t config to string value', async () => {
|
||||
await patch({
|
||||
network: {
|
||||
proxy: {
|
||||
dns: '4.3.2.1:51',
|
||||
},
|
||||
},
|
||||
});
|
||||
const { network } = await get();
|
||||
expect(network.proxy).to.deep.equal({
|
||||
...defaultConf.proxy,
|
||||
dns: '4.3.2.1:51',
|
||||
});
|
||||
await expect(fs.readFile(redsocksConf, 'utf-8')).to.eventually.equal(
|
||||
stripIndent`
|
||||
base {
|
||||
log_debug = off;
|
||||
log_info = on;
|
||||
log = stderr;
|
||||
daemon = off;
|
||||
redirector = iptables;
|
||||
}
|
||||
|
||||
redsocks {
|
||||
ip = example.org;
|
||||
port = 1080;
|
||||
type = socks5;
|
||||
login = "foo";
|
||||
password = "bar";
|
||||
local_ip = 127.0.0.1;
|
||||
local_port = 12345;
|
||||
}
|
||||
|
||||
dnsu2t {
|
||||
remote_ip = 4.3.2.1;
|
||||
remote_port = 51;
|
||||
local_ip = 127.0.0.1;
|
||||
local_port = 53;
|
||||
}` + '\n',
|
||||
);
|
||||
});
|
||||
|
||||
it('patches dnsu2t config to empty', async () => {
|
||||
await patch({
|
||||
network: {
|
||||
proxy: {
|
||||
dns: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
const { network } = await get();
|
||||
const { dns, ...proxyWithoutDns } = defaultConf.proxy;
|
||||
expect(network.proxy).to.deep.equal(proxyWithoutDns);
|
||||
});
|
||||
|
||||
it('adds dnsu2t config to config without dnsu2t when provided valid input', async () => {
|
||||
// Delete dns config to set up test
|
||||
await patch({
|
||||
network: {
|
||||
proxy: {
|
||||
dns: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
const { network } = await get();
|
||||
expect(network.proxy).to.not.have.property('dns');
|
||||
expect(await fs.readFile(redsocksConf, 'utf-8')).to.not.contain('dnsu2t');
|
||||
|
||||
// Add valid dns config
|
||||
await patch({
|
||||
network: {
|
||||
proxy: {
|
||||
dns: '5.5.5.5:55',
|
||||
},
|
||||
},
|
||||
});
|
||||
const { network: n2 } = await get();
|
||||
expect(n2.proxy).to.deep.equal({
|
||||
...defaultConf.proxy,
|
||||
dns: '5.5.5.5:55',
|
||||
});
|
||||
await expect(fs.readFile(redsocksConf, 'utf-8')).to.eventually.equal(
|
||||
stripIndent`
|
||||
base {
|
||||
log_debug = off;
|
||||
log_info = on;
|
||||
log = stderr;
|
||||
daemon = off;
|
||||
redirector = iptables;
|
||||
}
|
||||
|
||||
redsocks {
|
||||
ip = example.org;
|
||||
port = 1080;
|
||||
type = socks5;
|
||||
login = "foo";
|
||||
password = "bar";
|
||||
local_ip = 127.0.0.1;
|
||||
local_port = 12345;
|
||||
}
|
||||
|
||||
dnsu2t {
|
||||
remote_ip = 5.5.5.5;
|
||||
remote_port = 55;
|
||||
local_ip = 127.0.0.1;
|
||||
local_port = 53;
|
||||
}` + '\n',
|
||||
);
|
||||
});
|
||||
|
||||
it("does not add dnsu2t config when redsocks proxy isn't configured", async () => {
|
||||
// Delete redsocks config to set up test
|
||||
await patch({
|
||||
network: {
|
||||
proxy: {},
|
||||
},
|
||||
});
|
||||
const { network } = await get();
|
||||
expect(network).to.not.have.property('proxy');
|
||||
expect(await fs.readdir(proxyBase)).to.not.have.members([
|
||||
'redsocks.conf',
|
||||
'no_proxy',
|
||||
]);
|
||||
|
||||
// Add valid dns config
|
||||
await patch({
|
||||
network: {
|
||||
proxy: {
|
||||
dns: '1.2.3.4:54',
|
||||
},
|
||||
},
|
||||
});
|
||||
const { network: n2 } = await get();
|
||||
expect(n2).to.deep.equal({
|
||||
hostname: defaultConf.hostname,
|
||||
});
|
||||
expect(await fs.readdir(proxyBase)).to.not.have.members([
|
||||
'redsocks.conf',
|
||||
'no_proxy',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
@ -4,8 +4,12 @@ import type { SinonStub } from 'sinon';
|
||||
|
||||
import * as hostConfig from '~/src/host-config';
|
||||
import { RedsocksConf } from '~/src/host-config/proxy';
|
||||
import { DnsInput } from '~/src/host-config/types';
|
||||
import type { RedsocksConfig, ProxyConfig } from '~/src/host-config/types';
|
||||
import {
|
||||
type RedsocksConfig,
|
||||
type ProxyConfig,
|
||||
LegacyHostConfiguration,
|
||||
DnsInput,
|
||||
} from '~/src/host-config/types';
|
||||
import log from '~/lib/supervisor-console';
|
||||
|
||||
describe('RedsocksConf', () => {
|
||||
@ -838,7 +842,11 @@ describe('src/host-config', () => {
|
||||
|
||||
it('throws error for HostConfiguration without network key', () => {
|
||||
expect(() => hostConfig.parse({})).to.throw(
|
||||
'Could not parse host config input to a valid format',
|
||||
'Could not parse host config input to a valid format:\n' +
|
||||
'Expecting Partial<{| ' +
|
||||
'proxy: ({ [K in string]: any } & Partial<{ dns: (AddressString | boolean) }>), ' +
|
||||
'hostname: string ' +
|
||||
'|}> at network but instead got: undefined',
|
||||
);
|
||||
});
|
||||
|
||||
@ -847,7 +855,11 @@ describe('src/host-config', () => {
|
||||
network: 123,
|
||||
};
|
||||
expect(() => hostConfig.parse(conf)).to.throw(
|
||||
'Could not parse host config input to a valid format',
|
||||
'Could not parse host config input to a valid format:\n' +
|
||||
'Expecting Partial<{| ' +
|
||||
'proxy: ({ [K in string]: any } & Partial<{ dns: (AddressString | boolean) }>), ' +
|
||||
'hostname: string ' +
|
||||
'|}> at network but instead got: 123',
|
||||
);
|
||||
});
|
||||
|
||||
@ -858,10 +870,63 @@ describe('src/host-config', () => {
|
||||
},
|
||||
};
|
||||
expect(() => hostConfig.parse(conf)).to.throw(
|
||||
'Could not parse host config input to a valid format',
|
||||
'Could not parse host config input to a valid format:\n' +
|
||||
'Expecting string at network.hostname but instead got: 123',
|
||||
);
|
||||
});
|
||||
|
||||
it('throws error for HostConfiguration with invalid dns', () => {
|
||||
const invalids = [
|
||||
123, // wrong type
|
||||
'invalid-because-no-colon',
|
||||
':', // only colon
|
||||
':53', // missing address
|
||||
'1.1.1.1', // missing port
|
||||
'example.com:not-a-port', // wrong port type
|
||||
];
|
||||
for (const dns of invalids) {
|
||||
const conf = {
|
||||
network: {
|
||||
proxy: {
|
||||
type: 'http-connect',
|
||||
ip: 'test.balena.io',
|
||||
port: 1081,
|
||||
login: '"foo"',
|
||||
password: '"bar"',
|
||||
dns,
|
||||
},
|
||||
},
|
||||
};
|
||||
const formattedDns = typeof dns === 'string' ? `"${dns}"` : dns;
|
||||
expect(() => hostConfig.parse(conf)).to.throw(
|
||||
'Could not parse host config input to a valid format:\n' +
|
||||
'Expecting one of:\n' +
|
||||
' AddressString\n' +
|
||||
' boolean\n' +
|
||||
`at network.proxy.1.dns but instead got: ${formattedDns}`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('parses valid dns input', () => {
|
||||
const valids = ['balena.io:53', '1.1.1.1:5', 'balena.io:65535'];
|
||||
for (const dns of valids) {
|
||||
const conf = {
|
||||
network: {
|
||||
proxy: {
|
||||
type: 'http-connect',
|
||||
ip: 'test.balena.io',
|
||||
port: 1081,
|
||||
login: '"foo"',
|
||||
password: '"bar"',
|
||||
dns,
|
||||
},
|
||||
},
|
||||
};
|
||||
expect(hostConfig.parse(conf)).to.deep.equal(conf);
|
||||
}
|
||||
});
|
||||
|
||||
it("strips additional inputs from HostConfiguration while not erroring if some optional inputs aren't present", () => {
|
||||
const conf = {
|
||||
network: {
|
||||
@ -919,5 +984,86 @@ describe('src/host-config', () => {
|
||||
expect(DnsInput.is('example.com')).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('LegacyHostConfiguration', () => {
|
||||
it('maintains strict dns typing for LegacyHostConfiguration', () => {
|
||||
expect(
|
||||
LegacyHostConfiguration.is({
|
||||
network: {
|
||||
proxy: {
|
||||
legacy: 'field',
|
||||
dns: 123,
|
||||
},
|
||||
},
|
||||
}),
|
||||
).to.be.false;
|
||||
|
||||
expect(
|
||||
LegacyHostConfiguration.is({
|
||||
network: {
|
||||
proxy: {
|
||||
legacy: 'field',
|
||||
dns: '1.1.1.1:53',
|
||||
},
|
||||
},
|
||||
}),
|
||||
).to.be.true;
|
||||
|
||||
expect(
|
||||
LegacyHostConfiguration.is({
|
||||
network: {
|
||||
proxy: {
|
||||
legacy: 'field',
|
||||
dns: true,
|
||||
},
|
||||
},
|
||||
}),
|
||||
).to.be.true;
|
||||
|
||||
expect(
|
||||
LegacyHostConfiguration.is({
|
||||
network: {
|
||||
proxy: {
|
||||
legacy: 'field',
|
||||
dns: false,
|
||||
},
|
||||
},
|
||||
}),
|
||||
).to.be.true;
|
||||
|
||||
expect(
|
||||
LegacyHostConfiguration.is({
|
||||
network: {
|
||||
proxy: {
|
||||
legacy: 'field',
|
||||
dns: '',
|
||||
},
|
||||
},
|
||||
}),
|
||||
).to.be.false;
|
||||
|
||||
expect(
|
||||
LegacyHostConfiguration.is({
|
||||
network: {
|
||||
proxy: {
|
||||
legacy: 'field',
|
||||
dns: ':53',
|
||||
},
|
||||
},
|
||||
}),
|
||||
).to.be.false;
|
||||
|
||||
expect(
|
||||
LegacyHostConfiguration.is({
|
||||
network: {
|
||||
proxy: {
|
||||
legacy: 'field',
|
||||
dns: '1.1.1.1',
|
||||
},
|
||||
},
|
||||
}),
|
||||
).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user