UNPKG

7.39 kBJavaScriptView Raw
1'use strict';
2
3const httpx = require('httpx');
4const cheerio = require('cheerio');
5const _ = require('lodash');
6const { red } = require('colors');
7const { Transform } = require('stream');
8const unrefTimeout = require('./unref-timeout');
9const detectMocha = require('detect-mocha');
10
11class FilterChain {
12 constructor(options) {
13 this.processors = [
14 new PuppeteerInvalidPlatformProcessor(options),
15 new DynamicLinkLibraryMissingProcessor(options),
16 new NoSpaceLeftOnDeviceProcessor(options),
17 new MissingAptGetProcessor(options)];
18 }
19
20 async process(message) {
21 for (const processor of this.processors) {
22 if (processor.match(message)) {
23 await processor.process(message);
24 await processor.postProcess();
25 return;
26 }
27 }
28 }
29}
30
31class ErrorProcessor {
32 constructor(options = {}) {
33 this.serviceName = options.serviceName;
34 this.functionName = options.functionName;
35 }
36
37 match(message) { }
38 async process(message) { }
39
40 _autoExist() {
41 process.nextTick(() => {
42 console.log(red('\nFun will auto exit after 3 seconds.\n'));
43
44 if (!detectMocha()) {
45 unrefTimeout(() => {
46 process.emit('SIGINT');
47 }, 3000);
48 }
49 });
50 }
51
52 async postProcess() {
53
54 }
55}
56
57// 发生在 fun install 安装依赖,但是依赖包含解决方案,比如 puppeteer,需要使用 apt-get 安装,如果宿主机没有,那就提示使用 fun install -d
58class MissingAptGetProcessor extends ErrorProcessor {
59 match(message) {
60 return _.includes(message, 'touch: /var/cache/apt/pkgcache.bin: No such file or directory');
61 }
62
63 async process(message) {
64 process.nextTick(() => {
65 console.log(red(`Tips: Fun has detected that there is no apt-get installed on the machine, you need use 'fun install --use-docker' to reinstall.
66Type 'fun install -h' for more help.`));
67 });
68 }
69}
70
71class NoSpaceLeftOnDeviceProcessor extends ErrorProcessor {
72 match(message) {
73 return _.includes(message, 'no space left on device');
74 }
75
76 async process(message) {
77 process.nextTick(() => {
78 console.log(red(`Tips: Fun has detected that docker is no space left.
79if You are using Docker for Windows/Mac, you can select the Docker icon and then Preferences > Resources > Advanced and increase docker image size.
80Please refer to https://docs.docker.com/docker-for-mac/space/ for more help.
81`));
82 });
83 }
84}
85
86class DynamicLinkLibraryMissingProcessor extends ErrorProcessor {
87
88 constructor(options) {
89 super(options);
90
91 this.prefix = 'error while loading shared libraries: ';
92 this.suffix = ': cannot open shared object file: No such file or directory';
93 this.debianPakcageUrlPrefix = 'https://packages.debian.org/search?lang=en&suite=jessie&arch=amd64&mode=path&searchon=contents&keywords=';
94 this.libPrefixWhiteList = ['/usr/lib/x86_64-linux-gnu', '/lib/x86_64-linux-gnu', '/usr/local/lib'];
95 }
96
97 match(message) {
98 return _.includes(message, this.prefix)
99 && _.includes(message, this.suffix);
100 }
101
102 async _findPackageByDlName(lib) {
103 const response = await httpx.request(`${this.debianPakcageUrlPrefix}${lib}`, { timeout: 10000 });
104
105 const body = await httpx.read(response, 'utf8');
106
107 const $ = cheerio.load(body);
108
109 const packagesTable = $('#pcontentsres table tbody tr').map((i, element) => ({
110 path: $(element).find('td:nth-of-type(1)').text().trim(),
111 name: $(element).find('td:nth-of-type(2)').text().trim()
112 })).get();
113
114 const packageInfo = _.find(packagesTable, info => _.some(this.libPrefixWhiteList, (prefix) => info.path.startsWith(prefix)));
115
116 if (packageInfo) {
117 return packageInfo.name;
118 }
119
120 return null;
121 }
122
123 async _fetchDlName(message) {
124 // error while loading shared libraries: libnss3.so: cannot open shared object file: No such file or directory
125
126 const prefixIdx = message.indexOf(this.prefix);
127 const suffixIdx = message.indexOf(this.suffix);
128
129 return message.substring(prefixIdx + this.prefix.length, suffixIdx);
130 }
131
132 async process(message) {
133 const lib = await this._fetchDlName(message);
134
135 const packageName = await this._findPackageByDlName(lib);
136
137 if (packageName) {
138 process.nextTick(() => {
139 console.log(red(`Tips: Fun has detected that you are missing ${lib} library, you can try to install it like this:
140
141 step1: fun install sbox -f ${this.serviceName}/${this.functionName} -i
142 step2: fun-install apt-get install ${packageName}
143 step3: type 'exit' to exit container and then reRun your function
144
145Also you can install dependencies through one command:
146
147 fun install sbox -f ${this.serviceName}/${this.functionName} --cmd 'fun-install apt-get install ${packageName}'
148`));
149 });
150 } else {
151 console.log(red(`Tips: Fun has detected that you are missing ${lib} library, you can try to install it like this:
152
153 step1: open this page ${this.debianPakcageUrlPrefix}${lib} to find your missing dependency
154 step2: fun install sbox -f ${this.serviceName}/${this.functionName} -i
155 step3: fun-install apt-get install YourPackageName
156 step4: type 'exit' to exit container and then reRun your function
157
158Also you can install dependencies through one command:
159
160 fun install sbox -f ${this.serviceName}/${this.functionName} --cmd 'fun-install apt-get install YourPackageName'
161`));
162 }
163
164 this._autoExist();
165 }
166}
167
168class PuppeteerInvalidPlatformProcessor extends ErrorProcessor {
169 match(message) {
170 return _.includes(message, 'Error: Chromium revision is not downloaded. Run "npm install" or "yarn install"');
171 }
172
173 async process(message) {
174 process.nextTick(() => {
175 console.log(red(`Tips: Fun has detected that your puppeteer installation platform is incorrect.
176Please reinstall it like this:
177
1781. fun install sbox -f ${this.serviceName}/${this.functionName} -i
1792. fun-install npm install puppeteer
1803. type 'exit' to exit container and then reRun your function
181
182Also you can install puppeteer through one command:
183fun install sbox -f puppeteer/html2png --cmd 'fun-install npm install puppeteer'`));
184
185 this._autoExist();
186 });
187 }
188}
189
190class ChunkSplitTransform extends Transform {
191 constructor(options) {
192 super(options);
193 this._buffer = '';
194 this._separator = options.separator || '\n';
195 }
196
197 _transform(chunk, encoding, done) {
198 let sepPos;
199 this._buffer += chunk.toString();
200
201 while ((sepPos = this._buffer.indexOf(this._separator)) !== -1) {
202 const portion = this._buffer.substr(0, sepPos);
203 this.push(portion + this._separator);
204 this._buffer = this._buffer.substr(sepPos + this._separator.length);
205 }
206
207 done();
208 }
209
210 _flush(done) {
211 this.push(this._buffer);
212 done();
213 }
214}
215
216class FcErrorTransform extends Transform {
217 constructor(options) {
218 super(options);
219 this.filterChain = new FilterChain(options);
220 }
221 _transform(chunk, encoding, done) {
222 const message = chunk.toString();
223 this.filterChain.process(message).then(() => {
224 this.push(message);
225 done();
226 });
227 }
228}
229
230function processorTransformFactory({
231 serviceName,
232 functionName,
233 errorStream
234}) {
235 const transform = new ChunkSplitTransform({
236 separator: '\n'
237 });
238
239 transform.pipe(new FcErrorTransform({
240 serviceName: serviceName,
241 functionName: functionName
242 })).pipe(errorStream);
243
244 return transform;
245}
246
247module.exports = {
248 processorTransformFactory,
249 FilterChain
250};