diff --git a/server/lib/system/system.getGladysContainerId.js b/server/lib/system/system.getGladysContainerId.js index ae73903998..800098e283 100644 --- a/server/lib/system/system.getGladysContainerId.js +++ b/server/lib/system/system.getGladysContainerId.js @@ -13,6 +13,28 @@ async function getGladysContainerId() { if (!this.dockerode) { throw new PlatformNotCompatible('SYSTEM_NOT_RUNNING_DOCKER'); } + + let containerId = await getContainerIdFromCidFile(); + + if (containerId === undefined) { + // Not found in cidfile try on cgroup + containerId = await getContainerIdFromCgroup(); + } + + if (containerId === undefined) { + // Not found in cgroup try on mountinfo + containerId = await getContainerIdFromMountInfo(); + } + + if (containerId === undefined) { + throw new PlatformNotCompatible('DOCKER_CONTAINER_ID_NOT_AVAILABLE'); + } + + // we return the containerId trimed, just in case + return containerId.trim(); +} + +async function getContainerIdFromCidFile() { try { // We try if the cidfile exist in the container await fs.promises.access(CIDFILE_FILE_PATH_IN_CONTAINER, fs.constants.F_OK); @@ -21,8 +43,18 @@ async function getGladysContainerId() { // we return the containerId trimed, just in case return containerId.trim(); } catch (e) { - // if not, we get the containerId from the cgroup - const cgroup = await fs.promises.readFile('/proc/self/cgroup', 'utf-8'); + // container id not found return undefined + return; + } +} + +async function getContainerIdFromCgroup() { + const cgroupFile = '/proc/self/cgroup'; + try { + // We try if the cgroup file exist in the container + await fs.promises.access(cgroupFile, fs.constants.F_OK); + // if yes, we read it + const cgroup = await fs.promises.readFile(cgroupFile, 'utf-8'); // String looks like this in cgroup v2 (Debian 11) // 0::/system.slice/docker-2bb2c94b0c395fc8fdff9fa4ce364a3be0dd05792145ffc93ce8d665d06521f1.scope // Or this in cgroup v1 (Debian 10) @@ -41,20 +73,33 @@ async function getGladysContainerId() { [, firstPart] = lineWithDocker.split('docker-'); // then, we remove .scope [containerId] = firstPart.split('.scope'); - } else { - const mountinfo = await fs.promises.readFile('/proc/self/mountinfo', 'utf-8'); - - if (mountinfo.indexOf('/docker/containers/') !== -1) { - const allLines = mountinfo.split('\n'); - const lineWithDocker = allLines.find((line) => line.indexOf('/docker/containers/') !== -1); - [, containerId] = /\/docker\/containers\/(\w+)/gm.exec(lineWithDocker); - } else { - throw new PlatformNotCompatible('DOCKER_CGROUP_CONTAINER_ID_NOT_AVAILABLE'); - } } + return containerId; + } catch (e) { + // container id not found return undefined + return; + } +} - // we return the containerId trimed, just in case - return containerId.trim(); +async function getContainerIdFromMountInfo() { + const mountInfoFile = '/proc/self/mountinfo'; + try { + // We try if the mountinfo file exist in the container + await fs.promises.access(mountInfoFile, fs.constants.F_OK); + // container id not found return undefined + const mountinfo = await fs.promises.readFile(mountInfoFile, 'utf-8'); + // String looks like this in mountinfo (Debian 11) + // 1077 888 179:2 /var/lib/docker/containers/83fa542c0b140e45e63ad7263c539ac08e2cbf7916894f1c51c3f016397b168e/hosts /etc/hosts rw,noatime - ext4 /dev/root rw + let containerId; + if (mountinfo.indexOf('/docker/containers/') !== -1) { + const allLines = mountinfo.split('\n'); + const lineWithDocker = allLines.find((line) => line.indexOf('/docker/containers/') !== -1); + [, containerId] = /\/docker\/containers\/(\w+)/gm.exec(lineWithDocker); + } + return containerId; + } catch (e) { + // container id not found return undefined + return; } } diff --git a/server/test/lib/system/system.getGladysContainerId.test.js b/server/test/lib/system/system.getGladysContainerId.test.js index c285a7c4ca..2f2e1e01de 100644 --- a/server/test/lib/system/system.getGladysContainerId.test.js +++ b/server/test/lib/system/system.getGladysContainerId.test.js @@ -86,7 +86,7 @@ describe('system.getGladysContainerId', () => { beforeEach(async () => { FsMock = { promises: { - access: fake.resolves(null), + access: sinon.stub(), readFile: sinon.stub(), }, constants: { @@ -128,12 +128,19 @@ describe('system.getGladysContainerId', () => { }); it('should return containerId through cidfile', async () => { + FsMock.promises.access.withArgs('/var/lib/gladysassistant/containerId').resolves(null); FsMock.promises.readFile.resolves('967ef3114fa2ceb8c4f6dbdbc78ee411a6f33fb1fe1d32455686ef6e89f41d1c'); const containerId = await system.getGladysContainerId(); expect(containerId).to.eq('967ef3114fa2ceb8c4f6dbdbc78ee411a6f33fb1fe1d32455686ef6e89f41d1c'); }); it('should return containerId through exec in mountinfo (Debian 11)', async () => { - FsMock.promises.access = fake.rejects(); + FsMock.promises.access + .withArgs('/var/lib/gladysassistant/containerId') + .rejects() + .withArgs('/proc/self/cgroup') + .resolves(null) + .withArgs('/proc/self/mountinfo') + .resolves(null); FsMock.promises.readFile .withArgs('/proc/self/cgroup', 'utf-8') .resolves('0::/') @@ -143,31 +150,53 @@ describe('system.getGladysContainerId', () => { expect(containerId2).to.eq('83fa542c0b140e45e63ad7263c539ac08e2cbf7916894f1c51c3f016397b168e'); }); it('should return containerId through exec in cgroup v2 (Debian 11)', async () => { - FsMock.promises.access = fake.rejects(); + FsMock.promises.access + .withArgs('/var/lib/gladysassistant/containerId') + .rejects() + .withArgs('/proc/self/cgroup') + .resolves(null); FsMock.promises.readFile = fake.resolves(procSelfCpuGroupDebia11); const containerId2 = await system.getGladysContainerId(); expect(containerId2).to.eq('2bb2c94b0c395fc8fdff9fa4ce364a3be0dd05792145ffc93ce8d665d06521f1'); }); it('should return containerId through exec in cgroup v2 (Debian 11) with containerId not on first line', async () => { - FsMock.promises.access = fake.rejects(); + FsMock.promises.access + .withArgs('/var/lib/gladysassistant/containerId') + .rejects() + .withArgs('/proc/self/cgroup') + .resolves(null); FsMock.promises.readFile = fake.resolves(procSelfCpuGroupDebia11WithDataInSecondLine); const containerId2 = await system.getGladysContainerId(); expect(containerId2).to.eq('2bb2c94b0c395fc8fdff9fa4ce364a3be0dd05792145ffc93ce8d665d06521f1'); }); it('should return containerId through exec in cgroup v1 (Debian 10)', async () => { - FsMock.promises.access = fake.rejects(); + FsMock.promises.access + .withArgs('/var/lib/gladysassistant/containerId') + .rejects() + .withArgs('/proc/self/cgroup') + .resolves(null); FsMock.promises.readFile = fake.resolves(procSelfCpuGroupDebian10); const containerId2 = await system.getGladysContainerId(); expect(containerId2).to.eq('357e73ad015211a5acd76a8973b9287d4de75922e9802d94ba46b756f2bb5350'); }); it('should return containerId through exec in cgroup v1 (Debian 10) with containerId not on first line', async () => { - FsMock.promises.access = fake.rejects(); + FsMock.promises.access + .withArgs('/var/lib/gladysassistant/containerId') + .rejects() + .withArgs('/proc/self/cgroup') + .resolves(null); FsMock.promises.readFile = fake.resolves(procSelfCpuGroupDebian10WithDataInSecondLine); const containerId2 = await system.getGladysContainerId(); expect(containerId2).to.eq('357e73ad015211a5acd76a8973b9287d4de75922e9802d94ba46b756f2bb5350'); }); it('should return error, system not compatible', async () => { - FsMock.promises.access = fake.rejects(); + FsMock.promises.access + .withArgs('/var/lib/gladysassistant/containerId') + .rejects() + .withArgs('/proc/self/mountinfo') + .rejects() + .withArgs('/proc/self/cgroup') + .resolves(null); FsMock.promises.readFile = fake.resolves('3:rdma:/'); try { await system.getGladysContainerId();