Move hostname get/set to separate "module" (directory)

Signed-off-by: Christina Ying Wang <christina@balena.io>
This commit is contained in:
Christina Ying Wang 2024-04-30 20:42:13 -07:00
parent 1c81eafd7c
commit 0cf5a4bf18
5 changed files with 60 additions and 56 deletions

View File

@ -449,10 +449,5 @@ export const patchHostConfig = async (
conf: Parameters<typeof hostConfig.patch>[0], conf: Parameters<typeof hostConfig.patch>[0],
force: boolean, force: boolean,
) => { ) => {
// If hostname is an empty string, return first 7 digits of device uuid
if (conf.network?.hostname === '') {
const uuid = await config.get('uuid');
conf.network.hostname = uuid?.slice(0, 7);
}
await hostConfig.patch(conf, force); await hostConfig.patch(conf, force);
}; };

View File

@ -1,19 +1,13 @@
import { stripIndent } from 'common-tags'; import { stripIndent } from 'common-tags';
import _ from 'lodash'; import _ from 'lodash';
import { promises as fs } from 'fs';
import path from 'path'; import path from 'path';
import * as config from './config';
import * as applicationManager from './compose/application-manager'; import * as applicationManager from './compose/application-manager';
import { readHostname, setHostname } from './host-config/index';
import * as dbus from './lib/dbus'; import * as dbus from './lib/dbus';
import { isENOENT } from './lib/errors'; import { isENOENT } from './lib/errors';
import { mkdirp, unlinkAll } from './lib/fs-utils'; import { mkdirp, unlinkAll } from './lib/fs-utils';
import { import { writeToBoot, readFromBoot, pathOnBoot } from './lib/host-utils';
writeToBoot,
readFromBoot,
pathOnRoot,
pathOnBoot,
} from './lib/host-utils';
import * as updateLock from './lib/update-lock'; import * as updateLock from './lib/update-lock';
const redsocksHeader = stripIndent` const redsocksHeader = stripIndent`
@ -178,19 +172,6 @@ async function setProxy(maybeConf: ProxyConfig | null): Promise<void> {
} }
} }
const hostnamePath = pathOnRoot('/etc/hostname');
async function readHostname() {
const hostnameData = await fs.readFile(hostnamePath, 'utf-8');
return _.trim(hostnameData);
}
async function setHostname(val: string) {
// Changing the hostname on config.json will trigger
// the OS config-json service to restart the necessary services
// so the change gets reflected on containers
await config.set({ hostname: val });
}
export async function get(): Promise<HostConfig> { export async function get(): Promise<HostConfig> {
return { return {
network: { network: {

24
src/host-config/index.ts Normal file
View File

@ -0,0 +1,24 @@
import { promises as fs } from 'fs';
import * as config from '../config';
import { pathOnRoot } from '../lib/host-utils';
const hostnamePath = pathOnRoot('/etc/hostname');
export async function readHostname() {
const hostnameData = await fs.readFile(hostnamePath, 'utf-8');
return hostnameData.trim();
}
export async function setHostname(val: string) {
let hostname = val;
// If hostname is an empty string, return first 7 digits of device uuid
if (!val) {
const uuid = await config.get('uuid');
hostname = uuid?.slice(0, 7) as string;
}
// Changing the hostname on config.json will trigger
// the OS config-json service to restart the necessary services
// so the change gets reflected on containers
await config.set({ hostname });
}

View File

@ -1294,18 +1294,4 @@ describe('patches host config', () => {
await actions.patchHostConfig(conf, true); await actions.patchHostConfig(conf, true);
expect(hostConfigPatch).to.have.been.calledWith(conf, true); expect(hostConfigPatch).to.have.been.calledWith(conf, true);
}); });
it('patches hostname as first 7 digits of uuid if hostname parameter is empty string', async () => {
const conf = {
network: {
hostname: '',
},
};
const uuid = await config.get('uuid');
await actions.patchHostConfig(conf, true);
expect(hostConfigPatch).to.have.been.calledWith(
{ network: { hostname: uuid?.slice(0, 7) } },
true,
);
});
}); });

View File

@ -6,7 +6,8 @@ import type { SinonStub } from 'sinon';
import { stub } from 'sinon'; import { stub } from 'sinon';
import * as fs from 'fs/promises'; import * as fs from 'fs/promises';
import * as hostConfig from '~/src/host-config'; import { get, patch } from '~/src/host-config';
import * as hostConfig from '~/src/host-config/index';
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';
@ -80,8 +81,25 @@ describe('host-config', () => {
(dbus.restartService as SinonStub).restore(); (dbus.restartService 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');
});
});
it('reads proxy configs and hostname', async () => { it('reads proxy configs and hostname', async () => {
const { network } = await hostConfig.get(); const { network } = await get();
expect(network).to.have.property('hostname', 'deadbeef'); expect(network).to.have.property('hostname', 'deadbeef');
expect(network).to.have.property('proxy'); expect(network).to.have.property('proxy');
expect(network.proxy).to.have.property('ip', 'example.org'); expect(network.proxy).to.have.property('ip', 'example.org');
@ -99,7 +117,7 @@ describe('host-config', () => {
stub(applicationManager, 'getCurrentApps').resolves(currentApps); stub(applicationManager, 'getCurrentApps').resolves(currentApps);
try { try {
await hostConfig.patch({ network: { hostname: 'test' } }); await patch({ network: { hostname: 'test' } });
expect.fail('Expected hostConfig.patch to throw UpdatesLockedError'); expect.fail('Expected hostConfig.patch to throw UpdatesLockedError');
} catch (e: unknown) { } catch (e: unknown) {
expect(e).to.be.instanceOf(UpdatesLockedError); expect(e).to.be.instanceOf(UpdatesLockedError);
@ -112,7 +130,7 @@ describe('host-config', () => {
stub(applicationManager, 'getCurrentApps').resolves(currentApps); stub(applicationManager, 'getCurrentApps').resolves(currentApps);
try { try {
await hostConfig.patch({ network: { hostname: 'deadreef' } }, true); await patch({ network: { hostname: 'deadreef' } }, true);
expect(await config.get('hostname')).to.equal('deadreef'); expect(await config.get('hostname')).to.equal('deadreef');
} 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}`);
@ -122,14 +140,14 @@ describe('host-config', () => {
}); });
it('patches hostname', async () => { it('patches hostname', async () => {
await hostConfig.patch({ network: { hostname: 'test' } }); await patch({ network: { hostname: 'test' } });
// /etc/hostname isn't changed until the balena-hostname service // /etc/hostname isn't changed until the balena-hostname service
// is restarted by the OS. // is restarted by the OS.
expect(await config.get('hostname')).to.equal('test'); expect(await config.get('hostname')).to.equal('test');
}); });
it('patches proxy', async () => { it('patches proxy', async () => {
await hostConfig.patch({ await patch({
network: { network: {
proxy: { proxy: {
ip: 'example2.org', ip: 'example2.org',
@ -141,7 +159,7 @@ describe('host-config', () => {
}, },
}, },
}); });
const { network } = await hostConfig.get(); const { network } = await get();
expect(network).to.have.property('proxy'); expect(network).to.have.property('proxy');
expect(network.proxy).to.have.property('ip', 'example2.org'); expect(network.proxy).to.have.property('ip', 'example2.org');
expect(network.proxy).to.have.property('port', 1090); expect(network.proxy).to.have.property('port', 1090);
@ -156,7 +174,7 @@ describe('host-config', () => {
it('skips restarting proxy services when part of redsocks-conf.target', async () => { it('skips restarting proxy services when part of redsocks-conf.target', async () => {
(dbus.servicePartOf as SinonStub).resolves(['redsocks-conf.target']); (dbus.servicePartOf as SinonStub).resolves(['redsocks-conf.target']);
await hostConfig.patch({ await patch({
network: { network: {
proxy: { proxy: {
ip: 'example2.org', ip: 'example2.org',
@ -169,7 +187,7 @@ describe('host-config', () => {
}, },
}); });
expect(dbus.restartService as SinonStub).to.not.have.been.called; expect(dbus.restartService as SinonStub).to.not.have.been.called;
const { network } = await hostConfig.get(); const { network } = await get();
expect(network).to.have.property('proxy'); expect(network).to.have.property('proxy');
expect(network.proxy).to.have.property('ip', 'example2.org'); expect(network.proxy).to.have.property('ip', 'example2.org');
expect(network.proxy).to.have.property('port', 1090); expect(network.proxy).to.have.property('port', 1090);
@ -183,8 +201,8 @@ describe('host-config', () => {
}); });
it('patches redsocks.conf to be empty if prompted', async () => { it('patches redsocks.conf to be empty if prompted', async () => {
await hostConfig.patch({ network: { proxy: {} } }); await patch({ network: { proxy: {} } });
const { network } = await hostConfig.get(); const { network } = await get();
expect(network).to.have.property('proxy', undefined); expect(network).to.have.property('proxy', undefined);
expect(await fs.readdir(proxyBase)).to.not.have.members([ expect(await fs.readdir(proxyBase)).to.not.have.members([
'redsocks.conf', 'redsocks.conf',
@ -193,23 +211,23 @@ describe('host-config', () => {
}); });
it('patches no_proxy to be empty if prompted', async () => { it('patches no_proxy to be empty if prompted', async () => {
await hostConfig.patch({ await patch({
network: { network: {
proxy: { proxy: {
noProxy: [], noProxy: [],
}, },
}, },
}); });
const { network } = await hostConfig.get(); const { network } = await get();
expect(network).to.have.property('proxy'); expect(network).to.have.property('proxy');
expect(network.proxy).to.not.have.property('noProxy'); expect(network.proxy).to.not.have.property('noProxy');
expect(await fs.readdir(proxyBase)).to.not.have.members(['no_proxy']); expect(await fs.readdir(proxyBase)).to.not.have.members(['no_proxy']);
}); });
it("doesn't update hostname or proxy when both are empty", async () => { it("doesn't update hostname or proxy when both are empty", async () => {
const { network } = await hostConfig.get(); const { network } = await get();
await hostConfig.patch({ network: {} }); await patch({ network: {} });
const { network: newNetwork } = await hostConfig.get(); const { network: newNetwork } = await get();
expect(network.hostname).to.equal(newNetwork.hostname); expect(network.hostname).to.equal(newNetwork.hostname);
expect(network.proxy).to.deep.equal(newNetwork.proxy); expect(network.proxy).to.deep.equal(newNetwork.proxy);
}); });