Remove unnecessary exports from host-config

This limits the host-config interface to necessary methods
only

Signed-off-by: Christina Ying Wang <christina@balena.io>
This commit is contained in:
Christina Ying Wang 2024-07-01 19:07:00 -07:00
parent 53f5641ef1
commit f99ccb58c6
5 changed files with 14 additions and 157 deletions

View File

@ -10,7 +10,7 @@ import * as hostConfig from '../host-config';
import type { import type {
HostConfiguration, HostConfiguration,
LegacyHostConfiguration, LegacyHostConfiguration,
} from '../host-config'; } from '../host-config/types';
import * as applicationManager from '../compose/application-manager'; import * as applicationManager from '../compose/application-manager';
import type { CompositionStepAction } from '../compose/composition-steps'; import type { CompositionStepAction } from '../compose/composition-steps';
import { generateStep } from '../compose/composition-steps'; import { generateStep } from '../compose/composition-steps';

View File

@ -12,17 +12,14 @@ import { pathOnRoot } from '../lib/host-utils';
import log from '../lib/supervisor-console'; import log from '../lib/supervisor-console';
import * as updateLock from '../lib/update-lock'; import * as updateLock from '../lib/update-lock';
export * from './proxy';
export * from './types';
const hostnamePath = pathOnRoot('/etc/hostname'); const hostnamePath = pathOnRoot('/etc/hostname');
export async function readHostname() { async function readHostname() {
const hostnameData = await fs.readFile(hostnamePath, 'utf-8'); const hostnameData = await fs.readFile(hostnamePath, 'utf-8');
return hostnameData.trim(); return hostnameData.trim();
} }
export async function setHostname(val: string) { async function setHostname(val: string) {
let hostname = val; let hostname = val;
// If hostname is an empty string, return first 7 digits of device uuid // If hostname is an empty string, return first 7 digits of device uuid
if (!val) { if (!val) {
@ -59,7 +56,7 @@ export function parse(
throw new Error('Could not parse host config input to a valid format'); throw new Error('Could not parse host config input to a valid format');
} }
export function patchProxy( function patchProxy(
currentConf: RedsocksConfig, currentConf: RedsocksConfig,
inputConf: Partial<{ inputConf: Partial<{
redsocks: Partial<ProxyConfig>; redsocks: Partial<ProxyConfig>;
@ -123,10 +120,12 @@ export async function patch(
} }
export async function get(): Promise<HostConfiguration> { export async function get(): Promise<HostConfiguration> {
const proxy = await readProxy();
return { return {
network: { network: {
hostname: await readHostname(), hostname: await readHostname(),
proxy: await readProxy(), // Only return proxy if readProxy is not undefined
...(proxy && { proxy }),
}, },
}; };
} }

View File

@ -219,7 +219,7 @@ async function restartProxyServices() {
} }
} }
export async function readNoProxy(): Promise<string[]> { async function readNoProxy(): Promise<string[]> {
try { try {
const noProxy = await readFromBoot(noProxyPath, 'utf-8') const noProxy = await readFromBoot(noProxyPath, 'utf-8')
// Prevent empty newline from being reported as a noProxy address // Prevent empty newline from being reported as a noProxy address
@ -238,7 +238,7 @@ export async function readNoProxy(): Promise<string[]> {
} }
} }
export async function setNoProxy(list: Nullable<string[]>) { async function setNoProxy(list: Nullable<string[]>) {
const current = await readNoProxy(); const current = await readNoProxy();
if (!list || !Array.isArray(list) || !list.length) { if (!list || !Array.isArray(list) || !list.length) {
await unlinkAll(noProxyPath); await unlinkAll(noProxyPath);

View File

@ -7,7 +7,6 @@ import { stub } from 'sinon';
import * as fs from 'fs/promises'; import * as fs from 'fs/promises';
import { get, patch } from '~/src/host-config'; import { get, patch } from '~/src/host-config';
import * as hostConfig from '~/src/host-config';
import * as config from '~/src/config'; import * as config from '~/src/config';
import * as applicationManager from '~/src/compose/application-manager'; import * as applicationManager from '~/src/compose/application-manager';
import type { InstancedAppState } from '~/src/compose/types'; import type { InstancedAppState } from '~/src/compose/types';
@ -83,96 +82,6 @@ describe('host-config', () => {
(applicationManager.getCurrentApps as SinonStub).restore(); (applicationManager.getCurrentApps as SinonStub).restore();
}); });
describe('hostname', () => {
it('reads hostname', async () => {
expect(await hostConfig.readHostname()).to.equal('deadbeef');
});
it('sets hostname', async () => {
await hostConfig.setHostname('test');
expect(await config.get('hostname')).to.equal('test');
});
it('sets hostname to first 7 characters of UUID if empty', async () => {
await config.set({ uuid: '1234567' });
await hostConfig.setHostname('');
expect(await config.get('hostname')).to.equal('1234567');
});
});
describe('noProxy', () => {
it('reads IPs to exclude from proxy', async () => {
expect(await hostConfig.readNoProxy()).to.deep.equal([
'152.10.30.4',
'253.1.1.0/16',
]);
});
it('sets IPs to exclude from proxy', async () => {
await hostConfig.setNoProxy(['balena.io', '1.1.1.1']);
expect(await fs.readFile(noProxy, 'utf-8')).to.equal(
'balena.io\n1.1.1.1',
);
await hostConfig.setNoProxy(['balena.io', '2.2.2.2']);
expect(await fs.readFile(noProxy, 'utf-8')).to.equal(
'balena.io\n2.2.2.2',
);
});
it('returns whether noProxy was changed', async () => {
// Set initial no_proxy as empty
await hostConfig.setNoProxy([]);
// Change no_proxy
expect(await hostConfig.setNoProxy(['balena.io', '1.1.1.1'])).to.be.true;
expect(await hostConfig.readNoProxy())
.to.deep.include.members(['balena.io', '1.1.1.1'])
.and.have.lengthOf(2);
// Change no_proxy to same value
expect(await hostConfig.setNoProxy(['1.1.1.1', 'balena.io'])).to.be.false;
expect(await hostConfig.readNoProxy())
.to.deep.include.members(['balena.io', '1.1.1.1'])
.and.have.lengthOf(2);
// Remove a value
expect(await hostConfig.setNoProxy(['1.1.1.1'])).to.be.true;
expect(await hostConfig.readNoProxy())
.to.deep.include.members(['1.1.1.1'])
.and.have.lengthOf(1);
// Add a value
expect(await hostConfig.setNoProxy(['2.2.2.2', '1.1.1.1'])).to.be.true;
expect(await hostConfig.readNoProxy())
.to.deep.include.members(['2.2.2.2', '1.1.1.1'])
.and.have.lengthOf(2);
// Remove no_proxy
expect(await hostConfig.setNoProxy([])).to.be.true;
expect(await hostConfig.readNoProxy()).to.deep.equal([]);
});
it('removes no_proxy file if empty or invalid', async () => {
// Set initial no_proxy
await hostConfig.setNoProxy(['2.2.2.2']);
expect(await hostConfig.readNoProxy()).to.deep.equal(['2.2.2.2']);
// Set to empty array
await hostConfig.setNoProxy([]);
expect(await hostConfig.readNoProxy()).to.deep.equal([]);
expect(await fs.readdir(proxyBase)).to.not.have.members(['no_proxy']);
// Reset initial no_proxy
await hostConfig.setNoProxy(['2.2.2.2']);
expect(await hostConfig.readNoProxy()).to.deep.equal(['2.2.2.2']);
// Set to invalid value
await hostConfig.setNoProxy(null as any);
expect(await hostConfig.readNoProxy()).to.deep.equal([]);
expect(await fs.readdir(proxyBase)).to.not.have.members(['no_proxy']);
});
});
it('reads proxy configs and hostname', async () => { it('reads proxy configs and hostname', async () => {
const { network } = await get(); const { network } = await get();
expect(network).to.have.property('hostname', 'deadbeef'); expect(network).to.have.property('hostname', 'deadbeef');
@ -204,10 +113,10 @@ describe('host-config', () => {
try { try {
await patch({ network: { proxy: {} } }, true); await patch({ network: { proxy: {} } }, true);
expect(await hostConfig.readProxy()).to.be.undefined;
} catch (e: unknown) { } catch (e: unknown) {
expect.fail(`Expected hostConfig.patch to not throw, but got ${e}`); expect.fail(`Expected hostConfig.patch to not throw, but got ${e}`);
} }
expect(await get()).to.deep.equal({ network: { hostname: 'deadbeef' } });
}); });
it('patches hostname regardless of update locks', async () => { it('patches hostname regardless of update locks', async () => {
@ -279,7 +188,7 @@ describe('host-config', () => {
it('patches proxy to empty if input is empty', async () => { it('patches proxy to empty if input is empty', async () => {
await patch({ network: { proxy: {} } }); await patch({ network: { proxy: {} } });
const { network } = await get(); const { network } = await get();
expect(network).to.have.property('proxy', undefined); expect(network).to.not.have.property('proxy');
}); });
it('keeps current proxy if input is invalid', async () => { it('keeps current proxy if input is invalid', async () => {
@ -341,7 +250,7 @@ describe('host-config', () => {
it('patches redsocks.conf to be empty if prompted', async () => { it('patches redsocks.conf to be empty if prompted', async () => {
await patch({ network: { proxy: {} } }); await patch({ network: { proxy: {} } });
const { network } = await get(); const { network } = await get();
expect(network).to.have.property('proxy', undefined); expect(network).to.not.have.property('proxy');
expect(await fs.readdir(proxyBase)).to.not.have.members([ expect(await fs.readdir(proxyBase)).to.not.have.members([
'redsocks.conf', 'redsocks.conf',
'no_proxy', 'no_proxy',

View File

@ -3,8 +3,8 @@ import { stripIndent } from 'common-tags';
import type { SinonStub } from 'sinon'; import type { SinonStub } from 'sinon';
import * as hostConfig from '~/src/host-config'; import * as hostConfig from '~/src/host-config';
import { RedsocksConf } from '~/src/host-config'; import { RedsocksConf } from '~/src/host-config/proxy';
import type { RedsocksConfig, ProxyConfig } from '~/src/host-config'; import type { RedsocksConfig, ProxyConfig } from '~/src/host-config/types';
import log from '~/lib/supervisor-console'; import log from '~/lib/supervisor-console';
describe('RedsocksConf', () => { describe('RedsocksConf', () => {
@ -379,57 +379,6 @@ describe('RedsocksConf', () => {
}); });
describe('src/host-config', () => { describe('src/host-config', () => {
describe('patchProxy', () => {
it('patches RedsocksConfig with new values', () => {
const current = {
redsocks: {
type: 'socks5',
ip: 'example.org',
port: 1080,
login: '"foo"',
password: '"bar"',
},
} as RedsocksConfig;
const input = {
redsocks: {
type: 'http-connect',
ip: 'test.balena.io',
},
} as any;
const patched = hostConfig.patchProxy(current, input);
expect(patched).to.deep.equal({
redsocks: {
// Patched fields are updated
type: 'http-connect',
ip: 'test.balena.io',
// Unpatched fields retain their original values
port: 1080,
login: '"foo"',
password: '"bar"',
},
});
});
it('returns empty RedsocksConfig if redsocks config block is empty or invalid', () => {
const current: RedsocksConfig = {
redsocks: {
type: 'socks5',
ip: 'example.org',
port: 1080,
login: '"foo"',
password: '"bar"',
},
};
expect(hostConfig.patchProxy(current, { redsocks: {} })).to.deep.equal(
{},
);
expect(
hostConfig.patchProxy(current, { redsocks: true } as any),
).to.deep.equal({});
expect(hostConfig.patchProxy(current, {})).to.deep.equal({});
});
});
describe('parse', () => { describe('parse', () => {
it('parses valid HostConfiguration', () => { it('parses valid HostConfiguration', () => {
const conf = { const conf = {