add and show build wait reason

This commit is contained in:
oleg 2015-06-15 02:27:58 +03:00
parent b23edf202f
commit 361a5a5436
4 changed files with 65 additions and 16 deletions

View File

@ -37,6 +37,9 @@ Distributor.prototype._runNext = function(callback) {
Steppy( Steppy(
function() { function() {
// update wait reasons for all queue items before run
self._updateWaitReasons();
var node; var node;
var queueItemIndex = _(self.queue).findIndex(function(item) { var queueItemIndex = _(self.queue).findIndex(function(item) {
node = _(self.nodes).find(function(node) { node = _(self.nodes).find(function(node) {
@ -88,11 +91,28 @@ Distributor.prototype._runNext = function(callback) {
executor.once('scmData', function(scmData) { executor.once('scmData', function(scmData) {
self._updateBuild(build, {scm: scmData}); self._updateBuild(build, {scm: scmData});
}); });
// update wait reasons for all queue items after run
self._updateWaitReasons();
}, },
callback callback
); );
}; };
Distributor.prototype._updateWaitReasons = function() {
var self = this;
_(self.queue).each(function(item) {
var waitReasons = _(self.nodes).map(function(node) {
return node.getExecutorWaitReason(item.project);
});
var waitReason = _(waitReasons).compact().join(', ');
if (waitReason !== item.build.waitReason) {
self._updateBuild(item.build, {waitReason: waitReason});
}
});
};
Distributor.prototype._onBuildComplete = function(err, build, callback) { Distributor.prototype._onBuildComplete = function(err, build, callback) {
var self = this; var self = this;

View File

@ -12,13 +12,21 @@ function Node(params) {
exports.Node = Node; exports.Node = Node;
Node.prototype.getExecutorWaitReason = function(project) {
var waitReason;
if (_(this.executors).size() >= this.maxExecutorsCount) {
waitReason = 'All executors are busy';
} else if (project.name in this.executors) {
waitReason = 'Project already running on node';
}
return waitReason;
};
Node.prototype.hasFreeExecutor = function(project) { Node.prototype.hasFreeExecutor = function(project) {
return ( return !this.getExecutorWaitReason(project);
// can't build same project twice at the same time on same node };
project.name in this.executors === false &&
_(this.executors).size() < this.maxExecutorsCount
);
}
Node.prototype._createExecutor = function(project) { Node.prototype._createExecutor = function(project) {
return createExecutor({ return createExecutor({
@ -29,8 +37,12 @@ Node.prototype._createExecutor = function(project) {
Node.prototype.run = function(project, params, callback) { Node.prototype.run = function(project, params, callback) {
var self = this; var self = this;
if (!this.hasFreeExecutor(project)) {
throw new Error('No free executors for project: ' + project.name); var waitReason = this.getExecutorWaitReason(project);
if (waitReason) {
throw new Error(
'Project "' + project.name + '" should wait because: ' + waitReason
);
} }
this.executors[project.name] = this._createExecutor(project); this.executors[project.name] = this._createExecutor(project);

View File

@ -30,6 +30,11 @@ mixin statusText(build)
span # span #
span= build.number span= build.number
if build.waitReason
span (
span= build.waitReason
span , waiting)
if build.status === 'in-progress' && build.currentStep if build.status === 'in-progress' && build.currentStep
span ( span (
span= build.currentStep span= build.currentStep

View File

@ -69,14 +69,20 @@ describe('Distributor', function() {
expect(changes.completed).equal(false); expect(changes.completed).equal(false);
}); });
it('build should be in-progress', function() { it('build should have empty wait reason', function() {
var changes = updateBuildSpy.getCall(1).args[1]; var changes = updateBuildSpy.getCall(1).args[1];
expect(changes).only.have.keys('waitReason');
expect(changes.waitReason).equal('');
});
it('build should be in-progress', function() {
var changes = updateBuildSpy.getCall(2).args[1];
expect(changes).only.have.keys('startDate', 'status'); expect(changes).only.have.keys('startDate', 'status');
expect(changes.status).equal('in-progress'); expect(changes.status).equal('in-progress');
}); });
it('build should be done', function() { it('build should be done', function() {
var changes = updateBuildSpy.getCall(2).args[1]; var changes = updateBuildSpy.getCall(3).args[1];
expect(changes).only.have.keys( expect(changes).only.have.keys(
'endDate', 'status', 'completed', 'error' 'endDate', 'status', 'completed', 'error'
); );
@ -85,8 +91,8 @@ describe('Distributor', function() {
expect(changes.error).equal(null); expect(changes.error).equal(null);
}); });
it('update build called 3 times in total', function() { it('update build called 4 times in total', function() {
expect(updateBuildSpy.callCount).equal(3); expect(updateBuildSpy.callCount).equal(4);
}); });
after(function() { after(function() {
@ -128,20 +134,26 @@ describe('Distributor', function() {
expect(changes.status).equal('queued'); expect(changes.status).equal('queued');
}); });
it('build should be in-progress', function() { it('build should have empty wait reason', function() {
var changes = updateBuildSpy.getCall(1).args[1]; var changes = updateBuildSpy.getCall(1).args[1];
expect(changes).only.have.keys('waitReason');
expect(changes.waitReason).equal('');
});
it('build should be in-progress', function() {
var changes = updateBuildSpy.getCall(2).args[1];
expect(changes.status).equal('in-progress'); expect(changes.status).equal('in-progress');
}); });
it('build should be fail', function() { it('build should be fail', function() {
var changes = updateBuildSpy.getCall(2).args[1]; var changes = updateBuildSpy.getCall(3).args[1];
expect(changes.status).equal('error'); expect(changes.status).equal('error');
expect(changes.completed).equal(true); expect(changes.completed).equal(true);
expect(changes.error).equal('Some error'); expect(changes.error).equal('Some error');
}); });
it('update build called 3 times in total', function() { it('update build called 4 times in total', function() {
expect(updateBuildSpy.callCount).equal(3); expect(updateBuildSpy.callCount).equal(4);
}); });
after(function() { after(function() {