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