docker-storage-gc 3.5.12 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.versionbot/CHANGELOG.yml +19 -1
- package/CHANGELOG.md +6 -0
- package/build/docker-event-stream.d.ts +8 -0
- package/build/docker-event-stream.js +80 -49
- package/build/docker-event-stream.js.map +1 -0
- package/build/docker-image-tree.d.ts +15 -0
- package/build/docker-image-tree.js +57 -76
- package/build/docker-image-tree.js.map +1 -0
- package/build/docker.d.ts +2 -0
- package/build/docker.js +21 -25
- package/build/docker.js.map +1 -0
- package/build/index.d.ts +33 -0
- package/build/index.js +174 -229
- package/build/index.js.map +1 -0
- package/lib/docker-event-stream.ts +93 -0
- package/lib/docker-image-tree.ts +94 -0
- package/lib/docker.ts +17 -0
- package/lib/index.ts +272 -0
- package/package.json +21 -13
- package/test/docker-event-stream.ts +46 -0
- package/test/docker-image-tree.ts +137 -0
- package/test/index.ts +228 -0
- package/tools/graphviz.ts +39 -0
- package/tsconfig.dev.json +13 -0
- package/tsconfig.json +22 -0
- package/index.d.ts +0 -23
- package/lib/docker-event-stream.coffee +0 -46
- package/lib/docker-image-tree.coffee +0 -56
- package/lib/docker.coffee +0 -13
- package/lib/index.coffee +0 -168
- package/test/docker-event-stream.coffee +0 -30
- package/test/docker-image-tree.coffee +0 -124
- package/test/index.coffee +0 -208
- package/tools/graphviz.coffee +0 -24
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
{ expect } = require 'chai'
|
|
2
|
-
fs = require 'fs'
|
|
3
|
-
tk = require 'timekeeper'
|
|
4
|
-
es = require 'event-stream'
|
|
5
|
-
|
|
6
|
-
{ parseEventStream } = require '../lib/docker-event-stream'
|
|
7
|
-
{ createTree } = require '../lib/docker-image-tree'
|
|
8
|
-
|
|
9
|
-
dockerUtils = require('../lib/docker.coffee')
|
|
10
|
-
|
|
11
|
-
getLayerMtimes = ->
|
|
12
|
-
dockerUtils.getDocker({})
|
|
13
|
-
.then (docker) ->
|
|
14
|
-
parseEventStream(docker)
|
|
15
|
-
.then (streamParser) ->
|
|
16
|
-
new Promise (resolve, reject) ->
|
|
17
|
-
mtimes = null
|
|
18
|
-
fs.createReadStream(__dirname + '/fixtures/docker-events.json')
|
|
19
|
-
.pipe(streamParser)
|
|
20
|
-
.on 'error', reject
|
|
21
|
-
.pipe es.mapSync (data) ->
|
|
22
|
-
mtimes = data
|
|
23
|
-
.on 'end', -> resolve(mtimes)
|
|
24
|
-
.on 'error', reject
|
|
25
|
-
|
|
26
|
-
describe 'createTree', ->
|
|
27
|
-
it.skip 'should work with empty input', ->
|
|
28
|
-
|
|
29
|
-
it 'should return a tree of images', ->
|
|
30
|
-
images = require('./fixtures/docker-images.json')
|
|
31
|
-
containers = require('./fixtures/docker-containers.json')
|
|
32
|
-
getLayerMtimes()
|
|
33
|
-
.then (mtimes) ->
|
|
34
|
-
tk.freeze(Date.UTC(2016, 0, 1))
|
|
35
|
-
tree = createTree(images, containers, mtimes)
|
|
36
|
-
tk.reset()
|
|
37
|
-
output = {
|
|
38
|
-
'id': '0000000000000000000000000000000000000000000000000000000000000000',
|
|
39
|
-
'size': 0,
|
|
40
|
-
'repoTags': [],
|
|
41
|
-
'repoDigests': [],
|
|
42
|
-
'mtime': 1451606400000000000,
|
|
43
|
-
'isUsedByAContainer': false,
|
|
44
|
-
'children': {
|
|
45
|
-
'sha256:6d15899cef812e2876b9d5d43d4cd863eda7b278f7b52d00975f6a9a8e817c74': {
|
|
46
|
-
'id': 'sha256:6d15899cef812e2876b9d5d43d4cd863eda7b278f7b52d00975f6a9a8e817c74',
|
|
47
|
-
'size': 125151141,
|
|
48
|
-
'repoTags': [],
|
|
49
|
-
'repoDigests': [],
|
|
50
|
-
'mtime': 1451606400000000000,
|
|
51
|
-
'isUsedByAContainer': false,
|
|
52
|
-
'children': {
|
|
53
|
-
'sha256:e53bd4df04f86919156c4510cdc6e6c9491ec8ec226381d36aca573b46bbbbbc': {
|
|
54
|
-
'id': 'sha256:e53bd4df04f86919156c4510cdc6e6c9491ec8ec226381d36aca573b46bbbbbc',
|
|
55
|
-
'size': 0,
|
|
56
|
-
'repoTags': [
|
|
57
|
-
'project1'
|
|
58
|
-
],
|
|
59
|
-
'repoDigests': [],
|
|
60
|
-
'mtime': 1451606400000000000,
|
|
61
|
-
'isUsedByAContainer': false,
|
|
62
|
-
'children': {
|
|
63
|
-
'sha256:6d41a4a0bf8168363e29da8a5ecbf3cd6c37e3f5a043decd5e7da6e427ba869c': {
|
|
64
|
-
'id': 'sha256:6d41a4a0bf8168363e29da8a5ecbf3cd6c37e3f5a043decd5e7da6e427ba869c',
|
|
65
|
-
'size': 330389,
|
|
66
|
-
'repoTags': [
|
|
67
|
-
'project2'
|
|
68
|
-
],
|
|
69
|
-
'repoDigests': [],
|
|
70
|
-
'mtime': 1448576073085559863,
|
|
71
|
-
'isUsedByAContainer': false,
|
|
72
|
-
'children': {
|
|
73
|
-
'sha256:80dc79d29cd8618e678da508fc32f7289e6f72defb534f3f287731b1f8b355ea': {
|
|
74
|
-
'id': 'sha256:80dc79d29cd8618e678da508fc32f7289e6f72defb534f3f287731b1f8b355ea',
|
|
75
|
-
'size': 98872,
|
|
76
|
-
'repoTags': [],
|
|
77
|
-
'repoDigests': [],
|
|
78
|
-
'mtime': 1451606400000000000,
|
|
79
|
-
'isUsedByAContainer': false,
|
|
80
|
-
'children': {}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
},
|
|
88
|
-
'sha256:902b87aaaec929e80541486828959f14fa061f529ad7f37ab300d4ef9f3a0dbf': {
|
|
89
|
-
'id': 'sha256:902b87aaaec929e80541486828959f14fa061f529ad7f37ab300d4ef9f3a0dbf',
|
|
90
|
-
'size': 125151141,
|
|
91
|
-
'repoTags': [],
|
|
92
|
-
'repoDigests': [],
|
|
93
|
-
'mtime': 1451606400000000000,
|
|
94
|
-
'isUsedByAContainer': false,
|
|
95
|
-
'children': {
|
|
96
|
-
'sha256:9a61b6b1315e6b457c31a03346ab94486a2f5397f4a82219bee01eead1c34c2e': {
|
|
97
|
-
'id': 'sha256:9a61b6b1315e6b457c31a03346ab94486a2f5397f4a82219bee01eead1c34c2e',
|
|
98
|
-
'size': 0,
|
|
99
|
-
'repoTags': [
|
|
100
|
-
'resin/project3'
|
|
101
|
-
],
|
|
102
|
-
'repoDigests': [],
|
|
103
|
-
'mtime': 1448576073203895800,
|
|
104
|
-
'isUsedByAContainer': false,
|
|
105
|
-
'children': {}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
},
|
|
109
|
-
'sha256:5b0d59026729b68570d99bc4f3f7c31a2e4f2a5736435641565d93e7c25bd2c3': {
|
|
110
|
-
'id': 'sha256:5b0d59026729b68570d99bc4f3f7c31a2e4f2a5736435641565d93e7c25bd2c3',
|
|
111
|
-
'size': 125151141,
|
|
112
|
-
'repoTags': [
|
|
113
|
-
'busybox:latest'
|
|
114
|
-
],
|
|
115
|
-
'repoDigests': [
|
|
116
|
-
'sha256:a8cf7ff6367c2afa2a90acd081b484cbded349a7076e7bdf37a05279f276bc12'
|
|
117
|
-
],
|
|
118
|
-
'mtime': 1448576072937294800,
|
|
119
|
-
'isUsedByAContainer': true,
|
|
120
|
-
'children': {},
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
expect(tree).to.deep.equal(output)
|
package/test/index.coffee
DELETED
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
Bluebird = require 'bluebird'
|
|
2
|
-
{ expect } = require 'chai'
|
|
3
|
-
|
|
4
|
-
DockerGC = require('../lib/index')
|
|
5
|
-
dockerUtils = require('../lib/docker.coffee')
|
|
6
|
-
|
|
7
|
-
SKIP_GC_TEST = process.env.SKIP_GC_TEST == '1' || false
|
|
8
|
-
IMAGES = [ 'alpine:3.1', 'debian:squeeze', 'ubuntu:lucid' ]
|
|
9
|
-
|
|
10
|
-
# TODO: Move it to a proper repo
|
|
11
|
-
# Same image (same id), different repo, different digest
|
|
12
|
-
NONE_TAG_IMAGES = ['hello-world@sha256:8e3114318a995a1ee497790535e7b88365222a21771ae7e53687ad76563e8e76',
|
|
13
|
-
'balenaplayground/hello-world@sha256:90659bf80b44ce6be8234e6ff90a1ac34acbeb826903b02cfa0da11c82cbc042']
|
|
14
|
-
|
|
15
|
-
promiseToBool = (p) ->
|
|
16
|
-
p.return(true).catchReturn(false)
|
|
17
|
-
|
|
18
|
-
pullAsync = (docker, tag) ->
|
|
19
|
-
console.log("[TEST] Pulling #{tag}")
|
|
20
|
-
docker.pull(tag)
|
|
21
|
-
.then (stream) ->
|
|
22
|
-
new Promise (resolve, reject) ->
|
|
23
|
-
stream.resume()
|
|
24
|
-
stream.once('error', reject)
|
|
25
|
-
stream.once('end', resolve)
|
|
26
|
-
|
|
27
|
-
# This test case is a little weird, it requires that no other images are present on
|
|
28
|
-
# the system to ensure that the correct one is being removed. Because of this, you
|
|
29
|
-
# can use the SKIP_GC_TEST env var to inform the test suite not to run this test
|
|
30
|
-
describe 'Garbage collection', ->
|
|
31
|
-
beforeEach ->
|
|
32
|
-
@dockerStorage = new DockerGC()
|
|
33
|
-
# Use either local or CI docker
|
|
34
|
-
Bluebird.join(
|
|
35
|
-
dockerUtils.getDocker({
|
|
36
|
-
socketPath: '/tmp/dind/docker.sock',
|
|
37
|
-
Promise: Bluebird,
|
|
38
|
-
})
|
|
39
|
-
@dockerStorage.setDocker({
|
|
40
|
-
socketPath: '/tmp/dind/docker.sock',
|
|
41
|
-
Promise: Bluebird,
|
|
42
|
-
})
|
|
43
|
-
(docker) =>
|
|
44
|
-
@dockerStorage.setupMtimeStream()
|
|
45
|
-
@docker = docker
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
afterEach ->
|
|
49
|
-
docker = @docker
|
|
50
|
-
console.log('[afterEach] Cleaning up...')
|
|
51
|
-
for image in IMAGES.concat(NONE_TAG_IMAGES)
|
|
52
|
-
docker.getImage(image).remove()
|
|
53
|
-
.catch ->
|
|
54
|
-
|
|
55
|
-
it 'should remove a image by tag', ->
|
|
56
|
-
this.timeout(600000)
|
|
57
|
-
docker = @docker
|
|
58
|
-
dockerStorage = @dockerStorage
|
|
59
|
-
|
|
60
|
-
pullAsync(docker, IMAGES[0])
|
|
61
|
-
.then ->
|
|
62
|
-
docker.getImage(IMAGES[0]).inspect()
|
|
63
|
-
.then ->
|
|
64
|
-
dockerStorage.garbageCollect(1)
|
|
65
|
-
.then ->
|
|
66
|
-
promiseToBool(docker.getImage(IMAGES[0]).inspect())
|
|
67
|
-
.then (image_found) ->
|
|
68
|
-
expect(image_found).to.be.false
|
|
69
|
-
|
|
70
|
-
it 'should remove a image by digest if its tag == none', ->
|
|
71
|
-
this.timeout(600000)
|
|
72
|
-
docker = @docker
|
|
73
|
-
dockerStorage = @dockerStorage
|
|
74
|
-
|
|
75
|
-
pullAsync(docker, NONE_TAG_IMAGES[0])
|
|
76
|
-
.then ->
|
|
77
|
-
docker.getImage(NONE_TAG_IMAGES[0]).inspect()
|
|
78
|
-
.then ->
|
|
79
|
-
dockerStorage.garbageCollect(1)
|
|
80
|
-
.then ->
|
|
81
|
-
promiseToBool(docker.getImage(NONE_TAG_IMAGES[0]).inspect())
|
|
82
|
-
.then (image_found) ->
|
|
83
|
-
expect(image_found).to.be.false
|
|
84
|
-
|
|
85
|
-
it 'should remove a image with tag == none even if it is in several repos', ->
|
|
86
|
-
this.timeout(600000)
|
|
87
|
-
docker = @docker
|
|
88
|
-
dockerStorage = @dockerStorage
|
|
89
|
-
|
|
90
|
-
Bluebird.each NONE_TAG_IMAGES, (image) ->
|
|
91
|
-
pullAsync(docker, image)
|
|
92
|
-
.then ->
|
|
93
|
-
docker.getImage(NONE_TAG_IMAGES[0]).inspect()
|
|
94
|
-
.then ->
|
|
95
|
-
dockerStorage.garbageCollect(1)
|
|
96
|
-
.then ->
|
|
97
|
-
Bluebird.map NONE_TAG_IMAGES, (image) ->
|
|
98
|
-
promiseToBool(docker.getImage(image).inspect())
|
|
99
|
-
.then (imagesFound) ->
|
|
100
|
-
expect(imagesFound).to.deep.equal([false, false])
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
it 'should remove all tags of an image', ->
|
|
104
|
-
this.timeout(600000)
|
|
105
|
-
return Promise.resolve() if SKIP_GC_TEST
|
|
106
|
-
|
|
107
|
-
docker = @docker
|
|
108
|
-
|
|
109
|
-
# first pull some images, so we know in which order they are referenced
|
|
110
|
-
pullAsync(docker, IMAGES[0])
|
|
111
|
-
.then ->
|
|
112
|
-
docker.getImage(IMAGES[0]).tag(repo: 'some-repo', tag: 'some-tag')
|
|
113
|
-
.then =>
|
|
114
|
-
@dockerStorage.garbageCollect(1)
|
|
115
|
-
.then ->
|
|
116
|
-
promiseToBool(docker.getImage(IMAGES[0]).inspect())
|
|
117
|
-
.then (imagesFound) ->
|
|
118
|
-
expect(imagesFound).to.be.false
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
it 'should remove the LRU image', ->
|
|
122
|
-
this.timeout(600000)
|
|
123
|
-
return Promise.resolve() if SKIP_GC_TEST
|
|
124
|
-
|
|
125
|
-
docker = @docker
|
|
126
|
-
|
|
127
|
-
# first pull some images, so we know in which order they are referenced
|
|
128
|
-
pullAsync(docker, IMAGES[0])
|
|
129
|
-
.then ->
|
|
130
|
-
docker.getImage(IMAGES[0]).tag(repo: 'some-repo', tag: 'some-tag')
|
|
131
|
-
.then ->
|
|
132
|
-
Bluebird.each IMAGES.slice(1), (image) ->
|
|
133
|
-
pullAsync(docker, image)
|
|
134
|
-
.then =>
|
|
135
|
-
# Attempt to remove a single byte, which will remove the LRU image,
|
|
136
|
-
# which should be alpine
|
|
137
|
-
@dockerStorage.garbageCollect(1)
|
|
138
|
-
.then ->
|
|
139
|
-
Bluebird.map IMAGES, (image) ->
|
|
140
|
-
promiseToBool(docker.getImage(image).inspect())
|
|
141
|
-
.then (imagesFound) ->
|
|
142
|
-
expect(imagesFound).to.deep.equal([false, true, true])
|
|
143
|
-
|
|
144
|
-
it 'should remove more than one image if necessary', ->
|
|
145
|
-
this.timeout(600000)
|
|
146
|
-
return Promise.resolve() if SKIP_GC_TEST
|
|
147
|
-
|
|
148
|
-
docker = @docker
|
|
149
|
-
|
|
150
|
-
Bluebird.each IMAGES, (image) ->
|
|
151
|
-
pullAsync(docker, image)
|
|
152
|
-
.then ->
|
|
153
|
-
# Get the size of the first image, so we can add one to it to remove
|
|
154
|
-
# the next one in addition
|
|
155
|
-
docker.getImage(IMAGES[0]).inspect().get('Size')
|
|
156
|
-
.then (size) =>
|
|
157
|
-
@dockerStorage.garbageCollect(size + 1)
|
|
158
|
-
.then ->
|
|
159
|
-
Bluebird.map IMAGES, (image) ->
|
|
160
|
-
promiseToBool(docker.getImage(image).inspect())
|
|
161
|
-
.then (imagesFound) ->
|
|
162
|
-
expect(imagesFound).to.deep.equal([false, false, true])
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
it 'should not consider images in use', ->
|
|
166
|
-
this.timeout(600000)
|
|
167
|
-
containerName = 'dont-consider-images-in-use-test'
|
|
168
|
-
return Promise.resolve() if SKIP_GC_TEST
|
|
169
|
-
|
|
170
|
-
docker = @docker
|
|
171
|
-
|
|
172
|
-
pullAsync(docker, IMAGES[0])
|
|
173
|
-
.then ->
|
|
174
|
-
docker.createContainer({
|
|
175
|
-
Image: IMAGES[0],
|
|
176
|
-
Tty: true,
|
|
177
|
-
Cmd: ['sh', '-c', 'while true; do echo test; sleep 1; done'],
|
|
178
|
-
name: containerName,
|
|
179
|
-
HostConfig: { AutoRemove: true }
|
|
180
|
-
})
|
|
181
|
-
.then (container) ->
|
|
182
|
-
container.start()
|
|
183
|
-
.then =>
|
|
184
|
-
@dockerStorage.garbageCollect(1)
|
|
185
|
-
.then ->
|
|
186
|
-
promiseToBool(docker.getImage(IMAGES[0]).inspect())
|
|
187
|
-
.then (imageInspect) ->
|
|
188
|
-
expect(imageInspect).to.be.true
|
|
189
|
-
.finally ->
|
|
190
|
-
docker.getContainer(containerName).stop()
|
|
191
|
-
|
|
192
|
-
it 'should get daemon host disk usage', ->
|
|
193
|
-
this.timeout(600000)
|
|
194
|
-
@dockerStorage.getDaemonFreeSpace()
|
|
195
|
-
.then (du) ->
|
|
196
|
-
expect(du).to.be.an('object')
|
|
197
|
-
expect(du).to.have.property('free').that.is.a('number')
|
|
198
|
-
expect(du).to.have.property('used').that.is.a('number')
|
|
199
|
-
expect(du).to.have.property('total').that.is.a('number')
|
|
200
|
-
|
|
201
|
-
it 'should get the correct architecture for a remote host', ->
|
|
202
|
-
@dockerStorage.getDaemonArchitecture()
|
|
203
|
-
.then (arch) ->
|
|
204
|
-
expect(arch).to.be.a('string')
|
|
205
|
-
|
|
206
|
-
it 'should set a base image to be used', ->
|
|
207
|
-
@dockerStorage.baseImagePromise.then (img) ->
|
|
208
|
-
expect(img).to.be.a('string')
|
package/tools/graphviz.coffee
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
exports.createDot = (tree) ->
|
|
2
|
-
_createDot = (tree) ->
|
|
3
|
-
id = (tree) -> "foo_#{tree.id[0..6]}"
|
|
4
|
-
|
|
5
|
-
label = (tree) ->
|
|
6
|
-
name = tree.repoTags[0] or '\\<none\\>:\\<none\\>'
|
|
7
|
-
mtime = new Date(tree.mtime).toISOString()
|
|
8
|
-
|
|
9
|
-
return "label=\"{ #{tree.id[0..12]} | #{name} | { #{mtime} | #{tree.size} } }\""
|
|
10
|
-
|
|
11
|
-
color = (tree) ->
|
|
12
|
-
if tree.repoTags.length is 0
|
|
13
|
-
'color="black"'
|
|
14
|
-
else
|
|
15
|
-
'color="red"'
|
|
16
|
-
|
|
17
|
-
# define the node
|
|
18
|
-
[ "#{id(tree)} [shape=record, #{label(tree)}, #{color(tree)}]" ]
|
|
19
|
-
# define all relations with child nodes
|
|
20
|
-
.concat(("#{id(tree)} -> #{id(child)}" for own _, child of tree.children))
|
|
21
|
-
# recurse to children
|
|
22
|
-
.concat((_createDot(child) for own id, child of tree.children)...)
|
|
23
|
-
|
|
24
|
-
return 'digraph {\n' + _createDot(tree).join('\n') + '\n}\n'
|