mesos-framework
Version:
A wrapper around the Mesos HTTP APIs for Schedulers and Executors. Write your Mesos framework in pure JavaScript!
344 lines (270 loc) • 16.7 kB
HTML
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>executor.js - Documentation</title>
<script src="scripts/prettify/prettify.js"></script>
<script src="scripts/prettify/lang-css.js"></script>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="https://code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css">
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<input type="checkbox" id="nav-trigger" class="nav-trigger" />
<label for="nav-trigger" class="navicon-button x">
<div class="navicon"></div>
</label>
<label for="nav-trigger" class="overlay"></label>
<nav>
<li class="nav-link nav-home-link"><a href="index.html">Home</a></li><li class="nav-heading">Classes</li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Builder.html">Builder</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Executor.html">Executor</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Executor.html#message">message</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Executor.html#subscribe">subscribe</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Executor.html#update">update</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Mesos.html">Mesos</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Mesos.html#build">build</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Mesos.html#getBuilder">getBuilder</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Mesos.html#getMesos">getMesos</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Mesos.html#getProtoBuf">getProtoBuf</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="Scheduler.html">Scheduler</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Scheduler.html#accept">accept</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Scheduler.html#acceptInverseOffers">acceptInverseOffers</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Scheduler.html#acknowledge">acknowledge</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Scheduler.html#acknowledgeOperationStatus">acknowledgeOperationStatus</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Scheduler.html#backOff">backOff</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Scheduler.html#decline">decline</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Scheduler.html#declineInverseOffers">declineInverseOffers</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Scheduler.html#getRunningTasks">getRunningTasks</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Scheduler.html#kill">kill</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Scheduler.html#message">message</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Scheduler.html#reconcile">reconcile</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Scheduler.html#reconcileOperations">reconcileOperations</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Scheduler.html#request">request</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Scheduler.html#revive">revive</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Scheduler.html#shutdown">shutdown</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Scheduler.html#subscribe">subscribe</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Scheduler.html#suppress">suppress</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Scheduler.html#sync">sync</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="Scheduler.html#teardown">teardown</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="TaskHealthHelper.html">TaskHealthHelper</a></span></li><li class="nav-heading"><span class="nav-item-type type-class">C</span><span class="nav-item-name"><a href="TaskHelper.html">TaskHelper</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="TaskHelper.html#deleteTask">deleteTask</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="TaskHelper.html#loadTasks">loadTasks</a></span></li><li class="nav-item"><span class="nav-item-type type-function">F</span><span class="nav-item-name"><a href="TaskHelper.html#saveTask">saveTask</a></span></li>
</nav>
<div id="main">
<h1 class="page-title">executor.js</h1>
<section>
<article>
<pre class="prettyprint source linenums"><code>"use strict";
var http = require("http");
var EventEmitter = require('events').EventEmitter;
var util = require('util');
var uuid = require('uuid');
var helpers = require("./helpers");
var executorHandlers = require("./executorHandlers");
var mesos = require("./mesos")().getMesos();
/**
* Represents a Mesos framework executor.
* @constructor
* @param {object} options - The option map object.
*/
function Executor (options) {
if (!(this instanceof Executor)) {
return new Executor(options);
}
// Inherit from EventEmitter
EventEmitter.call(this);
var self = this;
self.options = {};
// Optional env variables
var envMap = {
"MESOS_CHECKPOINT": "checkpointEnabled",
"MESOS_EXECUTOR_SHUTDOWN_GRACE_PERIOD": "shutdownGracePeriod",
"MESOS_RECOVERY_TIMEOUT": "recoveryTimeout",
"MESOS_SUBSCRIPTION_BACKOFF_MAX": "subscriptionBackoffMax"
};
// Check for frameworkId
if (process.env.MESOS_FRAMEWORK_ID) {
self.frameworkId = new mesos.FrameworkID(process.env.MESOS_FRAMEWORK_ID);
} else {
console.error("No MESOS_FRAMEWORK_ID environment parameter found! Exiting...");
process.exit(1);
}
// Check fo rexecutorId
if (process.env.MESOS_EXECUTOR_ID) {
self.executorId = new mesos.ExecutorID(process.env.MESOS_EXECUTOR_ID);
} else {
console.error("No MESOS_EXECUTOR_ID environment parameter found! Exiting...");
process.exit(2);
}
// Check for working directory
if (process.env.MESOS_DIRECTORY) {
self.workingDirectory = process.env.MESOS_DIRECTORY;
} else {
console.error("No MESOS_DIRECTORY environment parameter found! Exiting...");
process.exit(3);
}
// Check for agent endpoint
if (process.env.MESOS_AGENT_ENDPOINT) {
var endpointArray = process.env.MESOS_AGENT_ENDPOINT.split(":");
if (endpointArray.length === 2) {
self.options.agentUrl = endpointArray[0];
self.options.port = parseInt(endpointArray[1]);
} else {
console.error("MESOS_AGENT_ENDPOINT didn't contain a ip:port combination! Exiting...");
process.exit(4);
}
} else {
console.log("No MESOS_AGENT_ENDPOINT environment parameter found! Using default values...");
self.options.agentUrl = options.agentUrl || "127.0.0.1";
self.options.port = parseInt(options.port) || 5051;
}
// Check for other env variables and inline them
Object.getOwnPropertyNames(envMap).forEach(function (envVariable) {
if (process.env[envVariable]) {
self[envMap[envVariable]] = process.env[envVariable];
}
});
// Template for issuing Mesos Scheduler HTTP API requests
self.requestTemplate = {
host: self.options.agentUrl,
port: self.options.port,
path: "/api/v1/executor",
method: "POST",
headers: {
'Content-Type': 'application/json'
}
};
// Customer event handlers will be registered here
self.customEventHandlers = {};
// List of allowed event handler function names and their argument length
var allowedEventHandlers = {
"SUBSCRIBED": 1,
"LAUNCH": 1,
"KILL": 1,
"ACKNOWLEDGED": 1,
"MESSAGE": 1,
"ERROR": 1,
"SHUTDOWN": 1
};
// Add custom event handlers if present
if (options.handlers && Object.getOwnPropertyNames(options.handlers).length > 0) {
Object.getOwnPropertyNames(options.handlers).forEach(function (handlerName) {
// Check if name is allowed, is a function and the length of the function arguments fit to the ones defined in allowedEventHandlers
if (Object.getOwnPropertyNames(allowedEventHandlers).indexOf(handlerName.toUpperCase()) > -1 && helpers.isFunction(options.handlers[handlerName]) && options.handlers[handlerName].length === allowedEventHandlers[handlerName]) {
self.customEventHandlers[handlerName.toUpperCase()] = options.handlers[handlerName];
}
});
}
}
// Inhertit from EventEmitter
util.inherits(Executor, EventEmitter);
/**
* Subscribes the framework executor to the according Mesos agent.
*/
Executor.prototype.subscribe = function () {
var self = this;
/**
* The handler funciton for the incoming Mesos agent events for this executor.
* @param {object} eventData - The data object for an incoming event. Contains the event details (type etc.).
*/
function handleEvent (eventData) {
try {
var event = JSON.parse(eventData);
// Determine event handler, use custom one if it exists
if (self.customEventHandlers[event.type]) {
// Call custom handler
self.customEventHandlers[event.type].call(self, event[event.type.toLocaleLowerCase()]);
} else {
// Call default handler
schedulerHandlers[event.type].call(self, event[event.type.toLocaleLowerCase()]);
}
// Emit original objects
self.emit(event.type.toLocaleLowerCase(), event[event.type.toLocaleLowerCase()]);
} catch (error) {
self.emit("error", { message: "Couldn't parse as JSON: " + eventData, stack: (error.stack || "") });
}
}
var req = http.request(self.requestTemplate, function (res) {
// Set encoding to UTF8
res.setEncoding('utf8');
if (res.statusCode === 200) {
self.emit("sent_subscribe", { mesosStreamId: self.mesosStreamId });
}
// Local cache for chunked JSON messages
var cache = "";
// Watch for data/chunks
res.on('data', function (chunk) {
console.log("BODY: " + chunk);
var expectedLength = 0;
if (chunk.indexOf("\n") > -1) {
var temp = chunk.split("\n");
if (temp.length === 2) {
expectedLength = parseInt(temp[0]);
if (temp[1].length < expectedLength) {
// Add to cache
cache += temp[1];
} else {
// Empty cache
cache = "";
// Handle event
handleEvent(temp[1]);
}
} else {
self.emit("error", { message: "Other linebreak count found than expected! Actual count: " + temp.length });
}
} else {
if (cache.length > 0) {
// Concatenate cached partial data with this chunk, replace the erroneous parts
var eventData = cache + chunk;
// Handle event
handleEvent(eventData);
// Empty cache
cache = "";
}
}
});
res.on('end', function () {
self.emit("error", { message: "Long-running connection was closed!" });
});
});
req.on('error', function (e) {
self.emit("error", { message: "There was a problem with the request: " + e.message});
});
// write data to request body
req.write(JSON.stringify({
"type": "SUBSCRIBE",
"framework_id": self.frameworkId,
"executor_id": self.executorId
}));
req.end();
};
/**
* Communicate the state of managed tasks. It is crucial that a terminal update (e.g., TASK_FINISHED, TASK_KILLED or TASK_FAILED) is sent to the agent as soon as the task terminates, in order to allow Mesos to release the resources allocated to the task.
* The scheduler must explicitly respond to this call through an ACKNOWLEDGE message (see ACKNOWLEDGED in the Events section below for the semantics). The executor must maintain a list of unacknowledged updates. If for some reason, the executor is disconnected from the agent, these updates must be sent as part of SUBSCRIBE request in the unacknowledged_updates field.
* @param {Object} taskStatus The {@link https://github.com/apache/mesos/blob/c6e9ce16850f69fda719d4e32be3f2a2e1d80387/include/mesos/v1/mesos.proto#L1330|TaskStatus} object containing the update details.
*/
Executor.prototype.update = function (taskStatus) {
var self = this;
var payload = {
"type": "UPDATE",
"framework_id": self.frameworkId,
"executor_id": self.executorId,
"update": {
"status": taskStatus
}
};
helpers.doRequest.call(self, payload, function (error, response) {
if (error) {
self.emit("error", error.message);
} else {
self.emit("sent_update");
}
});
};
/**
* Send arbitrary data to the scheduler. Note that Mesos neither interprets this data nor makes any guarantees about the delivery of this message to the executor.
* @param {string} data The string which's raw bytes will be encoded in Base64.
*/
Executor.prototype.message = function (data) {
var self = this;
var payload = {
"type": "MESSAGE",
"framework_id": self.frameworkId,
"executor_id": self.executorId,
"message": {
"data": new Buffer(data).toString('base64')
}
};
helpers.doRequest.call(self, payload, function (error, response) {
if (error) {
self.emit("error", error.message);
} else {
self.emit("sent_message");
}
});
};
module.exports = Executor;
</code></pre>
</article>
</section>
</div>
<br class="clear">
<footer>
Generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.5.5</a> on Sun Mar 04 2018 17:37:29 GMT+0100 (CET) using the Minami theme.
</footer>
<script>prettyPrint();</script>
<script src="scripts/linenumber.js"></script>
</body>
</html>