Skip to content
This repository has been archived by the owner on Jun 13, 2024. It is now read-only.

Commit

Permalink
Add a service plan to azure-sqldb service to support registering exis…
Browse files Browse the repository at this point in the history
…ting database as service instance (#180)

* Add a new service plan to support registering existing database as azure-sql instance; Allow user-provided database login/password.

* add a example config of azure-sqldb plan: registered

* add more unit test cases
  • Loading branch information
zhongyi-zhang authored Jul 30, 2018
1 parent 8c7754c commit 7ec54c1
Show file tree
Hide file tree
Showing 13 changed files with 547 additions and 96 deletions.
6 changes: 6 additions & 0 deletions examples/sqldb-example-registered-database-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"sqlServerName": "<name-of-server-provided-in-manifest>",
"sqldbName": "<name-of-existing-database>",
"userProvidedDatabaseLogin": "<name-of-existing-database-login>",
"userProvidedDatabaseLoginPassword": "<password-of-existing-database-login>"
}
12 changes: 8 additions & 4 deletions lib/services/azuresqldb/cmd-bind.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,20 @@ var sqldbBind = function (params) {
log.info(util.format('sqldb cmd-bind: resourceGroupName: %s, sqldbName: %s, sqlServerName: %s', resourceGroupName, sqldbName, sqlServerName));

this.bind = function (sqldbOperations, next) {

if (reqParams.userProvidedDatabaseLogin) {
// assume the login/password provided by user always work
return next(null, {
databaseLogin: reqParams.userProvidedDatabaseLogin,
databaseLoginPassword: reqParams.userProvidedDatabaseLoginPassword
});
}
sqldbOperations.setParameters(resourceGroupName, sqlServerName, sqldbName);

var databaseLogin, databaseLoginPassword;

async.waterfall([
function (callback) {
databaseLogin = common.generateName();
databaseLoginPassword = common.generateStrongPassword();

// create the login
sqldbOperations.createDatabaseLogin(
provisioningResult.fullyQualifiedDomainName,
Expand Down
32 changes: 19 additions & 13 deletions lib/services/azuresqldb/cmd-deprovision.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,32 @@ var sqldbDeprovision = function (params) {

var provisioningResult = params.provisioning_result;
var resourceGroupName = provisioningResult.resourceGroup;
var sqldbName = provisioningResult.name;
var sqldbName = provisioningResult.name;
var sqlServerName = provisioningResult.sqlServerName;

log.info(util.format('sqldb cmd-deprovision: resourceGroupName: %s, sqldbName: %s, sqlServerName: %s', resourceGroupName, sqldbName, sqlServerName));

this.deprovision = function (sqldbOperations, next) {

sqldbOperations.setParameters(resourceGroupName, sqlServerName, sqldbName);

sqldbOperations.deleteDatabase(function (err) {
if (err) {
log.error('sqldb cmd-deprovision: async.waterfall/deleteDatabase: err: %j', err);
return next(err);
} else {
var result = {};
result.state = 'succeeded';
result.description = 'Deleted database';
next(null, result);
}
});
if (params.plan_id === '4b1cfc28-dda6-407b-abeb-7aa0b89f52bf') {
var result = {};
result.state = 'succeeded';
result.description = 'Unregistered the database';
next(null, result);
} else {
sqldbOperations.deleteDatabase(function (err) {
if (err) {
log.error('sqldb cmd-deprovision: async.waterfall/deleteDatabase: err: %j', err);
return next(err);
} else {
var result = {};
result.state = 'succeeded';
result.description = 'Deleted database';
next(null, result);
}
});
}
};
};

Expand Down
32 changes: 20 additions & 12 deletions lib/services/azuresqldb/cmd-poll.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,32 @@ var log = common.getLogger(Config.name);
var sqldbPoll = function (params) {

var reqParams = params.parameters || {};
var provisioningResult = params.provisioning_result || {};
var provisioningResult = params.provisioning_result || {};
var lastoperation = params.last_operation || '';
var resourceGroupName = provisioningResult.resourceGroup || '';
var sqldbName = reqParams.sqldbName || '';
var sqlServerName = reqParams.sqlServerName || '';
var tde = reqParams.transparentDataEncryption;
if (tde === undefined) tde = params.defaultSettings.sqldb.transparentDataEncryption;

log.info(util.format('sqldb cmd-poll: resourceGroupName: %s, sqldbName: %s, sqlServerName: %s', resourceGroupName, sqldbName, sqlServerName));

this.poll = function (sqldbOperations, next) {

sqldbOperations.setParameters(resourceGroupName, sqlServerName, sqldbName);

if (lastoperation === 'update'){
if (params.plan_id === '4b1cfc28-dda6-407b-abeb-7aa0b89f52bf') {
// we only need handle (lastoperation === 'provision' || lastoperation === 'deprovision') for the plan as update is prevented.
var result = {};
result.body = provisioningResult;
result.value = {
state: 'succeeded',
description: 'Registered the database as a service instance.'
};
if (lastoperation === 'deprovision') {
result.value.description = 'Unregistered the database.';
}
return next(null, result);
} else if (lastoperation === 'update'){
// Use the operationResult endpoint to poll long async requests like Update
async.waterfall([
function (callback){
Expand Down Expand Up @@ -63,9 +74,7 @@ var sqldbPoll = function (params) {
result.value = reply;
return next(err, result);
});
}
else {

} else {
async.waterfall([
function (callback) {
// get status of database
Expand All @@ -84,12 +93,12 @@ var sqldbPoll = function (params) {
if (lastoperation === 'provision') {
if (result.statusCode === HttpStatus.OK) {
result.body = _.extend(result.body, provisioningResult);

reply.state = 'succeeded';
reply.description = 'Created logical database ' + reqParams.sqldbName + ' on logical server ' + reqParams.sqlServerName + '.';
} else if (result.statusCode === HttpStatus.NOT_FOUND) {
} else if (result.statusCode === HttpStatus.NOT_FOUND) {
result.body = provisioningResult;

reply.state = 'in progress';
reply.description = 'Creating logical database ' + reqParams.sqldbName + ' on logical server ' + reqParams.sqlServerName + '.';
}
Expand All @@ -107,7 +116,7 @@ var sqldbPoll = function (params) {
}
});
},
function(result, callback) { // Set Transparent data encryption
function(result, callback) { // Set Transparent data encryption
if ( lastoperation === 'provision' && result.value.state === 'succeeded' && tde === true){
log.info('sqldb cmd-poll: async.waterfall/Set transparent data encryption: Enabled');
sqldbOperations.setTransparentDataEncryption(function (err, tdeResult) {
Expand Down Expand Up @@ -135,4 +144,3 @@ var sqldbPoll = function (params) {
};

module.exports = sqldbPoll;

129 changes: 107 additions & 22 deletions lib/services/azuresqldb/cmd-provision.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ var sqldbProvision = function (params) {
// this is to minimize the amount of static data
// that must be included in the developer's parameters file
this.fixupParameters = function () {

// patch up the sqlServerParameters
if (_.isUndefined(params.parameters.sqlServerParameters)) {
params.parameters.sqlServerParameters = {};
Expand All @@ -66,6 +65,12 @@ var sqldbProvision = function (params) {
}
params.parameters.sqlServerParameters.properties.version = '12.0';

if (_.isUndefined(params.parameters.sqldbParameters)) {
params.parameters.sqldbParameters = {};
}
if (_.isUndefined(params.parameters.sqldbParameters.properties)) {
params.parameters.sqldbParameters.properties = {};
}
// figure out what plan they selected and fill in some properties
var planId = params.plan_id;
log.info('SqlDb/cmd-provision/fixup parameters/planid: %j', planId);
Expand Down Expand Up @@ -95,30 +100,20 @@ var sqldbProvision = function (params) {
if (err) {
log.error('sqldb cmd-provision: get the sql server: err: %j', err);
return callback(err);
} else {
log.info('sqldb cmd-provision: get the sql server: %j', result);
callback(null, result);
}
log.debug('sqldb cmd-provision: getting sql server result: %j', result);
callback(null, result);
});
}

function getDatabase(callback) {
log.info('sqldb cmd-provision: check existence of database');
sqldbOperations.getDatabase(function (err, result) {
if (err) {
log.error('sqldb cmd-provision: check existence of sql database: err: %j', err);
callback(err);
} else if (result.statusCode === HttpStatus.NOT_FOUND) {
callback(null);
} else if (result.statusCode === HttpStatus.OK) {
var error = Error('The database name is not available.');
error.statusCode = HttpStatus.CONFLICT;
callback(error);
} else {
var errorMsg = util.format('sqldb cmd-provision: check existence of sql database, unexpected error: result: %j', result);
log.error(errorMsg);
callback(Error(errorMsg));
return callback(err);
}
log.debug('sqldb cmd-provision: getting sql database result: %j', result);
callback(null, result);
});
}

Expand All @@ -145,18 +140,67 @@ var sqldbProvision = function (params) {
}

sqldbOperations.setParameters(resourceGroupName, sqlServerName, sqldbName, location);

if (!privilege.allowToCreateSqlServer) {
if (params.plan_id === '4b1cfc28-dda6-407b-abeb-7aa0b89f52bf') {
async.waterfall([
function (callback) { // get sql server status (existence check)
getServer(callback);
},
function (result, callback) {
if (result.statusCode === HttpStatus.NOT_FOUND) {
var errorMessage = 'The specified server does not exist.';
callback(Error(errorMessage));
} else if (result.statusCode === HttpStatus.OK) { // sql server exists
log.info('sqldb cmd-provision: found the sql server.');
fullyQualifiedDomainName = JSON.parse(result.body).properties.fullyQualifiedDomainName;
callback(null);
} else {
var errorMessage = util.format('Unexpected error: %j', result);
callback(Error(errorMessage));
}
},
function (callback) { // get sql database status (existence check)
getDatabase(callback);
},
function (result, callback) {
if (result.statusCode === HttpStatus.NOT_FOUND) {
var errorMessage = 'The specified database does not exist on the server.';
callback(Error(errorMessage));
} else if (result.statusCode === HttpStatus.OK) { // sql database exists
log.info('sqldb cmd-provision: found the sql database.');
result.body.resourceGroup = resourceGroupName;
result.body.sqlServerName = sqlServerName;
result.body.fullyQualifiedDomainName = fullyQualifiedDomainName;
result.body.administratorLogin = administratorLogin;
result.body.administratorLoginPassword = administratorLoginPassword;

result.value = {};
result.value.state = 'in progress';
result.value.description = 'Found the database ' + reqParams.sqldbName + ' on server ' + reqParams.sqlServerName + '.';
callback(null, result);
} else {
var errorMessage = util.format('Unexpected error: %j', result);
callback(Error(errorMessage));
}
}
], function (err, result) {
if (err) {
log.error('sqldb cmd-provision: final callback: err: ', err);
} else {
log.info('sqldb cmd-provision: final callback: result: %j', result.value);
}
next(err, result);
});
} else if (!privilege.allowToCreateSqlServer) {
async.waterfall([
function (callback) { // get sql server status (existence check)
getServer(callback);
},
function (result, callback) {
if (result.statusCode === HttpStatus.NOT_FOUND) {
var errorMessage = util.format('The specified server does not exist but you do not have the privilege to create a new server.');
var errorMessage = 'The specified server does not exist but you do not have the privilege to create a new server.';
callback(Error(errorMessage));
} else if (firewallRules !== null) {
var errorMessage = util.format('You specify the firewall rules for the server but you do not have the privilege to operate the server.');
var errorMessage = 'You specify the firewall rules for the server but you do not have the privilege to operate the server.';
callback(Error(errorMessage));
} else if (result.statusCode === HttpStatus.OK) {
fullyQualifiedDomainName = JSON.parse(result.body).properties.fullyQualifiedDomainName;
Expand All @@ -169,6 +213,19 @@ var sqldbProvision = function (params) {
function (callback) { // see if db exists (in the case that sql server was there already)
getDatabase(callback);
},
function (result, callback) {
if (result.statusCode === HttpStatus.NOT_FOUND) {
callback(null);
} else if (result.statusCode === HttpStatus.OK) {
var error = Error('The database name is not available.');
error.statusCode = HttpStatus.CONFLICT;
callback(error);
} else {
var errorMsg = util.format('sqldb cmd-provision: check existence of sql database, unexpected error: result: %j', result);
log.error(errorMsg);
callback(Error(errorMsg));
}
},
function (callback) { // create the database
createDatabase(callback);
}
Expand Down Expand Up @@ -212,7 +269,7 @@ var sqldbProvision = function (params) {
callback(null);
} else {
var errorMessage = util.format('Unexpected error: %j', result);
log.error(Error(errorMessage));
log.error(errorMessage);
callback(Error(errorMessage));
}
},
Expand Down Expand Up @@ -262,6 +319,19 @@ var sqldbProvision = function (params) {
function (callback) { // see if db exists (in the case that sql server was there already)
getDatabase(callback);
},
function (result, callback) {
if (result.statusCode === HttpStatus.NOT_FOUND) {
callback(null);
} else if (result.statusCode === HttpStatus.OK) {
var error = Error('The database name is not available.');
error.statusCode = HttpStatus.CONFLICT;
callback(error);
} else {
var errorMsg = util.format('sqldb cmd-provision: check existence of sql database, unexpected error: result: %j', result);
log.error(errorMsg);
callback(Error(errorMsg));
}
},
function (callback) { // create the database
createDatabase(callback);
}
Expand Down Expand Up @@ -335,16 +405,31 @@ var sqldbProvision = function (params) {
return true;
};

this.checkUserProvidedDatabaseLogin = function () {
var bothOrNeither = ((reqParams.userProvidedDatabaseLogin === undefined) ===
(reqParams.userProvidedDatabaseLoginPassword === undefined));
if (!bothOrNeither) {
return ['userProvidedDatabaseLogin', 'userProvidedDatabaseLoginPassword'];
} else if (reqParams.userProvidedDatabaseLogin) {
var ret = [];
if (!_.isString(reqParams.userProvidedDatabaseLogin)) ret.push('userProvidedDatabaseLogin');
if (!_.isString(reqParams.userProvidedDatabaseLoginPassword)) ret.push('userProvidedDatabaseLoginPassword');
return ret;
} else {
return [];
}
};

this.getInvalidParams = function () {
var invalidParams = [];
invalidParams = invalidParams.concat(this.parametersNotProvided());

if (!this.sqlDbCollationWasProvided()) invalidParams.push('collation');
if (!this.firewallRuleIsOk()) invalidParams.push('allowSqlServerFirewallRules');
if (!this.noDeprecatedParametersWereProvided()) invalidParams.push('sqlServerCreateIfNotExist');
invalidParams = invalidParams.concat(this.checkUserProvidedDatabaseLogin());
return invalidParams;
};
};

module.exports = sqldbProvision;

6 changes: 4 additions & 2 deletions lib/services/azuresqldb/cmd-unbind.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ var sqldbUnbind = function (params) {
log.info(util.format('sqldb cmd-unbind: resourceGroupName: %s, sqldbName: %s, sqlServerName: %s', resourceGroupName, sqldbName, sqlServerName));

this.unbind = function (sqldbOperations, next) {
if (reqParams.userProvidedDatabaseLogin) {
return next(null);
}
sqldbOperations.setParameters(resourceGroupName, sqlServerName, sqldbName);

async.waterfall([
function (callback) {
log.info('sqldb cmd-unbind: async.waterfall/dropUserOfDatabaseLogin');
Expand Down Expand Up @@ -58,7 +60,7 @@ var sqldbUnbind = function (params) {
log.info('sqldb cmd-unbind: async.waterfall/final callback: err: ', err);
next(err);
});

};
};

Expand Down
Loading

0 comments on commit 7ec54c1

Please sign in to comment.