Feature: add deployment + deployment logs

This commit is contained in:
2015-11-25 15:13:00 +00:00
parent 9c8ed27c1b
commit e135c5d2c6
19 changed files with 416 additions and 68 deletions

View File

@@ -1,6 +1,6 @@
var DEPLOYMENT_FOLDER = 'deployment',
DEPLOYMENT_FOLDER = '/home/ubuntu/deployment';
SCRIPTS = {
SCRIPTS = {
CREATE : [
{
cmd: 'mkdir %CWD%',
@@ -8,17 +8,27 @@ var DEPLOYMENT_FOLDER = 'deployment',
cwd: '%ROOT_CWD%'
}
},
{
cmd: 'cd %CWD%',
options : {
cwd: '%ROOT_CWD%'
}
},
{
cmd: 'git clone %GIT% .',
options : {
cwd: '%ROOT_CWD%/%CWD%'
}
}
],
UPDATE : [
{
cmd: 'git pull',
options : {
cwd: '%ROOT_CWD%/%CWD%'
}
}
],
DELETE : [
{
cmd: 'rm -rf %CWD%',
options : {
cwd: '%ROOT_CWD%'
}
}
]
};

72
server/jobs/deploy.job.js Normal file
View File

@@ -0,0 +1,72 @@
Job.processJobs('projectDeployerJobQueue', 'create_repository',
function(job, callback) {
var deployment = DeploymentService.get(job.data.deploymentId),
project = ProjectService.get(deployment.project_id);
DeploymentService.update_status( deployment._id, 'pending', function() {
CommandRunner.run(
{
script: SCRIPTS.CREATE,
deployment: deployment,
project: project,
stdout: function(data) {
DeploymentService.appendLog(job.data.deploymentId, data, false);
},
stderr: function(data) {
DeploymentService.appendLog(job.data.deploymentId, data, true);
}
},
function() {
if( callback ) {
callback();
}
}
);
});
}
);
Job.processJobs('projectDeployerJobQueue', 'delete_repository',
function(job, callback) {
CommandRunner.run(
{
script: SCRIPTS.DELETE,
project: job.data.project
},
function() {
if( callback ) {
callback();
}
}
);
}
);
Job.processJobs('projectDeployerJobQueue', 'update_repository',
function(job, callback) {
var deployment = DeploymentService.get(job.data.deploymentId),
project = ProjectService.get(deployment.project_id);
DeploymentService.update_status( deployment._id, 'pending', function() {
CommandRunner.run(
{
script: SCRIPTS.UPDATE,
deployment: deployment,
project: project,
stdout: function(data) {
DeploymentService.appendLog(job.data.deploymentId, data, false);
},
stderr: function(data) {
DeploymentService.appendLog(job.data.deploymentId, data, true);
}
},
function() {
if( callback ) {
callback();
}
}
);
});
}
);

View File

@@ -1,35 +1,83 @@
var exec = Npm.require('child_process').exec,
execSync = function(cmd, options, stdoutHandler, stderrHandler) {
execSync = function(cmd, options, stdoutHandler, stderrHandler, callback) {
stdoutHandler('$ ' + cmd);
exec(cmd,
options,
Meteor.bindEnvironment(
function(error, stdout, stderr) {
if( stdout != '' ) {
if( stdout !== '' ) {
stdoutHandler(stdout);
}
if( stderr != '' ) {
stderrHandler(stderr);
}
callback();
}
)
);
},
replace = function(string, customs = {}) {
var globals = {'%ROOT_CWD%': DEPLOYMENT_FOLDER};
for(var key in globals) {
string = string.replace(key, globals[key]);
}
for(var key in customs) {
string = string.replace(key, customs[key]);
}
return string;
};
var CommandRunner = {
run: function( script, deployment, stdout, stderr, counter, callback ) {
var command = script[command].cmd.replace('%ROOT_CWD%', DEPLOYMENT_FOLDER).replace('%CWD%', deployment._id),
options = script[command].options;
options.cwd.replace('%ROOT_CWD%', DEPLOYMENT_FOLDER).replace('%CWD%', deployment._id);
CommandRunner = {
run: function( data, callback = undefined) {
var bundle = _.extend({deployment: {}, project:{}, stdout: console.log, stderr: console.error, counter: 0, deploy_script: true}, data),
customs = {'%CWD%': bundle.project._id, '%GIT%': bundle.project.git_url};
var line = bundle.script[bundle.counter],
command = replace(line.cmd, customs ),
options = line.options;
options.cwd = replace(options.cwd, customs);
execSync(command, options, stdout, stderr, function() {
counter++;
if( counter > script.length ) {
if( callback ) {
execSync(command, options, bundle.stdout, bundle.stderr, function() {
bundle.counter++;
if( bundle.counter >= bundle.script.length ) {
if( bundle.deploy_script && bundle.project.commands ) {
bundle.deploy_script = false;
bundle.script = bundle.project.commands.split('\n');
bundle.counter = 0;
CommandRunner.commands(bundle, callback);
} else if( callback ) {
if( bundle.deployment ) {
DeploymentService.update_status(bundle.deployment._id, 'closed', callback);
} else {
callback();
}
}
} else {
CommandRunner.run(bundle, callback);
}
});
},
commands: function(bundle, callback = undefined) {
var command = bundle.script[bundle.counter],
customs = {'%CWD%': bundle.project._id},
options = {
cwd: replace('%ROOT_CWD%/%CWD%', customs)
};
execSync(command, options, bundle.stdout, bundle.stderr, function() {
bundle.counter++;
if( bundle.counter >= bundle.script.length ) {
if( bundle.deployment ) {
DeploymentService.update_status(bundle.deployment._id, 'closed', callback);
} else {
callback();
}
} else {
CommandRunner.run(script, deployment, stdout, stderr, counter, callback);
CommandRunner.commands(bundle, callback);
}
});
}

View File

@@ -2,30 +2,65 @@ Deployments = new Mongo.Collection('deployments');
DeploymentService = {
get: function(id) {
return Deployments.find({_id: id}, {date: 1});
return Deployments.findOne({_id: id});
},
appendLog: function(id, data, error) {
var deployment = DeploymentService.get(id),
project = ProjectService.get(deployment.project_id);
Deployments.update({ _id: id },{ $push: {
output: {
timestamp : new Date().getTime(),
data : data
data : data.replace(new RegExp(project._id, 'g'), project.label),
error: error
}
}});
},
create: function(project, callback) {
return Deployment.insert({
project_id: projet._id,
create: function(project) {
return Deployments.insert({
project_id: project._id,
timestamp: new Date().getTime(),
output: [],
status: 'opened'
}, function(errors, deploymentId) {
JobService.create_repository(deploymentId);
});
},
delete: function(projectId) {
Deployments.remove({project_id: projectId}, function(errors) {
if( !errors ) {
JobService.delete_repository({_id: projectId});
}
});
},
update: function(projectId) {
Deployments.insert({
project_id: projectId,
timestamp: new Date().getTime(),
output: []
}, function(errors, deploymentId) {
_execSync(cmd, function(data) {
DeploymentService.appendLog(deploymentId, data, false);
}, function(data) {
DeploymentService.appendLog(deploymentId, data, true);
});
JobService.update_repository(deploymentId);
});
},
},
update_status: function(id, status, callback) {
Deployments.update(
id,
{ $set: {
status: status
}
},
callback
);
},
list: function(projectId) {
return Deployments.find({project_id: projectId}, {sort: {timestamp: -1}});
},
deploy: function(project) {

View File

@@ -0,0 +1,51 @@
var jobs = JobCollection('projectDeployerJobQueue');
jobs.allow({
// Grant full permission to any authenticated user
admin: function (userId, method, params) {
return true;
}
});
jobs.startJobServer();
JobService = {
create_repository : function(deploymentId) {
Job(jobs, 'create_repository',
{
deploymentId: deploymentId
})
.priority('normal')
.retry({
retries: 5,
wait: 10 * 1000
})
.save();
},
update_repository : function(deploymentId) {
Job(jobs, 'update_repository',
{
deploymentId: deploymentId
})
.priority('normal')
.retry({
retries: 5,
wait: 10 * 1000
})
.save();
},
delete_repository: function(project) {
Job(jobs, 'delete_repository',
{
project: project
})
.priority('normal')
.retry({
retries: 5,
wait: 10 * 1000
})
.save();
}
}

View File

@@ -10,18 +10,24 @@ Meteor.methods({
addProject: function(label, git_url, public_url ,commands) {
return ProjectService.insert(label, git_url, public_url ,commands, function(errors, id) {
if( id ) {
/*DeploymentService.deploy(ProjectService.get(id), function(errors, deploymentId) {
});*/
DeploymentService.create(ProjectService.get(id));
}
});
},
editProject: function(id, label, git_url, public_url ,commands) {
ProjectService.update(id, label, git_url, public_url ,commands);
ProjectService.update(id, label, git_url, public_url ,commands, function(errors, updated_count) {
if( updated_count ) {
DeploymentService.update(id);
}
});
},
deleteProject: function(id) {
ProjectService.delete(id);
ProjectService.delete(id, function(errors) {
if( ! errors ) {
DeploymentService.delete(id);
}
});
}
});

View File

@@ -10,7 +10,7 @@ ProjectService = {
}, callback);
},
update: function(id, label, git_url, public_url ,commands) {
update: function(id, label, git_url, public_url ,commands, callback) {
Projects.update(
id,
{ $set: {
@@ -19,12 +19,13 @@ ProjectService = {
public_url: public_url,
commands: commands
}
}
},
callback
);
},
delete: function(id) {
Projects.remove(id);
delete: function(id, callback) {
Projects.remove(id, callback);
},
get: function(id) {

View File

@@ -3,5 +3,9 @@ Meteor.publish('projects', function() {
});
Meteor.publish('deployment', function(id) {
return DeploymentService.get(id);
return DeploymentService.get(id);
});
Meteor.publish('deployments', function(project_id) {
return DeploymentService.list(project_id);
});