UNPKG

azure-cli

Version:

Microsoft Azure Cross Platform Command Line tool

1,551 lines (1,337 loc) 50.1 kB
// // Copyright (c) Microsoft and contributors. 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 crypto = require('crypto'); var fs = require('fs'); var path = require('path'); var url = require('url'); var util = require('util'); var uuid = require('uuid'); var azureCommon = require('azure-common'); var _ = require('underscore'); var blobUtils = require('./blobUtils'); var constants = require('./constants'); var log = require('./logging'); var utilsCore = require('./utilsCore'); var userAgentCore = require('./userAgentCore'); var commandMetadataFilter = require('./commandMetadataFilter'); var cliUserAgentFilter = require('./cliUserAgentFilter'); var locale = require('../locales/en-us.json'); var BEGIN_CERT = '-----BEGIN CERTIFICATE-----'; var END_CERT = '-----END CERTIFICATE-----'; exports.POLL_REQUEST_INTERVAL = 1000; var moduleVersion = require('../../package.json').version; exports.moduleVersion = moduleVersion; exports.azureDir = utilsCore.azureDir; exports.camelcase = utilsCore.camelcase; exports.ignoreCaseEquals = utilsCore.ignoreCaseEquals; exports.stringStartsWith = utilsCore.stringStartsWith; exports.pathExistsSync = utilsCore.pathExistsSync; var getUserAgent = exports.getUserAgent = function () { var userAgent = util.format('AzureXplatCLI/%s', moduleVersion); // if this env var is set, then use that in User Agent header if (process.env.AZURE_HTTP_USER_AGENT) { userAgent += ';' + process.env.AZURE_HTTP_USER_AGENT; } else { // else, construct our custom User Agent header. // add OS and command info into the user agent. // Note: Do this only if the above env variable is *not set*. var _userAgentInfo = userAgentCore.getUserAgentData(); if (_userAgentInfo) { userAgent += ';' + Object.keys(_userAgentInfo).map(function (key) { return key + ':' + _userAgentInfo[key]; }).join(';'); } } return userAgent; }; exports.createClient = function (factoryMethod, credentials, endpoint, options) { if(!options) { options = {}; } var client = factoryMethod(credentials, exports.stringTrimEnd(endpoint, '/')) .withFilter(exports.certAuthFilter(credentials)) .withFilter(cliUserAgentFilter.create(exports.getUserAgent())) .withFilter(exports.createPostBodyFilter()) .withFilter(polishErrorCausedByArmProviderNotRegistered()) .withFilter(commandMetadataFilter.create(userAgentCore.getCommandData())); if(!options.disableAutoRedirect) { client = client.withFilter(exports.createFollowRedirectFilter()); } if(!options.disableLogFilter) { client = client.withFilter(log.createLogFilter()); } return client; }; /** * Create Autorest client * @param {function} Method to create an instance of the client * @param {object} Subscription object * @param {object} [options] - Optional parameters * @param {boolean} [options.isTenantBased] - A Boolean value (true) indicating that the client is tenant based * @param {string} [options.endPoint] - The url specifying the endpoint (baseUrl) for the client */ exports.createAutoRestClient = function (FactoryMethod, subscription, options) { ensureAADBackedSubscription(subscription); var credentials = null; var baseUri = null; var subscriptionOrTenantId = null; if (options && options.isTenantBased && options.isTenantBased === true) { if (!options.endPoint) { options.endPoint = subscription.resourceManagerEndpointUrl; } subscriptionOrTenantId = subscription.tenantId; credentials = subscription._createCredentials(options.endPoint); baseUri = exports.stringTrimEnd(options.endPoint, '/'); } else { subscriptionOrTenantId = subscription.id; credentials = subscription._createCredentials(); baseUri = exports.stringTrimEnd(subscription.resourceManagerEndpointUrl, '/'); } var factoryMethodOptions = {}; factoryMethodOptions.filters = [ exports.certAuthFilter(credentials), log.createLogFilter(), cliUserAgentFilter.create(exports.getUserAgent()), exports.createPostBodyFilter(), exports.createFollowRedirectFilter(), polishErrorCausedByArmProviderNotRegistered(), commandMetadataFilter.create(userAgentCore.getCommandData()) ]; // there are a subset of clients that do not require a subscription // TODO: need to figure out a way to pass options into factoryMethodOptions. if (options && options.noSubscription) { if (options.requiresBaseUri) { return new FactoryMethod(credentials, baseUri, factoryMethodOptions); } else { return new FactoryMethod(credentials, factoryMethodOptions); } } else { return new FactoryMethod(credentials, subscriptionOrTenantId, baseUri, factoryMethodOptions); } }; function _createAsmClient(factoryMethod, subscription) { return exports.createClient(factoryMethod, subscription._createCredentials(), subscription.managementEndpointUrl); } function _createArmClient(factoryMethod, subscription, options) { ensureAADBackedSubscription(subscription); if(!options) { options = {}; } if(!options.endpoint) { options.endpoint = subscription.resourceManagerEndpointUrl; } return exports.createClient(factoryMethod, subscription._createCredentials(), options.endpoint, options); } function ensureAADBackedSubscription(subscription) { if (subscription.wasCreatedFromPublishSettingsFile()) { throw new Error('The current cmdlet requires you to log in using Azure Active Directory account, ' + 'not from a .publishsettings file. Please run \'azure login\' or use \'azure account set\' ' + 'to select a correct subscription.'); } } exports.getHDInsightClusterManagementClient = function (cloudServiceName, subscription) { var factoryMethod = require('azure-asm-hdinsight').createHDInsightClusterManagementClient; var client = _createAsmClient(factoryMethod, subscription); client.cloudServiceName = cloudServiceName; return client; }; exports.getHDInsightCluster2ManagementClient = function (cloudServiceName, subscription) { var factoryMethod = require('azure-asm-hdinsight').createHDInsightCluster2ManagementClient; var client = _createAsmClient(factoryMethod, subscription); client.cloudServiceName = cloudServiceName; return client; }; exports.getHDInsightJobManagementClient = function (clusterDnsName, userName, password) { var hdinsight = require('azure-asm-hdinsight'); var client = hdinsight.createHDInsightJobManagementClient(clusterDnsName, new hdinsight.createBasicAuthenticationCloudCredentials({ username: userName, password: password })); return client; }; exports.createCdnManagementClient = function (subscription) { var factoryMethod = require('azure-arm-cdn'); return this.createAutoRestClient(factoryMethod, subscription); }; exports.createRedisCacheManagementClient = function (subscription) { var factoryMethod = require('azure-arm-rediscache'); return this.createAutoRestClient(factoryMethod, subscription); }; exports.createHDInsightManagementClient = function (subscription) { var factoryMethod = require('azure-arm-hdinsight').createHDInsightManagementClient; return _createArmClient(factoryMethod, subscription); }; exports.createHDInsightJobManagementClient = function (subscription) { var factoryMethod = require('azure-arm-hdinsight-jobs').createHDInsightJobManagementClient; return _createArmClient(factoryMethod, subscription); }; exports.createInsightsClient = function (subscription) { var factoryMethod = require('azure-arm-insights').createInsightsClient; return _createArmClient(factoryMethod, subscription); }; exports.createInsightsManagementClient = function (subscription) { var factoryMethod = require('azure-arm-insights').createInsightsManagementClient; return _createArmClient(factoryMethod, subscription); }; //website provider might not be registered yet, so make sure to register it exports.createWebsiteClient = function (subscription, callback) { var client; subscription.registerAsmProvider('website', function (err) { if (err) { return callback(err); } var factoryMethod = require('azure-asm-website').createWebSiteManagementClient; client = _createAsmClient(factoryMethod, subscription); return callback(null, client); }); }; exports.createWebSiteExtensionsClient = function (siteName, hostNameSuffix, username, password) { var baseUri = util.format('https://%s.scm.%s:443', siteName, hostNameSuffix); var azureWebSite = require('azure-asm-website'); var service = azureWebSite.createWebSiteExtensionsClient(siteName, new azureWebSite.createBasicAuthenticationCloudCredentials({ username: username, password: password, }), baseUri) .withFilter(log.createLogFilter()) .withFilter(cliUserAgentFilter.create(getUserAgent())) .withFilter(createPostBodyFilter()) .withFilter(createFollowRedirectFilter()) .withFilter(commandMetadataFilter.create(userAgentCore.getCommandData())); return service; }; exports.createWebappManagementClient = function (subscription) { var factoryMethod = require('azure-arm-website'); return this.createAutoRestClient(factoryMethod, subscription); }; exports.createUsageManagementClient = function (subscription) { var factoryMethod = require('azure-arm-commerce'); return this.createAutoRestClient(factoryMethod, subscription); }; exports.createSqlClient = function (subscription) { var factoryMethod = require('azure-asm-sql').createSqlManagementClient; return _createAsmClient(factoryMethod, subscription); }; exports.createServiceBusClient = function (subscription) { var factoryMethod = require('azure-asm-sb').createServiceBusManagementClient; return _createAsmClient(factoryMethod, subscription); }; exports.createManagementClient = function (subscription) { var factoryMethod = require('azure-asm-mgmt').createManagementClient; return _createAsmClient(factoryMethod, subscription); }; exports.createStorageClient = function (subscription) { var factoryMethod = require('azure-asm-storage').createStorageManagementClient; return _createAsmClient(factoryMethod, subscription); }; exports.createStorageResourceProviderClient = function (subscription) { var clientConstructor = require('azure-arm-storage'); return this.createAutoRestClient(clientConstructor, subscription); }; exports.createServerManagementClient = function(subscription) { var clientConstructor = require('azure-arm-servermanagement'); return this.createAutoRestClient(clientConstructor, subscription); }; exports.createBatchResourceProviderClient = function (subscription) { var factoryMethod = require('azure-arm-batch'); return this.createAutoRestClient(factoryMethod, subscription); }; exports.createBatchClient = function (accountName, accountKey, taskUri) { var batch = require('azure-batch'); var credentials = new batch.SharedKeyCredentials(accountName, accountKey); var options = {}; options.filters = [ log.createLogFilter(), cliUserAgentFilter.create(exports.getUserAgent()), exports.createPostBodyFilter(), exports.createFollowRedirectFilter(), commandMetadataFilter.create(userAgentCore.getCommandData()) ]; options.generateClientRequestId = false; var client = new batch.ServiceClient(credentials, taskUri, options); return client; }; /** * Deprecated, use createComputeManagementClient instead. */ exports.createComputeResourceProviderClient = function (subscription) { // TODO: delete 'armsdk' folder after migration to autorest SDK var factoryMethod = require('./../commands/arm/armsdk/compute').createComputeResourceProviderClient; return _createArmClient(factoryMethod, subscription); }; exports.createComputeManagementClient = function (subscription) { // TODO: Publish Compute npm package, uncomment the following 2 lines, and delete the other 2 lines after. //var factoryMethod = require('azure-arm-compute'); // return this.createAutoRestClient(factoryMethod, subscription); var factoryMethod = require('./../commands/arm/armsdk/compute').createComputeManagementClient; return this.createAutoRestClient(factoryMethod, subscription); }; exports.createNetworkManagementClient = function (subscription) { var factoryMethod = require('azure-arm-network'); return this.createAutoRestClient(factoryMethod, subscription); }; exports.createPowerbiManagementClient = function (subscription) { var factoryMethod = require('azure-arm-powerbiembedded'); return this.createAutoRestClient(factoryMethod, subscription); }; exports.createGraphManagementClient = function (subscription) { var factoryMethod = require('azure-graph'); // Specify tenant based client and endpoint in options var options = { isTenantBased: true, endPoint: subscription.activeDirectoryGraphResourceId }; return this.createAutoRestClient(factoryMethod, subscription, options); }; exports.createTrafficManagerManagementClient = function (subscription) { var factoryMethod = require('azure-arm-trafficmanager'); return this.createAutoRestClient(factoryMethod, subscription); }; exports.createDnsResourceProviderClient = function (subscription) { var factoryMethod = require('azure-arm-dns'); return this.createAutoRestClient(factoryMethod, subscription); }; exports.createComputeClient = function (subscription) { var factoryMethod = require('azure-asm-compute').createComputeManagementClient; return _createAsmClient(factoryMethod, subscription); }; exports.createNetworkClient = function (subscription) { var factoryMethod = require('azure-asm-network').createNetworkManagementClient; return _createAsmClient(factoryMethod, subscription); }; exports.createTrafficManagerClient = function (subscription) { var factoryMethod = require('azure-asm-trafficmanager').createTrafficManagerManagementClient; return _createAsmClient(factoryMethod, subscription); }; exports.createResourceClient = function (subscription) { var factoryMethod = require('azure-arm-resource').ResourceManagementClient; return this.createAutoRestClient(factoryMethod, subscription); }; exports.createSubscriptionClient = function (subscription) { var options = { noSubscription: true, requiresBaseUri: true }; var factoryMethod = require('azure-arm-resource').SubscriptionClient; return this.createAutoRestClient(factoryMethod, subscription, options); }; exports.createPolicyClient = function (subscription) { var factoryMethod = require('azure-arm-resource').PolicyClient; return this.createAutoRestClient(factoryMethod, subscription); }; exports.createResourceManagerClient = function (subscription) { // TODO: delete 'armsdk' folder after migration to autorest SDK var factoryMethod = require('./../commands/arm/armsdk/resource').createResourceManagementClient; return _createArmClient(factoryMethod, subscription); }; exports.createResourceFeatureClient = function (subscription) { var factoryMethod = require('azure-arm-resource').FeatureClient; return this.createAutoRestClient(factoryMethod, subscription); }; exports.createEventsClient = function (subscription) { var factoryMethod = require('azure-monitoring').createEventsClient; return _createArmClient(factoryMethod, subscription); }; exports.createKeyVaultClient = function (subscription) { var keyvault = require('azure-keyvault'); var authenticator = function (challenge, callback) { // challenge.resource contains keyvault resourceId var retrieveTokenCallback = function(err, scheme, token) { return callback(err, util.format('%s %s', scheme, token)); }; var tokenCredentials = subscription._createCredentials(challenge.resource); return tokenCredentials.retrieveTokenFromCache(retrieveTokenCallback); }; var credentials; if(subscription.user) { credentials = new keyvault.KeyVaultCredentials(authenticator); } else if(subscription.managementCertificate) { credentials = subscription._createCredentials(); } else { throw new Error('Unsupported subscription object.'); } var options = { filters: [ exports.certAuthFilter(credentials), cliUserAgentFilter.create(exports.getUserAgent()), exports.createPostBodyFilter(), polishErrorCausedByArmProviderNotRegistered(), commandMetadataFilter.create(userAgentCore.getCommandData()) ] }; return keyvault.createKeyVaultClient(credentials, options); }; exports.createGalleryClient = function (subscription) { var factoryMethod = require('azure-gallery').createGalleryClient; return exports.createClient(factoryMethod, new azureCommon.AnonymousCloudCredentials(), subscription.galleryEndpointUrl); }; exports.createMobileClient = function (subscription) { return _createAsmClient(function (credentials) { //propagates errors that ErrorHandlingFilter in azureCommon.Service would swallow var errorPropagationFilter = function handle(resource, next, callback) { return next(resource, function (err, response, body) { if (body !== undefined && response !== undefined && (response.statusCode < 200 || response.statusCode >= 300)) { callback(body, response, body); } else { callback(err, response, body); } }); }; var client = new azureCommon.Service(credentials, [errorPropagationFilter]); client.longRunningOperationRetryTimeout = 5000; return client; }, subscription); }; exports.createDataLakeStoreFileSystemManagementClient = function (subscription) { var factoryMethod = require('azure-arm-datalake-store').DataLakeStoreFileSystemClient; var options = { noSubscription: true, dnsSuffix: subscription.azureDataLakeStoreFileSystemEndpointSuffix, }; return this.createAutoRestClient(factoryMethod, subscription, options); }; exports.createDataLakeStoreManagementClient = function (subscription) { var factoryMethod = require('azure-arm-datalake-store').DataLakeStoreAccountClient; return this.createAutoRestClient(factoryMethod, subscription); }; exports.createDataLakeAnalyticsManagementClient = function (subscription) { var factoryMethod = require('azure-arm-datalake-analytics').DataLakeAnalyticsAccountClient; return this.createAutoRestClient(factoryMethod, subscription); }; exports.createDataLakeAnalyticsJobManagementClient = function (subscription) { var options = { noSubscription: true, dnsSuffix: subscription.azureDataLakeAnalyticsCatalogAndJobEndpointSuffix }; var factoryMethod = require('azure-arm-datalake-analytics').DataLakeAnalyticsJobClient; return this.createAutoRestClient(factoryMethod, subscription, options); }; exports.createDataLakeAnalyticsCatalogManagementClient = function (subscription) { var options = { noSubscription: true, dnsSuffix: subscription.azureDataLakeAnalyticsCatalogAndJobEndpointSuffix }; var factoryMethod = require('azure-arm-datalake-analytics').DataLakeAnalyticsCatalogClient; return this.createAutoRestClient(factoryMethod, subscription, options); }; exports.createDevTestLabsClient = function (subscription) { var clientConstructor = require('azure-arm-devtestlabs'); return this.createAutoRestClient(clientConstructor, subscription); }; exports.getiotHubClient = function (subscription) { var clientConstructor = require('azure-arm-iothub'); return this.createAutoRestClient(clientConstructor, subscription); }; /** * Create old-style service object * @param {string} serviceFactoryName name of factory function off azure module */ function createService(factoryMethod, subscription) { var managementEndpoint = url.parse(subscription.managementEndpointUrl); var service = factoryMethod(subscription.id, { keyvalue: subscription.managementCertificate.key, certvalue: subscription.managementCertificate.cert, }, { host: managementEndpoint.hostname, port: managementEndpoint.port, serializetype: 'XML' }).withFilter(new RequestLogFilter(log)); return service; } exports.createWebsiteManagementService = function (subscription) { var factoryMethod = require('azure-asm-website').createWebsiteManagementService; return createService(factoryMethod, subscription); }; // TODO: workaround for release 0.7.4. Remove in vnext and fix underlying issue in SDK. function createPostBodyFilter() { return function handle(resource, next, callback) { if ((resource.method === 'POST' || resource.method === 'PUT' || resource.method === 'PATCH') && !resource.body) { resource.body = ''; } var stream = next(resource, callback); stream.on('error', function () { }); return stream; }; } exports.createPostBodyFilter = createPostBodyFilter; function certAuthFilter(credentials) { return function handle(resource, next, callback) { if (credentials && credentials.credentials && credentials.credentials.key && credentials.credentials.cert) { resource.key = credentials.credentials.key; resource.cert = credentials.credentials.cert; } return next(resource, callback); }; } exports.certAuthFilter = certAuthFilter; function createFollowRedirectFilter() { return function handle(resource, next, callback) { function handleRedirect(err, response, body) { if (response && response.headers.location && response.statusCode >= 300 && response.statusCode < 400) { resource.url = response.headers.location; next(resource, handleRedirect); } else if (callback) { callback(err, response, body); } } return next(resource, handleRedirect); }; } exports.createFollowRedirectFilter = createFollowRedirectFilter; polishErrorCausedByArmProviderNotRegistered = function () { return function handle(resource, next, callback) { var nextStream = next(resource, function (err, response, body) { if (err && err.message && response && response.statusCode === 409) { var re = /.*registered to use namespace \'(.+)\'/i; var found = re.exec(err.message); var providerName = (found && found.length === 2) ? found[1] : null; if (providerName) { err.message = util.format('The subscription must be registered to use namespace "%s". ' + 'This can be achieved by executing the command: "azure provider ' + 'register %s".', providerName, providerName); } } callback(err, response, body); }); return nextStream; }; }; exports.createScmManagementService = function (repository, auth) { var authentication = auth.split(':'); var repositoryUrl = url.parse(repository); var azureWebSite = require('azure-asm-website'); var service = azureWebSite.createScmService({ user: authentication[0], pass: authentication[1] }, { host: repositoryUrl.hostname, port: repositoryUrl.port }); service.userAgent = getUserAgent(); return service; }; exports.createBlobService = function () { var storage = require('azure-storage'); var blobService = storage.createBlobService.apply(this, arguments); blobService.userAgent = getUserAgent(); return blobService; }; exports.createSqlService = function () { var azureSqlMgmt = require('azure-asm-sql'); var sqlService = azureSqlMgmt.createSqlService.apply(this, arguments); sqlService.userAgent = getUserAgent(); return sqlService; }; exports.getLocaleString = function (string) { var result = locale[string]; if (!result) { if (process.env.AZURE_DEBUG_LABELS) { throw new Error(util.format('Invalid resource %s', string)); } else { return string; } } return result; }; function RequestLogFilter(logger) { this.logger = logger; } RequestLogFilter.prototype.handle = function (requestOptions, next) { var self = this; this.logger.silly('requestOptions'); this.logger.json('silly', requestOptions); if (next) { next(requestOptions, function (returnObject, finalCallback, nextPostCallback) { self.logger.silly('returnObject'); self.logger.json('silly', returnObject); if (nextPostCallback) { nextPostCallback(returnObject); } else if (finalCallback) { finalCallback(returnObject); } }); } }; exports.RequestLogFilter = RequestLogFilter; exports.isSha1Hash = function (str) { return (/\b([a-fA-F0-9]{40})\b/).test(str); }; exports.webspaceFromName = function (name) { return (name.replace(/ /g, '').toLowerCase() + 'webspace'); }; exports.getCertFingerprint = function (pem) { var certBase64 = exports.extractBase64CertFromPEM(pem); // Calculate sha1 hash of the cert var cert = new Buffer(certBase64, 'base64'); var sha1 = crypto.createHash('sha1'); sha1.update(cert); return sha1.digest('hex'); }; exports.isPemCert = function (data) { return data.indexOf(BEGIN_CERT) !== -1 && data.indexOf(END_CERT) !== -1; }; exports.extractBase64CertFromPEM = function (pem) { // Extract the base64 encoded cert out of pem file var beginCert = pem.indexOf(BEGIN_CERT) + BEGIN_CERT.length; if (pem[beginCert] === '\n') { beginCert = beginCert + 1; } else if (pem[beginCert] === '\r' && pem[beginCert + 1] === '\n') { beginCert = beginCert + 2; } var endCert = '\n' + pem.indexOf(END_CERT); if (endCert === -1) { endCert = '\r\n' + pem.indexOf(END_CERT); } return pem.substring(beginCert, endCert); }; exports.getOrCreateBlobStorage = function (cli, storageClient, location, affinityGroup, name, storagePrefix, callback) { var progress; /*jshint camelcase:false*/ function callback_(error, blobStorageUrl) { progress.end(); callback(error, blobStorageUrl); } function createNewStorageAccount_() { if (exports.stringIsNullOrEmpty(storagePrefix)) { storagePrefix = name; } var storageAccountName = blobUtils.normalizeServiceName(storagePrefix + (new Date()).getTime().toString()); cli.output.verbose('Creating a new storage account \'' + storageAccountName + '\''); var storageOptions = { name: storageAccountName, label: storageAccountName, geoReplicationEnabled: false, accountType: 'Standard_LRS' }; if (affinityGroup) { storageOptions.affinityGroup = affinityGroup; } else if (location) { storageOptions.location = location; } else { throw new Error('location or affinityGroup must be specified'); } progress = cli.interaction.progress('Creating a new storage account \'' + storageAccountName + '\''); storageClient.storageAccounts.create(storageOptions, function (error) { if (error) { callback_(error); } else { cli.output.verbose('Storage account successfully created'); cli.output.verbose('Getting properties for \'' + storageAccountName + '\' storage account'); storageClient.storageAccounts.get(storageAccountName, function (error, response) { if (error) { callback_(error); } else { var storageAccount = response.storageAccount; if (storageAccount) { var blobStorageUrl = storageAccount.properties.endpoints[0]; if (blobStorageUrl.slice(-1) === '/') { blobStorageUrl = blobStorageUrl.slice(0, -1); } callback_(null, blobStorageUrl); } else { callback_(new Error('No storage account found')); } } }); } }); } progress = cli.interaction.progress('Retrieving storage accounts'); cli.output.verbose('Getting list of available storage accounts'); storageClient.storageAccounts.list(function (error, response) { if (error) { callback_(error); } else { var storageAccounts = response.storageAccounts; for (var i = 0; i < storageAccounts.length; i++) { if ((location && storageAccounts[i].properties.location && storageAccounts[i].properties.location.toLowerCase() === location.toLowerCase()) || affinityGroup && storageAccounts[i].properties.affinityGroup && storageAccounts[i].properties.affinityGroup.toLowerCase() === affinityGroup.toLowerCase()) { if (!exports.stringIsNullOrEmpty(storagePrefix)) { if (!exports.stringStartsWith(storageAccounts[i].name, storagePrefix)) { continue; } } var blobStorageUrl = storageAccounts[i].properties.endpoints[0]; if (blobStorageUrl.slice(-1) === '/') { blobStorageUrl = blobStorageUrl.slice(0, -1); } callback_(null, blobStorageUrl); return; } } createNewStorageAccount_(); } }); }; exports.writeFileSyncMode = function writeFileSyncMode(path, data, encoding, mode) { mode = mode || parseInt('600', 8); // maximum protection by default var fd = fs.openSync(path, 'w', mode); try { if (typeof data === 'string') { fs.writeSync(fd, data, 0, encoding); } else { fs.writeSync(fd, data, 0, data.length, 0); } } finally { fs.closeSync(fd); } }; exports.getDnsPrefix = function (dnsName, allowEmpty) { if (dnsName) { // remove protocol if any, take the last element dnsName = dnsName.split('://').slice(-1)[0]; // take first element dnsName = dnsName.split('.', 1)[0]; } if (!dnsName && !allowEmpty) { throw new Error('Missing or invalid dns-name'); } return dnsName; }; /** * Resolve location name if 'name' is location display name. * * @param {string} name The display name or location name. Required * @param {function} callback The callback function called on completion. Required. */ exports.resolveLocationName = function (managementClient, name, callback) { managementClient.locations.list(function (error, response) { var resolvedLocation = null; if (!error) { if (response.locations.length > 0) { for (var i = 0; i < response.locations.length; i++) { var locationInfo = response.locations[i]; if (exports.ignoreCaseEquals(locationInfo.name, name)) { callback(null, locationInfo); return; } else if (!resolvedLocation && (exports.ignoreCaseEquals(locationInfo.DisplayName, name))) { // This is the first matched display name save the corresponding location // We ignore further matched display name, but will continue with location // matching resolvedLocation = locationInfo; } } if (resolvedLocation) { callback(null, resolvedLocation); } else { callback({ message: 'No location found which has DisplayName or Name same as value of --location', code: 'Not Found' }, name); } } else { // Return a valid error callback({ message: 'Server returns empty location list', code: 'Not Found' }, name); } } else { callback(error, null); } }); }; exports.parseInt = function (value) { var intValue = parseInt(value, 10); if (intValue != value || value >= 65536 * 65536) { // just some limits return NaN; } return intValue; }; exports.parseBool = function (value, paramName) { if (typeof value === 'boolean') { return value; } if (!this.ignoreCaseEquals(value, 'true') && !this.ignoreCaseEquals(value, 'false')) { throw new Error(util.format('%s parameter must be true or false', paramName)); } return value.toLowerCase() === 'true'; }; /** * Creates a anonymous function that validator if the given string is a valid datetime. * * @param {string} stringDateTime The datetime string. * @return {Date} */ exports.parseDateTime = function (stringDateTime) { try { return new Date(stringDateTime); } catch (e) { throw new Error($('The date format is incorrect')); } }; exports.getUTCTimeStamp = function () { var now = new Date(); return (now.getUTCFullYear() + '-' + ('0' + (now.getUTCMonth() + 1)).slice(-2) + '-' + ('0' + now.getUTCDate()).slice(-2) + ' ' + ('0' + now.getUTCHours()).slice(-2) + ':' + ('0' + now.getUTCMinutes()).slice(-2)); }; exports.logLineFormat = function logLineFormat(object, logFunc, prefix) { prefix = prefix || ''; switch (typeof object) { case 'object': // if this is a date then we call toISOString and print that if (_.isDate(object)) { logFunc(prefix.cyan + object.toISOString().green); } else { for (var i in object) { logLineFormat(object[i], logFunc, prefix + i + ' '); } } return; case 'string': logFunc(prefix.cyan + ('"' + object + '"').green); return; case 'boolean': logFunc(prefix.cyan + object.toString().green); return; case 'number': logFunc(prefix.cyan + object.toString().green); return; case 'undefined': return; default: logFunc(prefix.cyan + '?' + object + '?'); // unknown type } }; exports.validateEndpoint = function (endpoint) { if (!exports.stringStartsWith(endpoint, 'http://') && !exports.stringStartsWith(endpoint, 'https://')) { // Default to https endpoint = 'https://' + endpoint; } var parts = url.parse(endpoint); if (!parts.hostname) { throw new Error('Invalid endpoint format.'); } parts.port = (parts.port && parseInt(parts.port, 10)) || (/https/i.test(parts.protocol) ? constants.DEFAULT_HTTPS_PORT : constants.DEFAULT_HTTP_PORT); return url.format(parts); }; /** * Determines if a string is null or empty. * * @param {string} text The string to test. * @return {Bool} True if the string string is null or empty; false otherwise. */ exports.stringIsNullOrEmpty = function (text) { return text === null || text === undefined || text.trim() === ''; }; exports.stripBOM = function (content) { if (Buffer.isBuffer(content)) { content = content.toString(); } if (content.charCodeAt(0) === 0xFEFF) { content = content.slice(1); } return content; }; /** * Determines if a string ends with another. * * @param {string} text The string to assert. * @param {string} suffix The string suffix. * @param {bool} ignoreCase Boolean value indicating if casing should be ignored. * @return {Bool} True if the string ends with the suffix; false otherwise. */ exports.stringEndsWith = function (text, suffix, ignoreCase) { if (_.isNull(suffix)) { return true; } if (ignoreCase) { text = text.toLowerCase(); suffix = suffix.toLowerCase(); } return text.substr(text.length - suffix.length) === suffix; }; exports.stringTrimEnd = function (text, charToTrim) { if (!text) { return text; } if (!charToTrim) { charToTrim = ' '; } var subtract = 0; while (subtract < text.length && text[text.length - (subtract + 1)] === charToTrim) { subtract++; } return text.substr(0, text.length - subtract); }; exports.logError = function (log, message, err) { if (arguments.length == 1) { err = message; message = undefined; } else { log.error(message); } if (err) { if (err.message) { // log.error(err.message); log.verbose('stack', err.stack); log.json('silly', err); } else if (err.Message) { // log.error(err.Message); log.json('verbose', err); } } }; exports.clearConfig = function () { var azureConfigPath = path.join(exports.azureDir(), 'config.json'); if (exports.pathExistsSync(azureConfigPath)) { fs.unlinkSync(azureConfigPath); return true; } }; exports.copyIisNodeWhenServerJsPresent = function (log, rootPath, callback) { try { var iisnodeyml = 'iisnode.yml'; log.silly('copyWebConfigWhenServerJsPresent'); if (!exports.pathExistsSync(iisnodeyml) && (exports.pathExistsSync(path.join(rootPath, 'server.js')) || exports.pathExistsSync(path.join(rootPath, 'app.js')))) { log.info('Creating default ' + iisnodeyml + ' file'); var sourcePath = path.join(__dirname, '../templates/node/' + iisnodeyml); fs.readFile(sourcePath, function (err, result) { if (err) { callback(err); return; } fs.writeFile(path.join(rootPath, iisnodeyml), result, callback); }); } else { callback(); } } catch (e) { callback(e); } }; exports.normalizeParameters = function (paramDescription) { var key, positionalValue, optionValue; var paramNames = Object.keys(paramDescription); var finalValues = {}; for (var i = 0; i < paramNames.length; ++i) { key = paramNames[i]; positionalValue = paramDescription[key][0]; optionValue = paramDescription[key][1]; if (!_.isUndefined(positionalValue) && !_.isUndefined(optionValue)) { return { err: new Error('You must specify ' + key + ' either positionally or by name, but not both') }; } else { finalValues[key] = positionalValue || optionValue; } } return { values: finalValues }; }; /** * fs.exists wrapper for streamline */ exports.fileExists = function (filePath, cb) { var func = fs.exists; if (!func) { func = path.exists; } func(filePath, function (exists) { cb(null, exists); }); }; /** * Wildcard Util only support two wildcard character * and ? */ exports.Wildcard = { /** * does the specified the character contain wildcards */ containWildcards: function (str) { var wildcardReg = /[*?]/img; return str !== null && wildcardReg.test(str); }, /** * Get the max prefix string of the specified string which doesn't contain wildcard */ getNonWildcardPrefix: function (str) { var nonWildcardReg = /[^*?]*/img; var prefix = ''; if (str !== null) { var result = str.match(nonWildcardReg); if (result !== null && result.length > 0) { prefix = result[0]; } } return prefix; }, /** * Convert wildcard pattern to regular expression */ wildcardToRegexp: function (str, isCaseInsensitive) { var strRegexp = ''; if (str !== null) { strRegexp = str.replace(/\?/g, '.').replace(/\*/g, '.*'); } var regexp = new RegExp(); if (isCaseInsensitive && isCaseInsensitive === true) { regexp.compile('^' + strRegexp + '$', 'ig'); } else { regexp.compile('^' + strRegexp + '$'); } return regexp; }, /** * Is the specified string match the case sensitive specified wildcard pattern */ isMatch: function (str, pattern) { var reg = exports.Wildcard.wildcardToRegexp(pattern); return reg.test(str); }, /** * Is the specified string match the case insensitive specified wildcard pattern */ isMatchCaseInsensitive: function (str, pattern) { var reg = exports.Wildcard.wildcardToRegexp(pattern, true); return reg.test(str); } }; /** * Invalid file name chars in windows. * http://msdn.microsoft.com/en-us/library/system.io.path.getinvalidfilenamechars.aspx */ exports.invalidFileNameChars = [34, 60, 62, 124, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 58, 42, 63, 92, 47]; /** * Reserved file name in windows */ exports.reservedBaseFileNamesInWindows = ['con', 'prn', 'aux', 'nul', 'com1', 'com2', 'com3', 'com4', 'com5', 'com6', 'com7', 'com8', 'com9', 'lpt1', 'lpt2', 'lpt3', 'lpt4', 'lpt5', 'lpt6', 'lpt7', 'lpt8', 'lpt9']; /** * Is the reserved file name in windows */ exports.isReservedFileNameInWindows = function (name) { name = (name || '').toLowerCase(); var index = exports.reservedBaseFileNamesInWindows.indexOf(name); return index !== -1; }; /* * Escape file path */ exports.escapeFilePath = function (name) { if (exports.isWindows()) { //only escape file name on windows var regExp = exports.getReplaceRegExpFromCharCode(exports.invalidFileNameChars); name = name.replace(regExp, function (code) { return '%' + code.charCodeAt(0).toString(16); }); var extName = path.extname(name); var baseName = path.basename(name, extName); if (exports.isReservedFileNameInWindows(baseName)) { name = util.format('%s (1)%s', baseName, extName); } } return name; }; /** * Is windows platform */ exports.isWindows = function () { return !!process.platform.match(/^win/); }; /** * Join the char code into a replace regular expression * For example, * [65,66] => /A|B/img * [63,66] => /\?|B/img */ exports.getReplaceRegExpFromCharCode = function (charCodeArray) { function charCodeToRegChar(charCode) { var str = String.fromCharCode(charCode); switch (str) { case '*': case '?': case '.': case '\\': case '|': case '/': str = '\\' + str; break; } return str; } var regExp = new RegExp(); if (charCodeArray.length) { var regStr = charCodeToRegChar(charCodeArray[0]); for (var i = 1; i < charCodeArray.length; i++) { regStr += '|' + charCodeToRegChar(charCodeArray[i]); } regExp.compile(regStr, 'gim'); } return regExp; }; /** * Add function overloads to an object that vary by * declared argument length. * * @param {function} func the function overloads. * * @returns The 'overloaded' function */ exports.overload = function () { function final() { throw new Error(util.format($('Unknown overload for %s parameters'), arguments.length)); } var func = final; /* jshint loopfunc: true */ for (var i = 0; i < arguments.length; ++i) { func = (function (old, func) { return function () { if (func.length === arguments.length) { return func.apply(this, arguments); } else if (typeof old === 'function') { return old.apply(this, arguments); } }; })(func, arguments[i]); } return func; }; //"<root>\test\framework\cli-test.js" contains associated test stubs. Please keep them in sync. exports.uuidGen = function () { return uuid.v4(); }; //Provides a random string starting with the given (string) prefix. If prefix is not provided //then it provides a random alphanumeric string exports.getRandomString = function (prefix) { var randomStr = Math.random().toString(36).substr(2, 12); if (prefix !== null && prefix !== undefined && typeof prefix.valueOf() === 'string') { return prefix + randomStr; } return randomStr; }; exports.toLowerCaseAndRemoveSpace = function (str) { if (!str) { return str; } return str.replace(/ /gi, '').toLowerCase(); }; exports.ignoreCaseAndSpaceEquals = function (a, b) { return a === b || (exports.toLowerCaseAndRemoveSpace(a) === exports.toLowerCaseAndRemoveSpace(b)); }; exports.atLeastOneParameIsSet = function (params) { for (var i = 0; i < params.length; i++) { if (!exports.stringIsNullOrEmpty(params[i])) { return true; } } return false; }; exports.allParamsAreSet = function (params) { for (var i = 0; i < params.length; i++) { if (exports.stringIsNullOrEmpty(params[i])) { return false; } } return true; }; // Many of the commands accepts arguments whose valid values can be from an enum. For example // protocol argument for endpoints create, valid values are ['tcp', 'udp', 'http']. // This method can be used to validate user provided input against the possible valid // values (supportedTypes). This method do case ignore comparison and return matched enum // value. If no match found exception will be thrown and exception message will inform valid // values. exports.verifyParamExistsInCollection = function (supportedTypes, paramToCheck, paramName) { var i = _.indexOf(_.map(supportedTypes, function (s) { return s.toLowerCase(); }), paramToCheck.toLowerCase()); if (i === -1) { throw new Error(util.format(exports.getLocaleString('Given %s "%s" is invalid, supported values are: %s'), paramName, paramToCheck, supportedTypes.join(', '))); } return supportedTypes[i]; }; exports.hasValidProperty = function (object, propName) { if (object === null || object === undefined) { return false; } if (!object.hasOwnProperty(propName)) { return false; } if (object[propName] === null || object[propName] === undefined) { return false; } return true; }; exports.parseResourceReferenceUri = function (referenceUri) { var parts = referenceUri.split('/'); return { subscriptionId: parts[2], resourceGroupName: parts[4], provider: parts[6], // e.g. Microsoft.Network parentResource: parts.slice(7, parts.length - 1).join('/'), // e.g. virtualNetworks/<vnet-name>/subnets resourceName: parts[parts.length - 1] // e.g. <subnet-name> }; }; exports.isAttributesMatched = function (item, attributes) { for (var key in attributes) { var value1 = attributes[key]; var value2 = item[key]; if (typeof value1 === 'string' && typeof value2 === 'string') { value1 = value1.toLowerCase(); value2 = value2.toLowerCase(); } if (value1 !== value2) return false; } return true; }; /** * Looks through the list and returns the element that matches attributes, * case-insensitive. */ exports.findFirstCaseIgnore = function (list, attributes) { if (_.isEmpty(attributes)) { return undefined; } return _.find(list, function (item) { return exports.isAttributesMatched(item, attributes); }); }; /** * Looks through the list and returns the index of element that matches attributes or -1 if it is not present, * case-insensitive. */ exports.indexOfCaseIgnore = function (list, attributes) { if (_.isEmpty(list) || _.isEmpty(attributes)) { return -1; } for (var i = 0; i < list.length; i++) { var item = list[i]; var match = exports.isAttributesMatched(item, attributes); if (match) return i; } return -1; }; exports.argHasValue = function (argument) { return (argument !== true && argument !== '\'\''); }; exports.trimTrailingChar = function (str, charToTrim) { while (str.length > 0 && str.charAt(str.length - 1) == charToTrim) { str = str.substr(0, str.length - 1); } return str; }; exports.appendArray = function (array, otherArray) { for (var i = 0; i < otherArray.length; i++) { array.push(otherArray[i]); } }; exports.capitalizeFirstLetter = function (str) { return str.charAt(0).toUpperCase() + str.slice(1); }; exports.toRange = function (array) { return '[' + array[0] + '-' + array[1] + ']'; }; exports.setIndent = function(num) { if (num > 0) { return new Array(num + 1).join(' '); } return ''; }; exports.getHash = function (name) { // Define Utility Functions - Reference: http://www.cse.yorku.ca/~oz/hash.html var hash = 5381; var seedstr = name; for (var i = 0; i < seedstr.length; i++) { var c = parseInt(seedstr.charCodeAt(i)); hash = ((((hash << 5) + hash) + c) % Number.MAX_VALUE) >>> 0; } return hash; }; exports.vmImageAliasUrl = 'https://raw.githubusercontent.com/Azure/azure-rest-api-specs/master/arm-compute/quickstart-templates/aliases.json'; exports.getImageAliasFilePath = function() { return __dirname + '/aliases/image.json'; }; exports.getImageAliasFileContent = function() { var content = fs.readFileSync(exports.getImageAliasFilePath(), 'utf8'); content = content.replace(/^\uFEFF/, ''); return content; }; exports.parseImageAliasDict = function(aliasObj) { var aliasDict = {}; var key = null; for (key in aliasObj.outputs.aliases.value.Windows) { aliasDict[key] = aliasObj.outputs.aliases.value.Windows[key]; } for (key in aliasObj.outputs.aliases.value.Linux) { aliasDict[key] = aliasObj.outputs.aliases.value.Linux[key]; } return aliasDict; }; exports.getImageAliasUrn = function(alias, aliasObj) { if (!aliasObj) { var content = exports.getImageAliasFileContent(); aliasObj = JSON.parse(content); } var aliasArr = exports.parseImageAliasDict(aliasObj); for (var i in aliasArr) { if (i.toLowerCase() === alias.toLowerCase()) { return [aliasArr[i].publisher, aliasArr[i].offer, aliasArr[i].sku, aliasArr[i].version].join(':'); } } return null; }; exports.getImageAliasList = function() { var content = null; if (fs.existsSync(exports.getImageAliasFilePath())) { content = exports.getImageAliasFileContent(); } else { var request = require('sync-request'); var response = request('GET', exports.vmImageAliasUrl); content = response.getBody(); exports.writeFileSyncMode(exports.getImageAliasFilePath(), content, 'utf-8'); } var aliasObj = JSON.parse(content); var list = []; var aliasArr = exports.parseImageAliasDict(aliasObj); for (var i in aliasArr) { list.push(i + ' = ' + exports.getImageAliasUrn(i, aliasObj)); } return list; }; exports.takeDefault = function(output, defValue, paramName) { output.warn(util.format(('Using default %s %s'), paramName, defValue)); return defValue; }; exports.getPublicIp = function(vm) { var publicIPAddress; if(!vm.networkProfile.networkInterfaces[0].expanded.ipConfigurations[0].publicIPAddress) { publicIPAddress = 'N/A'; } else { publicIPAddress = vm.networkProfile.networkInterfaces[0].expanded.ipConfigurations[0].publicIPAddress.expanded.ipAddress; } return publicIPAddress; }; /** * trunc * * truncates the string with '...' endings * @returns {string} */ functio