infinispan-cere
Version:
Infinispan Javascript client
789 lines (674 loc) • 21.8 kB
JavaScript
// Commons functions
var _ = require('underscore');
var log4js = require('log4js');
var promiseFinally = require('promise.prototype.finally');
promiseFinally.shim(); // will be a no-op if not needed
var readFile = require('fs').readFile;
var httpRequest = require('request');
var util = require('util');
var f = require('../../lib/functional');
var ispn = require('../../lib/infinispan');
var u = require('../../lib/utils');
var protocols = require('../../lib/protocols');
exports.serverDirName = "infinispan-server";
exports.authOpts = {
authentication: {
enabled: true,
saslMechanism: 'PLAIN',
userName: 'admin',
password: 'pass'
}
};
exports.local = {port: 11222, host: '127.0.0.1'};
exports.cluster1 = {port: 11322, host: '127.0.0.1'};
exports.cluster2 = {port: 11332, host: '127.0.0.1'};
exports.cluster3 = {port: 11342, host: '127.0.0.1'};
exports.cluster = [exports.cluster1, exports.cluster2, exports.cluster3];
exports.failover1 = {port: 11422, host: '127.0.0.1'};
exports.failover2 = {port: 11432, host: '127.0.0.1'};
exports.failover3 = {port: 11442, host: '127.0.0.1'};
exports.failoverConfig = 'infinispan-clustered.xml';
exports.failoverMCastAddr= '234.99.64.14';
// All ssl invocations needs to be directed to localhost instead of 127.0.0.1
// because Node.js uses `localhost` as default server name if none provided.
exports.sslTrust = {port: 11232, host: 'localhost'};
exports.sslAuth = {port: 11232, host: 'localhost'};
exports.sslSni = {port: 11232, host: 'localhost'};
exports.xsiteCacheName = 'xsiteCache';
exports.earth1 = {port: 11522, host: '127.0.0.1'};
exports.moon1 = {port: 11532, host: '127.0.0.1'};
exports.earth1Config = 'infinispan-xsite-EARTH.xml';
exports.moon1Config = 'infinispan-xsite-MOON.xml';
exports.earth1MCastAddr = '234.99.74.14';
exports.moon1MCastAddr = '234.99.74.24';
exports.json = {
dataFormat: {
keyType: 'application/json'
, valueType: 'application/json'
},
authentication: exports.authOpts.authentication,
cacheName: 'jsonCache'
};
var CLUSTER_NODES = ['server-one', 'server-two', 'server-three'];
var MAX_WAIT = 7500;
var logger = u.logger('testing');
exports.configureLogging = function() {
try {
log4js.configure('spec/utils/test-log4js.json');
} catch (error) {
console.log(error);
// In case running specs from IDEs
log4js.configure('utils/test-log4js.json');
}
};
exports.client = function(args, opts) {
this.configureLogging();
if (!f.existy(opts) || !f.existy(opts.version)) {
var version = exports.getHotrodProtocolVersion();
if (!f.existy(opts))
opts = {};
if (f.existy(version)) {
opts.version = version;
}
}
return ispn.client(args, opts);
};
exports.protocol25 = function() { return protocols.version25(); };
exports.protocol29 = function(clientOpts) { return protocols.version29(clientOpts); };
exports.put = function(k, v, opts) {
return function(client) { return client.put(k, v, opts); }
};
exports.get = function(k) {
return function(client) { return client.get(k); }
};
exports.getM = function(k) {
return function(client) { return client.getWithMetadata(k); }
};
exports.putIfAbsent = function(k, v, opts) {
return function(client) { return client.putIfAbsent(k, v, opts); }
};
exports.replace = function(k, v, opts) {
return function(client) { return client.replace(k, v, opts); }
};
exports.remove = function(k, opts) {
return function(client) { return client.remove(k, opts); }
};
exports.containsKey = function(k) {
return function(client) { return client.containsKey(k); }
};
exports.conditional = function(writeFun, getFun, k, old, v, opts) {
return function(client) {
return getFun(k)(client).then(function(versioned) {
expect(versioned.value).toEqual(old);
expect(versioned.version).toBeDefined();
return writeFun(k, versioned.version, v, opts)(client);
});
}
};
exports.replaceV = function(k, version, v, opts) {
return function(client) {
return client.replaceWithVersion(k, v, version, opts);
}
};
exports.putAll = function(pairs, opts) {
return function(client) { return client.putAll(pairs, opts); }
};
exports.size = function() {
return function(client) { return client.size(); }
};
exports.stats = function() {
return function(client) { return client.stats(); }
};
exports.clear = function() {
return function(client) {
return client.clear();
}
};
exports.ping = function() {
return function(client) {
return client.ping();
}
};
exports.disconnect = function() {
return function(client) {
return client.disconnect();
}
};
exports.on = function(event, listener, opts) {
return function(client) {
return client.addListener(event, listener(client), opts)
.then(function() { return client; });
}
};
exports.onMany = function(eventListeners) {
return function(client) {
var head = _.head(eventListeners);
var tail = _.tail(eventListeners);
return client.addListener(head.event, head.listener).then(function(listenerId) {
var promises = _.map(tail, function(e) {
return client.addListener(e.event, e.listener(client), {'listenerId' : listenerId });
});
return Promise.all(promises)
.then(function(listenerIds) {
_.map(listenerIds, function(lid) { expect(lid).toBe(listenerId); });
return client;
})
});
};
};
exports.authMech = function() {
return function(client) {
return client.authMechList();
}
};
exports.exec = function(scriptName, params) {
return function(client) {
return client.execute(scriptName, params);
}
};
exports.loadAndExec = function(path, name) {
return function(client) {
return Promise.all([client, readFileAsync(path)])
.then(function(vals) {
var c = vals[0];
var scriptName = f.existy(name) ? name : path.split('/').pop();
return c.addScript(scriptName, vals[1].toString())
.then(function() { return c; } );
})
}
};
exports.getTopologyId = function() {
return function(client) {
return Promise.resolve(client.getTopologyInfo().getTopologyId());
}
};
exports.getMembers = function() {
return function(client) {
return Promise.resolve(
_.sortBy(client.getTopologyInfo().getMembers(), 'port'));
}
};
exports.assert = function(fun, expectFun) {
if (f.existy(expectFun)) {
return function(client) {
return fun(client).then(function(value) {
expectFun(value);
return client;
});
}
}
return function(client) {
return fun(client).then(function() { return client; });
}
};
exports.assertStats = function(fun, statsFun) {
return function(client) {
var before = client.stats().then(function(before) {return before});
var put = before.then(function() { return fun(client); });
var after = put.then(function() { return client.stats(); });
return Promise.all([before, after]).then(function(stats) {
statsFun(stats[0], stats[1]);
return client;
});
}
};
exports.resetStats = function(client) {
var resets = _.map(CLUSTER_NODES, function(nodeName) {
var op = {
operation: 'reset-statistics',
address: [
{ host : 'master'},
{ server : nodeName},
{ subsystem : 'datagrid-infinispan'},
{ 'cache-container' : 'clustered' },
{ 'distributed-cache' : 'default' }
]
};
return invokeDmrHttp(op);
});
return Promise.all(resets).then(function() { return client; });
};
exports.clusterSize = function() { return exports.cluster.length; };
exports.toBe = function(value) {
return function(actual) { expect(actual).toBe(value); }
};
exports.toEqual = function(value) {
return function(actual) {
var laundered = JSON.stringify({value: 'native-value'});
var relaundered = JSON.parse(laundered);
// logger.tracef('Match actual=%s and expected=%s? %s'
// , u.str(relaundered)
// , u.str({value: 'native-value'})
// , _.isMatch(relaundered, {value: 'native-value'})
// );
var match = _.isMatch(actual, value);
logger.tracef('Match actual=%s and expected=%s? %s'
, u.str(actual), u.str(value), match
);
expect(actual).toEqual(value);
}
};
exports.toContain = function(value) {
return function(actual) {
if (_.isObject(value)) expect(actual).toEqual(jasmine.objectContaining(value));
else expect(actual).toContain(value);
}
};
exports.toBeDefined = function(actual) { expect(actual).toBeDefined() };
exports.toBeUndefined = function(actual) { expect(actual).toBeUndefined(); };
exports.toBeTruthy = function(actual) { expect(actual).toBeTruthy(); };
exports.toBeFalsy = function(actual) { expect(actual).toBeFalsy(); };
exports.toBeUndefinedVersioned = function(actual) {
expect(actual.value).toBeUndefined();
expectToBeBuffer(actual.version, new Buffer());
};
exports.vNumSize = function(num) {
var limits = [7,14,21,28,35,42,49,53];
for (var i = 0; i < limits.length; i++) {
var limit = limits[i];
if (num < Math.pow(2, limit)) return Math.ceil(limit / 7);
}
};
exports.newByteBuf = function(size) {
return {buf: Buffer.alloc(f.existy(size) ? size : 128), offset: 0};
};
exports.assertEncode = function(bytebuf, encode, expectedBytes) {
expect(encode(bytebuf)).toBe(expectedBytes);
expect(bytebuf.buf.length).toBe(expectedBytes);
return bytebuf;
};
exports.expectToBeBuffer = expectToBeBuffer;
function expectToBeBuffer(actual, expected) {
expect(JSON.stringify(actual)).toBe(JSON.stringify(expected));
}
exports.expectEvent = function(key, done, removeAfterEvent, value) {
return function(client) {
if (f.existy(value)) {
return function(eventKey, eventVersion, listenerId) {
expect(eventKey).toEqual(key);
assertListenerVersioned(key, value, eventVersion)(client)
.then(function() {
removeListener(client, listenerId, removeAfterEvent, done);
})
.catch(function(err) {
client.removeListener(listenerId)
.finally(exports.failed(done)(err));
});
}
} else {
return function(eventKey, listenerId) {
expect(eventKey).toEqual(key);
removeListener(client, listenerId, removeAfterEvent, done);
}
}
}
};
exports.expectEventKeyOnly = function(key, done) {
return function(client) {
if (f.existy(done)) {
return function (eventKey, listenerId) {
expect(eventKey).toBe(key);
removeListener(client, listenerId, true, done);
}
} else {
return function(eventKey) {
expect(eventKey).toBe(key);
}
}
}
};
exports.expectCustomEvent = function(custom, done) {
return function(client) {
return function(eventCustom, listenerId) {
expect(eventCustom).toEqual(custom);
removeListener(client, listenerId, true, done);
}
}
};
function removeListener(client, listenerId, removeAfterEvent, done) {
if (removeAfterEvent)
return client.removeListener(listenerId)
.then(function() { done(); })
.catch(function(err) { done(err); });
else
return client.removeListener(listenerId);
}
function assertListenerVersioned(key, value, version) {
return function(client) {
return client.getWithMetadata(key)
.then(function(getM) {
expect(getM.value).toEqual(value);
expectToBeBuffer(getM.version, version);
})
}
}
exports.expectEvents = function(keys, done, disconnect) {
logger.debugf("Expect events for keys: %s", keys);
return function(client) {
var remain = keys;
return function(eventKey, eventVersion, listenerId) {
var match = _.filter(remain, function(k) {
return _.isEqual(k, eventKey);
});
expect(match.length).toBe(1);
remain = _.without(remain, eventKey);
logger.debugf("Received event key=%s, still remaining [%s]", eventKey, remain);
if (_.isEmpty(remain)) {
var removed = removeListener(client, listenerId, true, done);
if (disconnect) {
removed.then(function() {
return client.disconnect();
});
}
}
}
}
};
exports.failed = function(done) {
return function(error) {
done(error);
};
};
exports.randomStr = function(size) {
var text = "";
var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (var i = 0; i < size; i++)
text += possible.charAt(Math.floor(Math.random() * possible.length));
return text;
};
exports.findKeyForServers = function(client, addrs) {
var attempts = 10000;
var key;
do {
key = exports.randomStr(8);
var owners = client.getTopologyInfo().findOwners(key);
attempts--;
} while (!_.isEqual(addrs, owners) && attempts >= 0);
if (attempts < 0)
throw new Error("Could not find any key owned by: " + u.showArrayAddress(addrs));
logger.debugf("Generated key=%s hashing to %s", key, u.showArrayAddress(addrs));
return key;
};
exports.getAll = function(keys) {
return function(client) {
return client.getAll(keys);
}
};
exports.invalidVersion = function() {
return Buffer.from([48, 49, 50, 51, 52, 53, 54, 55]);
};
exports.notReplaceWithVersion = function(k, opts) {
return function(client) {
return client.replaceWithVersion(k, 'ignore', exports.invalidVersion(), opts);
}
};
exports.removeWithVersion = function(k, version, opts) {
return function(client) {
return client.removeWithVersion(k, version, opts);
}
};
exports.notRemoveWithVersion = function(k, opts) {
return function(client) {
return client.removeWithVersion(k, exports.invalidVersion(), opts);
}
};
exports.expectIteratorDone = function(it) {
return function() {
return it.next().then(function(entry) {
expect(entry.done).toBeTruthy();
})
}
};
exports.seqIterator = function(sortByF, batchSize, expected, opts) {
return function(client) {
return client.iterator(batchSize, opts).then(function(it) {
var p = _.foldl(_.range(expected.length),
function(p) {
return p.then(function(array) {
return it.next().then(function(entry) {
array.push(entry);
return array;
})
});
}, Promise.resolve([]));
return p
.then(function(array) { exports.toContainAllEntries(expected)(sortByF, array); })
.then(function() { return it.close(); }) // Close iterator
.then(function() { return client; });
})
}
};
exports.toEqualPairs = function(sortByF, value) {
return function(actual) {
expect(_.sortBy(actual, sortByF)).toEqual(value);
}
};
exports.toContainAllEntries = function(expected) {
return function(sortByF, actual) {
var sorted = _.sortBy(actual, sortByF);
return exports.toContainAll(expected)(sorted);
}
};
exports.toContainAll = function(expected) {
return function(actual) {
var zipped = _.zip(actual, expected);
_.map(zipped, function(e) {
var actualEntry = e[0];
var expectedEntry = e[1];
exports.toContain(expectedEntry)(actualEntry);
});
}
};
exports.toBeStatIncr = function(stat) {
return function(before, after) {
expect(after[stat]).toBe(before[stat] + 1);
}
};
exports.prev = function() {
return { previous: true };
};
exports.expectToThrow = function(func, errorMessage, done) {
expect(func).toThrow(errorMessage);
if (f.existy(done)) done();
};
exports.getHotrodProtocolVersion = function() {
var version;
if (f.existy(process.env.protocol)) {
version = process.env.protocol;
}
return version;
};
exports.stopClusterNode = function(port, waitStop) {
return function() {
var opUrl = "/server?action=stop";
if (waitStop) {
return invokeDmrHttpGet(opUrl, port).then(function() {
return waitUntilStopped(port);
});
}
return invokeDmrHttpGet(opUrl, port);
}
};
function waitUntilStopped(port) {
return waitUntil(
function(resp) { expect(resp).toEqual(0); },
function(resp) { return _.isEqual(resp, 0) },
getServerStatus(port)
);
}
function getServerStatus(port) {
return function() {
return new Promise(function(fulfil, reject) {
var spawn = require('child_process').spawn;
var child = spawn('fuser', [port + '/tcp']);
var count = 0;
child.stdout.on('data', function(chunk) {
chunk.toString().split('\n').forEach(function(line) {
count++;
});
});
child.stdout.on('end', function(chunk) {
fulfil(count);
});
})
};
}
exports.stopAndWaitView = function(nodeStop, expectNumMembers, nodeView) {
return function() {
return exports.stopClusterNode(nodeStop, false)()
.then(function() { return exports.waitUntilView(expectNumMembers, nodeView); });
}
};
exports.waitUntilView = function(expectNumMembers, port) {
logger.debugf("Wait until view of %d nodes on '%s'", expectNumMembers, port);
return waitUntil(
function(members) {
logger.debugf(
"Final check, check the expect %d nodes and got %d",
expectNumMembers, members
);
expect(members).toEqual(expectNumMembers);
logger.debugf("Check passed");
},
function(members) {
var success = _.isEqual(expectNumMembers, members);
logger.debugf(
"Expected %d nodes, got %d, success=%s",
expectNumMembers, members, success
);
return success;
},
getClusterMembers(port)
);
};
exports.launchClusterNodeAndWaitView = function(nodeName, config, port, mcastAddr, expectNumMembers, client) {
return new Promise(function (fulfill, reject) {
var spawn = require('child_process').spawn;
var path = require('path');
var serverPath = path.resolve('server/' + exports.serverDirName + "/");
var standaloneShPath = serverPath + '/bin/server.sh';
var cmd = spawn(
standaloneShPath,
['-c', config, '-p', port, '-s',serverPath + "/" + nodeName,
'-Djgroups.mcast_addr=' + mcastAddr,
'-Dinfinispan.node.name=' + nodeName,
'-Djgroups.join_timeout=1000'
]);
cmd.stderr.on('data', function (data) {
logger.debugf('Stderr [%s]: %s', nodeName, data);
reject(data);
});
cmd.on('exit', function (code) {
logger.debugf('Child process exited with code %d', code);
});
fulfill();
}).then(function() {
return exports.waitUntilView(expectNumMembers, port);
}).then(function() {
return client;
});
};
function waitUntil(expectF, cond, op) {
var now = new Date().getTime();
function done(actual) {
var expired = new Date().getTime() < now + MAX_WAIT;
logger.debugf(
"Expired waiting for condition? %s", expired
);
return cond(actual) && !expired;
}
function loop(promise) {
exports.sleepFor(1000); // brief sleep
// Simple recursive loop until condition has been met
return promise
.then(function(response) {
var isDone = done(response);
logger.debugf("Is waiting done for condition? %s", isDone);
return !isDone
? loop(op())
: response;
})
.catch(function() {
return loop(op());
});
}
return loop(op())
.then(function(actual) {
expectF(actual);
});
}
exports.sleepFor = function(sleepDuration) {
var now = new Date().getTime();
while(new Date().getTime() < now + sleepDuration){ /* do nothing */ }
};
function getClusterMembers(port) {
return function() {
var opUrl ="/cache-managers/clustered/";
return invokeDmrHttpGet(opUrl, port)
.then(function(response) {
logger.debugf("Server '%s' replied with cluster members: %s", port, response.cluster_size);
var members = response.cluster_size;
logger.debugf("Members are: %s", members);
return members;
});
}
}
function invokeDmrHttp(op, port) {
return new Promise(function(fulfil, reject) {
httpRequest({
method: 'POST',
url: 'http://localhost:' + port + '/rest/v2',
auth: {
user: 'admin',
pass: 'mypassword',
sendImmediately: false
},
headers: {
'Content-Type' : 'application/json'
},
body: JSON.stringify(op)
}, function(error, response, body) {
if (!error && response.statusCode == 200) {
fulfil(JSON.parse(body));
} else {
reject(util.format('Error (%s), body (%s), response(%s)',
error, body, JSON.stringify(response)));
}
});
});
}
function invokeDmrHttpGet(opUrl, port) {
return new Promise(function(fulfil, reject) {
httpRequest({
method: 'GET',
url: 'http://localhost:' + port + '/rest/v2' + opUrl,
auth: {
user: 'admin',
pass: 'mypassword',
sendImmediately: false
},
headers: {
'Content-Type' : 'application/json'
}
}, function(error, response, body) {
if (!error && response.statusCode == 200) {
var resp = "";
if (body) {
resp = JSON.parse(body);
}
fulfil(resp);
} else {
reject(util.format('Error (%s), body (%s), response(%s)',
error, body, JSON.stringify(response)));
}
});
});
}
function readFileAsync(path) {
return new Promise(function(resolve,reject){
readFile(path, function(err, data){
if(err !== null) return reject(err);
resolve(data);
});
});
}