mirror of
https://gitlab.silvrtree.co.uk/martind2000/nci.git
synced 2025-01-10 21:15:08 +00:00
add usage srategies for node
This commit is contained in:
parent
16d4367741
commit
84747099b6
@ -35,9 +35,19 @@ exports.Distributor = Distributor;
|
||||
|
||||
// do deferred initialization (e.g. create nodes after all plugins load)
|
||||
Distributor.prototype.init = function() {
|
||||
var self = this;
|
||||
// nodes to execute builds
|
||||
var self = this,
|
||||
namesHash = {};
|
||||
|
||||
self.nodes = _(self.nodes).map(function(nodeParams) {
|
||||
if (!nodeParams.name) {
|
||||
nodeParams.name = nodeParams.type;
|
||||
}
|
||||
|
||||
if (namesHash[nodeParams.name]) {
|
||||
throw new Error('Node name `' + nodeParams.name + ' already used');
|
||||
}
|
||||
namesHash[nodeParams.name] = 1;
|
||||
|
||||
return self._createNode(nodeParams);
|
||||
});
|
||||
};
|
||||
@ -195,7 +205,7 @@ Distributor.prototype._updateWaitReasons = function() {
|
||||
});
|
||||
|
||||
var waitReason = _(waitReasons).compact().join(', ');
|
||||
// set only non-empty reasons
|
||||
// set only non-empty and new reasons
|
||||
if (waitReason && waitReason !== item.build.waitReason) {
|
||||
self._updateBuild(item.build, {waitReason: waitReason});
|
||||
}
|
||||
|
@ -6,11 +6,20 @@ var _ = require('underscore');
|
||||
function Node(params) {
|
||||
this.type = params.type;
|
||||
this.maxExecutorsCount = params.maxExecutorsCount;
|
||||
this.name = params.name;
|
||||
this.usageStrategy = params.usageStrategy || 'maximum';
|
||||
|
||||
if (!this.usageStrategiesHash[this.usageStrategy]) {
|
||||
throw new Error('Unknown usage strategy: ' + this.usageStrategy);
|
||||
}
|
||||
|
||||
this.executors = {};
|
||||
}
|
||||
|
||||
exports.Node = Node;
|
||||
|
||||
Node.prototype.usageStrategiesHash = {maximum: 1, specificProject: 1};
|
||||
|
||||
Node.prototype._getBlockerExecutor = function(getBlockers, getTarget) {
|
||||
return _(this.executors).find(function(executor) {
|
||||
var target = getTarget(executor);
|
||||
@ -27,10 +36,20 @@ Node.prototype._getBlockerExecutor = function(getBlockers, getTarget) {
|
||||
Node.prototype.getExecutorWaitReason = function(project) {
|
||||
var waitReason;
|
||||
|
||||
if (_(this.executors).size() >= this.maxExecutorsCount) {
|
||||
waitReason = 'All executors are busy';
|
||||
var targetNodeNames = project.node && project.node.target;
|
||||
|
||||
if (targetNodeNames && !_(targetNodeNames).isArray()) {
|
||||
targetNodeNames = [targetNodeNames];
|
||||
}
|
||||
|
||||
if (targetNodeNames && !_(targetNodeNames).contains(this.name)) {
|
||||
waitReason = this.name + ': not a target node';
|
||||
} else if (this.usageStrategy === 'specificProject' && !targetNodeNames) {
|
||||
waitReason = this.name + ': only for specific projects';
|
||||
} else if (_(this.executors).size() >= this.maxExecutorsCount) {
|
||||
waitReason = this.name + ': all executors are busy';
|
||||
} else if (project.name in this.executors) {
|
||||
waitReason = 'Project already running on node';
|
||||
waitReason = this.name + ': project already running on node';
|
||||
} else {
|
||||
var blockerExecutor;
|
||||
|
||||
@ -58,7 +77,7 @@ Node.prototype.getExecutorWaitReason = function(project) {
|
||||
|
||||
if (blockerExecutor) {
|
||||
waitReason = (
|
||||
'Blocked by currently running "' +
|
||||
this.name + ': blocked by currently running "' +
|
||||
blockerExecutor.project.name + '"'
|
||||
);
|
||||
}
|
||||
|
@ -21,6 +21,7 @@ module.exports = function(config, callback) {
|
||||
properties: {
|
||||
name: {type: 'string'},
|
||||
type: {type: 'string'},
|
||||
usageStrategy: {type: 'string'},
|
||||
maxExecutorsCount: {type: 'integer'},
|
||||
options: {type: 'object'}
|
||||
}
|
||||
|
@ -93,7 +93,7 @@ describe('Distributor blocking with max 2 executors count', function() {
|
||||
var spy = updateBuildSpy;
|
||||
expect(spy.getCall(3).args[0].project.name).equal('project2');
|
||||
expect(spy.getCall(3).args[1].waitReason).equal(
|
||||
'Blocked by currently running "project1"'
|
||||
'local: blocked by currently running "project1"'
|
||||
);
|
||||
});
|
||||
|
||||
|
111
test/node.js
111
test/node.js
@ -1,7 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
var createNode = require('../lib/node').createNode,
|
||||
expect = require('expect.js');
|
||||
expect = require('expect.js'),
|
||||
_ = require('underscore');
|
||||
|
||||
|
||||
describe('Node', function() {
|
||||
@ -9,6 +10,114 @@ describe('Node', function() {
|
||||
project1 = {name: 'project1'},
|
||||
project2 = {name: 'project2'};
|
||||
|
||||
var createNodeMock = function(params) {
|
||||
params = params || {};
|
||||
|
||||
var node = createNode(_({
|
||||
name: 'executor1',
|
||||
type: 'local',
|
||||
maxExecutorsCount: 1,
|
||||
usageStrategy: 'maximum'
|
||||
}).extend(params));
|
||||
|
||||
// only for testing
|
||||
if (params.executors) {
|
||||
node.executors = params.executors;
|
||||
}
|
||||
return node;
|
||||
};
|
||||
|
||||
describe('wait reason', function() {
|
||||
|
||||
it('should be not a target node when node target is not match', function() {
|
||||
var waitReason = createNodeMock({
|
||||
name: 'executor1'
|
||||
}).getExecutorWaitReason({
|
||||
name: 'project1',
|
||||
node: {target: 'other executor'}
|
||||
});
|
||||
expect(waitReason).eql('executor1: not a target node');
|
||||
});
|
||||
|
||||
it('should be falsy when node target match', function() {
|
||||
var waitReason = createNodeMock({
|
||||
name: 'executor1'
|
||||
}).getExecutorWaitReason({
|
||||
name: 'project1',
|
||||
node: {target: 'executor1'}
|
||||
});
|
||||
expect(waitReason).not.ok();
|
||||
});
|
||||
|
||||
it('should be falsy when node target (array) match', function() {
|
||||
var waitReason = createNodeMock({
|
||||
name: 'executor1'
|
||||
}).getExecutorWaitReason({
|
||||
name: 'project1',
|
||||
node: {target: ['executor1']}
|
||||
});
|
||||
expect(waitReason).not.ok();
|
||||
});
|
||||
|
||||
it('should be only for specific projects when target is not set', function() {
|
||||
var waitReason = createNodeMock({
|
||||
usageStrategy: 'specificProject'
|
||||
}).getExecutorWaitReason({
|
||||
name: 'project1'
|
||||
});
|
||||
expect(waitReason).eql('executor1: only for specific projects');
|
||||
});
|
||||
|
||||
it('should be all executors are busy when true', function() {
|
||||
var waitReason = createNodeMock({
|
||||
maxExecutorsCount: 1,
|
||||
executors: {project2: 1}
|
||||
}).getExecutorWaitReason({
|
||||
name: 'project1'
|
||||
});
|
||||
expect(waitReason).eql('executor1: all executors are busy');
|
||||
});
|
||||
|
||||
it('should be project already running on node when true', function() {
|
||||
var waitReason = createNodeMock({
|
||||
maxExecutorsCount: 2,
|
||||
executors: {project1: 1}
|
||||
}).getExecutorWaitReason({
|
||||
name: 'project1'
|
||||
});
|
||||
expect(waitReason).eql('executor1: project already running on node');
|
||||
});
|
||||
|
||||
it('should be blocked by project when blocked by executing', function() {
|
||||
var waitReason = createNodeMock({
|
||||
maxExecutorsCount: 2,
|
||||
executors: {project2: {project: {name: 'project2'}}}
|
||||
}).getExecutorWaitReason({
|
||||
name: 'project1',
|
||||
blockedBy: ['project2']
|
||||
});
|
||||
expect(waitReason).eql(
|
||||
'executor1: blocked by currently running "project2"'
|
||||
);
|
||||
});
|
||||
|
||||
it('should be blocked by project when executing blocks it', function() {
|
||||
var waitReason = createNodeMock({
|
||||
maxExecutorsCount: 2,
|
||||
executors: {project2: {project: {
|
||||
name: 'project2',
|
||||
blocks: ['project1']
|
||||
}}}
|
||||
}).getExecutorWaitReason({
|
||||
name: 'project1'
|
||||
});
|
||||
expect(waitReason).eql(
|
||||
'executor1: blocked by currently running "project2"'
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
var expectNodeHasFreeExecutor = function(project, value) {
|
||||
it('should' + (value ? ' ' : ' not ') + 'has free executors for ' +
|
||||
project.name, function() {
|
||||
|
Loading…
Reference in New Issue
Block a user