Skip to content
This repository was archived by the owner on Mar 5, 2018. It is now read-only.

Commit 4f85af7

Browse files
committed
Add event subscriptions
1 parent d496296 commit 4f85af7

File tree

5 files changed

+256
-18
lines changed

5 files changed

+256
-18
lines changed

lib/marathon.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ var rapi = require('rapi');
1212
var util = require('util');
1313

1414
var Apps = require('./marathon/apps').Apps;
15+
var EventSubscriptions = require('./marathon/event_subscriptions')
16+
.EventSubscriptions;
1517

1618
/**
1719
* Initialize a new `Marathon` client.
@@ -35,6 +37,7 @@ function Marathon(options) {
3537
rapi.Rapi.call(this, options);
3638

3739
this.apps = new Apps(this);
40+
this.eventSubscriptions = new EventSubscriptions(this);
3841
}
3942

4043
util.inherits(Marathon, rapi.Rapi);

lib/marathon/apps.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,7 @@ function Apps(marathon) {
1717
*/
1818

1919
Apps.prototype.create = function(options, callback) {
20-
if (typeof options === 'function') {
21-
callback = options;
22-
options = {};
23-
}
20+
options = options || {};
2421

2522
this.marathon.emit('debug', 'apps.create', options);
2623

lib/marathon/event_subscriptions.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**
2+
* Event subscriptions
3+
*/
4+
5+
'use strict';
6+
7+
/**
8+
* Initialize a new `EventSubscriptions` client.
9+
*/
10+
11+
function EventSubscriptions(marathon) {
12+
this.marathon = marathon;
13+
}
14+
15+
/**
16+
* Register a callback URL as an event subscriber.
17+
*/
18+
19+
EventSubscriptions.prototype.register = function(options, callback) {
20+
if (typeof options === 'string') {
21+
options = { url: options };
22+
}
23+
24+
this.marathon.emit('debug', 'eventSubscriptions.register', options);
25+
26+
if (!options.url) return callback(new Error('url required'));
27+
28+
var req = {
29+
method: 'POST',
30+
path: '/eventSubscriptions',
31+
query: { callbackUrl: options.url },
32+
};
33+
34+
this.marathon.request(req, function(err) {
35+
if (err) return callback(err);
36+
37+
callback();
38+
});
39+
};
40+
41+
/**
42+
* List all event subscriber callback URLs.
43+
*/
44+
45+
EventSubscriptions.prototype.list = function(options, callback) {
46+
if (typeof options === 'function') {
47+
callback = options;
48+
options = {};
49+
}
50+
51+
this.marathon.emit('debug', 'eventSubscriptions.list', options);
52+
53+
var req = {
54+
method: 'GET',
55+
path: '/eventSubscriptions',
56+
};
57+
58+
this.marathon.request(req, function(err, res) {
59+
if (err) return callback(err);
60+
61+
callback(null, res.body.callbackUrls);
62+
});
63+
};
64+
65+
/**
66+
* Unregister a callback URL from the event subscribers list.
67+
*/
68+
69+
EventSubscriptions.prototype.unregister = function(options, callback) {
70+
if (typeof options === 'string') {
71+
options = { url: options };
72+
}
73+
74+
this.marathon.emit('debug', 'eventSubscriptions.unregister', options);
75+
76+
if (!options.url) return callback(new Error('url required'));
77+
78+
var req = {
79+
method: 'DELETE',
80+
path: '/eventSubscriptions',
81+
query: { callbackUrl: options.url },
82+
};
83+
84+
this.marathon.request(req, function(err) {
85+
if (err) return callback(err);
86+
87+
callback();
88+
});
89+
};
90+
91+
/**
92+
* Module Exports.
93+
*/
94+
95+
exports.EventSubscriptions = EventSubscriptions;

test/helper.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,9 +63,38 @@ function waitOnTask(marathon, id, callback) {
6363
);
6464
}
6565

66+
/**
67+
* Marathon cleanup
68+
*/
69+
70+
function cleanMarathon(test, callback) {
71+
var jobs = {};
72+
73+
jobs.eventSubscriptions = function(cb) {
74+
test.marathon.eventSubscriptions.unregister(test.callbackUrl, cb);
75+
};
76+
77+
jobs.list = test.marathon.apps.list.bind(test);
78+
79+
jobs.destroy = ['list', function(cb, results) {
80+
var ids = results.list.map(function(app) {
81+
return app.id;
82+
}).filter(function(id) {
83+
return id.match(/^test-/);
84+
});
85+
86+
if (!ids.length) return cb();
87+
88+
async.map(ids, test.marathon.apps.destroy.bind(test), cb);
89+
}];
90+
91+
async.auto(jobs, callback);
92+
}
93+
6694
/**
6795
* Module Exports.
6896
*/
6997

7098
exports.debug = debugBuffer;
7199
exports.waitOnTask = waitOnTask;
100+
exports.cleanMarathon = cleanMarathon;

test/marathon.js

Lines changed: 128 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66

77
var async = require('async');
8+
var http = require('http');
89
var should = require('should');
910
var uuid = require('node-uuid');
1011

@@ -35,33 +36,146 @@ describe('Marathon', function() {
3536
instances: 1,
3637
};
3738

38-
self.marathon.apps.create(options, function(err) {
39-
should.not.exist(err);
39+
self.events = [];
4040

41-
done();
41+
self.http = http.createServer(function(req, res) {
42+
var chunks = [];
43+
var bodyLength = 0;
44+
45+
req.on('data', function(chunk) {
46+
chunks.push(chunk);
47+
bodyLength += chunk.length;
48+
});
49+
50+
req.on('end', function() {
51+
var body = Buffer.concat(chunks, bodyLength).toString();
52+
53+
self.events.push({
54+
path: req.url,
55+
body: body,
56+
});
57+
58+
res.writeHead(200);
59+
res.end();
60+
});
4261
});
62+
63+
var eventHost = process.env.EVENT_HOST || '10.141.141.1';
64+
var eventPort = process.env.EVENT_PORT || 8088;
65+
66+
self.callbackUrl = 'http://' + eventHost + ':' + eventPort + '/callback';
67+
68+
var jobs = {};
69+
70+
jobs.httpListen = function(cb) {
71+
self.http.listen(eventPort, eventHost, cb);
72+
};
73+
74+
jobs.clean = function(cb) {
75+
helper.cleanMarathon(self, cb);
76+
};
77+
78+
jobs.app = ['clean', function(cb) {
79+
self.marathon.apps.create(options, function(err) {
80+
should.not.exist(err);
81+
82+
cb();
83+
});
84+
}];
85+
86+
async.auto(jobs, done);
4387
});
4488

4589
after(function(done) {
90+
this.http.close();
91+
92+
helper.cleanMarathon(this, done);
93+
});
94+
95+
it('should handle event subscription', function(done) {
4696
var self = this;
4797

48-
var jobs = {};
98+
var id = 'test-' + uuid.v4();
4999

50-
jobs.list = self.marathon.apps.list.bind(self);
100+
var jobs = [];
51101

52-
jobs.destroy = ['list', function(cb, results) {
53-
var ids = results.list.map(function(app) {
54-
return app.id;
55-
}).filter(function(id) {
56-
return id.match(/^test-/);
102+
jobs.push(function(cb) {
103+
self.marathon.eventSubscriptions.list(function(err, data) {
104+
should.not.exist(err);
105+
106+
should(data).be.instanceof(Array);
107+
108+
data.should.not.containEql(self.callbackUrl);
109+
110+
cb();
57111
});
112+
});
58113

59-
if (!ids.length) return cb();
114+
jobs.push(function(cb) {
115+
self.marathon.eventSubscriptions.register(self.callbackUrl, cb);
116+
});
60117

61-
async.map(ids, self.marathon.apps.destroy.bind(self), cb);
62-
}];
118+
jobs.push(function(cb) {
119+
self.marathon.eventSubscriptions.list(function(err, data) {
120+
should.not.exist(err);
63121

64-
async.auto(jobs, done);
122+
should(data).be.instanceof(Array);
123+
124+
data.should.containEql(self.callbackUrl);
125+
126+
cb();
127+
});
128+
});
129+
130+
jobs.push(function(cb) {
131+
var options = {
132+
id: id,
133+
cmd: 'sleep 123',
134+
cpus: 1,
135+
mem: 16,
136+
instances: 1,
137+
};
138+
139+
self.marathon.apps.create(options, function(err) {
140+
should.not.exist(err);
141+
142+
cb();
143+
});
144+
});
145+
146+
jobs.push(function(cb) {
147+
async.retry(
148+
100,
149+
function(cb) {
150+
if (!self.events.length) {
151+
var err = new Error('No events found');
152+
return setTimeout(function() { cb(err); }, 100);
153+
}
154+
155+
cb();
156+
},
157+
cb
158+
);
159+
});
160+
161+
async.series(jobs, function(err) {
162+
should.not.exist(err);
163+
164+
var events = self.events;
165+
166+
events.should.not.eql([]);
167+
168+
events = events.filter(function(res) {
169+
var data = res && res.body && JSON.parse(res.body);
170+
171+
return data.eventType === 'api_post_event' && data.appDefinition &&
172+
data.appDefinition.id === id;
173+
});
174+
175+
events.should.not.eql([]);
176+
177+
done();
178+
});
65179
});
66180

67181
it('should return all tasks', function(done) {

0 commit comments

Comments
 (0)