diff --git a/.gitignore b/.gitignore index 8d87b1d..8850a3b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules/* +.idea/* diff --git a/docker-chaos.js b/docker-chaos.js index ed9bb91..06b4457 100755 --- a/docker-chaos.js +++ b/docker-chaos.js @@ -37,13 +37,14 @@ var knownOpts = { "composeFile" : [String], "plan": [String], "logPath": [String], - "duration": [Number] + "duration": [Number], + "projectName": [String] }; var shortHands = {}; var options = nopt(knownOpts, shortHands, process.argv); -var dockerComposeFile, planFile, command, duration; +var dockerComposeFile, planFile, command, duration, project_name; if (!options.duration) { options.duration = 30000; @@ -61,16 +62,21 @@ try { } if (!options.plan) { - console.log("A plan file is needed so we know where to create chaos"); + console.log("A plan file is needed so we know how to create chaos"); + process.exit(1); +} +if (!options.projectName) { + console.log("A project name is needed so we know where to create chaos"); process.exit(1); } planFile = fs.realpathSync(options.plan); +project_name = options.projectName; // // Command // -var command = options.argv.remain[0]; +command = options.argv.remain[0]; if (!command) { console.log("you need to pass the command to be executed"); process.exit(1); @@ -102,54 +108,38 @@ console.log(); var planData = fs.readFileSync(planFile); var chaosPlanFactory = new ChaosPlanFactory(); -var chaosPlan = chaosPlanFactory.getChaosPlan(planData); - -var exitCode = 0; -var executor = new Executor(); - -var spinner = new Spinner('Running ... '); - -executor.on('start', function() { - spinner.start(); -}); -executor.on('pass', function() { - console.log(clc.green("Passed")); - spinner.stop(); -}); -executor.on('fail', function(stdout, stderr) { - exitCode = 1; - console.log(clc.red("Failed")); - if (stdout.length) { - console.log(stdout); - } - if (stderr.length) { - console.log(stderr); - } - spinner.stop(); -}); -executor.exec(command); +var chaosPlan = chaosPlanFactory.getChaosPlan(planData, project_name); -process.on('beforeExit', function() { - process.exit(exitCode); -}); +var chaosPlanExecutor = new ChaosPlanExecutor(dockerComposeFile, chaosPlan); +var testExecutor = new Executor(command); var dockerLogger = new DockerLogger(logPath); dockerLogger.start(function() {}); -var chaosPlanExecutor = new ChaosPlanExecutor(); -chaosPlanExecutor.exec(dockerComposeFile, chaosPlan); -chaosPlanExecutor.on('dockerContainersChanged', function() { - dockerLogger.update(function() {}); -}); -chaosPlanExecutor.on('error', function(err) { - console.log(err); - process.exit(1); -}); +function mainloop(lastStartTime, retryCount) { + if(retryCount === void 0) { + retryCount = 0; + } + console.log("Running tests... Attempt %s", retryCount + 1); + testExecutor.runTests(function(err, result) { + if(err) { + console.log("Tests errored out!", result); + return mainloop(lastStartTime, retryCount + 1); + } + console.log("Test run succeeded! Number of retries: %s. Time until recovery: %s seconds", retryCount, (new Date() - lastStartTime)/1000); + return chaosPlanExecutor.runNextScenario(function(err) { + if (err) { + console.log("Error setting up scenario"); + } + mainloop(new Date()); + }); + }) +} + +mainloop(new Date()); setTimeout(function() { console.log("Stopping execution"); - executor.stop(); - chaosPlanExecutor.stop(); dockerLogger.stop(); - process.exit(exitCode); + //process.exit(); }, duration); diff --git a/lib/ChaosPlanExecutor.js b/lib/ChaosPlanExecutor.js index c868dce..ba4125b 100644 --- a/lib/ChaosPlanExecutor.js +++ b/lib/ChaosPlanExecutor.js @@ -22,46 +22,27 @@ * THE SOFTWARE. **/ -var util = require('util'); -var EventEmitter = require('events').EventEmitter; -var ScenarioExecuter = require('./ScenarioExecutor'); +var ScenarioExecutor = require('./ScenarioExecutor'); -function ChaosPlanExecutors (scenarioExecuter) { - this.scenarioExecuter = scenarioExecuter || new ScenarioExecuter(); - this.stopped = false; +function ChaosPlanExecutors (composeFile, chaosPlan, scenarioExecutor) { + this.composeFile = composeFile; + this.chaosPlan = chaosPlan; + this.scenarioId = 0; + + this.scenarioExecutor = scenarioExecutor || new ScenarioExecutor(); } -util.inherits(ChaosPlanExecutors, EventEmitter); -ChaosPlanExecutors.prototype.exec = function(composeFile, chaosPlan) { - var scenarios = chaosPlan.getScenarios(); +ChaosPlanExecutors.prototype.runNextScenario = function(callback) { + var scenarios = this.chaosPlan.getScenarios(); - var self = this; - var scenario = 0; - function executeScenario(scenario) { - if (scenarios.length === scenario) { - scenario = 0; - } - self.scenarioExecuter.exec(composeFile, scenarios[scenario], function(err) { - if (err) { - self.emit('error', new Error("Failed to execute Scenario: " + err)); - return; - } - if (!self.stopped) { - scenario++; - executeScenario(scenario); - } - }); - self.scenarioExecuter.on('scaled', function() { - self.emit('dockerContainersChanged'); - }); + if (this.scenarioId >= scenarios.length) { + this.scenarioId = 0; } - - executeScenario(scenario); -}; - -ChaosPlanExecutors.prototype.stop = function() { - this.stopped = true; + var scenario = scenarios[this.scenarioId]; + console.log("Starting scenario %s", scenario); + this.scenarioExecutor.exec(this.composeFile, scenario, callback); + this.scenarioId ++; }; module.exports = ChaosPlanExecutors; diff --git a/lib/ChaosPlanFactory.js b/lib/ChaosPlanFactory.js index 99240e9..552a522 100644 --- a/lib/ChaosPlanFactory.js +++ b/lib/ChaosPlanFactory.js @@ -32,13 +32,13 @@ function ChaosPlanFactory (yamlInj, scenariosFactory) { this.scenarios = scenariosFactory || new ScenariosFactory(); } -ChaosPlanFactory.prototype.getChaosPlan = function(chaosPlanData) { +ChaosPlanFactory.prototype.getChaosPlan = function(chaosPlanData, project_name) { var yaml = this.yaml.safeLoad(chaosPlanData); var chaosPlan = new ChaosPlan(); chaosPlan.setName(yaml.name); - var scenarios = this.scenarios.getScenarios(yaml.scenarios); + var scenarios = this.scenarios.getScenarios(yaml.scenarios, project_name); chaosPlan.setScenarios(scenarios); return chaosPlan; diff --git a/lib/Docker.js b/lib/Docker.js index 7970212..dadfb03 100644 --- a/lib/Docker.js +++ b/lib/Docker.js @@ -28,7 +28,7 @@ function Docker (exec) { this.exec = exec || require("child_process").exec; } -Docker.prototype.modify = function(composeFile, modifications, callback) { +Docker.prototype.modify = function(composeFile, modifications, project_name, callback) { var containers = ""; var name, amount; modifications.forEach(function (value) { @@ -37,7 +37,7 @@ Docker.prototype.modify = function(composeFile, modifications, callback) { containers += name + "=" + amount + " "; }); - var command = "docker-compose --file " + composeFile + " scale " + containers; + var command = "docker-compose --file " + composeFile + " --project-name " + project_name + " scale " + containers; this.exec(command, function(code, stdout, stderr) { if (code !== null) { callback(code, stdout, stderr); diff --git a/lib/Executor.js b/lib/Executor.js index 67f4fbe..70bb4ea 100644 --- a/lib/Executor.js +++ b/lib/Executor.js @@ -22,37 +22,16 @@ * THE SOFTWARE. **/ -var util = require("util"); -var events = require("events"); -function Executor (exec) { +function Executor (script, exec) { + this.script = script; this.execImpl = exec || require('child_process').exec; - this.stopped = false; } -util.inherits(Executor, events.EventEmitter); - -Executor.prototype.exec = function(script) { - if (this.stopped) { - return; - } - - this.stopped = false; - - var self = this; - self.emit('start'); - this.execImpl(script, function(error, stdout, stderr) { - if (error !== null) { - self.emit('fail', stdout, stderr); - } else { - self.emit('pass'); - } - self.exec(script); +Executor.prototype.runTests = function(callback) { + this.execImpl(this.script, function(err, stdout, stderr) { + callback(err, stdout + stderr); }); }; -Executor.prototype.stop = function() { - this.stopped = true; -}; - module.exports = Executor; diff --git a/lib/Scaler.js b/lib/Scaler.js index 38e6055..4957712 100644 --- a/lib/Scaler.js +++ b/lib/Scaler.js @@ -32,17 +32,18 @@ function Scaler (implementation, random) { this.random = random || function(){return Math.floor(Math.random() * (max - min + 1)) + min} } -Scaler.prototype.restore = function(file, containers, callback) { - var name; - containers.forEach(function (value) { - name = Object.keys(value)[0]; - value[name] = 1; +Scaler.prototype.restore = function(file, containers, project_name, callback) { + var restoreContainers = containers.map(function (value) { + var containerName = Object.keys(value)[0]; + var obj ={}; + obj[containerName] = 1; + return obj; }); - this.implementation.modify(file, containers, callback); -} + this.implementation.modify(file, restoreContainers, project_name, callback); +}; -Scaler.prototype.scale = function(file, containers, callback) { +Scaler.prototype.scale = function(file, containers, project_name, callback) { var self = this; containers.forEach(function (value) { name = Object.keys(value)[0]; @@ -51,7 +52,7 @@ Scaler.prototype.scale = function(file, containers, callback) { } }); - this.implementation.modify(file, containers, callback); + this.implementation.modify(file, containers, project_name, callback); }; diff --git a/lib/Scenario.js b/lib/Scenario.js index 0cd943f..6ee5852 100644 --- a/lib/Scenario.js +++ b/lib/Scenario.js @@ -43,6 +43,18 @@ Scenario.prototype.getContainers = function() { return this.containers; }; +Scenario.prototype.setProjectName = function(project_name) { + this.project_name = project_name; +}; + +Scenario.prototype.getProjectName = function() { + return this.project_name; +}; + +Scenario.prototype.toString = function() { + return "[Scenario " + this.getName() + "]"; +}; + module.exports = Scenario; diff --git a/lib/ScenarioExecutor.js b/lib/ScenarioExecutor.js index ec718be..5b4b538 100644 --- a/lib/ScenarioExecutor.js +++ b/lib/ScenarioExecutor.js @@ -43,21 +43,21 @@ util.inherits(ScenarioExecutor, EventEmitter); ScenarioExecutor.prototype.exec = function(composeFile, scenario, callback) { var containers = scenario.getContainers(); + var project_name = scenario.getProjectName(); var self = this; - this.scaler.scale(composeFile, containers, function (err){ + this.scaler.scale(composeFile, containers, project_name, function (err){ if (err) { callback(err); return; } self.emit('scaled', scenario); - self.timeout(function() { - self.emit('beforeRestore', scenario); - self.scaler.restore(composeFile, containers, function() { - callback(); - }); - }, self.restoreTime); + self.emit('beforeRestore', scenario); + self.scaler.restore(composeFile, containers, project_name, function() { + console.log("Restored containers!"); + callback(); + }); }); }; diff --git a/lib/ScenariosFactory.js b/lib/ScenariosFactory.js index 969b74e..f77720f 100644 --- a/lib/ScenariosFactory.js +++ b/lib/ScenariosFactory.js @@ -27,7 +27,7 @@ var Scenario = require('./Scenario'); function ScenariosFactory () { } -ScenariosFactory.prototype.getScenarios = function(scenariosData) { +ScenariosFactory.prototype.getScenarios = function(scenariosData, project_name) { var scenarios = []; var scenario, name; scenariosData.forEach(function (value) { @@ -37,7 +37,7 @@ ScenariosFactory.prototype.getScenarios = function(scenariosData) { var scena = value[name]; scenario.setContainers(scena.containers); - + scenario.setProjectName(project_name); scenarios.push(scenario); });