├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── lib ├── api_client.js ├── client.js └── helper.js ├── package.json ├── src ├── api_client.coffee ├── client.coffee └── helper.coffee └── upload_files ├── __runner__.sh └── node_modules └── node_helper.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Iron.io, Inc 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | `npm bin`/coffee -o lib -c src/*.coffee 3 | 4 | all: build 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | iron_worker_node is NODE.JS language binding for IronWorker. 2 | 3 | IronWorker is a massively scalable background processing system. 4 | [See How It Works](http://www.iron.io/products/worker/how) 5 | 6 | # Getting Started 7 | 8 | 9 | 1\. Install the gem: 10 | 11 | ``` 12 | npm install iron_worker 13 | ``` 14 | 15 | 2\. [Setup your Iron.io credentials](http://dev.iron.io/mq/reference/configuration/) 16 | 17 | 3\. Create an IronWorker Client object: 18 | 19 | ```javascript 20 | var iron_worker = require('iron_worker'); 21 | var worker = new iron_worker.Client(); 22 | ``` 23 | 24 | Or pass in credentials: 25 | 26 | ```javascript 27 | var worker = new iron_worker.Client({token: "MY_TOKEN", project_id: "MY_PROJECT_ID"}); 28 | ``` 29 | 30 | ## Creating a Worker 31 | 32 | Here's an example worker: 33 | 34 | ```javascript 35 | console.log("Hello Node World!"); 36 | ``` 37 | 38 | ## Upload code to server 39 | 40 | ### Using CLI tool (preferred) 41 | 42 | * Get [CLI](http://dev.iron.io/worker/reference/cli) tool 43 | * Download or create `iron.json` config file with project_id/password 44 | * Create `HelloWorld.worker` file, example: 45 | 46 | ```ruby 47 | runtime 'node' 48 | exec 'HelloWorld.js' 49 | ``` 50 | * Upload! 51 | 52 | ```sh 53 | $ iron_worker upload HelloWorld 54 | ``` 55 | 56 | [.worker syntax reference](http://dev.iron.io/worker/reference/dotworker/) 57 | 58 | ### Parsing payload, config within running worker 59 | 60 | * Add this library to list of dependencies (`package.json`): 61 | * Use it: 62 | 63 | ```javascript 64 | var iron_worker = require('iron_worker'); 65 | console.log(iron_worker.params()); 66 | console.log(iron_worker.config()); 67 | console.log(iron_worker.taskId()); 68 | ``` 69 | 70 | ## Worker examples 71 | 72 | You can find plenty of good worker examples here: [iron_worker_examples](https://github.com/iron-io/iron_worker_examples/tree/master/node) 73 | 74 | ## Queueing a Worker 75 | 76 | ```javascript 77 | worker.tasksCreate('HelloWorld', {}, {}, function(err,res){ 78 | task_id = res.id; 79 | console.log("Pushed new task: task_id = "+task_id); 80 | }); 81 | ``` 82 | Worker should start in a few seconds. 83 | 84 | If you need to pass some data you can use payload parameter 85 | 86 | ```javascript 87 | var payload = {first: 'Hello', second: 'World'}; 88 | var options = {priority: 1}; 89 | worker.tasksCreate('HelloWorld', payload, options, function(error, body) {}); 90 | ``` 91 | 92 | ### Queueing Options 93 | 94 | - **priority**: Setting the priority of your job. Valid values are 0, 1, and 2. The default is 0. 95 | - **timeout**: The maximum runtime of your task in seconds. No task can exceed 3600 seconds (60 minutes). The default is 3600 but can be set to a shorter duration. 96 | - **delay**: The number of seconds to delay before actually queuing the task. Default is 0. 97 | - **label**: Optional text label for your task. 98 | - **cluster**: cluster name ex: "high-mem" or "dedicated". This is a premium feature for customers to have access to more powerful or custom built worker solutions. Dedicated worker clusters exist for users who want to reserve a set number of workers just for their queued tasks. If not set default is set to "default" which is the public IronWorker cluster. 99 | 100 | 101 | ## Status of a Worker 102 | To get the status of a task and other info, you can use the ```tasksGet()``` method. 103 | 104 | ```javascript 105 | worker.tasksCreate('HelloWorld', {}, {}, function(err,res){ 106 | task_id = res.id; 107 | worker.tasksGet(task_id, function(error, res) { 108 | console.log("Full info about the task:\n"+JSON.stringify(res)); 109 | }); 110 | }); 111 | ``` 112 | 113 | ## Get Worker Log 114 | 115 | Use any function that print text inside your worker to put messages to log. 116 | 117 | ```javascript 118 | worker.tasksCreate('HelloWorld', {}, {}, function(err,res){ 119 | task_id = res.id; 120 | worker.tasksWaitForLog(task_id, {}, function (err, res) { 121 | worker.tasksLog(task_id, function (err, res) {console.log(res)}); 122 | }) 123 | }); 124 | ``` 125 | 126 | ## Scheduling a Worker 127 | 128 | Like with `tasksCreate` 129 | 130 | ```javascript 131 | worker.schedulesCreate('HelloWorld', payload, {run_times: 10}, function(error, body) {}); 132 | ``` 133 | 134 | ### Scheduling Options 135 | 136 | - **run_every**: The amount of time, in seconds, between runs. By default, the task will only run once. run_every will return a 400 error if it is set to less than 60. 137 | - **end_at**: The time tasks will stop being queued. 138 | - **run_times**: The number of times a task will run. 139 | - **priority**: Setting the priority of your job. Valid values are 0, 1, and 2. The default is 0. Higher values means tasks spend less time in the queue once they come off the schedule. 140 | - **start_at**: The time the scheduled task should first be run. 141 | - **label**: Optional text label for your task. 142 | - **cluster**: cluster name ex: "high-mem" or "dedicated". 143 | 144 | 145 | # Full Documentation 146 | 147 | You can find more documentation here: 148 | 149 | * http://dev.iron.io Full documetation for iron.io products. 150 | * [IronWorker Node Examples](https://github.com/iron-io/dockerworker/tree/master/node) 151 | -------------------------------------------------------------------------------- /lib/api_client.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.12.7 2 | (function() { 3 | var APIClient, _, ironCore, version, 4 | extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, 5 | hasProp = {}.hasOwnProperty; 6 | 7 | require('pkginfo')(module); 8 | 9 | version = this.version; 10 | 11 | _ = require('underscore'); 12 | 13 | ironCore = require('iron_core'); 14 | 15 | APIClient = (function(superClass) { 16 | extend(APIClient, superClass); 17 | 18 | APIClient.prototype.AWS_US_EAST_HOST = 'worker-aws-us-east-1.iron.io'; 19 | 20 | function APIClient(options) { 21 | var defaultOptions; 22 | defaultOptions = { 23 | scheme: 'https', 24 | host: this.AWS_US_EAST_HOST, 25 | port: 443, 26 | api_version: 2, 27 | user_agent: this.version() 28 | }; 29 | APIClient.__super__.constructor.call(this, 'iron', 'worker', options, defaultOptions, ['project_id', 'token', 'api_version']); 30 | } 31 | 32 | APIClient.prototype.version = function() { 33 | return "iron_worker_node-" + version + " (" + (APIClient.__super__.version.call(this)) + ")"; 34 | }; 35 | 36 | APIClient.prototype.url = function() { 37 | return APIClient.__super__.url.call(this) + this.options.api_version.toString() + '/'; 38 | }; 39 | 40 | APIClient.prototype.headers = function() { 41 | return _.extend({}, APIClient.__super__.headers.call(this), { 42 | 'Authorization': "OAuth " + this.options.token 43 | }); 44 | }; 45 | 46 | APIClient.prototype.codesList = function(options, cb) { 47 | var parseResponseBind; 48 | parseResponseBind = _.bind(this.parseResponse, this); 49 | return this.get("projects/" + this.options.project_id + "/codes", options, function(error, response, body) { 50 | return parseResponseBind(error, response, body, cb); 51 | }); 52 | }; 53 | 54 | APIClient.prototype.codesGet = function(id, cb) { 55 | var parseResponseBind; 56 | parseResponseBind = _.bind(this.parseResponse, this); 57 | return this.get("projects/" + this.options.project_id + "/codes/" + id, {}, function(error, response, body) { 58 | return parseResponseBind(error, response, body, cb); 59 | }); 60 | }; 61 | 62 | APIClient.prototype.codesDelete = function(id, cb) { 63 | var parseResponseBind; 64 | parseResponseBind = _.bind(this.parseResponse, this); 65 | return this["delete"]("projects/" + this.options.project_id + "/codes/" + id, {}, function(error, response, body) { 66 | return parseResponseBind(error, response, body, cb, false); 67 | }); 68 | }; 69 | 70 | APIClient.prototype.codesRevisions = function(id, options, cb) { 71 | var parseResponseBind; 72 | parseResponseBind = _.bind(this.parseResponse, this); 73 | return this.get("projects/" + this.options.project_id + "/codes/" + id + "/revisions", options, function(error, response, body) { 74 | return parseResponseBind(error, response, body, cb); 75 | }); 76 | }; 77 | 78 | APIClient.prototype.codesDownload = function(id, options, cb) { 79 | var parseResponseBind; 80 | parseResponseBind = _.bind(this.parseResponse, this); 81 | return this.get("projects/" + this.options.project_id + "/codes/" + id + "/download", options, function(error, response, body) { 82 | return parseResponseBind(error, response, body, cb, false); 83 | }); 84 | }; 85 | 86 | APIClient.prototype.codesUpload = function(options, cb) { 87 | var endpoint, headers, needle, parseResponseBind; 88 | needle = require("needle"); 89 | parseResponseBind = _.bind(this.parseResponse, this); 90 | headers = { 91 | multipart: true, 92 | headers: this.headers() 93 | }; 94 | endpoint = this.url() + "projects/" + this.options.project_id + "/codes"; 95 | return needle.post(endpoint, options, headers, function(error, response, body) { 96 | return parseResponseBind(error, response, body, cb, false); 97 | }); 98 | }; 99 | 100 | APIClient.prototype.tasksList = function(options, cb) { 101 | var parseResponseBind; 102 | parseResponseBind = _.bind(this.parseResponse, this); 103 | return this.get("projects/" + this.options.project_id + "/tasks", options, function(error, response, body) { 104 | return parseResponseBind(error, response, body, cb); 105 | }); 106 | }; 107 | 108 | APIClient.prototype.tasksGet = function(id, cb) { 109 | var parseResponseBind; 110 | parseResponseBind = _.bind(this.parseResponse, this); 111 | return this.get("projects/" + this.options.project_id + "/tasks/" + id, {}, function(error, response, body) { 112 | return parseResponseBind(error, response, body, cb); 113 | }); 114 | }; 115 | 116 | APIClient.prototype.tasksCreate = function(tasks, cb) { 117 | var parseResponseBind, tasksFormatted; 118 | parseResponseBind = _.bind(this.parseResponse, this); 119 | tasksFormatted = tasks.map((function(_this) { 120 | return function(task) { 121 | return _.extend({ 122 | code_name: task.codeName, 123 | payload: typeof task.payload === 'string' ? task.payload : JSON.stringify(task.payload) 124 | }, task.options); 125 | }; 126 | })(this)); 127 | return this.post("projects/" + this.options.project_id + "/tasks", { 128 | tasks: tasksFormatted 129 | }, function(error, response, body) { 130 | return parseResponseBind(error, response, body, cb); 131 | }); 132 | }; 133 | 134 | APIClient.prototype.tasksRetry = function(id, delay, cb) { 135 | var parseResponseBind; 136 | parseResponseBind = _.bind(this.parseResponse, this); 137 | return this.post("projects/" + this.options.project_id + "/tasks/" + id + "/retry", { 138 | 'delay': delay 139 | }, function(error, response, body) { 140 | return parseResponseBind(error, response, body, cb); 141 | }); 142 | }; 143 | 144 | APIClient.prototype.tasksCancel = function(id, cb) { 145 | var parseResponseBind; 146 | parseResponseBind = _.bind(this.parseResponse, this); 147 | return this.post("projects/" + this.options.project_id + "/tasks/" + id + "/cancel", {}, function(error, response, body) { 148 | return parseResponseBind(error, response, body, cb); 149 | }); 150 | }; 151 | 152 | APIClient.prototype.tasksCancelAll = function(id, cb) { 153 | var parseResponseBind; 154 | parseResponseBind = _.bind(this.parseResponse, this); 155 | return this.post("projects/" + this.options.project_id + "/codes/" + id + "/cancel_all", {}, function(error, response, body) { 156 | return parseResponseBind(error, response, body, cb); 157 | }); 158 | }; 159 | 160 | APIClient.prototype.tasksLog = function(id, cb) { 161 | var parseResponseBind; 162 | parseResponseBind = _.bind(this.parseResponse, this); 163 | return this.get("projects/" + this.options.project_id + "/tasks/" + id + "/log", {}, function(error, response, body) { 164 | return parseResponseBind(error, response, body, cb, false); 165 | }); 166 | }; 167 | 168 | APIClient.prototype.tasksStdout = function(id, cb) { 169 | var parseResponseBind; 170 | parseResponseBind = _.bind(this.parseResponse, this); 171 | return this.get("projects/" + this.options.project_id + "/tasks/" + id + "/outlog", {}, function(error, response, body) { 172 | return parseResponseBind(error, response, body, cb, false); 173 | }); 174 | }; 175 | 176 | APIClient.prototype.tasksSetProgress = function(id, options, cb) { 177 | var parseResponseBind; 178 | parseResponseBind = _.bind(this.parseResponse, this); 179 | return this.post("projects/" + this.options.project_id + "/tasks/" + id + "/progress", options, function(error, response, body) { 180 | return parseResponseBind(error, response, body, cb); 181 | }); 182 | }; 183 | 184 | APIClient.prototype.schedulesList = function(options, cb) { 185 | var parseResponseBind; 186 | parseResponseBind = _.bind(this.parseResponse, this); 187 | return this.get("projects/" + this.options.project_id + "/schedules", options, function(error, response, body) { 188 | return parseResponseBind(error, response, body, cb); 189 | }); 190 | }; 191 | 192 | APIClient.prototype.schedulesGet = function(id, cb) { 193 | var parseResponseBind; 194 | parseResponseBind = _.bind(this.parseResponse, this); 195 | return this.get("projects/" + this.options.project_id + "/schedules/" + id, {}, function(error, response, body) { 196 | return parseResponseBind(error, response, body, cb); 197 | }); 198 | }; 199 | 200 | APIClient.prototype.schedulesCreate = function(codeName, payload, options, cb) { 201 | var parseResponseBind; 202 | parseResponseBind = _.bind(this.parseResponse, this); 203 | return this.post("projects/" + this.options.project_id + "/schedules", { 204 | 'schedules': [ 205 | _.extend({ 206 | 'code_name': codeName, 207 | 'payload': payload 208 | }, options) 209 | ] 210 | }, function(error, response, body) { 211 | return parseResponseBind(error, response, body, cb); 212 | }); 213 | }; 214 | 215 | APIClient.prototype.schedulesCancel = function(id, cb) { 216 | var parseResponseBind; 217 | parseResponseBind = _.bind(this.parseResponse, this); 218 | return this.post("projects/" + this.options.project_id + "/schedules/" + id + "/cancel", {}, function(error, response, body) { 219 | return parseResponseBind(error, response, body, cb); 220 | }); 221 | }; 222 | 223 | APIClient.prototype.clustersList = function(options, cb) { 224 | var parseResponseBind; 225 | parseResponseBind = _.bind(this.parseResponse, this); 226 | return this.get("clusters", options, function(error, response, body) { 227 | return parseResponseBind(error, response, body, cb); 228 | }); 229 | }; 230 | 231 | APIClient.prototype.clustersGet = function(id, cb) { 232 | var parseResponseBind; 233 | parseResponseBind = _.bind(this.parseResponse, this); 234 | return this.get("clusters/" + id, {}, function(error, response, body) { 235 | return parseResponseBind(error, response, body, cb); 236 | }); 237 | }; 238 | 239 | APIClient.prototype.clustersStats = function(id, cb) { 240 | var parseResponseBind; 241 | parseResponseBind = _.bind(this.parseResponse, this); 242 | return this.get("clusters/" + id + "/stats", {}, function(error, response, body) { 243 | return parseResponseBind(error, response, body, cb); 244 | }); 245 | }; 246 | 247 | APIClient.prototype.clustersCreate = function(clusterName, memory, disk, options, cb) { 248 | var parseResponseBind; 249 | parseResponseBind = _.bind(this.parseResponse, this); 250 | return this.post("clusters", _.extend({ 251 | 'name': clusterName, 252 | 'memory': memory, 253 | 'disk': disk 254 | }, options), function(error, response, body) { 255 | return parseResponseBind(error, response, body, cb); 256 | }); 257 | }; 258 | 259 | APIClient.prototype.clustersUpdate = function(id, options, cb) { 260 | var parseResponseBind; 261 | parseResponseBind = _.bind(this.parseResponse, this); 262 | return this.put("clusters/" + id, options, function(error, response, body) { 263 | return parseResponseBind(error, response, body, cb); 264 | }); 265 | }; 266 | 267 | APIClient.prototype.clustersDelete = function(id, cb) { 268 | var parseResponseBind; 269 | parseResponseBind = _.bind(this.parseResponse, this); 270 | return this["delete"]("clusters/" + id, {}, function(error, response, body) { 271 | return parseResponseBind(error, response, body, cb); 272 | }); 273 | }; 274 | 275 | APIClient.prototype.instanceTerminate = function(id, instance_id, cb) { 276 | var parseResponseBind; 277 | parseResponseBind = _.bind(this.parseResponse, this); 278 | return this.post("clusters/" + id + "/terminate", { 279 | "instance_ids": [instance_id] 280 | }, function(error, response, body) { 281 | return parseResponseBind(error, response, body, cb); 282 | }); 283 | }; 284 | 285 | return APIClient; 286 | 287 | })(ironCore.Client); 288 | 289 | module.exports.APIClient = APIClient; 290 | 291 | }).call(this); 292 | -------------------------------------------------------------------------------- /lib/client.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.12.7 2 | (function() { 3 | var Client, _, apiClient, helper; 4 | 5 | _ = require('underscore'); 6 | 7 | apiClient = require('./api_client'); 8 | 9 | helper = require('./helper'); 10 | 11 | Client = (function() { 12 | var setDelayBetweenRetries; 13 | 14 | function Client(options) { 15 | this.api = new apiClient.APIClient(options); 16 | } 17 | 18 | Client.prototype.codesList = function(options, cb) { 19 | return this.api.codesList(options, function(error, body) { 20 | if (error == null) { 21 | return cb(error, body.codes); 22 | } else { 23 | return cb(error, body); 24 | } 25 | }); 26 | }; 27 | 28 | Client.prototype.codesGet = function(codeId, cb) { 29 | return this.api.codesGet(codeId, function(error, body) { 30 | if (error == null) { 31 | return cb(error, body); 32 | } else { 33 | return cb(error, body); 34 | } 35 | }); 36 | }; 37 | 38 | Client.prototype.codesDelete = function(codeId, cb) { 39 | return this.api.codesDelete(codeId, function(error, body) { 40 | if (error == null) { 41 | return cb(error, body); 42 | } else { 43 | return cb(error, body); 44 | } 45 | }); 46 | }; 47 | 48 | Client.prototype.codesRevisions = function(codeId, options, cb) { 49 | return this.api.codesRevisions(codeId, options, function(error, body) { 50 | if (error == null) { 51 | return cb(error, body.revisions); 52 | } else { 53 | return cb(error, body); 54 | } 55 | }); 56 | }; 57 | 58 | Client.prototype.codesDownload = function(codeId, options, cb) { 59 | return this.api.codesDownload(codeId, options, function(error, body) { 60 | if (error == null) { 61 | return cb(error, body); 62 | } else { 63 | return cb(error, body); 64 | } 65 | }); 66 | }; 67 | 68 | Client.prototype.tasksList = function(options, cb) { 69 | return this.api.tasksList(options, function(error, body) { 70 | if (error == null) { 71 | return cb(error, body.tasks); 72 | } else { 73 | return cb(error, body); 74 | } 75 | }); 76 | }; 77 | 78 | Client.prototype.tasksGet = function(taskId, cb) { 79 | return this.api.tasksGet(taskId, function(error, body) { 80 | if (error == null) { 81 | return cb(error, body); 82 | } else { 83 | return cb(error, body); 84 | } 85 | }); 86 | }; 87 | 88 | Client.prototype.tasksCreateBulk = function(tasks, cb) { 89 | return this.api.tasksCreate(tasks, function(error, body) { 90 | if (error == null) { 91 | return cb(error, body.tasks[0]); 92 | } else { 93 | return cb(error, body); 94 | } 95 | }); 96 | }; 97 | 98 | Client.prototype.tasksCreate = function(codeName, payload, options, cb) { 99 | return this.tasksCreateBulk([ 100 | { 101 | codeName: codeName, 102 | payload: payload, 103 | options: options 104 | } 105 | ], cb); 106 | }; 107 | 108 | Client.prototype.tasksRetry = function(taskId, delay, cb) { 109 | return this.api.tasksRetry(taskId, delay, function(error, body) { 110 | if (error == null) { 111 | return cb(error, body); 112 | } else { 113 | return cb(error, body); 114 | } 115 | }); 116 | }; 117 | 118 | Client.prototype.tasksCancel = function(taskId, cb) { 119 | return this.api.tasksCancel(taskId, function(error, body) { 120 | if (error == null) { 121 | return cb(error, body); 122 | } else { 123 | return cb(error, body); 124 | } 125 | }); 126 | }; 127 | 128 | Client.prototype.tasksCancelAll = function(codeId, cb) { 129 | return this.api.tasksCancelAll(codeId, function(error, body) { 130 | if (error == null) { 131 | return cb(error, body); 132 | } else { 133 | return cb(error, body); 134 | } 135 | }); 136 | }; 137 | 138 | Client.prototype.tasksLog = function(taskId, cb) { 139 | return this.api.tasksLog(taskId, function(error, body) { 140 | if (error == null) { 141 | return cb(error, body); 142 | } else { 143 | return cb(error, body); 144 | } 145 | }); 146 | }; 147 | 148 | Client.prototype.tasksStdout = function(taskId, cb) { 149 | return this.api.tasksStdout(taskId, function(error, body) { 150 | if (error == null) { 151 | return cb(error, body); 152 | } else { 153 | return cb(error, body); 154 | } 155 | }); 156 | }; 157 | 158 | Client.prototype.tasksSetProgress = function(taskId, options, cb) { 159 | return this.api.tasksSetProgress(taskId, options, function(error, body) { 160 | if (error == null) { 161 | return cb(error, body); 162 | } else { 163 | return cb(error, body); 164 | } 165 | }); 166 | }; 167 | 168 | Client.prototype.tasksWaitFor = function(taskId, options, cb) { 169 | var tasksWaitForBind; 170 | tasksWaitForBind = _.bind(this.tasksWaitFor, this); 171 | if (options.sleep == null) { 172 | options.sleep = 0.25; 173 | } 174 | if (options.timeout == null) { 175 | options.timeout = 3600; 176 | } 177 | if (options.tries == null) { 178 | options.tries = Math.round(options.timeout / 60) + 20; 179 | } 180 | options.tries--; 181 | if (options.tries < 0) { 182 | return cb(new Error('Timeout waiting for task execution'), null); 183 | } 184 | return this.tasksGet(taskId, function(error, body) { 185 | if (error == null) { 186 | setDelayBetweenRetries(options); 187 | if (body.status === 'queued' || body.status === 'preparing' || body.status === 'running') { 188 | return _.delay(tasksWaitForBind, options.sleep * 1000, taskId, options, cb); 189 | } else { 190 | return cb(error, body); 191 | } 192 | } else { 193 | return cb(error, body); 194 | } 195 | }); 196 | }; 197 | 198 | Client.prototype.tasksWaitForLog = function(taskId, options, cb) { 199 | var tasksWaitForLogBind; 200 | tasksWaitForLogBind = _.bind(this.tasksWaitForLog, this); 201 | if (options.sleep == null) { 202 | options.sleep = 0.25; 203 | } 204 | if (options.tries == null) { 205 | options.tries = 10; 206 | } 207 | options.tries--; 208 | if (options.tries < 0) { 209 | return cb(new Error('Timeout waiting for task log'), null); 210 | } 211 | return this.tasksLog(taskId, function(error, body) { 212 | if (error && error.message.match(/log/i)) { 213 | setDelayBetweenRetries(options); 214 | return _.delay(tasksWaitForLogBind, options.sleep * 1000, taskId, options, cb); 215 | } else { 216 | return cb(error, body); 217 | } 218 | }); 219 | }; 220 | 221 | Client.prototype.tasksWaitForSyncTaskStdout = function(taskId, options, cb) { 222 | var tasksWaitForSyncTaskStdoutBind; 223 | tasksWaitForSyncTaskStdoutBind = _.bind(this.tasksWaitForSyncTaskStdout, this); 224 | if (options.sleep == null) { 225 | options.sleep = 0.25; 226 | } 227 | if (options.tries == null) { 228 | options.tries = 10; 229 | } 230 | options.tries--; 231 | if (options.tries < 0) { 232 | return cb(new Error('Timeout waiting for task stdout'), null); 233 | } 234 | return this.tasksStdout(taskId, function(error, body) { 235 | if (error && error.message.match(/log/i)) { 236 | setDelayBetweenRetries(options); 237 | return _.delay(tasksWaitForSyncTaskStdoutBind, options.sleep * 1000, taskId, options, cb); 238 | } else { 239 | return cb(error, body); 240 | } 241 | }); 242 | }; 243 | 244 | Client.prototype.tasksRun = function(codeName, params, options, cb) { 245 | var tasksWaitForBind, tasksWaitForSyncTaskStdoutBind; 246 | options.sync = true; 247 | tasksWaitForBind = _.bind(this.tasksWaitFor, this); 248 | tasksWaitForSyncTaskStdoutBind = _.bind(this.tasksWaitForSyncTaskStdout, this); 249 | return this.tasksCreate(codeName, params, options, function(error, body) { 250 | var task_id; 251 | if (error) { 252 | return cb(error, body); 253 | } else { 254 | task_id = body.id; 255 | return tasksWaitForBind(task_id, {}, function(error, body) { 256 | if (error) { 257 | return cb(error, body); 258 | } else { 259 | return tasksWaitForSyncTaskStdoutBind(task_id, {}, function(error, body) { 260 | if (error) { 261 | return cb(error, body); 262 | } else { 263 | return cb(error, body); 264 | } 265 | }); 266 | } 267 | }); 268 | } 269 | }); 270 | }; 271 | 272 | Client.prototype.schedulesList = function(options, cb) { 273 | return this.api.schedulesList(options, function(error, body) { 274 | if (error == null) { 275 | return cb(error, body.schedules); 276 | } else { 277 | return cb(error, body); 278 | } 279 | }); 280 | }; 281 | 282 | Client.prototype.schedulesGet = function(scheduleId, cb) { 283 | return this.api.schedulesGet(scheduleId, function(error, body) { 284 | if (error == null) { 285 | return cb(error, body); 286 | } else { 287 | return cb(error, body); 288 | } 289 | }); 290 | }; 291 | 292 | Client.prototype.schedulesCreate = function(codeName, params, options, cb) { 293 | var payload; 294 | payload = ''; 295 | if (typeof params === 'string') { 296 | payload = params; 297 | } else { 298 | payload = JSON.stringify(params); 299 | } 300 | return this.api.schedulesCreate(codeName, payload, options, function(error, body) { 301 | if (error == null) { 302 | return cb(error, body.schedules[0]); 303 | } else { 304 | return cb(error, body); 305 | } 306 | }); 307 | }; 308 | 309 | Client.prototype.schedulesCancel = function(scheduleId, cb) { 310 | return this.api.schedulesCancel(scheduleId, function(error, body) { 311 | if (error == null) { 312 | return cb(error, body); 313 | } else { 314 | return cb(error, body); 315 | } 316 | }); 317 | }; 318 | 319 | Client.prototype.codesUpload = function(name, destination, fileName, cb) { 320 | var AdmZip, api, fs, ncp, needle, zip; 321 | needle = require("needle"); 322 | ncp = require("ncp").ncp; 323 | fs = require("fs"); 324 | AdmZip = require("adm-zip"); 325 | zip = new AdmZip(); 326 | if (destination.charAt(destination.length - 1) !== "/") { 327 | destination += "/"; 328 | } 329 | api = this.api; 330 | ncp.limit = 16; 331 | return ncp("upload_files", destination, function(err) { 332 | if (err) { 333 | throw err; 334 | } 335 | return fs.appendFile(destination + "__runner__.sh", "\nnode " + fileName, function(err) { 336 | var params; 337 | if (err) { 338 | throw err; 339 | } 340 | zip.addLocalFolder(destination); 341 | params = { 342 | data: JSON.stringify({ 343 | name: name, 344 | file_name: "__runner__.sh", 345 | runtime: "sh", 346 | content_type: "text/plain" 347 | }), 348 | file: { 349 | buffer: zip.toBuffer(), 350 | content_type: "application/zip" 351 | } 352 | }; 353 | return api.codesUpload(params, function(err, body) { 354 | if (err == null) { 355 | return cb(err, body); 356 | } else { 357 | return cb(err, body); 358 | } 359 | }); 360 | }); 361 | }); 362 | }; 363 | 364 | Client.prototype.clustersList = function(options, cb) { 365 | return this.api.clustersList(options, function(error, body) { 366 | if (error == null) { 367 | return cb(error, body); 368 | } else { 369 | return cb(error, body); 370 | } 371 | }); 372 | }; 373 | 374 | Client.prototype.clustersGet = function(clusterId, cb) { 375 | return this.api.clustersGet(clusterId, function(error, body) { 376 | if (error == null) { 377 | return cb(error, body); 378 | } else { 379 | return cb(error, body); 380 | } 381 | }); 382 | }; 383 | 384 | Client.prototype.clustersStats = function(clusterId, cb) { 385 | return this.api.clustersStats(clusterId, function(error, body) { 386 | if (error == null) { 387 | return cb(error, body); 388 | } else { 389 | return cb(error, body); 390 | } 391 | }); 392 | }; 393 | 394 | Client.prototype.clustersCreate = function(clusterName, memory, disk, options, cb) { 395 | return this.api.clustersCreate(clusterName, memory, disk, options, function(error, body) { 396 | if (error == null) { 397 | return cb(error, body); 398 | } else { 399 | return cb(error, body); 400 | } 401 | }); 402 | }; 403 | 404 | Client.prototype.clustersUpdate = function(clusterId, options, cb) { 405 | return this.api.clustersUpdate(clusterId, options, function(error, body) { 406 | if (error == null) { 407 | return cb(error, body); 408 | } else { 409 | return cb(error, body); 410 | } 411 | }); 412 | }; 413 | 414 | Client.prototype.clustersDelete = function(clusterId, cb) { 415 | return this.api.clustersDelete(clusterId, function(error, body) { 416 | if (error == null) { 417 | return cb(error, body); 418 | } else { 419 | return cb(error, body); 420 | } 421 | }); 422 | }; 423 | 424 | Client.prototype.instanceTerminate = function(clusterId, instance_id, cb) { 425 | return this.api.instanceTerminate(clusterId, instance_id, function(error, body) { 426 | if (error == null) { 427 | return cb(error, body); 428 | } else { 429 | return cb(error, body); 430 | } 431 | }); 432 | }; 433 | 434 | setDelayBetweenRetries = function(options) { 435 | if (options.sleep < 60) { 436 | return options.sleep *= 2; 437 | } 438 | }; 439 | 440 | return Client; 441 | 442 | })(); 443 | 444 | module.exports.Client = Client; 445 | 446 | module.exports.params = helper.params; 447 | 448 | module.exports.config = helper.config; 449 | 450 | module.exports.taskId = helper.taskId; 451 | 452 | module.exports.taskDir = helper.taskDir; 453 | 454 | }).call(this); 455 | -------------------------------------------------------------------------------- /lib/helper.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.12.7 2 | (function() { 3 | var _, config, config_file, params, parseCLIData, payload_file, task_dir, task_id; 4 | 5 | _ = require('underscore'); 6 | 7 | params = null; 8 | 9 | task_id = null; 10 | 11 | config = null; 12 | 13 | task_dir = null; 14 | 15 | payload_file = null; 16 | 17 | config_file = null; 18 | 19 | parseCLIData = function() { 20 | var e, fs, parsed, querystring; 21 | fs = require('fs'); 22 | querystring = require('querystring'); 23 | process.argv.forEach(function(val, index, array) { 24 | if (val === "-payload") { 25 | payload_file = process.argv[index + 1]; 26 | } 27 | if (val === "-config") { 28 | config_file = process.argv[index + 1]; 29 | } 30 | if (val === "-id") { 31 | task_id = process.argv[index + 1]; 32 | } 33 | if (val === "-d") { 34 | return task_dir = process.argv[index + 1]; 35 | } 36 | }); 37 | if (process.env.TASK_ID) { 38 | task_id = process.env.TASK_ID; 39 | } 40 | if (process.env.TASK_DIR) { 41 | task_dir = process.env.TASK_DIR; 42 | } 43 | if (process.env.PAYLOAD_FILE) { 44 | payload_file = process.env.PAYLOAD_FILE; 45 | } 46 | if (process.env.CONFIG_FILE) { 47 | config_file = process.env.CONFIG_FILE; 48 | } 49 | if (payload_file != null) { 50 | params = fs.readFileSync(payload_file, 'utf8'); 51 | try { 52 | params = JSON.parse(params); 53 | } catch (error) { 54 | e = error; 55 | try { 56 | parsed = querystring.parse(params); 57 | if (!(Object.keys(parsed).length === 1 && parsed[Object.keys(parsed)[0]] === '')) { 58 | params = parsed; 59 | } 60 | } catch (error) { 61 | e = error; 62 | } 63 | } 64 | } 65 | if (config_file != null) { 66 | config = fs.readFileSync(config_file, 'utf8'); 67 | try { 68 | return config = JSON.parse(config); 69 | } catch (error) { 70 | e = error; 71 | } 72 | } 73 | }; 74 | 75 | module.exports.params = function() { 76 | if (!params) { 77 | parseCLIData(); 78 | } 79 | return params; 80 | }; 81 | 82 | module.exports.taskId = function() { 83 | if (!task_id) { 84 | parseCLIData(); 85 | } 86 | return task_id; 87 | }; 88 | 89 | module.exports.config = function() { 90 | if (!config) { 91 | parseCLIData(); 92 | } 93 | return config; 94 | }; 95 | 96 | module.exports.taskDir = function() { 97 | if (!task_dir) { 98 | parseCLIData(); 99 | } 100 | return task_dir; 101 | }; 102 | 103 | }).call(this); 104 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iron_worker", 3 | "version": "0.1.8", 4 | "description": "Node client for IronWorker", 5 | "homepage": "https://github.com/iron-io/iron_worker_node", 6 | "author": "Andrew Kirilenko & Iron.io, Inc", 7 | "main": "./lib/client", 8 | "dependencies": { 9 | "pkginfo": "0.2.3", 10 | "underscore": "1.3.3", 11 | "iron_core": "0.2", 12 | "needle": "0.7.8", 13 | "ncp": "0.6.0", 14 | "adm-zip": "0.4.4" 15 | }, 16 | "devDependencies": { 17 | "coffee-script": "1.12.7" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/iron-io/iron_worker_node.git" 22 | }, 23 | "engines": { 24 | "node": ">= 0.6.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/api_client.coffee: -------------------------------------------------------------------------------- 1 | require('pkginfo')(module) 2 | version = @version 3 | 4 | _ = require('underscore') 5 | 6 | ironCore = require('iron_core'); 7 | 8 | class APIClient extends ironCore.Client 9 | AWS_US_EAST_HOST: 'worker-aws-us-east-1.iron.io' 10 | 11 | constructor: (options) -> 12 | defaultOptions = 13 | scheme: 'https', 14 | host: @AWS_US_EAST_HOST, 15 | port: 443, 16 | api_version: 2, 17 | user_agent: @version() 18 | 19 | super('iron', 'worker', options, defaultOptions, ['project_id', 'token', 'api_version']) 20 | 21 | version: -> 22 | "iron_worker_node-#{version} (#{super()})" 23 | 24 | url: -> 25 | super() + @options.api_version.toString() + '/' 26 | 27 | headers: -> 28 | _.extend({}, super(), {'Authorization': "OAuth #{@options.token}"}) 29 | 30 | codesList: (options, cb) -> 31 | parseResponseBind = _.bind(@parseResponse, @) 32 | 33 | @get("projects/#{@options.project_id}/codes", options, (error, response, body) -> 34 | parseResponseBind(error, response, body, cb) 35 | ) 36 | 37 | codesGet: (id, cb) -> 38 | parseResponseBind = _.bind(@parseResponse, @) 39 | 40 | @get("projects/#{@options.project_id}/codes/#{id}", {}, (error, response, body) -> 41 | parseResponseBind(error, response, body, cb) 42 | ) 43 | 44 | codesDelete: (id, cb) -> 45 | parseResponseBind = _.bind(@parseResponse, @) 46 | 47 | @delete("projects/#{@options.project_id}/codes/#{id}", {}, (error, response, body) -> 48 | parseResponseBind(error, response, body, cb, false) 49 | ) 50 | 51 | codesRevisions: (id, options, cb) -> 52 | parseResponseBind = _.bind(@parseResponse, @) 53 | 54 | @get("projects/#{@options.project_id}/codes/#{id}/revisions", options, (error, response, body) -> 55 | parseResponseBind(error, response, body, cb) 56 | ) 57 | 58 | codesDownload: (id, options, cb) -> 59 | parseResponseBind = _.bind(@parseResponse, @) 60 | 61 | @get("projects/#{@options.project_id}/codes/#{id}/download", options, (error, response, body) -> 62 | parseResponseBind(error, response, body, cb, false) 63 | ) 64 | 65 | codesUpload: (options, cb) -> 66 | needle = require("needle") 67 | parseResponseBind = _.bind(@parseResponse, this) 68 | headers = 69 | multipart: true 70 | headers: @headers() 71 | endpoint = @url() + "projects/" + @options.project_id + "/codes" 72 | needle.post(endpoint, options, headers, (error, response, body) -> 73 | parseResponseBind(error, response, body, cb, false) 74 | ) 75 | 76 | tasksList: (options, cb) -> 77 | parseResponseBind = _.bind(@parseResponse, @) 78 | 79 | @get("projects/#{@options.project_id}/tasks", options, (error, response, body) -> 80 | parseResponseBind(error, response, body, cb) 81 | ) 82 | 83 | tasksGet: (id, cb) -> 84 | parseResponseBind = _.bind(@parseResponse, @) 85 | 86 | @get("projects/#{@options.project_id}/tasks/#{id}", {}, (error, response, body) -> 87 | parseResponseBind(error, response, body, cb) 88 | ) 89 | 90 | tasksCreate: (tasks, cb) -> 91 | parseResponseBind = _.bind(@parseResponse, @) 92 | 93 | tasksFormatted = tasks.map (task) => _.extend( 94 | { 95 | code_name: task.codeName, 96 | payload: if typeof(task.payload) == 'string' then task.payload else JSON.stringify(task.payload) 97 | }, 98 | task.options 99 | ) 100 | 101 | @post("projects/#{@options.project_id}/tasks", {tasks: tasksFormatted}, (error, response, body) -> 102 | parseResponseBind(error, response, body, cb) 103 | ) 104 | 105 | tasksRetry: (id, delay, cb) -> 106 | parseResponseBind = _.bind(@parseResponse, @) 107 | 108 | @post("projects/#{@options.project_id}/tasks/#{id}/retry", {'delay': delay}, (error, response, body) -> 109 | parseResponseBind(error, response, body, cb) 110 | ) 111 | 112 | tasksCancel: (id, cb) -> 113 | parseResponseBind = _.bind(@parseResponse, @) 114 | 115 | @post("projects/#{@options.project_id}/tasks/#{id}/cancel", {}, (error, response, body) -> 116 | parseResponseBind(error, response, body, cb) 117 | ) 118 | 119 | tasksCancelAll: (id, cb) -> 120 | parseResponseBind = _.bind(@parseResponse, @) 121 | 122 | @post("projects/#{@options.project_id}/codes/#{id}/cancel_all", {}, (error, response, body) -> 123 | parseResponseBind(error, response, body, cb) 124 | ) 125 | 126 | tasksLog: (id, cb) -> 127 | parseResponseBind = _.bind(@parseResponse, @) 128 | 129 | @get("projects/#{@options.project_id}/tasks/#{id}/log", {}, (error, response, body) -> 130 | parseResponseBind(error, response, body, cb, false) 131 | ) 132 | 133 | tasksStdout: (id, cb) -> 134 | parseResponseBind = _.bind(@parseResponse, @) 135 | 136 | @get("projects/#{@options.project_id}/tasks/#{id}/outlog", {}, (error, response, body) -> 137 | parseResponseBind(error, response, body, cb, false) 138 | ) 139 | 140 | tasksSetProgress: (id, options, cb) -> 141 | parseResponseBind = _.bind(@parseResponse, @) 142 | 143 | @post("projects/#{@options.project_id}/tasks/#{id}/progress", options, (error, response, body) -> 144 | parseResponseBind(error, response, body, cb) 145 | ) 146 | 147 | schedulesList: (options, cb) -> 148 | parseResponseBind = _.bind(@parseResponse, @) 149 | 150 | @get("projects/#{@options.project_id}/schedules", options, (error, response, body) -> 151 | parseResponseBind(error, response, body, cb) 152 | ) 153 | 154 | schedulesGet: (id, cb) -> 155 | parseResponseBind = _.bind(@parseResponse, @) 156 | 157 | @get("projects/#{@options.project_id}/schedules/#{id}", {}, (error, response, body) -> 158 | parseResponseBind(error, response, body, cb) 159 | ) 160 | 161 | schedulesCreate: (codeName, payload, options, cb) -> 162 | parseResponseBind = _.bind(@parseResponse, @) 163 | 164 | @post("projects/#{@options.project_id}/schedules", {'schedules': [_.extend({'code_name': codeName, 'payload': payload}, options)]}, (error, response, body) -> 165 | parseResponseBind(error, response, body, cb) 166 | ) 167 | 168 | schedulesCancel: (id, cb) -> 169 | parseResponseBind = _.bind(@parseResponse, @) 170 | 171 | @post("projects/#{@options.project_id}/schedules/#{id}/cancel", {}, (error, response, body) -> 172 | parseResponseBind(error, response, body, cb) 173 | ) 174 | 175 | clustersList: (options, cb) -> 176 | parseResponseBind = _.bind(@parseResponse, @) 177 | 178 | @get("clusters", options, (error, response, body) -> 179 | parseResponseBind(error, response, body, cb) 180 | ) 181 | 182 | clustersGet: (id, cb) -> 183 | parseResponseBind = _.bind(@parseResponse, @) 184 | 185 | @get("clusters/#{id}", {}, (error, response, body) -> 186 | parseResponseBind(error, response, body, cb) 187 | ) 188 | 189 | clustersStats: (id, cb) -> 190 | parseResponseBind = _.bind(@parseResponse, @) 191 | 192 | @get("clusters/#{id}/stats", {}, (error, response, body) -> 193 | parseResponseBind(error, response, body, cb) 194 | ) 195 | 196 | clustersCreate: (clusterName, memory, disk, options, cb) -> 197 | parseResponseBind = _.bind(@parseResponse, @) 198 | 199 | @post("clusters", _.extend({'name': clusterName, 'memory': memory, 'disk': disk}, options), (error, response, body) -> 200 | parseResponseBind(error, response, body, cb) 201 | ) 202 | 203 | clustersUpdate: (id, options, cb) -> 204 | parseResponseBind = _.bind(@parseResponse, @) 205 | 206 | @put("clusters/#{id}", options, (error, response, body) -> 207 | parseResponseBind(error, response, body, cb) 208 | ) 209 | 210 | clustersDelete: (id, cb) -> 211 | parseResponseBind = _.bind(@parseResponse, @) 212 | 213 | @delete("clusters/#{id}", {}, (error, response, body) -> 214 | parseResponseBind(error, response, body, cb) 215 | ) 216 | 217 | instanceTerminate: (id, instance_id, cb) -> 218 | parseResponseBind = _.bind(@parseResponse, @) 219 | 220 | @post("clusters/#{id}/terminate", {"instance_ids": [instance_id]}, (error, response, body) -> 221 | parseResponseBind(error, response, body, cb) 222 | ) 223 | 224 | module.exports.APIClient = APIClient 225 | -------------------------------------------------------------------------------- /src/client.coffee: -------------------------------------------------------------------------------- 1 | _ = require('underscore') 2 | 3 | apiClient = require('./api_client') 4 | helper = require('./helper') 5 | 6 | class Client 7 | constructor: (options) -> 8 | @api = new apiClient.APIClient(options) 9 | 10 | codesList: (options, cb) -> 11 | @api.codesList(options, (error, body) -> 12 | if not error? 13 | cb(error, body.codes) 14 | else 15 | cb(error, body) 16 | ) 17 | 18 | codesGet: (codeId, cb) -> 19 | @api.codesGet(codeId, (error, body) -> 20 | if not error? 21 | cb(error, body) 22 | else 23 | cb(error, body) 24 | ) 25 | 26 | codesDelete: (codeId, cb) -> 27 | @api.codesDelete(codeId, (error, body) -> 28 | if not error? 29 | cb(error, body) 30 | else 31 | cb(error, body) 32 | ) 33 | 34 | codesRevisions: (codeId, options, cb) -> 35 | @api.codesRevisions(codeId, options, (error, body) -> 36 | if not error? 37 | cb(error, body.revisions) 38 | else 39 | cb(error, body) 40 | ) 41 | 42 | codesDownload: (codeId, options, cb) -> 43 | @api.codesDownload(codeId, options, (error, body) -> 44 | if not error? 45 | cb(error, body) 46 | else 47 | cb(error, body) 48 | ) 49 | 50 | tasksList: (options, cb) -> 51 | @api.tasksList(options, (error, body) -> 52 | if not error? 53 | cb(error, body.tasks) 54 | else 55 | cb(error, body) 56 | ) 57 | 58 | tasksGet: (taskId, cb) -> 59 | @api.tasksGet(taskId, (error, body) -> 60 | if not error? 61 | cb(error, body) 62 | else 63 | cb(error, body) 64 | ) 65 | 66 | tasksCreateBulk: (tasks, cb) -> 67 | @api.tasksCreate(tasks, (error, body) -> 68 | if not error? 69 | cb(error, body.tasks[0]) 70 | else 71 | cb(error, body) 72 | ) 73 | 74 | tasksCreate: (codeName, payload, options, cb) -> 75 | @tasksCreateBulk([{ codeName, payload, options }], cb) 76 | 77 | tasksRetry: (taskId, delay, cb) -> 78 | @api.tasksRetry(taskId, delay, (error, body) -> 79 | if not error? 80 | cb(error, body) 81 | else 82 | cb(error, body) 83 | ) 84 | 85 | tasksCancel: (taskId, cb) -> 86 | @api.tasksCancel(taskId, (error, body) -> 87 | if not error? 88 | cb(error, body) 89 | else 90 | cb(error, body) 91 | ) 92 | 93 | tasksCancelAll: (codeId, cb) -> 94 | @api.tasksCancelAll(codeId, (error, body) -> 95 | if not error? 96 | cb(error, body) 97 | else 98 | cb(error, body) 99 | ) 100 | 101 | tasksLog: (taskId, cb) -> 102 | @api.tasksLog(taskId, (error, body) -> 103 | if not error? 104 | cb(error, body) 105 | else 106 | cb(error, body) 107 | ) 108 | 109 | tasksStdout: (taskId, cb) -> 110 | @api.tasksStdout(taskId, (error, body) -> 111 | if not error? 112 | cb(error, body) 113 | else 114 | cb(error, body) 115 | ) 116 | 117 | tasksSetProgress: (taskId, options, cb) -> 118 | @api.tasksSetProgress(taskId, options, (error, body) -> 119 | if not error? 120 | cb(error, body) 121 | else 122 | cb(error, body) 123 | ) 124 | 125 | tasksWaitFor: (taskId, options, cb) -> 126 | tasksWaitForBind = _.bind(@tasksWaitFor, @) 127 | 128 | options.sleep ?= 0.25 129 | options.timeout ?= 3600 130 | options.tries ?= Math.round((options.timeout) / 60) + 20 131 | options.tries-- 132 | if options.tries < 0 133 | return cb(new Error('Timeout waiting for task execution'), null) 134 | 135 | @tasksGet(taskId, (error, body) -> 136 | if not error? 137 | setDelayBetweenRetries(options) 138 | if body.status == 'queued' or body.status == 'preparing' or body.status == 'running' 139 | _.delay(tasksWaitForBind, options.sleep * 1000, taskId, options, cb) 140 | else 141 | cb(error, body) 142 | else 143 | cb(error, body) 144 | ) 145 | 146 | tasksWaitForLog: (taskId, options, cb) -> 147 | tasksWaitForLogBind = _.bind(@tasksWaitForLog, @) 148 | options.sleep ?= 0.25 149 | options.tries ?= 10 150 | options.tries-- 151 | if options.tries < 0 152 | return cb(new Error('Timeout waiting for task log'), null) 153 | 154 | @tasksLog(taskId, (error, body) -> 155 | if error and error.message.match(/log/i) 156 | setDelayBetweenRetries(options) 157 | _.delay(tasksWaitForLogBind, options.sleep * 1000, taskId, options, cb) 158 | else 159 | cb(error, body) 160 | ) 161 | 162 | tasksWaitForSyncTaskStdout: (taskId, options, cb) -> 163 | tasksWaitForSyncTaskStdoutBind = _.bind(@tasksWaitForSyncTaskStdout, @) 164 | options.sleep ?= 0.25 165 | options.tries ?= 10 166 | options.tries-- 167 | if options.tries < 0 168 | return cb(new Error('Timeout waiting for task stdout'), null) 169 | 170 | @tasksStdout(taskId, (error, body) -> 171 | if error and error.message.match(/log/i) 172 | setDelayBetweenRetries(options) 173 | _.delay(tasksWaitForSyncTaskStdoutBind, options.sleep * 1000, taskId, options, cb) 174 | else 175 | cb(error, body) 176 | ) 177 | 178 | tasksRun: (codeName, params, options, cb) -> 179 | options.sync = true 180 | tasksWaitForBind = _.bind(@tasksWaitFor, @) 181 | tasksWaitForSyncTaskStdoutBind = _.bind(@tasksWaitForSyncTaskStdout, @) 182 | 183 | @tasksCreate(codeName, params, options, (error, body) -> 184 | if error 185 | cb(error, body) 186 | else 187 | task_id = body.id 188 | tasksWaitForBind(task_id, {}, (error, body) -> 189 | if error 190 | cb(error, body) 191 | else 192 | tasksWaitForSyncTaskStdoutBind(task_id, {}, (error, body) -> 193 | if error 194 | cb(error, body) 195 | else 196 | cb(error, body) 197 | ) 198 | ) 199 | ) 200 | 201 | schedulesList: (options, cb) -> 202 | @api.schedulesList(options, (error, body) -> 203 | if not error? 204 | cb(error, body.schedules) 205 | else 206 | cb(error, body) 207 | ) 208 | 209 | schedulesGet: (scheduleId, cb) -> 210 | @api.schedulesGet(scheduleId, (error, body) -> 211 | if not error? 212 | cb(error, body) 213 | else 214 | cb(error, body) 215 | ) 216 | 217 | schedulesCreate: (codeName, params, options, cb) -> 218 | payload = '' 219 | 220 | if typeof(params) == 'string' 221 | payload = params 222 | else 223 | payload = JSON.stringify(params) 224 | 225 | @api.schedulesCreate(codeName, payload, options, (error, body) -> 226 | if not error? 227 | cb(error, body.schedules[0]) 228 | else 229 | cb(error, body) 230 | ) 231 | 232 | schedulesCancel: (scheduleId, cb) -> 233 | @api.schedulesCancel(scheduleId, (error, body) -> 234 | if not error? 235 | cb(error, body) 236 | else 237 | cb(error, body) 238 | ) 239 | 240 | codesUpload: (name, destination, fileName, cb) -> 241 | needle = require("needle") 242 | ncp = require("ncp").ncp 243 | fs = require("fs") 244 | AdmZip = require("adm-zip") 245 | zip = new AdmZip() 246 | destination += "/" unless destination.charAt(destination.length - 1) is "/" 247 | api = @api 248 | ncp.limit = 16 249 | ncp "upload_files", destination, (err) -> 250 | throw err if err 251 | fs.appendFile destination + "__runner__.sh", "\nnode " + fileName, (err) -> 252 | throw err if err 253 | zip.addLocalFolder destination 254 | params = 255 | data: JSON.stringify( 256 | name: name 257 | file_name: "__runner__.sh" 258 | runtime: "sh" 259 | content_type: "text/plain" 260 | ) 261 | file: 262 | buffer: zip.toBuffer() 263 | content_type: "application/zip" 264 | 265 | api.codesUpload params, (err, body) -> 266 | unless err? 267 | cb err, body 268 | else 269 | cb err, body 270 | 271 | clustersList: (options, cb) -> 272 | @api.clustersList(options, (error, body) -> 273 | if not error? 274 | cb(error, body) 275 | else 276 | cb(error, body) 277 | ) 278 | 279 | clustersGet: (clusterId, cb) -> 280 | @api.clustersGet(clusterId, (error, body) -> 281 | if not error? 282 | cb(error, body) 283 | else 284 | cb(error, body) 285 | ) 286 | 287 | clustersStats: (clusterId, cb) -> 288 | @api.clustersStats(clusterId, (error, body) -> 289 | if not error? 290 | cb(error, body) 291 | else 292 | cb(error, body) 293 | ) 294 | 295 | clustersCreate: (clusterName, memory, disk, options, cb) -> 296 | @api.clustersCreate(clusterName, memory, disk, options, (error, body) -> 297 | if not error? 298 | cb(error, body) 299 | else 300 | cb(error, body) 301 | ) 302 | 303 | clustersUpdate: (clusterId, options, cb) -> 304 | @api.clustersUpdate(clusterId, options, (error, body) -> 305 | if not error? 306 | cb(error, body) 307 | else 308 | cb(error, body) 309 | ) 310 | 311 | clustersDelete: (clusterId, cb) -> 312 | @api.clustersDelete(clusterId, (error, body) -> 313 | if not error? 314 | cb(error, body) 315 | else 316 | cb(error, body) 317 | ) 318 | 319 | instanceTerminate: (clusterId, instance_id, cb) -> 320 | @api.instanceTerminate(clusterId, instance_id, (error, body) -> 321 | if not error? 322 | cb(error, body) 323 | else 324 | cb(error, body) 325 | ) 326 | 327 | setDelayBetweenRetries = (options) -> 328 | if options.sleep < 60 329 | options.sleep *= 2 330 | 331 | module.exports.Client = Client 332 | module.exports.params = helper.params 333 | module.exports.config = helper.config 334 | module.exports.taskId = helper.taskId 335 | module.exports.taskDir = helper.taskDir -------------------------------------------------------------------------------- /src/helper.coffee: -------------------------------------------------------------------------------- 1 | _ = require('underscore') 2 | 3 | params = null 4 | task_id = null 5 | config = null 6 | task_dir = null 7 | payload_file = null 8 | config_file = null 9 | 10 | parseCLIData = -> 11 | fs = require('fs') 12 | querystring = require('querystring') 13 | 14 | process.argv.forEach (val, index, array) -> 15 | if val == "-payload" 16 | payload_file = process.argv[index + 1] 17 | 18 | if val == "-config" 19 | config_file = process.argv[index + 1] 20 | 21 | if val == "-id" 22 | task_id = process.argv[index + 1] 23 | 24 | if val == "-d" 25 | task_dir = process.argv[index + 1] 26 | 27 | task_id = process.env.TASK_ID if process.env.TASK_ID 28 | task_dir = process.env.TASK_DIR if process.env.TASK_DIR 29 | payload_file = process.env.PAYLOAD_FILE if process.env.PAYLOAD_FILE 30 | config_file = process.env.CONFIG_FILE if process.env.CONFIG_FILE 31 | 32 | if payload_file? 33 | params = fs.readFileSync(payload_file, 'utf8') 34 | try 35 | params = JSON.parse(params) 36 | catch e 37 | try 38 | parsed = querystring.parse(params) 39 | if !(Object.keys(parsed).length == 1 && parsed[Object.keys(parsed)[0]] == '') 40 | params = parsed 41 | catch e 42 | 43 | if config_file? 44 | config = fs.readFileSync(config_file, 'utf8') 45 | try 46 | config = JSON.parse(config) 47 | catch e 48 | 49 | module.exports.params = -> 50 | parseCLIData() if !params 51 | params 52 | 53 | module.exports.taskId = -> 54 | parseCLIData() if !task_id 55 | task_id 56 | 57 | module.exports.config = -> 58 | parseCLIData() if !config 59 | config 60 | 61 | module.exports.taskDir = -> 62 | parseCLIData() if !task_dir 63 | task_dir -------------------------------------------------------------------------------- /upload_files/__runner__.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | root() { 4 | while [ $# -gt 1 ]; do 5 | if [ "$1" = "-d" ]; then 6 | printf "%s" "$2" 7 | break 8 | fi 9 | 10 | shift 11 | done 12 | } 13 | 14 | cd "$(root "$@")" 15 | 16 | LD_LIBRARY_PATH=.:./lib:./__debs__/usr/lib:./__debs__/usr/lib/x86_64-linux-gnu:./__debs__/lib:./__debs__/lib/x86_64-linux-gnu 17 | export LD_LIBRARY_PATH 18 | 19 | PATH=.:./bin:./__debs__/usr/bin:./__debs__/bin:$PATH 20 | export PATH 21 | 22 | -------------------------------------------------------------------------------- /upload_files/node_modules/node_helper.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var querystring = require('querystring'); 3 | var params = null; 4 | var task_id = null; 5 | var config = null; 6 | 7 | process.argv.forEach(function(val, index, array) { 8 | if (val == "-payload") { 9 | params = fs.readFileSync(process.argv[index + 1], 'utf8'); 10 | try { 11 | params = JSON.parse(params); 12 | } catch(e) { 13 | try { 14 | var parsed = querystring.parse(params); 15 | if (!(Object.keys(parsed).length == 1 && parsed[Object.keys(parsed)[0]] == '')) { 16 | params = parsed 17 | } 18 | } catch(e) { 19 | 20 | } 21 | } 22 | } 23 | 24 | if (val == "-config") { 25 | config = JSON.parse(fs.readFileSync(process.argv[index + 1], 'utf8')); 26 | } 27 | 28 | if (val == "-id") { 29 | task_id = process.argv[index + 1]; 30 | } 31 | }); 32 | 33 | exports.params = params; 34 | exports.config = config; 35 | exports.task_id = task_id; 36 | 37 | --------------------------------------------------------------------------------