mesos-framework
Version:
A wrapper around the Mesos HTTP APIs for Schedulers and Executors. Write your Mesos framework in pure JavaScript!
740 lines (605 loc) • 24.4 kB
JavaScript
;
// Global
var util = require("util");
var EventEmitter = require("events").EventEmitter;
// Project require
var lib = require("requirefrom")("lib");
var Scheduler = require("../index").Scheduler;
var helpers = lib("helpers");
var TaskHelper = lib("taskHelper");
var Builder = lib("builder");
var Mesos = lib("mesos")().getMesos();
// Lib require for stubs
var zookeeper = require("node-zookeeper-client");
// Testing require
var expect = require("chai").expect;
var sinon = require("sinon");
describe("Task helper constructor", function () {
it("Create TaskHelper based on scheduler instance", function () {
var scheduler = Scheduler({});
expect(scheduler).to.be.instanceOf(Scheduler);
var taskHelper = TaskHelper(scheduler);
expect(taskHelper.scheduler).to.be.instanceOf(Scheduler);
expect(taskHelper.scheduler).to.deep.equal(scheduler);
});
});
describe("Load tasks from Zk:", function () {
var zkClient = zookeeper.createClient("127.0.0.1");
var eventFired = false;
var sandbox;
function SchedulerStub() {
// Inherit from EventEmitter
EventEmitter.call(this);
return this;
}
util.inherits(SchedulerStub, EventEmitter);
beforeEach(function () {
sandbox = sinon.sandbox.create();
sandbox.stub(zkClient, "connect", function () {
this.emit("connected");
});
sandbox.stub(zkClient, "getChildren", function (path, cb) {
cb(zookeeper.Exception.create(zookeeper.Exception.CONNECTION_LOSS), null, 1);
});
eventFired = false;
});
afterEach(function (done) {
sandbox.restore();
done();
});
it("ZK is down while getting children", function (done) {
var logger = helpers.getLogger(null, null, "debug");
var schedulerStub = new SchedulerStub();
schedulerStub.on("ready", function () {
eventFired = true;
});
var taskHelper = new TaskHelper({
"zkClient": zkClient,
"logger": logger,
"pendingTasks": [],
"launchedTasks": []
});
taskHelper.scheduler = schedulerStub;
taskHelper.loadTasks();
setTimeout(function () {
done();
}, 100); //timeout with an error in one second
expect(eventFired).to.equal(true);
});
it("ZK is down while getting data", function (done) {
zkClient.getChildren.restore();
sandbox.stub(zkClient, "getChildren", function (path, cb) {
cb(null, ["one", "two"], 1);
});
sandbox.stub(zkClient, "getData", function (path, cb) {
cb(zookeeper.Exception.create(zookeeper.Exception.NO_NODE), null, 1);
});
var logger = helpers.getLogger(null, null, "debug");
var schedulerStub = new SchedulerStub();
schedulerStub.on("ready", function () {
eventFired = true;
});
var taskHelper = new TaskHelper({
"zkClient": zkClient,
"logger": logger,
"pendingTasks": [],
"launchedTasks": []
});
taskHelper.scheduler = schedulerStub;
taskHelper.loadTasks();
setTimeout(function () {
done();
}, 100); //timeout with an error in one second
expect(eventFired).to.equal(true);
});
it("Succeed to load tasks but not found in pending (should kill)", function (done) {
zkClient.getChildren.restore();
sandbox.stub(zkClient, "getChildren", function (path, cb) {
cb(null, ["one", "two"], 1);
});
// The container information object to be used
var ContainerInfo = new Mesos.ContainerInfo(
Mesos.ContainerInfo.Type.DOCKER, // Type
null, // Volumes
null, // Hostname
new Mesos.ContainerInfo.DockerInfo(
"mesoshq/flink:0.1.1", // Image
Mesos.ContainerInfo.DockerInfo.Network.HOST, // Network
null, // PortMappings
false, // Privileged
null, // Parameters
true, // forcePullImage
null // Volume Driver
)
);
var task1 = "{\"name\": \"task1\",\"taskId\": 1}";
var task2 = "{\"name\": \"task2\",\"taskId\": 2}";
sandbox.stub(zkClient, "getData", function (path, cb) {
if (path.includes("one")) {
cb(null, task1, 1);
} else {
cb(null, task2, 1);
}
});
var logger = helpers.getLogger(null, null, "debug");
var schedulerStub = new SchedulerStub();
schedulerStub.pendingTasks = [];
schedulerStub.killTasks = [];
schedulerStub.on("ready", function () {
eventFired = true;
});
var taskHelper = new TaskHelper({
"zkClient": zkClient,
"logger": logger,
"pendingTasks": [],
"launchedTasks": []
});
taskHelper.scheduler = schedulerStub;
taskHelper.loadTasks();
setTimeout(function () {
done();
}, 100); //timeout with an error in one second
expect(eventFired).to.equal(true);
// check that tasks were killed
expect(schedulerStub.killTasks.length).to.equal(2);
});
it("Succeed to load tasks and found in pending (should restore)", function (done) {
var deleted = false;
zkClient.getChildren.restore();
sandbox.stub(zkClient, "getChildren", function (path, cb) {
cb(null, ["one", "two", "three"], 1);
});
var task1 = {name: "/task1", taskId: "1", runtimeInfo: {agentId: "12345", state: "TASK_RUNNING"}};
var task2 = {name: "/task2", taskId: "2", runtimeInfo: {agentId: "12346", state: "TASK_RUNNING"}};
var task3 = {name: "/task3", taskId: "3", runtimeInfo: {agentId: "12446", state: "TASK_FINISHED"}};
sandbox.stub(zkClient, "getData", function (path, cb) {
if (path.includes("one")) {
cb(null, JSON.stringify(task1), 1);
} else if (path.includes("two")) {
cb(null, JSON.stringify(task2), 1);
} else {
cb(null, JSON.stringify(task3), 1);
}
});
sandbox.stub(zkClient, "remove", function (path, cb) {
cb(null, null);
deleted = true;
});
var logger = helpers.getLogger(null, null, "info");
var schedulerStub = new SchedulerStub();
schedulerStub.pendingTasks = [task1, task2, task3];
schedulerStub.killTasks = [];
schedulerStub.launchedTasks = [];
schedulerStub.reconcileTasks = [];
schedulerStub.on("ready", function () {
eventFired = true;
});
var taskHelper = new TaskHelper({
"zkClient": zkClient,
"logger": logger,
"pendingTasks": [],
"launchedTasks": []
});
taskHelper.scheduler = schedulerStub;
taskHelper.loadTasks();
setTimeout(function () {
done();
}, 100); //timeout with an error in one second
expect(eventFired).to.equal(true);
expect(deleted).to.be.true;
// check that tasks were killed
expect(schedulerStub.killTasks.length).to.equal(0);
expect(schedulerStub.launchedTasks.length).to.equal(2);
expect(schedulerStub.reconcileTasks.length).to.equal(2);
});
it("Succeed to load tasks with environment and found in pending (should restore)", function (done) {
var deleted = false;
zkClient.getChildren.restore();
sandbox.stub(zkClient, "getChildren", function (path, cb) {
cb(null, ["one", "two", "three"], 1);
});
var task1 = {
name: "/task1", taskId: "1",
"commandInfo": new Mesos.CommandInfo(
null, // URI
new Mesos.Environment([
new Builder("mesos.Environment.Variable").setName("FOO").setValue("BAR"),
new Builder("mesos.Environment.Variable").setName("HOST").setValue("214214.1244.412421"),
new Builder("mesos.Environment.Variable").setName("PORT0").setValue("3232")
]), // Environment
false, // Is shell?
null, // Command
null, // Arguments
null // User
),
runtimeInfo: {agentId: "12345", state: "TASK_RUNNING"}};
var task2 = {name: "/task2", taskId: "2",
"commandInfo": new Mesos.CommandInfo(
null, // URI
new Mesos.Environment([
new Builder("mesos.Environment.Variable").setName("FOO").setValue("BAR"),
new Builder("mesos.Environment.Variable").setName("HOST").setValue("214214.1244.412421"),
new Builder("mesos.Environment.Variable").setName("PORT0").setValue("3232")
]), // Environment
false, // Is shell?
null, // Command
null, // Arguments
null // User
),
runtimeInfo: {agentId: "12346", state: "TASK_RUNNING"}};
var task3 = {name: "/task3", taskId: "3", runtimeInfo: {agentId: "12446", state: "TASK_FINISHED"}};
sandbox.stub(zkClient, "getData", function (path, cb) {
if (path.includes("one")) {
cb(null, JSON.stringify(task1), 1);
} else if (path.includes("two")) {
cb(null, JSON.stringify(task2), 1);
} else {
cb(null, JSON.stringify(task3), 1);
}
});
sandbox.stub(zkClient, "remove", function (path, cb) {
cb(null, null);
deleted = true;
});
var logger = helpers.getLogger(null, null, "info");
var schedulerStub = new SchedulerStub();
schedulerStub.pendingTasks = [task1, task2, task3];
schedulerStub.killTasks = [];
schedulerStub.launchedTasks = [];
schedulerStub.reconcileTasks = [];
schedulerStub.on("ready", function () {
eventFired = true;
});
var taskHelper = new TaskHelper({
"zkClient": zkClient,
"logger": logger,
"pendingTasks": [],
"launchedTasks": []
});
taskHelper.scheduler = schedulerStub;
taskHelper.loadTasks();
setTimeout(function () {
done();
}, 100); //timeout with an error in one second
expect(eventFired).to.equal(true);
expect(deleted).to.be.true;
// check that tasks were killed
expect(schedulerStub.killTasks.length).to.equal(0);
expect(schedulerStub.launchedTasks.length).to.equal(2);
expect(schedulerStub.reconcileTasks.length).to.equal(2);
});
it("Succeed to load tasks with partial environment and found in pending (should restore)", function (done) {
var deleted = false;
zkClient.getChildren.restore();
sandbox.stub(zkClient, "getChildren", function (path, cb) {
cb(null, ["one", "two", "three"], 1);
});
var task1p = {
name: "/task1", taskId: "1",
"commandInfo": new Mesos.CommandInfo(
null, // URI
null, // Environment
false, // Is shell?
null, // Command
null, // Arguments
null // User
),
runtimeInfo: {agentId: "12345", state: "TASK_RUNNING"}};
var task2p = {name: "/task2", taskId: "2",
"commandInfo": null,
runtimeInfo: {agentId: "12346", state: "TASK_RUNNING"}};
var task1 = {
name: "/task1", taskId: "1",
"commandInfo": new Mesos.CommandInfo(
null, // URI
new Mesos.Environment([
new Builder("mesos.Environment.Variable").setName("FOO").setValue("BAR"),
new Builder("mesos.Environment.Variable").setName("HOST").setValue("214214.1244.412421"),
new Builder("mesos.Environment.Variable").setName("PORT0").setValue("3232")
]), // Environment
false, // Is shell?
null, // Command
null, // Arguments
null // User
),
runtimeInfo: {agentId: "12345", state: "TASK_RUNNING"}};
var task2 = {name: "/task2", taskId: "2",
"commandInfo": new Mesos.CommandInfo(
null, // URI
new Mesos.Environment([
new Builder("mesos.Environment.Variable").setName("FOO").setValue("BAR"),
new Builder("mesos.Environment.Variable").setName("HOST").setValue("214214.1244.412421"),
new Builder("mesos.Environment.Variable").setName("PORT0").setValue("3232")
]), // Environment
false, // Is shell?
null, // Command
null, // Arguments
null // User
),
runtimeInfo: {agentId: "12346", state: "TASK_RUNNING"}};
var task3 = {name: "/task3", taskId: "3", runtimeInfo: {agentId: "12446", state: "TASK_FINISHED"}};
sandbox.stub(zkClient, "getData", function (path, cb) {
if (path.includes("one")) {
cb(null, JSON.stringify(task1), 1);
} else if (path.includes("two")) {
cb(null, JSON.stringify(task2), 1);
} else {
cb(null, JSON.stringify(task3), 1);
}
});
sandbox.stub(zkClient, "remove", function (path, cb) {
cb(null, null);
deleted = true;
});
var logger = helpers.getLogger(null, null, "info");
var schedulerStub = new SchedulerStub();
schedulerStub.pendingTasks = [task1p, task2p, task3];
schedulerStub.killTasks = [];
schedulerStub.launchedTasks = [];
schedulerStub.reconcileTasks = [];
schedulerStub.on("ready", function () {
eventFired = true;
});
var taskHelper = new TaskHelper({
"zkClient": zkClient,
"logger": logger,
"pendingTasks": [],
"launchedTasks": []
});
taskHelper.scheduler = schedulerStub;
taskHelper.loadTasks();
setTimeout(function () {
done();
}, 100); //timeout with an error in one second
expect(eventFired).to.equal(true);
expect(deleted).to.be.true;
// check that tasks were killed
expect(schedulerStub.killTasks.length).to.equal(0);
expect(schedulerStub.launchedTasks.length).to.equal(2);
expect(schedulerStub.reconcileTasks.length).to.equal(2);
});
it("Succeed to load task list but fail to load task", function (done) {
zkClient.getChildren.restore();
sandbox.stub(zkClient, "getChildren", function (path, cb) {
cb(null, ["one", "two"], 1);
});
var deleted = false;
var task1 = {name: "/task1", taskId: "1", runtimeInfo: {agentId: "12345", state: "TASK_RUNNING"}};
var task2 = {name: "/task2", taskId: "2", runtimeInfo: {agentId: "12346", state: "TASK_RUNNING"}};
sandbox.stub(zkClient, "getData", function (path, cb) {
if (path.includes("one")) {
cb(null, JSON.stringify(task1), 1);
} else {
cb(null, null, 1);
}
});
sandbox.stub(zkClient, "remove", function (path, cb) {
cb(null, null);
deleted = true;
});
var logger = helpers.getLogger(null, null, "info");
var schedulerStub = new SchedulerStub();
schedulerStub.pendingTasks = [task1, task2];
schedulerStub.killTasks = [];
schedulerStub.launchedTasks = [];
schedulerStub.reconcileTasks = [];
schedulerStub.on("ready", function () {
eventFired = true;
});
var taskHelper = new TaskHelper({
"zkClient": zkClient,
"logger": logger,
"pendingTasks": [],
"launchedTasks": []
});
taskHelper.scheduler = schedulerStub;
taskHelper.loadTasks();
setTimeout(function () {
expect(deleted).to.be.true;
done();
}, 100); //timeout with an error in one second
expect(eventFired).to.equal(true);
// check that tasks were killed
expect(schedulerStub.killTasks.length).to.equal(0);
expect(schedulerStub.launchedTasks.length).to.equal(1);
expect(schedulerStub.reconcileTasks.length).to.equal(1);
});
it("Succeed to load tasks and no tasks", function (done) {
zkClient.getChildren.restore();
sandbox.stub(zkClient, "getChildren", function (path, cb) {
cb(null, [], 1);
});
var task1 = {name: "/task1", taskId: "1", runtimeInfo: {agentId: "12345"}};
var task2 = {name: "/task2", taskId: "2", runtimeInfo: {agentId: "12346"}};
sandbox.stub(zkClient, "getData", function (path, cb) {
if (path.includes("one")) {
cb(null, JSON.stringify(task1), 1);
} else {
cb(null, JSON.stringify(task2), 1);
}
});
var logger = helpers.getLogger(null, null, "info");
var schedulerStub = new SchedulerStub();
schedulerStub.pendingTasks = [task1, task2];
schedulerStub.killTasks = [];
schedulerStub.launchedTasks = [];
schedulerStub.reconcileTasks = [];
schedulerStub.on("ready", function () {
eventFired = true;
});
var taskHelper = new TaskHelper({
"zkClient": zkClient,
"logger": logger,
"pendingTasks": [],
"launchedTasks": []
});
taskHelper.scheduler = schedulerStub;
taskHelper.loadTasks();
setTimeout(function () {
done();
}, 100); //timeout with an error in one second
expect(eventFired).to.equal(true);
// check that tasks were killed
expect(schedulerStub.killTasks.length).to.equal(0);
expect(schedulerStub.launchedTasks.length).to.equal(0);
expect(schedulerStub.reconcileTasks.length).to.equal(0);
});
it("Succeed to load tasks and found in pending but no runtimeInfo (should delete from zk)", function (done) {
zkClient.getChildren.restore();
sandbox.stub(zkClient, "getChildren", function (path, cb) {
cb(null, ["/one", "/two"], 1);
});
var task1 = {name: "/task1", taskId: "1"};
var task2 = {name: "/task2", taskId: "2"};
sandbox.stub(zkClient, "getData", function (path, cb) {
if (path.includes("one")) {
cb(null, JSON.stringify(task1), 1);
} else {
cb(null, JSON.stringify(task2), 1);
}
});
sandbox.stub(zkClient, "remove", function (path, cb) {
cb(null, null);
});
var logger = helpers.getLogger(null, null, "info");
var schedulerStub = new SchedulerStub();
schedulerStub.pendingTasks = [task1, task2];
schedulerStub.killTasks = [];
schedulerStub.launchedTasks = [];
schedulerStub.reconcileTasks = [];
schedulerStub.on("ready", function () {
eventFired = true;
});
var taskHelper = new TaskHelper({
"zkClient": zkClient,
"logger": logger,
"pendingTasks": [],
"launchedTasks": []
});
taskHelper.scheduler = schedulerStub;
taskHelper.loadTasks();
setTimeout(function () {
done();
}, 100); //timeout with an error in one second
expect(eventFired).to.equal(true);
// check that tasks were killed
expect(schedulerStub.killTasks.length).to.equal(0);
expect(schedulerStub.launchedTasks.length).to.equal(0);
expect(schedulerStub.reconcileTasks.length).to.equal(0);
});
});
describe("Delete task:", function () {
var sandbox;
var zkClient = zookeeper.createClient("127.0.0.1");
before(function () {
sandbox = sinon.sandbox.create();
sandbox.stub(zkClient, "connect", function () {
this.emit("connected");
});
});
after(function (done) {
sandbox.restore();
done();
});
it("Succeeds", function () {
var logger = helpers.getLogger(null, null, "error");
var logspy = sinon.spy(logger, "debug");
sandbox.stub(zkClient, "remove", function (path, cb) {
cb(null, null);
});
var taskHelper = new TaskHelper({
"zkClient": zkClient,
"logger": logger,
"pendingTasks": [],
"launchedTasks": []
});
taskHelper.deleteTask("dummytask");
sinon.assert.calledOnce(logspy);
});
it("ZK is down while trying to remove task", function () {
var logger = helpers.getLogger(null, null, "error");
var logspy = sinon.spy(logger, "error");
zkClient.remove.restore();
sandbox.stub(zkClient, "remove", function (path, cb) {
cb(zookeeper.Exception.create(zookeeper.Exception.CONNECTION_LOSS), null);
});
var taskHelper = new TaskHelper({
"zkClient": zkClient,
"logger": logger,
"pendingTasks": [],
"launchedTasks": []
});
taskHelper.deleteTask("dummytask");
sinon.assert.calledOnce(logspy);
});
});
describe("Save task:", function () {
var sandbox;
var zkClient = zookeeper.createClient("127.0.0.1");
before(function () {
sandbox = sinon.sandbox.create();
sandbox.stub(zkClient, "connect", function () {
this.emit("connected");
});
});
after(function (done) {
sandbox.restore();
done();
});
it("ZK create dir fails", function () {
sandbox.stub(zkClient, "mkdirp", function (path, cb) {
cb(zookeeper.Exception.create(zookeeper.Exception.CONNECTION_LOSS), null);
});
var logger = helpers.getLogger(null, null, "error");
var logspy = sinon.spy(logger, "error");
var taskHelper = new TaskHelper({
"zkClient": zkClient,
"logger": logger,
"pendingTasks": [],
"launchedTasks": []
});
taskHelper.saveTask("dummytask")
sinon.assert.calledOnce(logspy);
});
it("ZK save data fails", function () {
var logger = helpers.getLogger(null, null, "error");
var logspy = sinon.spy(logger, "error");
zkClient.mkdirp.restore();
sandbox.stub(zkClient, "mkdirp", function (path, cb) {
cb(null, null);
});
sandbox.stub(zkClient, "setData", function (path, data, cb) {
cb(zookeeper.Exception.create(zookeeper.Exception.CONNECTION_LOSS), null);
});
var taskHelper = new TaskHelper({
"zkClient": zkClient,
"logger": logger,
"pendingTasks": [],
"launchedTasks": []
});
taskHelper.saveTask("dummytask")
sinon.assert.calledOnce(logspy);
});
it("Succeeds", function () {
var logger = helpers.getLogger(null, null, "error");
var debugSpy = sinon.spy(logger, "debug");
var errSpy = sinon.spy(logger, "error");
zkClient.mkdirp.restore();
zkClient.setData.restore();
sandbox.stub(zkClient, "mkdirp", function (path, cb) {
cb(null, null);
});
sandbox.stub(zkClient, "setData", function (path, data, cb) {
cb(null, null);
});
var taskHelper = new TaskHelper({
"zkClient": zkClient,
"logger": logger,
"pendingTasks": [],
"launchedTasks": []
});
taskHelper.saveTask("dummytask")
sinon.assert.calledOnce(debugSpy);
sinon.assert.notCalled(errSpy);
});
});