Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #11 from andrerod/dev

#439: Add CRUD support for storage accounts
  • Loading branch information...
commit 10e7c235ff255f8276cda74b7bb0b0c674d1ea7c 2 parents 6d0910a + 854f771
@andrerod andrerod authored
View
128 lib/commands/account.js
@@ -85,12 +85,11 @@ exports.init = function (cli) {
}
var subscriptionObject = filtered[0];
- var cfg = account.readConfig();
-
var subscriptionTag = importByName ? subscriptionObject.Name : subscriptionObject.Id;
log.info('Setting subscription to \'' + subscriptionTag + '\'');
- cfg.subscription = subscriptionObject.Id;
- account.writeConfig(cfg);
+
+ setSubscription(subscriptionObject.Id)
+
log.info('Changes saved.');
});
@@ -155,16 +154,11 @@ exports.init = function (cli) {
}
return callback();
- function convertPfx(pfx) {
- var pem = pfx2pem(pfx);
- utils.writeFileSyncMode(pemPath, pem.toString(), 'utf8');
- log.verbose('Converted PFX data to ' + pemPath);
- }
-
function processSettings(file, settings) {
if (!settings.PublishProfile ||
!settings.PublishProfile['@'] ||
- !settings.PublishProfile['@'].ManagementCertificate) {
+ (!settings.PublishProfile['@'].ManagementCertificate &&
+ !(settings.PublishProfile['@'].SchemaVersion === '2.0'))) {
throw new Error('Invalid publishSettings file. Use "azure account download" to download publishing credentials.');
}
@@ -176,6 +170,15 @@ exports.init = function (cli) {
subs = [subs];
}
+ if (subs.length === 0) {
+ log.warning('Importing profile with no subscriptions');
+ } else {
+ for (var index in subs) {
+ log.info('Found subscription:', subs[index]['@'].Name);
+ log.verbose(' Id:', subs[index]['@'].Id);
+ }
+ }
+
if (attribs.Url) {
var endpointInfo = utils.validateEndpoint(attribs.Url);
var config = account.readConfig();
@@ -184,29 +187,21 @@ exports.init = function (cli) {
log.info('Setting service endpoint to:', config.endpoint);
}
- if (subs.length === 0) {
- log.warning('Importing profile with no subscriptions');
- }
- else {
- for (var index in subs) {
- log.info('Found subscription:', subs[index]['@'].Name);
- log.verbose(' Id:', subs[index]['@'].Id);
- }
+ if (attribs.ManagementCertificate) {
+ log.verbose('Parsing management certificate');
+ var pfx = new Buffer(attribs.ManagementCertificate, 'base64');
+ convertPfx(pfx);
}
- log.verbose('Parsing management certificate');
- var pfx = new Buffer(attribs.ManagementCertificate, 'base64');
- convertPfx(pfx);
-
log.verbose('Storing account information at', publishSettingsFilePath);
utils.writeFileSyncMode(publishSettingsFilePath, readBuffer); // folder already created by convertPfx()
if (subs.length !== 0) {
log.info('Setting default subscription to:', subs[0]['@'].Name);
log.info('Use "azure account set" to change to a different one.')
- var config = account.readConfig();
- config.subscription = subs[0]['@'].Name;
- account.writeConfig(config);
+
+ setSubscription(subs[0]['@'].Id);
}
+
log.warn('The \'' + file + '\' file contains sensitive information.');
log.warn('Remember to delete it now that it has been imported.');
log.info('Account publish settings imported successfully');
@@ -286,44 +281,6 @@ exports.init = function (cli) {
}
account.listLAG = listLAG;
- var storage = account.category('storage')
- .description('Commands to manage your Azure storage account');
-
- storage.command('list')
- .description('List storage accounts available for your account')
- .execute(function (options, callback) {
- var channel = utils.createServiceManagementService(cli.category('account').lookupSubscriptionId(options.subscription),
- cli.category('account'), log);
-
- var progress = cli.progress('Fetching storage accounts');
- utils.doServiceManagementOperation(channel, 'listStorageAccounts', function (error, response) {
- progress.end();
- if (!error) {
- if (response.body.length > 0) {
- log.table(response.body, function (row, item) {
- row.cell('Name', item.ServiceName);
- var storageServiceProperties = item.StorageServiceProperties;
- if ('Label' in storageServiceProperties) {
- row.cell('Label', Buffer(storageServiceProperties.Label, 'base64').toString());
- }
- // This will display affinity group GUID and GeoPrimaryLocation (if present) if Location is not present
- // Affinity group or location display name is not present in the data
- row.cell('Location', storageServiceProperties.Location ||
- (storageServiceProperties.AffinityGroup || '') +
- (storageServiceProperties.GeoPrimaryRegion ? ' (' + storageServiceProperties.GeoPrimaryRegion + ')' : ''));
- });
- } else {
- if (log.format().json) {
- log.json([]);
- } else {
- log.info('No storage accounts found');
- }
- }
- }
- callback(error);
- });
- });
-
account.readPublishSettings = function () {
var publishSettings = {};
@@ -340,7 +297,7 @@ exports.init = function (cli) {
return publishSettings;
};
- account.readSubscriptions = function () {
+ function readSubscriptions () {
if (!utils.pathExistsSync(publishSettingsFilePath)) {
throw new Error('No publish settings file found. Please use "azure account import" first.');
}
@@ -375,10 +332,7 @@ exports.init = function (cli) {
} else {
var subscriptions = [];
for (var s in subs) {
- subscriptions[s] = {
- Name: subs[s]['@'].Name,
- Id: subs[s]['@'].Id
- };
+ subscriptions[s] = subs[s]['@'];
}
return subscriptions;
@@ -387,6 +341,42 @@ exports.init = function (cli) {
throw new Error('Invalid publish settings file.')
}
}
+ account.readSubscriptions = readSubscriptions;
+
+ function setSubscription (id) {
+ var subscriptions = readSubscriptions();
+ var subscription = subscriptions.filter(function (subscription) {
+ return subscription.Id === id;
+ })[0];
+
+ if (!subscription) {
+ throw new Error('Invalid subscription ' + id);
+ } else {
+ var config = account.readConfig();
+
+ if (subscription.ServiceManagementUrl && subscription.ServiceManagementUrl !== config.endpoint) {
+ var endpointInfo = utils.validateEndpoint(subscription.ServiceManagementUrl);
+ config.endpoint = endpointInfo;
+ log.info('Setting service endpoint to:', config.endpoint);
+ }
+
+ if (subscription.ManagementCertificate) {
+ log.verbose('Parsing management certificate');
+ var pfx = new Buffer(subscription.ManagementCertificate, 'base64');
+ convertPfx(pfx);
+ }
+
+ config.subscription = id;
+ account.writeConfig(config);
+ }
+ }
+ account.setSubscription = setSubscription;
+
+ function convertPfx(pfx) {
+ var pem = pfx2pem(pfx);
+ utils.writeFileSyncMode(pemPath, pem.toString(), 'utf8');
+ log.verbose('Converted PFX data to ' + pemPath);
+ }
account.defaultSubscriptionId = function () {
return account.readConfig().subscription;
View
261 lib/commands/storage.js
@@ -0,0 +1,261 @@
+/**
+* Copyright (c) Microsoft. All rights reserved.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+var common = require('../common');
+var fs = require('fs');
+var path = require('path');
+var url = require('url');
+var crypto = require('crypto');
+var pfx2pem = require('../util/certificates/pkcs').pfx2pem;
+var Channel = require('../channel');
+var async = require('async');
+var utils = require('../utils');
+var constants = require('../constants');
+var cacheUtils = require('../cacheUtils');
+
+var linkedRevisionControl = require('../linkedrevisioncontrol');
+
+exports.init = function (cli) {
+
+ var log = cli.output;
+ var account = cli.category('account');
+
+ var storage = account.category('storage')
+ .description('Commands to manage your Azure storage account');
+
+ var keys = storage.category('keys')
+ .description('Commands to manage your Azure storage account keys');
+
+ storage.command('list')
+ .description('List storage accounts')
+ .option('-s, --subscription <id>', 'use the subscription id')
+ .execute(function (options, callback) {
+ var channel = utils.createServiceManagementService(cli.category('account').lookupSubscriptionId(options.subscription),
+ cli.category('account'), log);
+
+ var progress = cli.progress('Fetching storage accounts');
+ utils.doServiceManagementOperation(channel, 'listStorageAccounts', function (error, response) {
+ progress.end();
+ if (!error) {
+ if (response.body.length > 0) {
+ log.table(response.body, function (row, item) {
+ row.cell('Name', item.ServiceName);
+ var storageServiceProperties = item.StorageServiceProperties;
+ if ('Label' in storageServiceProperties) {
+ row.cell('Label', Buffer(storageServiceProperties.Label, 'base64').toString());
+ }
+ // This will display affinity group GUID and GeoPrimaryLocation (if present) if Location is not present
+ // Affinity group or location display name is not present in the data
+ row.cell('Location', storageServiceProperties.Location ||
+ (storageServiceProperties.AffinityGroup || '') +
+ (storageServiceProperties.GeoPrimaryRegion ? ' (' + storageServiceProperties.GeoPrimaryRegion + ')' : ''));
+ });
+ } else {
+ if (log.format().json) {
+ log.json([]);
+ } else {
+ log.info('No storage accounts found');
+ }
+ }
+ }
+
+ callback(error);
+ });
+ });
+
+
+ storage.command('show <name>')
+ .description('Shows a storage account')
+ .option('-s, --subscription <id>', 'use the subscription id')
+ .execute(function (name, options, callback) {
+ var channel = utils.createServiceManagementService(cli.category('account').lookupSubscriptionId(options.subscription),
+ cli.category('account'), log);
+
+ var progress = cli.progress('Fetching storage account');
+ utils.doServiceManagementOperation(channel, 'getStorageAccountProperties', name, function (error, response) {
+ progress.end();
+ if (!error) {
+ if (response.body.StorageServiceProperties) {
+ if (log.format().json) {
+ log.json(clean(response.body));
+ } else {
+ log.data('Name', clean(response.body.ServiceName));
+ log.data('Url', clean(response.body.Url));
+ logEachData('Account Properties', clean(response.body.StorageServiceProperties));
+ logEachData('Extended Properties', clean(response.body.ExtendedProperties));
+ logEachData('Capabilities', clean(response.body.Capabilities));
+ }
+ } else {
+ log.info('No storage account found');
+ }
+ }
+
+ callback(error);
+ });
+ });
+
+ storage.command('create <name>')
+ .description('Creates a storage account')
+ .option('-s, --subscription <id>', 'use the subscription id')
+ .option('--label <label>', 'The label')
+ .option('--accountDescription <accountDescription>', 'The description')
+ .option('--location <location>', 'The location')
+ .option('--affinityGroup <affinityGroup>', 'The affinity group')
+ .execute(function (name, options, callback) {
+ var channel = utils.createServiceManagementService(cli.category('account').lookupSubscriptionId(options.subscription),
+ cli.category('account'), log);
+
+ var storageOptions = {
+ Location: options.location,
+ AffinityGroup: options.AffinityGroup,
+ Description: options.accountDescription,
+ Label: options.label
+ };
+
+ var progress = cli.progress('Creating storage account');
+ utils.doServiceManagementOperation(channel, 'createStorageAccount', name, storageOptions, function (error, response) {
+ progress.end();
+
+ callback(error);
+ });
+ });
+
+ storage.command('update <name>')
+ .description('Updates a storage account')
+ .option('-s, --subscription <id>', 'use the subscription id')
+ .option('--label <label>', 'The label')
+ .option('--accountDescription <accountDescription>', 'The description')
+ .option('--geoReplicationEnabled <geoReplicationEnabled>', 'Indicates if the geo replication is enabled')
+ .execute(function (name, options, callback) {
+ var channel = utils.createServiceManagementService(cli.category('account').lookupSubscriptionId(options.subscription),
+ cli.category('account'), log);
+
+ var storageOptions = {
+ Description: options.accountDescription,
+ Label: options.label,
+ GeoReplicationEnabled: options.geoReplicationEnabled
+ };
+
+ var progress = cli.progress('Updating storage account');
+ utils.doServiceManagementOperation(channel, 'updateStorageAccount', name, storageOptions, function (error, response) {
+ progress.end();
+
+ callback(error);
+ });
+ });
+
+ storage.command('delete <name>')
+ .description('Deletes a storage account')
+ .option('-s, --subscription <id>', 'use the subscription id')
+ .execute(function (name, options, callback) {
+ var channel = utils.createServiceManagementService(cli.category('account').lookupSubscriptionId(options.subscription),
+ cli.category('account'), log);
+
+ var progress = cli.progress('Deleting storage account');
+ utils.doServiceManagementOperation(channel, 'deleteStorageAccount', name, function (error, response) {
+ progress.end();
+
+ callback(error);
+ });
+ });
+
+ keys.command('list <name>')
+ .description('Lists the keys for a storage account')
+ .option('-s, --subscription <id>', 'use the subscription id')
+ .execute(function (name, options, callback) {
+ var channel = utils.createServiceManagementService(cli.category('account').lookupSubscriptionId(options.subscription),
+ cli.category('account'), log);
+
+ var progress = cli.progress('Getting storage account keys');
+ utils.doServiceManagementOperation(channel, 'getStorageAccountKeys', name, function (error, response) {
+ progress.end();
+
+ if (!error) {
+ if (response.body.StorageServiceKeys) {
+ if (log.format().json) {
+ log.json(response.body.StorageServiceKeys);
+ } else {
+ log.data('Primary: ', response.body.StorageServiceKeys.Primary);
+ log.data('Secondary: ', response.body.StorageServiceKeys.Secondary);
+ }
+ } else {
+ log.info('No storage account keys found');
+ }
+ }
+
+ callback(error);
+ });
+ });
+
+ keys.command('renew <name>')
+ .description('Renews a key for a storage account from your account')
+ .option('-s, --subscription <id>', 'use the subscription id')
+ .option('--primary', 'update the primary key')
+ .option('--secondary', 'update the secondary key')
+ .execute(function (name, options, callback) {
+ var channel = utils.createServiceManagementService(cli.category('account').lookupSubscriptionId(options.subscription),
+ cli.category('account'), log);
+
+ if (!options.primary && !options.secondary) {
+ throw new Error('Need to specify either --primary or --secondary');
+ }
+
+ var type = options.primary ? 'primary' : 'secondary';
+
+ var progress = cli.progress('Renewing storage account key');
+ utils.doServiceManagementOperation(channel, 'regenerateStorageAccountKeys', name, type, function (error, response) {
+ progress.end();
+
+ callback(error);
+ });
+ });
+
+ function clean(source) {
+ if (typeof (source) === 'string') {
+ return source;
+ }
+
+ var target = {};
+ var hasString = false;
+ var hasNonString = false;
+ var stringValue = '';
+
+ for (var prop in source) {
+ if (prop == '@') {
+ continue;
+ } else {
+ if (prop === '#' || prop === 'string' || prop.substring(prop.length - 7) === ':string') {
+ hasString = true;
+ stringValue = source[prop];
+ } else {
+ hasNonString = true;
+ }
+ target[prop] = clean(source[prop]);
+ }
+ }
+ if (hasString && !hasNonString) {
+ return stringValue;
+ }
+ return target;
+ }
+
+ function logEachData(title, data) {
+ var cleaned = clean(data);
+ for (var property in cleaned) {
+ log.data(title + ' ' + property, cleaned[property]);
+ }
+ }
+ storage.logEachData = logEachData;
+};
View
9 lib/iaas/upload/pageBlob.js
@@ -83,14 +83,14 @@ var copyBlob =
}
// Using default host provided by Node SDK if not specified
- var blobService = azure.createBlobService(splitDestination.accountName, destinationAccountKey, splitDestination.host);
+ var blobService = azure.createBlobService(splitDestination.accountName, destinationAccountKey);
var container = splitDestination.container || '$root';
blobService.createContainerIfNotExists(container, function(error1, containerCreated, response1) {
if (error1) {
callback(error1, null, response1);
return;
}
- var blobServiceEx = new BlobServiceEx(splitDestination.accountName, destinationAccountKey, splitDestination.host);
+ var blobServiceEx = new BlobServiceEx(splitDestination.accountName, destinationAccountKey);
blobServiceEx.copyBlobEx(sourceUri, container, splitDestination.blobName, checkStatus);
@@ -107,7 +107,6 @@ var copyBlob =
callback(error, blob, response);
}
});
-
};
var signBlobUri =
@@ -178,7 +177,7 @@ var uploadPageBlob =
}
// Using default host provided by Node SDK if not specified
- var blobService = azure.createBlobService(splitDestination.accountName, accountKey, splitDestination.host);
+ var blobService = azure.createBlobService(splitDestination.accountName, accountKey);
uploadPageBlobFromBlobService(blobService, splitDestination.resourceName, fileName, options, callback);
};
@@ -191,7 +190,7 @@ var deleteBlob =
}
// Using default host provided by Node SDK if not specified
- var blobService = azure.createBlobService(splitDestination.accountName, accountKey, splitDestination.host);
+ var blobService = azure.createBlobService(splitDestination.accountName, accountKey);
var splitName = splitBlobResourceName(splitDestination.resourceName);
blobService.deleteBlob(splitName.container, splitName.blobName, callback);
};
View
173 test/commands/cli.storage-tests.js
@@ -0,0 +1,173 @@
+/**
+* Copyright 2012 Microsoft Corporation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+var should = require('should');
+var uuid = require('node-uuid');
+
+var cli = require('../cli');
+var capture = require('../util').capture;
+
+var currentStorageName = 0;
+var storageNames = [ ('storage' + uuid()).toLowerCase().substr(0, 15) ];
+
+suite('cli', function(){
+ suite('storage', function() {
+ teardown(function (done) {
+ function deleteUsedStorage (storages) {
+ if (storages.length > 0) {
+ var storage = storages.pop();
+
+ var cmd = ('node cli.js account storage delete ' + storage + ' --json').split(' ');
+
+ capture(function() {
+ cli.parse(cmd);
+ }, function () {
+ deleteUsedStorage(storages);
+ });
+ } else {
+ done();
+ }
+ };
+
+ // Remove any existing repository hooks
+ deleteUsedStorage(storageNames.slice(0));
+ });
+
+ test('storage create', function(done) {
+ var storageName = storageNames[0];
+
+ var cmd = ('node cli.js account storage create ' + storageName + ' --json --location').split(' ');
+ cmd.push('East US');
+
+ capture(function() {
+ cli.parse(cmd);
+ }, function (result) {
+ result.text.should.equal('');
+ result.exitStatus.should.equal(0);
+
+ done();
+ });
+ });
+
+ test('storage list', function(done) {
+ var storageName = storageNames[0];
+
+ var cmd = ('node cli.js account storage create ' + storageName + ' --json --location').split(' ');
+ cmd.push('East US');
+
+ capture(function() {
+ cli.parse(cmd);
+ }, function (result) {
+ result.text.should.equal('');
+ result.exitStatus.should.equal(0);
+
+ var cmd = ('node cli.js account storage list --json').split(' ');
+
+ capture(function() {
+ cli.parse(cmd);
+ }, function (result) {
+ var storageAccounts = JSON.parse(result.text);
+ storageAccounts.some(function (account) {
+ return account.ServiceName === storageName;
+ }).should.be.true;
+
+ done();
+ });
+ });
+ });
+
+ test('storage update', function(done) {
+ var storageName = storageNames[0];
+
+ var cmd = ('node cli.js account storage create ' + storageName + ' --json --location').split(' ');
+ cmd.push('East US');
+
+ capture(function() {
+ cli.parse(cmd);
+ }, function (result) {
+ result.text.should.equal('');
+ result.exitStatus.should.equal(0);
+
+ var cmd = ('node cli.js account storage update ' + storageName + ' --label test --json').split(' ');
+
+ capture(function() {
+ cli.parse(cmd);
+ }, function (result) {
+ result.text.should.equal('');
+ result.exitStatus.should.equal(0);
+
+ var cmd = ('node cli.js account storage show ' + storageName + ' --json').split(' ');
+
+ capture(function() {
+ cli.parse(cmd);
+ }, function (result) {
+ var storageAccount = JSON.parse(result.text);
+ new Buffer(storageAccount.StorageServiceProperties.Label, 'base64').toString('ascii').should.equal('test');
+
+ done();
+ });
+ });
+ });
+ });
+
+ test('storage keys renew', function(done) {
+ var storageName = storageNames[0];
+
+ var cmd = ('node cli.js account storage create ' + storageName + ' --json --location').split(' ');
+ cmd.push('East US');
+
+ capture(function() {
+ cli.parse(cmd);
+ }, function (result) {
+ result.text.should.equal('');
+ result.exitStatus.should.equal(0);
+
+ var cmd = ('node cli.js account storage keys list ' + storageName + ' --json').split(' ');
+
+ capture(function() {
+ cli.parse(cmd);
+ }, function (result) {
+ var storageAccountKeys = JSON.parse(result.text);
+ storageAccountKeys.Primary.should.not.be.null;
+ storageAccountKeys.Secondary.should.not.be.null;
+
+ var cmd = ('node cli.js account storage keys renew ' + storageName + ' --primary --json').split(' ');
+
+ capture(function() {
+ cli.parse(cmd);
+ }, function (result) {
+ result.text.should.equal('');
+ result.exitStatus.should.equal(0);
+
+ var cmd = ('node cli.js account storage keys list ' + storageName + ' --json').split(' ');
+
+ capture(function() {
+ cli.parse(cmd);
+ }, function (result) {
+ var renewedStorageAccountKeys = JSON.parse(result.text);
+ renewedStorageAccountKeys.Primary.should.not.be.null;
+ renewedStorageAccountKeys.Secondary.should.not.be.null;
+
+ renewedStorageAccountKeys.Primary.should.not.equal(storageAccountKeys.Primary);
+ renewedStorageAccountKeys.Secondary.should.equal(storageAccountKeys.Secondary);
+
+ done();
+ });
+ });
+ });
+ });
+ });
+ });
+});
View
3  test/testlist.txt
@@ -2,4 +2,5 @@ commands/generate-psm1-utils-test.js
commands/cli.deployment-tests.js
commands/cli.site-tests.js
commands/cli.siteconfig-tests.js
-linkrevisioncontrol-tests.js
+linkrevisioncontrol-tests.js
+commands/cli.storage-tests.js
Please sign in to comment.
Something went wrong with that request. Please try again.