Skip to content

Commit a63ca57

Browse files
Merge branch 'nokia-artif_api' into develop
2 parents bd17141 + 6a0cb42 commit a63ca57

File tree

6 files changed

+133
-21
lines changed

6 files changed

+133
-21
lines changed

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,18 @@ This server exposes the following REST API's:
185185
- **[WIP] POST `/config`**
186186

187187
Allows you to edit the ElastAlert configuration from `config.yaml` in `elastalertPath` (from the config). The required body to be send will be edited when the work on this API is done.
188-
188+
189+
- **[WIP] POST `/download`**
190+
191+
Allows you to download a .tar archive with rules from a given HTTP endpoint. The archive will be downloaded, extracted and removed.
192+
Please note, body should contain URL pointing to tar archive, with tar extension.
193+
194+
Usage example:
195+
196+
```bash
197+
curl -X POST localhost:3030/download -d "url=https://artifactory.com:443/artifactory/raw/rules/rules.tar"
198+
```
199+
189200
## Contributing
190201
Want to contribute to this project? Great! Please read our [contributing guidelines](CONTRIBUTING.md) before submitting an issue or a pull request.
191202

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@
2929
"mkdirp": "^0.5.1",
3030
"object-resolve-path": "^1.1.1",
3131
"randomstring": "^1.1.5",
32-
"raven": "^2.3.0"
32+
"raven": "^2.3.0",
33+
"tar": "^4.4.1",
34+
"fs-extra": "^5.0.0",
35+
"request-promise-native": "^1.0.5"
3336
},
3437
"devDependencies": {
3538
"eslint": "^4.17.0",

src/common/errors/rule_request_errors.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,13 @@ export class RulesRootFolderNotCreatableError extends RequestError {
4141
super('rulesRootFolderNotCreatable', 'The rules folder wasn\'t found and couldn\'t be created by the file system.', 403);
4242
}
4343
}
44+
export class URLNotSentError extends RequestError {
45+
constructor() {
46+
super('URLNotSentError', 'URL is missing in the body', 400);
47+
}
48+
}
49+
export class URLNotPointingTar extends RequestError {
50+
constructor() {
51+
super('URLNotPointingTar', 'URL is not pointing to .tar file', 400);
52+
}
53+
}

src/controllers/rules/index.js

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,16 @@
1-
import {join as joinPath, normalize as normalizePath, extname as pathExtension} from 'path';
1+
import { join as joinPath, normalize as normalizePath, extname as pathExtension } from 'path';
22
import mkdirp from 'mkdirp';
3+
import tar from 'tar';
4+
import fs from 'fs-extra';
5+
import rq from 'request-promise-native';
36
import FileSystem from '../../common/file_system';
47
import config from '../../common/config';
58
import Logger from '../../common/logger';
6-
import {RuleNotFoundError, RuleNotReadableError, RuleNotWritableError,
7-
RulesFolderNotFoundError, RulesRootFolderNotCreatableError} from '../../common/errors/rule_request_errors';
9+
import path from 'path';
10+
import {
11+
RuleNotFoundError, RuleNotReadableError, RuleNotWritableError,
12+
RulesFolderNotFoundError, RulesRootFolderNotCreatableError
13+
} from '../../common/errors/rule_request_errors';
814

915
let logger = new Logger('RulesController');
1016

@@ -17,26 +23,26 @@ export default class RulesController {
1723
getRules(path) {
1824
const self = this;
1925
const fullPath = joinPath(self.rulesFolder, path);
20-
return new Promise(function (resolve, reject) {
26+
return new Promise(function(resolve, reject) {
2127
self._fileSystemController.readDirectory(fullPath)
22-
.then(function (directoryIndex) {
28+
.then(function(directoryIndex) {
2329

24-
directoryIndex.rules = directoryIndex.files.filter(function (fileName) {
30+
directoryIndex.rules = directoryIndex.files.filter(function(fileName) {
2531
return pathExtension(fileName).toLowerCase() === '.yaml';
26-
}).map(function (fileName) {
32+
}).map(function(fileName) {
2733
return fileName.slice(0, -5);
2834
});
2935

3036
delete directoryIndex.files;
3137
resolve(directoryIndex);
3238
})
33-
.catch(function (error) {
39+
.catch(function(error) {
3440

3541
// Check if the requested folder is the rules root folder
3642
if (normalizePath(self.rulesFolder) === fullPath) {
3743

3844
// Try to create the root folder
39-
mkdirp(fullPath, function (error) {
45+
mkdirp(fullPath, function(error) {
4046
if (error) {
4147
reject(new RulesRootFolderNotCreatableError());
4248
logger.warn(`The rules root folder (${fullPath}) couldn't be found nor could it be created by the file system.`);
@@ -54,29 +60,29 @@ export default class RulesController {
5460

5561
rule(id) {
5662
const self = this;
57-
return new Promise(function (resolve, reject) {
63+
return new Promise(function(resolve, reject) {
5864
self._findRule(id)
59-
.then(function (access) {
65+
.then(function(access) {
6066
console.log('rule resolved');
6167
resolve({
62-
get: function () {
68+
get: function() {
6369
if (access.read) {
6470
return self._getRule(id);
6571
}
6672
return self._getErrorPromise(new RuleNotReadableError(id));
6773
},
68-
edit: function (body) {
74+
edit: function(body) {
6975
if (access.write) {
7076
return self._editRule(id, body);
7177
}
7278
return self._getErrorPromise(new RuleNotWritableError(id));
7379
},
74-
delete: function () {
80+
delete: function() {
7581
return self._deleteRule(id);
7682
}
7783
});
7884
})
79-
.catch(function () {
85+
.catch(function() {
8086
console.log('catched');
8187
reject(new RuleNotFoundError(id));
8288
});
@@ -87,12 +93,16 @@ export default class RulesController {
8793
return this._editRule(id, content);
8894
}
8995

96+
downloadRules(URL) {
97+
return this._downloadRules(URL);
98+
}
99+
90100
_findRule(id) {
91101
let fileName = id + '.yaml';
92102
const self = this;
93-
return new Promise(function (resolve, reject) {
103+
return new Promise(function(resolve, reject) {
94104
self._fileSystemController.fileExists(joinPath(self.rulesFolder, fileName))
95-
.then(function (exists) {
105+
.then(function(exists) {
96106
if (!exists) {
97107
reject();
98108
} else {
@@ -104,7 +114,7 @@ export default class RulesController {
104114
});
105115
}
106116
})
107-
.catch(function (error) {
117+
.catch(function(error) {
108118
reject(error);
109119
});
110120
});
@@ -125,8 +135,21 @@ export default class RulesController {
125135
return this._fileSystemController.deleteFile(path);
126136
}
127137

138+
_downloadRules(URL) {
139+
const options = {
140+
uri: URL,
141+
strictSSL: false
142+
};
143+
const filename = path.basename(URL);
144+
145+
return rq.get(options)
146+
.then(buffer => fs.outputFile(filename, buffer)
147+
.then(() => this._untarFile(this.rulesFolder, filename))
148+
.then(() => fs.remove(filename)));
149+
}
150+
128151
_getErrorPromise(error) {
129-
return new Promise(function (resolve, reject) {
152+
return new Promise(function(resolve, reject) {
130153
reject(error);
131154
});
132155
}
@@ -140,4 +163,13 @@ export default class RulesController {
140163
return ruleFolderSettings.path;
141164
}
142165
}
166+
167+
_untarFile(path_to_extract, archive) {
168+
return tar.extract(
169+
{
170+
cwd: path_to_extract,
171+
file: archive
172+
}
173+
);
174+
}
143175
}

src/handlers/rules/id/download.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import RouteLogger from '../../../routes/route_logger';
2+
import { sendRequestError } from '../../../common/errors/utils';
3+
import { URLNotSentError, URLNotPointingTar } from '../../../common/errors/rule_request_errors';
4+
import path from 'path';
5+
6+
let logger = new RouteLogger('/download');
7+
8+
9+
export default function downloadRulesHandler(request, response) {
10+
/**
11+
* @type {ElastalertServer}
12+
*/
13+
let server = request.app.get('server');
14+
let body = request.body;
15+
16+
17+
function _test_body(body) {
18+
if (!body || !body.url) {
19+
return new URLNotSentError();
20+
}
21+
let filename = path.basename(body.url);
22+
if (!filename.endsWith('.tar')) {
23+
return new URLNotPointingTar();
24+
}
25+
return body;
26+
}
27+
28+
body = _test_body(body);
29+
if (body.error) {
30+
logger.sendFailed(body.error);
31+
sendRequestError(response, body.error);
32+
return;
33+
}
34+
35+
36+
server.rulesController.downloadRules(body.url)
37+
.then(() => {
38+
logger.sendSuccessful();
39+
return response.send({
40+
downloaded: true,
41+
extracted: true,
42+
url: body.url
43+
});
44+
})
45+
.catch((error) => {
46+
logger.sendFailed(error);
47+
sendRequestError(response, error);
48+
});
49+
50+
}

src/routes/routes.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import controlHandler from '../handlers/status/control';
44
import errorsHandler from '../handlers/status/errors';
55
import rulesHandler from '../handlers/rules';
66
import ruleGetHandler from '../handlers/rules/id/get';
7+
import downloadRulesHandler from '../handlers/rules/id/download';
78
import rulePostHandler from '../handlers/rules/id/post';
89
import ruleDeleteHandler from '../handlers/rules/id/delete';
910
import templatesHandler from '../handlers/templates';
@@ -68,6 +69,11 @@ let routes = [
6869
path: 'config',
6970
method: ['GET', 'POST'],
7071
handler: [configGetHandler, configPostHandler]
72+
},
73+
{
74+
path: 'download',
75+
method: ['POST'],
76+
handler: [downloadRulesHandler]
7177
}
7278
];
7379

0 commit comments

Comments
 (0)