|
1 | 1 | 'use strict'
|
2 | 2 |
|
3 |
| -var asyncMap = require('slide').asyncMap |
| 3 | +const Promise = require('bluebird') |
| 4 | + |
4 | 5 | var checksumStream = require('checksum-stream')
|
5 | 6 | var fixOwner = require('./util/fix-owner')
|
6 | 7 | var fs = require('graceful-fs')
|
7 | 8 | var index = require('./entry-index')
|
8 |
| -var lockfile = require('lockfile') |
| 9 | +var lockfile = Promise.promisifyAll(require('lockfile')) |
9 | 10 | var path = require('path')
|
10 |
| -var pipe = require('mississippi').pipe |
11 |
| -var rimraf = require('rimraf') |
| 11 | +var pipe = Promise.promisify(require('mississippi').pipe) |
| 12 | +var rimraf = Promise.promisify(require('rimraf')) |
| 13 | + |
| 14 | +Promise.promisifyAll(fs) |
12 | 15 |
|
13 | 16 | module.exports = verify
|
14 |
| -function verify (cache, opts, _cb) { |
15 |
| - if (!_cb) { |
16 |
| - _cb = opts |
17 |
| - opts = null |
18 |
| - } |
| 17 | +function verify (cache, opts) { |
19 | 18 | opts = opts || {}
|
20 |
| - var lock = path.join(cache, 'verify.lock') |
21 |
| - var cb = function (err, stats) { |
22 |
| - lockfile.unlock(lock, function (er) { |
23 |
| - _cb(er || err, stats) |
| 19 | + opts.log && opts.log.verbose('verify', 'verifying content cache at', cache) |
| 20 | + const startTime = +(new Date()) |
| 21 | + return fixOwner.mkdirfix( |
| 22 | + cache, opts.uid, opts.gid |
| 23 | + ).then(() => { |
| 24 | + const lockPath = path.join(cache, 'verify.lock') |
| 25 | + const lock = lockfile.lockAsync(lockPath).disposer(() => { |
| 26 | + return lockfile.unlock(lockPath) |
24 | 27 | })
|
25 |
| - } |
26 |
| - fixOwner.mkdirfix(cache, opts.uid, opts.gid, function (err) { |
27 |
| - if (err) { return _cb(err) } |
28 |
| - lockfile.lock(lock, function (err) { |
29 |
| - if (err) { return _cb(err) } |
30 |
| - garbageCollect(cache, opts, function (err, gcStats) { |
31 |
| - if (err) { return cb(err) } |
32 |
| - tidyIndex(cache, opts, function (err, tidyStats) { |
33 |
| - if (err) { return cb(err) } |
| 28 | + return Promise.using(lock, () => { |
| 29 | + return garbageCollect(cache, opts).then(gcStats => { |
| 30 | + return tidyIndex(cache, opts).then(tidyStats => { |
34 | 31 | var stats = tidyStats
|
35 | 32 | Object.keys(gcStats).forEach(function (key) {
|
36 | 33 | stats[key] = gcStats[key]
|
37 | 34 | })
|
38 |
| - var verifile = path.join(cache, '_lastverified') |
39 |
| - fs.writeFile(verifile, '' + (+(new Date())), function (err) { |
40 |
| - if (err) { return cb(err) } |
41 |
| - fixOwner.chownr(cache, opts.uid, opts.gid, function (err) { |
42 |
| - if (err) { return cb(err) } |
43 |
| - rimraf(path.join(cache, 'tmp'), function (err) { |
44 |
| - if (err) { return cb(err) } |
45 |
| - cb(null, stats) |
46 |
| - }) |
47 |
| - }) |
48 |
| - }) |
| 35 | + return stats |
49 | 36 | })
|
| 37 | + }).then(stats => { |
| 38 | + var verifile = path.join(cache, '_lastverified') |
| 39 | + opts.log && opts.log.verbose('verify', 'writing verifile to ' + verifile) |
| 40 | + return fs.writeFileAsync( |
| 41 | + verifile, '' + (+(new Date())) |
| 42 | + ).then(() => { |
| 43 | + opts.log && opts.log.verbose('verify', 'fixing cache ownership') |
| 44 | + return fixOwner.chownr(cache, opts.uid, opts.gid) |
| 45 | + }).then(() => { |
| 46 | + opts.log && opts.log.verbose('verify', 'clearing out tmp') |
| 47 | + return rimraf(path.join(cache, 'tmp')) |
| 48 | + }).then(() => stats) |
50 | 49 | })
|
51 | 50 | })
|
| 51 | + }).then(stats => { |
| 52 | + stats.runTime = (+(new Date()) - startTime) / 1000 |
| 53 | + opts.log && opts.log.verbose('verify', 'final stats:', stats) |
| 54 | + return stats |
52 | 55 | })
|
53 | 56 | }
|
54 | 57 |
|
55 |
| -function tidyIndex (cache, opts, cb) { |
56 |
| - index.ls(cache, function (err, entries) { |
57 |
| - if (err) { return cb(err) } |
58 |
| - rimraf(path.join(cache, 'index'), function (err) { |
59 |
| - if (err) { return cb(err) } |
| 58 | +function tidyIndex (cache, opts) { |
| 59 | + opts.log && opts.log.verbose('verify', 'tidying index') |
| 60 | + return index.ls(cache).then(entries => { |
| 61 | + return rimraf(path.join(cache, 'index')).then(() => { |
60 | 62 | var stats = {
|
61 | 63 | entriesRemoved: 0,
|
62 | 64 | digestMissing: 0,
|
63 | 65 | totalEntries: 0
|
64 | 66 | }
|
65 |
| - asyncMap(Object.keys(entries), function (key, cb) { |
| 67 | + return Promise.reduce(Object.keys(entries), (stats, key) => { |
66 | 68 | var entry = entries[key]
|
67 | 69 | if (!entry.digest) {
|
68 | 70 | stats.digestMissing++
|
69 |
| - return cb() |
| 71 | + return stats |
70 | 72 | }
|
71 | 73 | var content = path.join(cache, 'content', entries[key].digest)
|
72 |
| - fs.stat(content, function (err) { |
73 |
| - if (err && err.code === 'ENOENT') { |
74 |
| - stats.entriesRemoved++ |
75 |
| - return cb() |
76 |
| - } else { |
77 |
| - stats.totalEntries++ |
78 |
| - index.insert(cache, key, entry.digest, { |
79 |
| - uid: opts.uid, |
80 |
| - gid: opts.gid, |
81 |
| - metadata: entry.metadata |
82 |
| - }, cb) |
| 74 | + return fs.statAsync(content).catch(err => { |
| 75 | + if (err.code === 'ENOENT') { |
| 76 | + stats.entriesRemoves++ |
| 77 | + return stats |
83 | 78 | }
|
| 79 | + }).then(() => { |
| 80 | + stats.totalEntries++ |
| 81 | + return index.insert(cache, key, entry.digest, { |
| 82 | + uid: opts.uid, |
| 83 | + gid: opts.gid, |
| 84 | + metadata: entry.metadata |
| 85 | + }).then(() => stats) |
84 | 86 | })
|
85 |
| - }, function (err) { |
86 |
| - if (err) { return cb(err) } |
87 |
| - cb(null, stats) |
88 |
| - }) |
| 87 | + }, stats) |
89 | 88 | })
|
90 | 89 | })
|
91 | 90 | }
|
92 | 91 |
|
93 |
| -function garbageCollect (cache, opts, cb) { |
94 |
| - index.ls(cache, function (err, entries) { |
| 92 | +function garbageCollect (cache, opts) { |
| 93 | + opts.log && opts.log.verbose('verify', 'garbage collecting content') |
| 94 | + return index.ls(cache).then(entries => { |
95 | 95 | var byDigest = {}
|
96 | 96 | Object.keys(entries).forEach(function (k) {
|
97 | 97 | byDigest[entries[k].digest] = entries[k]
|
98 | 98 | })
|
99 |
| - if (err) { return cb(err) } |
100 |
| - var stats = { |
101 |
| - verifiedContent: 0, |
102 |
| - collectedCount: 0, |
103 |
| - reclaimedSize: 0 |
104 |
| - } |
105 | 99 | var contentDir = path.join(cache, 'content')
|
106 |
| - fs.readdir(contentDir, function (err, files) { |
107 |
| - if (err && err.code === 'ENOENT') { |
108 |
| - return cb(null, stats) |
109 |
| - } else if (err) { |
110 |
| - return cb(err) |
| 100 | + return fs.readdirAsync(contentDir).catch(err => { |
| 101 | + if (err.code === 'ENOENT') { |
| 102 | + return |
111 | 103 | } else {
|
112 |
| - asyncMap(files, function (f, cb) { |
113 |
| - var fullPath = path.join(contentDir, f) |
114 |
| - if (byDigest[f]) { |
115 |
| - var algo = opts.hashAlgorithm || 'sha1' |
116 |
| - verifyContent(fullPath, algo, function (err, collected) { |
117 |
| - if (err) { return cb(err) } |
118 |
| - if (collected != null) { |
119 |
| - stats.collectedCount++ |
120 |
| - stats.reclaimedSize += collected |
121 |
| - } else { |
122 |
| - stats.verifiedContent++ |
123 |
| - } |
124 |
| - cb() |
125 |
| - }) |
126 |
| - } else { |
127 |
| - stats.collectedCount++ |
128 |
| - fs.stat(fullPath, function (err, s) { |
129 |
| - if (err) { return cb(err) } |
130 |
| - stats.reclaimedSize += s.size |
131 |
| - rimraf(path.join(contentDir, f), cb) |
132 |
| - }) |
133 |
| - } |
134 |
| - }, function (err) { |
135 |
| - if (err) { return cb(err) } |
136 |
| - cb(null, stats) |
137 |
| - }) |
| 104 | + throw err |
138 | 105 | }
|
| 106 | + }).then(files => { |
| 107 | + var stats = { |
| 108 | + verifiedContent: 0, |
| 109 | + collectedCount: 0, |
| 110 | + reclaimedSize: 0, |
| 111 | + keptSize: 0 |
| 112 | + } |
| 113 | + return Promise.reduce(files, (stats, f) => { |
| 114 | + var fullPath = path.join(contentDir, f) |
| 115 | + if (byDigest[f]) { |
| 116 | + var algo = opts.hashAlgorithm || 'sha1' |
| 117 | + return verifyContent(fullPath, algo).then(info => { |
| 118 | + if (!info.valid) { |
| 119 | + stats.collectedCount++ |
| 120 | + stats.reclaimedSize += info.size |
| 121 | + } else { |
| 122 | + stats.verifiedContent++ |
| 123 | + stats.keptSize += info.size |
| 124 | + } |
| 125 | + return stats |
| 126 | + }) |
| 127 | + } else { |
| 128 | + stats.collectedCount++ |
| 129 | + return fs.statAsync(fullPath).then(s => { |
| 130 | + stats.reclaimedSize += s.size |
| 131 | + return rimraf(path.join(contentDir, f)).then(() => stats) |
| 132 | + }) |
| 133 | + } |
| 134 | + }, stats) |
139 | 135 | })
|
140 | 136 | })
|
141 | 137 | }
|
142 | 138 |
|
143 |
| -function verifyContent (filepath, algo, cb) { |
144 |
| - fs.stat(filepath, function (err, stat) { |
145 |
| - if (err) { return cb(err) } |
| 139 | +function verifyContent (filepath, algo) { |
| 140 | + return fs.statAsync(filepath).then(stat => { |
146 | 141 | var reader = fs.createReadStream(filepath)
|
147 | 142 | var checksummer = checksumStream({
|
148 | 143 | digest: path.basename(filepath),
|
149 | 144 | algorithm: algo
|
150 | 145 | })
|
151 |
| - checksummer.on('data', function () {}) |
152 |
| - pipe(reader, checksummer, function (err) { |
| 146 | + var contentInfo = { |
| 147 | + size: stat.size, |
| 148 | + valid: true |
| 149 | + } |
| 150 | + checksummer.on('data', () => {}) |
| 151 | + return pipe(reader, checksummer).catch(err => { |
153 | 152 | if (err && err.code === 'EBADCHECKSUM') {
|
154 |
| - rimraf(filepath, function (err) { |
155 |
| - if (err) { return cb(err) } |
156 |
| - cb(null, stat.size) |
| 153 | + return rimraf(filepath).then(() => { |
| 154 | + contentInfo.valid = false |
157 | 155 | })
|
158 |
| - } else if (err) { |
159 |
| - return cb(err) |
160 | 156 | } else {
|
161 |
| - cb(null, null) |
| 157 | + throw err |
162 | 158 | }
|
163 |
| - }) |
| 159 | + }).then(() => contentInfo) |
164 | 160 | })
|
165 | 161 | }
|
166 | 162 |
|
167 | 163 | module.exports.lastRun = lastRun
|
168 |
| -function lastRun (cache, cb) { |
169 |
| - fs.readFile(path.join(cache, '_lastverified'), 'utf8', function (err, data) { |
170 |
| - if (err) { return cb(err) } |
171 |
| - cb(null, new Date(+data)) |
172 |
| - }) |
| 164 | +function lastRun (cache) { |
| 165 | + return fs.readFileAsync( |
| 166 | + path.join(cache, '_lastverified'), 'utf8' |
| 167 | + ).then(data => new Date(+data)) |
173 | 168 | }
|
0 commit comments