UNPKG

9.81 kBJavaScriptView Raw
1'use strict';
2
3const httpx = require('httpx');
4const cheerio = require('cheerio');
5const detectMocha = require('detect-mocha');
6
7const { red } = require('colors');
8const { Transform } = require('stream');
9const { unrefTimeout } = require('./unref-timeout');
10
11const _ = require('lodash');
12
13class FilterChain {
14 constructor(options = {}) {
15 this.processors = [
16 new PuppeteerInvalidPlatformProcessor(options),
17 new DynamicLinkLibraryMissingProcessor(options),
18 new NoSpaceLeftOnDeviceProcessor(options),
19 new MissingAptGetProcessor(options),
20 new DockerNotStartedOrInstalledErrorProcessor(options),
21 new FcServiceNotEnabledProcessor(options),
22 new RamInactiveErrorProcessor(options),
23 new RosStackValidationErrorProcessor(options),
24 new LogInactiveErrorProcessor(options)
25
26 ];
27 }
28
29 async process(message, err) {
30 for (const processor of this.processors) {
31 if (!message) { message = ''; }
32
33 if (processor.match(message, err)) {
34 await processor.process(message, err);
35 await processor.postProcess();
36 return true;
37 }
38 }
39 }
40}
41
42class ErrorProcessor {
43 constructor(options = {}) {
44 this.serviceName = options.serviceName;
45 this.functionName = options.functionName;
46 }
47
48 match(message, err) { }
49 async process(message, err) { }
50
51 _autoExist() {
52 process.nextTick(() => {
53 console.log(red('\nFun will auto exit after 3 seconds.\n'));
54
55 if (!detectMocha()) {
56 unrefTimeout(() => {
57 process.emit('SIGINT');
58 }, 3000);
59 }
60 });
61 }
62
63 async postProcess() {
64 console.log();
65 }
66}
67
68class DockerNotStartedOrInstalledErrorProcessor extends ErrorProcessor {
69 match(message, err) {
70 if (_.includes(message, 'connect ECONNREFUSED /var/run/docker.sock')
71 || _.includes(message, 'Error: connect ENOENT //./pipe/docker_engine')) {
72 return true;
73 }
74
75 return false;
76 }
77
78 async process(message) {
79 console.log(red('Fun detected that Docker is not installed on your host or not started. Please run \'docker ps\' command to check docker status.'));
80 }
81}
82
83class FcServiceNotEnabledProcessor extends ErrorProcessor {
84 match(message, err) {
85 if (_.includes(message, 'FC service is not enabled for current user')) {
86 return true;
87 }
88
89 return false;
90 }
91
92 async process(message) {
93 console.log(red('FC service is not enabled for current user. Please enable FC service before using fun.\nYou can enable FC service on this page https://www.aliyun.com/product/fc .'));
94 }
95}
96
97class RamInactiveErrorProcessor extends ErrorProcessor {
98 match(message, err) {
99 return (_.includes(message, 'Account is inactive to this service') && _.includes(message, 'ram.aliyuncs.com'));
100 }
101
102 async process(message) {
103 console.log(red('Ram service is not enabled for current user. Please enable Ram service before using fun.\nYou can enable Ram service on this page https://www.aliyun.com/product/ram .'));
104 }
105}
106
107
108class RosStackValidationErrorProcessor extends ErrorProcessor {
109 match(message, err) {
110 return _.includes(message, 'Function CodeUri must be an oss bucket, try using package');
111 }
112
113 async process(message) {
114 console.log(red('StackValidationFailed: template syntax mismatch with ROS support. You may be able to solve it by executing the command \'fun package\'.'));
115 }
116}
117
118class LogInactiveErrorProcessor extends ErrorProcessor {
119 match(message, err) {
120 return err && err.code === 'InvalidAccessKeyId' && _.includes(message, 'AccessKeyId') && _.includes(message, 'is inactive');
121 }
122
123 async process(message) {
124 console.log(red('\nPlease go to https://sls.console.aliyun.com/ to open the LogServce.'));
125 }
126}
127
128// 发生在 fun install 安装依赖,但是依赖包含解决方案,比如 puppeteer,需要使用 apt-get 安装,如果宿主机没有,那就提示使用 fun install -d
129class MissingAptGetProcessor extends ErrorProcessor {
130 match(message) {
131 return _.includes(message, 'touch: /var/cache/apt/pkgcache.bin: No such file or directory');
132 }
133
134 async process(message) {
135 process.nextTick(() => {
136 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.
137Type 'fun install -h' for more help.`));
138 });
139 }
140}
141
142class NoSpaceLeftOnDeviceProcessor extends ErrorProcessor {
143 match(message) {
144 return _.includes(message, 'no space left on device');
145 }
146
147 async process(message) {
148 process.nextTick(() => {
149 console.log(red(`Tips: Fun has detected that docker is no space left.
150if You are using Docker for Windows/Mac, you can select the Docker icon and then Preferences > Resources > Advanced and increase docker image size.
151Please refer to https://docs.docker.com/docker-for-mac/space/ for more help.
152`));
153 });
154 }
155}
156
157class DynamicLinkLibraryMissingProcessor extends ErrorProcessor {
158
159 constructor(options) {
160 super(options);
161
162 this.prefix = 'error while loading shared libraries: ';
163 this.suffix = ': cannot open shared object file: No such file or directory';
164 this.debianPakcageUrlPrefix = 'https://packages.debian.org/search?lang=en&suite=jessie&arch=amd64&mode=path&searchon=contents&keywords=';
165 this.libPrefixWhiteList = ['/usr/lib/x86_64-linux-gnu', '/lib/x86_64-linux-gnu', '/usr/local/lib'];
166 }
167
168 match(message) {
169 return _.includes(message, this.prefix)
170 && _.includes(message, this.suffix);
171 }
172
173 async _findPackageByDlName(lib) {
174 const response = await httpx.request(`${this.debianPakcageUrlPrefix}${lib}`, { timeout: 10000 });
175
176 const body = await httpx.read(response, 'utf8');
177
178 const $ = cheerio.load(body);
179
180 const packagesTable = $('#pcontentsres table tbody tr').map((i, element) => ({
181 path: $(element).find('td:nth-of-type(1)').text().trim(),
182 name: $(element).find('td:nth-of-type(2)').text().trim()
183 })).get();
184
185 const packageInfo = _.find(packagesTable, info => _.some(this.libPrefixWhiteList, (prefix) => info.path.startsWith(prefix)));
186
187 if (packageInfo) {
188 return packageInfo.name;
189 }
190
191 return null;
192 }
193
194 async _fetchDlName(message) {
195 // error while loading shared libraries: libnss3.so: cannot open shared object file: No such file or directory
196
197 const prefixIdx = message.indexOf(this.prefix);
198 const suffixIdx = message.indexOf(this.suffix);
199
200 return message.substring(prefixIdx + this.prefix.length, suffixIdx);
201 }
202
203 async process(message) {
204 const lib = await this._fetchDlName(message);
205
206 const packageName = await this._findPackageByDlName(lib);
207
208 if (packageName) {
209 process.nextTick(() => {
210 console.log(red(`Tips: Fun has detected that you are missing ${lib} library, you can try to install it like this:
211
212 step1: fun install sbox -f ${this.serviceName}/${this.functionName} -i
213 step2: fun-install apt-get install ${packageName}
214 step3: type 'exit' to exit container and then reRun your function
215
216Also you can install dependencies through one command:
217
218 fun install sbox -f ${this.serviceName}/${this.functionName} --cmd 'fun-install apt-get install ${packageName}'
219`));
220 });
221 } else {
222 console.log(red(`Tips: Fun has detected that you are missing ${lib} library, you can try to install it like this:
223
224 step1: open this page ${this.debianPakcageUrlPrefix}${lib} to find your missing dependency
225 step2: fun install sbox -f ${this.serviceName}/${this.functionName} -i
226 step3: fun-install apt-get install YourPackageName
227 step4: type 'exit' to exit container and then reRun your function
228
229Also you can install dependencies through one command:
230
231 fun install sbox -f ${this.serviceName}/${this.functionName} --cmd 'fun-install apt-get install YourPackageName'
232`));
233 }
234
235 this._autoExist();
236 }
237}
238
239class PuppeteerInvalidPlatformProcessor extends ErrorProcessor {
240 match(message) {
241 return _.includes(message, 'Error: Chromium revision is not downloaded. Run "npm install" or "yarn install"');
242 }
243
244 async process(message) {
245 process.nextTick(() => {
246 console.log(red(`Tips: Fun has detected that your puppeteer installation platform is incorrect.
247Please reinstall it like this:
248
2491. fun install sbox -f ${this.serviceName}/${this.functionName} -i
2502. fun-install npm install puppeteer
2513. type 'exit' to exit container and then reRun your function
252
253Also you can install puppeteer through one command:
254fun install sbox -f puppeteer/html2png --cmd 'fun-install npm install puppeteer'`));
255
256 this._autoExist();
257 });
258 }
259}
260
261class ChunkSplitTransform extends Transform {
262 constructor(options) {
263 super(options);
264 this._buffer = '';
265 this._separator = options.separator || '\n';
266 }
267
268 _transform(chunk, encoding, done) {
269 let sepPos;
270 this._buffer += chunk.toString();
271
272 while ((sepPos = this._buffer.indexOf(this._separator)) !== -1) {
273 const portion = this._buffer.substr(0, sepPos);
274 this.push(portion + this._separator);
275 this._buffer = this._buffer.substr(sepPos + this._separator.length);
276 }
277
278 done();
279 }
280
281 _flush(done) {
282 this.push(this._buffer);
283 done();
284 }
285}
286
287class FcErrorTransform extends Transform {
288 constructor(options) {
289 super(options);
290 this.filterChain = new FilterChain(options);
291 }
292 _transform(chunk, encoding, done) {
293 const message = chunk.toString();
294 this.filterChain.process(message).then(() => {
295 this.push(message);
296 done();
297 });
298 }
299}
300
301function processorTransformFactory({
302 serviceName,
303 functionName,
304 errorStream
305}) {
306 const transform = new ChunkSplitTransform({
307 separator: '\n'
308 });
309
310 transform.pipe(new FcErrorTransform({
311 serviceName: serviceName,
312 functionName: functionName
313 })).pipe(errorStream);
314
315 return transform;
316}
317
318module.exports = {
319 processorTransformFactory,
320 FilterChain
321};