pkgcloud
Version:
An infrastructure-as-a-service agnostic cloud library for node.js
310 lines (283 loc) • 12.5 kB
JavaScript
/*
* flavor-test.js: Test that should be common to all providers.
*
* (C) 2012 Nodejitsu Inc.
*
*/
var fs = require('fs'),
path = require('path'),
vows = require('vows'),
assert = require('../../helpers/assert'),
helpers = require('../../helpers');
var clients = {},
testContext = {},
versions = JSON.parse(helpers.loadFixture('versions.json'));
function batchOne (providerClient, providerName) {
var name = providerName || 'rackspace',
client = providerClient || clients['rackspace'],
test = {};
test["The pkgcloud " + name + " compute client"] = {
"the getVersion() method": {
"with no arguments": {
topic: function () {
client.getVersion(this.callback);
},
"should return the version": function (err, version) {
assert.ok(typeof version === 'string');
if (version !== versions[name]) {
console.error(
'!! API Version for ' + name + ' is ' + version + '.'+
' we were expecting it to be ' + versions[name]
);
}
}
}
}
};
return test;
}
function batchTwo (providerClient, providerName) {
var name = providerName || 'rackspace',
client = providerClient || clients['rackspace'],
test = {},
m = process.env.NOCK ? 1 : 100;
test["The pkgcloud " + name + " compute client"] = {
"the setWait() method": {
"on flavors waiting for flavor with name crazyflavah": {
topic: function () {
var self = this;
client.getFlavors(function (err, flavors) {
if (err) { return self.callback(err); }
testContext.flavors = flavors;
var flavor = flavors[0];
var now = Date.now();
flavor.until({ name: 'crazyFlavah' }, 50*m, 50*m, function () {
self.callback(null, Date.now() - now);
});
});
},
"should timeout": function (err, duration) {
assert.ok(duration);
assert.ok(duration > 50);
}
}
}
};
return test;
}
function batchThree (providerClient, providerName, nock) {
var name = providerName || 'rackspace',
client = providerClient || clients['rackspace'],
test = {};
test["The pkgcloud " + name + " compute client"] = {
"the getImages() method": {
"with no details": {
topic: function () {
client.getImages(this.callback);
},
"should return the list of images": function (err, images) {
testContext.images = images;
images.forEach(function (image) {
assert.assertImage(image);
});
assert.assertNock(nock);
}
}
}
};
return test;
}
function batchFour (providerClient, providerName) {
var name = providerName || 'rackspace',
client = providerClient || clients['rackspace'],
test = {},
m = process.env.NOCK ? 1 : 100;
test["The pkgcloud " + name + " compute client"] = {
"the setWait() method": {
"waiting for a server to be operational": {
topic: function () {
var self = this;
client.createServer({
name: 'create-test-setWait',
image: testContext.images[0].id,
flavor: testContext.flavors[0].id
}, function (err, server) {
if (err) { return self.callback(err); }
server.setWait({ status: 'RUNNING' }, 100*m, function (err, srv) {
self.callback(null, srv);
});
});
},
"should a server in running state": function (err, server) {
assert.isNull(err);
assert.equal(server.name, 'create-test-setWait');
assert.equal(server.status, 'RUNNING');
assert.assertServerDetails(server);
testContext.serverId = server.id;
}
}
}
};
return test;
}
function batchFive (providerClient, providerName) {
var name = providerName || 'rackspace',
client = providerClient || clients['rackspace'],
test = {};
test["The pkgcloud " + name + " compute client"] = {
"the destroyServer() method": {
topic: function () {
client.destroyServer(testContext.serverId, this.callback);
},
"should respond correctly": function (err, response) {
assert.isNull(err);
assert.ok(response.ok);
assert.equal(response.ok, testContext.serverId);
}
}
};
return test;
}
JSON.parse(fs.readFileSync(__dirname + '/../../configs/providers.json'))
.forEach(function (provider) {
clients[provider] = helpers.createClient(provider, 'compute');
var client = clients[provider],
nock = require('nock');
if (process.env.NOCK) {
if (provider === 'joyent') {
nock('https://' + client.serversUrl)
.get('/' + client.account + '/datacenters')
.reply(200, "", { 'x-api-version': '6.5.0' })
.get('/' + client.account + '/datasets')
.reply(200, helpers.loadFixture('joyent/images.json'), {})
.get('/' + client.account + '/packages')
.reply(200, helpers.loadFixture('joyent/flavors.json'), {})
.get('/' + client.account + '/packages/Small%201GB')
.reply(200, helpers.loadFixture('joyent/flavor.json'), {})
.post('/' + client.account + '/machines',
"{\"name\":\"create-test-setWait\",\"package\":" +
"\"Small 1GB\",\"dataset\":\"sdc:sdc:nodejitsu:1.0.0\"}")
.reply(201, helpers.loadFixture('joyent/setWait.json'), {})
.get('/' + client.account +
'/machines/534aa63a-104f-4d6d-a3b1-c0d341a20a53')
.reply(200, helpers.loadFixture('joyent/setWaitResp1.json'), {});
} else if (provider === 'rackspace') {
nock('https://' + client.authUrl)
.get('/v1.0')
.reply(204, "",
JSON.parse(helpers.loadFixture('rackspace/auth.json')));
nock('https://' + client.serversUrl)
.get('/')
.reply(200,
"{\"versions\":[{\"id\":\"v1.0\",\"status\":\"BETA\"}]}", {})
.get('/v1.0/537645/images/detail.json')
.reply(200, helpers.loadFixture('rackspace/images.json'), {})
.get('/v1.0/537645/flavors/detail.json')
.reply(200, helpers.loadFixture('rackspace/serverFlavors.json'), {})
.get('/v1.0/537645/flavors/1')
.reply(200, helpers.loadFixture('rackspace/flavor.json'), {})
.post('/v1.0/537645/servers',
helpers.loadFixture('rackspace/setWait.json'))
.reply(202, helpers.loadFixture('rackspace/setWaitResp1.json'), {})
.post('/v1.0/537645/servers',
helpers.loadFixture('rackspace/setWait.json'))
.reply(202, helpers.loadFixture('rackspace/setWaitResp2.json'), {})
.get('/v1.0/537645/servers/20602046')
.reply(200, helpers.loadFixture('rackspace/20602046.json'), {});
} else if (provider === 'amazon') {
nock('https://' + client.serversUrl)
.filteringRequestBody(helpers.authFilter)
.post('/?Action=DescribeImages', { 'Owner.0': 'self' })
.reply(200, helpers.loadFixture('amazon/images.xml'), {})
.post('/?Action=RunInstances', {
'ImageId': 'ami-85db1cec',
'InstanceType': 'm1.small',
'MaxCount': '1',
'MinCount': '1',
'UserData': 'eyJuYW1lIjoiY3JlYXRlLXRlc3Qtc2V0V2FpdCJ9'
})
.reply(200, helpers.loadFixture('amazon/run-instances.xml'), {})
.post('/?Action=DescribeInstances', {
'Filter.1.Name': 'instance-state-code',
'Filter.1.Value.1': '0',
'Filter.1.Value.2': '16',
'Filter.1.Value.3': '32',
'Filter.1.Value.4': '64',
'Filter.1.Value.5': '80',
'InstanceId.1': 'i-1d48637b'
})
.reply(200, helpers.loadFixture('amazon/pending-server.xml'), {})
.post('/?Action=DescribeInstanceAttribute', {
'Attribute': 'userData',
'InstanceId': 'i-1d48637b'
})
.reply(200,
helpers.loadFixture('amazon/running-server-attr.xml', {}))
.post('/?Action=DescribeInstances', {
'Filter.1.Name': 'instance-state-code',
'Filter.1.Value.1': '0',
'Filter.1.Value.2': '16',
'Filter.1.Value.3': '32',
'Filter.1.Value.4': '64',
'Filter.1.Value.5': '80',
'InstanceId.1': 'i-1d48637b'
})
.reply(200, helpers.loadFixture('amazon/running-server.xml'), {})
.post('/?Action=DescribeInstanceAttribute', {
'Attribute': 'userData',
'InstanceId': 'i-1d48637b'
})
.reply(200,
helpers.loadFixture('amazon/running-server-attr.xml', {}));
} else if (provider === 'azure') {
// Note: response x-ms-request-id header always 'b67cc525ecc546618fd6fb3e57d724f5'.
// Azure would return a different value for each request
nock('https://' + client.serversUrl)
.get('/azure-account-subscription-id/services/images')
.reply(200,helpers.loadFixture('azure/images.xml'),{})
.get('/azure-account-subscription-id/services/hostedservices/create-test-setWait?embed-detail=true')
.reply(404,helpers.loadFixture('azure/hosted-service-404.xml'),{})
.post('/azure-account-subscription-id/services/hostedservices', helpers.loadFixture('azure/create-hosted-service.xml'))
.reply(201, "", {
location: 'https://management.core.windows.net/subscriptions/azure-account-subscription-id/compute/create-test-setWait',
'x-ms-request-id': 'b67cc525ecc546618fd6fb3e57d724f5'})
.get('/azure-account-subscription-id/operations/b67cc525ecc546618fd6fb3e57d724f5')
.reply(200, helpers.loadFixture('azure/operation-succeeded.xml'),{ })
.get('//azure-account-subscription-id/services/images/CANONICAL__Canonical-Ubuntu-12-04-amd64-server-20120528.1.3-en-us-30GB.vhd')
.reply(200,helpers.loadFixture('azure/image-1.xml'),{})
.post('/azure-account-subscription-id/services/hostedservices/create-test-setWait/deployments', helpers.loadFixture('azure/create-deployment.xml'))
.reply(202, "", {'x-ms-request-id': 'b67cc525ecc546618fd6fb3e57d724f5'})
.get('/azure-account-subscription-id/operations/b67cc525ecc546618fd6fb3e57d724f5')
.reply(200, helpers.loadFixture('azure/operation-inprogress.xml'),{ })
.get('/azure-account-subscription-id/operations/b67cc525ecc546618fd6fb3e57d724f5')
.reply(200, helpers.loadFixture('azure/operation-succeeded.xml'),{ })
// TODO: have to do this twice as setWait() does not check server status before calling server.refresh()?
.get('/azure-account-subscription-id/services/hostedservices/create-test-setWait?embed-detail=true')
.reply(200, helpers.loadFixture('azure/running-server.xml'), {})
.get('/azure-account-subscription-id/services/hostedservices/create-test-setWait?embed-detail=true')
.reply(200, helpers.loadFixture('azure/running-server.xml'), {});
nock('https://' + client.serversUrl)
.filteringRequestBody(/.*/, '*')
.post('/azure-account-subscription-id/services/hostedservices/create-test-setWait/certificates', '*')
.reply(202, "", {'x-ms-request-id': 'b67cc525ecc546618fd6fb3e57d724f5'})
.get('/azure-account-subscription-id/operations/b67cc525ecc546618fd6fb3e57d724f5')
.reply(200, helpers.loadFixture('azure/operation-succeeded.xml'),{ });
}
}
var suite = vows.describe('pkgcloud/common/compute/base [' + provider + ']')
.addBatch(batchOne(clients[provider], provider, nock))
.addBatch(batchTwo(clients[provider], provider, nock))
.addBatch(batchThree(clients[provider], provider, nock))
.addBatch(batchFour(clients[provider], provider, nock))
;
// Due the limit of one instance running, we need to clean up
// the servers and destroy them.
if (provider === 'openstack') {
suite
.addBatch(batchFive(clients[provider], provider, nock))
;
}
suite
.export(module)
;
});