Skip to content

Commit 7cabf67

Browse files
authored
add support for cgroupv2 inode fallback (#5508)
1 parent a5095be commit 7cabf67

6 files changed

Lines changed: 118 additions & 37 deletions

File tree

packages/dd-trace/src/exporters/common/docker.js

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,34 +2,64 @@
22

33
const fs = require('fs')
44

5+
const { DD_EXTERNAL_ENV } = process.env
6+
57
// The second part is the PCF / Garden regexp. We currently assume no suffix($) to avoid matching pod UIDs
68
// See https://github.com/DataDog/datadog-agent/blob/7.40.x/pkg/util/cgroups/reader.go#L50
79
const uuidSource =
810
'[0-9a-f]{8}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{4}[-_][0-9a-f]{12}|[0-9a-f]{8}(?:-[0-9a-f]{4}){4}$'
911
const containerSource = '[0-9a-f]{64}'
1012
const taskSource = '[0-9a-f]{32}-\\d+'
13+
const lineReg = /^(\d+):([^:]*):(.+)$/
1114
const entityReg = new RegExp(`.*(${uuidSource}|${containerSource}|${taskSource})(?:\\.scope)?$`, 'm')
1215

16+
const cgroup = readControlGroup()
1317
const entityId = getEntityId()
18+
const inode = getInode()
1419

1520
function getEntityId () {
16-
const cgroup = readControlGroup() || ''
17-
const match = cgroup.trim().match(entityReg) || []
21+
const match = cgroup.match(entityReg) || []
1822

1923
return match[1]
2024
}
2125

26+
function getInode () {
27+
const match = cgroup.match(lineReg) || []
28+
29+
return readInode(match[3])
30+
}
31+
2232
function readControlGroup () {
2333
try {
24-
return fs.readFileSync('/proc/self/cgroup').toString()
34+
return fs.readFileSync('/proc/self/cgroup').toString().trim()
35+
} catch (err) {
36+
return ''
37+
}
38+
}
39+
40+
function readInode (path) {
41+
if (!path) return 0
42+
43+
const strippedPath = path.replace(/^\//, '').replace(/\/$/, '')
44+
45+
try {
46+
return fs.statSync(`/sys/fs/cgroup/${strippedPath}`).ino
2547
} catch (err) {
26-
// ignore
48+
return 0
2749
}
2850
}
2951

3052
module.exports = {
31-
// can be the container ID but not always depending on the orchestrator
32-
id () {
33-
return entityId
53+
inject (carrier) {
54+
if (entityId) {
55+
carrier['Datadog-Container-Id'] = entityId
56+
carrier['Datadog-Entity-ID'] = `ci-${entityId}`
57+
} else if (inode) {
58+
carrier['Datadog-Entity-ID'] = `in-${inode}`
59+
}
60+
61+
if (DD_EXTERNAL_ENV) {
62+
carrier['Datadog-External-Env'] = DD_EXTERNAL_ENV
63+
}
3464
}
3565
}

packages/dd-trace/src/exporters/common/request.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ const { storage } = require('../../../../datadog-core')
1515
const log = require('../../log')
1616

1717
const maxActiveRequests = 8
18-
const containerId = docker.id()
1918

2019
let activeRequests = 0
2120

@@ -63,9 +62,7 @@ function request (data, options, callback) {
6362
options.headers['Content-Length'] = byteLength(dataArray)
6463
}
6564

66-
if (containerId) {
67-
options.headers['Datadog-Container-ID'] = containerId
68-
}
65+
docker.inject(options.headers)
6966

7067
options.agent = isSecure ? httpsAgent : httpAgent
7168

packages/dd-trace/src/profiling/exporters/agent.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ const perf = require('perf_hooks').performance
1616
const telemetryMetrics = require('../../telemetry/metrics')
1717
const profilersNamespace = telemetryMetrics.manager.namespace('profilers')
1818

19-
const containerId = docker.id()
20-
2119
const statusCodeCounters = []
2220
const requestCounter = profilersNamespace.count('profile_api.requests', [])
2321
const sizeDistribution = profilersNamespace.distribution('profile_api.bytes', [])
@@ -155,9 +153,7 @@ class AgentExporter extends EventSerializer {
155153
timeout: this._backoffTime * Math.pow(2, attempt)
156154
}
157155

158-
if (containerId) {
159-
options.headers['Datadog-Container-ID'] = containerId
160-
}
156+
docker.inject(options.headers)
161157

162158
if (this._url.protocol === 'unix:') {
163159
options.socketPath = this._url.pathname

packages/dd-trace/test/exporters/common/docker.spec.js

Lines changed: 73 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,95 +5,151 @@ require('../../setup/tap')
55
describe('docker', () => {
66
let docker
77
let fs
8+
let carrier
9+
let externalEnv
810

911
beforeEach(() => {
1012
fs = {
11-
readFileSync: sinon.stub()
13+
readFileSync: sinon.stub(),
14+
statSync: sinon.stub()
1215
}
16+
carrier = {}
17+
externalEnv = process.env.DD_EXTERNAL_ENV
1318
})
1419

15-
it('should return an empty ID when the cgroup cannot be read', () => {
20+
afterEach(() => {
21+
process.env.DD_EXTERNAL_ENV = externalEnv
22+
})
23+
24+
it('should not inject IDs when the cgroup cannot be read', () => {
1625
docker = proxyquire('../src/exporters/common/docker', { fs })
26+
docker.inject(carrier)
1727

18-
expect(docker.id()).to.be.undefined
28+
expect(carrier['Datadog-Container-Id']).to.be.undefined
29+
expect(carrier['Datadog-Entity-ID']).to.be.undefined
1930
})
2031

2132
it('should support IDs with long format', () => {
33+
const id = '34dc0b5e626f2c5c4c5170e34b10e7654ce36f0fcd532739f4445baabea03376'
2234
const cgroup = [
23-
'1:name=systemd:/docker/34dc0b5e626f2c5c4c5170e34b10e7654ce36f0fcd532739f4445baabea03376'
35+
`1:name=systemd:/docker/${id}`
2436
].join('\n')
2537

2638
fs.readFileSync.withArgs('/proc/self/cgroup').returns(Buffer.from(cgroup))
2739
docker = proxyquire('../src/exporters/common/docker', { fs })
40+
docker.inject(carrier)
2841

29-
expect(docker.id()).to.equal('34dc0b5e626f2c5c4c5170e34b10e7654ce36f0fcd532739f4445baabea03376')
42+
expect(carrier['Datadog-Container-Id']).to.equal(id)
43+
expect(carrier['Datadog-Entity-ID']).to.equal(`ci-${id}`)
3044
})
3145

3246
it('should support IDs with UUID format', () => {
47+
const id = '34dc0b5e-626f-2c5c-4c51-70e34b10e765'
3348
const cgroup = [
34-
'1:name=systemd:/uuid/34dc0b5e-626f-2c5c-4c51-70e34b10e765'
49+
`1:name=systemd:/uuid/${id}`
3550
].join('\n')
3651

3752
fs.readFileSync.withArgs('/proc/self/cgroup').returns(Buffer.from(cgroup))
3853
docker = proxyquire('../src/exporters/common/docker', { fs })
54+
docker.inject(carrier)
3955

40-
expect(docker.id()).to.equal('34dc0b5e-626f-2c5c-4c51-70e34b10e765')
56+
expect(carrier['Datadog-Container-Id']).to.equal(id)
57+
expect(carrier['Datadog-Entity-ID']).to.equal(`ci-${id}`)
4158
})
4259

4360
it('should support IDs with ECS task format', () => {
61+
const id = '34dc0b5e626f2c5c4c5170e34b10e765-1234567890'
4462
const cgroup = [
45-
'1:name=systemd:/ecs/34dc0b5e626f2c5c4c5170e34b10e765-1234567890'
63+
`1:name=systemd:/ecs/${id}`
4664
].join('\n')
4765

4866
fs.readFileSync.withArgs('/proc/self/cgroup').returns(Buffer.from(cgroup))
4967
docker = proxyquire('../src/exporters/common/docker', { fs })
68+
docker.inject(carrier)
5069

51-
expect(docker.id()).to.equal('34dc0b5e626f2c5c4c5170e34b10e765-1234567890')
70+
expect(carrier['Datadog-Container-Id']).to.equal(id)
71+
expect(carrier['Datadog-Entity-ID']).to.equal(`ci-${id}`)
5272
})
5373

5474
it('should support IDs with Kubernetes format', () => {
75+
const id = '7b8952daecf4c0e44bbcefe1b5c5ebc7b4839d4eefeccefe694709d3809b6199'
5576
const cgroup = [
56-
'1:name=systemd:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod2d3da189_6407_48e3_9ab6_78188d75e609.slice/docker-7b8952daecf4c0e44bbcefe1b5c5ebc7b4839d4eefeccefe694709d3809b6199.scope' // eslint-disable-line @stylistic/js/max-len
77+
`1:name=systemd:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-pod2d3da189_6407_48e3_9ab6_78188d75e609.slice/docker-${id}.scope` // eslint-disable-line @stylistic/js/max-len
5778
].join('\n')
5879

5980
fs.readFileSync.withArgs('/proc/self/cgroup').returns(Buffer.from(cgroup))
6081
docker = proxyquire('../src/exporters/common/docker', { fs })
82+
docker.inject(carrier)
6183

62-
expect(docker.id()).to.equal('7b8952daecf4c0e44bbcefe1b5c5ebc7b4839d4eefeccefe694709d3809b6199')
84+
expect(carrier['Datadog-Container-Id']).to.equal(id)
85+
expect(carrier['Datadog-Entity-ID']).to.equal(`ci-${id}`)
6386
})
6487

6588
it('should support finding IDs on any line of the cgroup', () => {
89+
const id = '34dc0b5e626f2c5c4c5170e34b10e7654ce36f0fcd532739f4445baabea03376'
6690
const cgroup = [
6791
'1:name=systemd:/nope',
68-
'2:pids:/docker/34dc0b5e626f2c5c4c5170e34b10e7654ce36f0fcd532739f4445baabea03376',
92+
`2:pids:/docker/${id}`,
6993
'3:cpu:/invalid'
7094
].join('\n')
7195

7296
fs.readFileSync.withArgs('/proc/self/cgroup').returns(Buffer.from(cgroup))
7397
docker = proxyquire('../src/exporters/common/docker', { fs })
98+
docker.inject(carrier)
7499

75-
expect(docker.id()).to.equal('34dc0b5e626f2c5c4c5170e34b10e7654ce36f0fcd532739f4445baabea03376')
100+
expect(carrier['Datadog-Container-Id']).to.equal(id)
101+
expect(carrier['Datadog-Entity-ID']).to.equal(`ci-${id}`)
76102
})
77103

78104
it('should support Control Group v2', () => {
105+
const id = '34dc0b5e626f2c5c4c5170e34b10e7654ce36f0fcd532739f4445baabea03376'
79106
const cgroup = [
80-
'0::/docker/34dc0b5e626f2c5c4c5170e34b10e7654ce36f0fcd532739f4445baabea03376'
107+
`0::/docker/${id}`
81108
].join('\n')
82109

83110
fs.readFileSync.withArgs('/proc/self/cgroup').returns(Buffer.from(cgroup))
84111
docker = proxyquire('../src/exporters/common/docker', { fs })
112+
docker.inject(carrier)
85113

86-
expect(docker.id()).to.equal('34dc0b5e626f2c5c4c5170e34b10e7654ce36f0fcd532739f4445baabea03376')
114+
expect(carrier['Datadog-Container-Id']).to.equal(id)
115+
expect(carrier['Datadog-Entity-ID']).to.equal(`ci-${id}`)
87116
})
88117

89118
it('should support Cloud Foundry', () => {
119+
const id = '6f265890-5165-7fab-6b52-18d1'
90120
const cgroup = [
91-
'1:name=systemd:/system.slice/garden.service/garden/6f265890-5165-7fab-6b52-18d1'
121+
`1:name=systemd:/system.slice/garden.service/garden/${id}`
92122
].join('\n')
93123

94124
fs.readFileSync.withArgs('/proc/self/cgroup').returns(Buffer.from(cgroup))
95125
docker = proxyquire('../src/exporters/common/docker', { fs })
126+
docker.inject(carrier)
127+
128+
expect(carrier['Datadog-Container-Id']).to.equal(id)
129+
expect(carrier['Datadog-Entity-ID']).to.equal(`ci-${id}`)
130+
})
131+
132+
it('should support inode when the ID is not available', () => {
133+
const ino = 1234
134+
const cgroup = [
135+
'1:name=systemd:/system.slice/garden.service/garden/'
136+
].join('\n')
137+
138+
fs.readFileSync.withArgs('/proc/self/cgroup').returns(Buffer.from(cgroup))
139+
fs.statSync.withArgs('/sys/fs/cgroup/system.slice/garden.service/garden').returns({ ino })
140+
docker = proxyquire('../src/exporters/common/docker', { fs })
141+
docker.inject(carrier)
142+
143+
expect(carrier['Datadog-Container-Id']).to.be.undefined
144+
expect(carrier['Datadog-Entity-ID']).to.equal(`in-${ino}`)
145+
})
146+
147+
it('should support external env', () => {
148+
process.env.DD_EXTERNAL_ENV = 'test'
149+
150+
docker = proxyquire('../src/exporters/common/docker', { fs })
151+
docker.inject(carrier)
96152

97-
expect(docker.id()).to.equal('6f265890-5165-7fab-6b52-18d1')
153+
expect(carrier['Datadog-External-Env']).to.equal('test')
98154
})
99155
})

packages/dd-trace/test/exporters/common/request.spec.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ describe('request', function () {
4343
debug: sinon.spy()
4444
}
4545
docker = {
46-
id: sinon.stub().returns('abcd')
46+
inject (carrier) {
47+
carrier['datadog-container-id'] = 'abcd'
48+
}
4749
}
4850
request = proxyquire('../src/exporters/common/request', {
4951
'./docker': docker,

packages/dd-trace/test/profiling/exporters/agent.spec.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ describe('exporters/agent', function () {
6565
let startSpan
6666

6767
function verifyRequest (req, profiles, start, end) {
68-
expect(req.headers).to.have.property('datadog-container-id', docker.id())
68+
expect(req.headers).to.have.property('test', 'injected')
6969
expect(req.headers).to.have.property('dd-evp-origin', 'dd-trace-js')
7070
expect(req.headers).to.have.property('dd-evp-origin-version', version)
7171

@@ -141,8 +141,8 @@ describe('exporters/agent', function () {
141141

142142
beforeEach(() => {
143143
docker = {
144-
id () {
145-
return 'container-id'
144+
inject (carrier) {
145+
carrier.test = 'injected'
146146
}
147147
}
148148
http = {

0 commit comments

Comments
 (0)