sleeveforarm
Version:
Making Azure Easy
237 lines • 12.9 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const FS = require("fs-extra");
const Path = require("path");
// tslint:disable-next-line:max-line-length
const ApplicationInsightsInfrastructure = require("./applicationInsightsInfrastructure");
const CommonUtilities = require("./common-utilities");
const data = require("./data");
const promiseGate_1 = require("./promiseGate");
const Resource = require("./resource");
const ServiceEnvironment = require("./serviceEnvironmentUtilities");
const webapp_node_azure_1 = require("./webapp-node-azure");
class BaseDeployWebappNodeAzureInfrastructure {
constructor(nodeInfra) {
this.nodeInfra = nodeInfra;
}
getDeployedURL() {
return __awaiter(this, void 0, void 0, function* () {
// tslint:disable-next-line:max-line-length
const azResult = yield CommonUtilities.runAzCommand(`az webapp show \
--resource-group ${this.nodeInfra.resourceGroup.resourceGroupName} \
--name ${this.nodeInfra.webAppDNSName}`);
return "http://" + azResult.defaultHostName;
});
}
}
exports.BaseDeployWebappNodeAzureInfrastructure = BaseDeployWebappNodeAzureInfrastructure;
class WebappNodeAzureInfrastructure extends webapp_node_azure_1.default {
constructor() {
super(...arguments);
this.promiseGate = new promiseGate_1.default();
}
initialize(resource, targetDirectoryPath) {
super.initialize(resource, targetDirectoryPath);
if (resource !== null) {
Object.assign(this, resource);
}
return this;
}
setup() {
return __awaiter(this, void 0, void 0, function* () {
return yield webapp_node_azure_1.default.internalSetup(__filename, this.targetDirectoryPath, data.data.WebAppNameLength);
});
}
hydrate(resourcesInEnvironment, deploymentType) {
const _super = name => super[name];
return __awaiter(this, void 0, void 0, function* () {
yield _super("hydrate").call(this, resourcesInEnvironment, deploymentType);
if (this.webAppDNSName === undefined) {
this.webAppDNSName = this.resourceGroup.resourceGroupName +
this.baseName;
}
if (this.webAppServicePlanName === undefined) {
this.webAppServicePlanName =
this.resourceGroup.resourceGroupName +
this.baseName + "webAppPlan";
}
return this;
});
}
deployResource(developmentDeploy = false) {
return __awaiter(this, void 0, void 0, function* () {
yield this.resourceGroup.getBaseDeployClassInstance();
const aiResource = CommonUtilities.findGlobalDefaultResourceByType(this.resourcesInEnvironment, ApplicationInsightsInfrastructure
.ApplicationInsightsInfrastructure);
const aiKeyID = (yield aiResource.getBaseDeployClassInstance()).getInstrumentationKey();
const storageResources = CommonUtilities.findInfraResourcesByInterface(this.resourcesInEnvironment, CommonUtilities.isIStorageResource);
const storagePromisesToWaitFor = [];
for (const storageResource of storageResources) {
storagePromisesToWaitFor
.push(storageResource.getBaseDeployClassInstance());
}
yield (this.deploymentType === Resource.DeployType.Production ?
this.deployToProduction(developmentDeploy, storagePromisesToWaitFor, aiKeyID) :
this.deployToDev(storagePromisesToWaitFor, aiKeyID));
this.promiseGate
.openGateSuccess(new BaseDeployWebappNodeAzureInfrastructure(this));
return this;
});
}
getBaseDeployClassInstance() {
return this.promiseGate.promise.then(function (baseClass) {
return baseClass;
});
}
deployToDev(storagePromisesToWaitFor, aiKeyId) {
return __awaiter(this, void 0, void 0, function* () {
const baseStorageResources = yield Promise.all(storagePromisesToWaitFor);
let environmentVariablesArray = [];
for (const baseStorageResource of baseStorageResources) {
environmentVariablesArray =
[...environmentVariablesArray,
...baseStorageResource.getEnvironmentVariables()];
}
environmentVariablesArray.push([ServiceEnvironment.aiEnvironmentVariableName, aiKeyId]);
const sleevePath = CommonUtilities.localScratchDirectory(this.targetDirectoryPath);
yield FS.ensureDir(sleevePath);
const variablePath = Path.join(CommonUtilities
.localScratchDirectory(this.targetDirectoryPath), ServiceEnvironment.environmentFileName);
FS.removeSync(variablePath);
for (const nameValuePair of environmentVariablesArray) {
FS.appendFileSync(variablePath, `${nameValuePair[0]} ${nameValuePair[1]}\n`);
}
});
}
deployToProduction(developmentDeploy, storagePromisesToWaitFor, aiKeyId) {
return __awaiter(this, void 0, void 0, function* () {
const resourceGroupName = this.resourceGroup.resourceGroupName;
const webPromise = CommonUtilities.runAzCommand(`az appservice plan create \
--name ${this.webAppServicePlanName} \
--resource-group ${resourceGroupName} --sku FREE`)
.then(() => {
return CommonUtilities.runAzCommand(`az webapp create \
--name ${this.webAppDNSName} \
--resource-group ${resourceGroupName} \
--plan ${this.webAppServicePlanName}`, CommonUtilities.azCommandOutputs.json);
});
const webAppCreateResult = yield webPromise;
const baseStorageResources = yield Promise.all(storagePromisesToWaitFor);
let environmentVariablesArray = [];
const secondStepPromises = [];
const webAppIPs = webAppCreateResult.outboundIpAddresses.split(",");
for (const baseStorageResource of baseStorageResources) {
webAppIPs.forEach((ipAddr) => {
secondStepPromises.push(baseStorageResource
.setFirewallRule(this.baseName, ipAddr));
});
environmentVariablesArray =
[...environmentVariablesArray,
...baseStorageResource.getEnvironmentVariables()];
}
let environmentalVariables = "";
for (const variablePair of environmentVariablesArray) {
environmentalVariables += `${variablePair[0]}=${variablePair[1]} `;
}
environmentalVariables +=
`${ServiceEnvironment.aiEnvironmentVariableName}=${aiKeyId}`;
if (environmentalVariables !== "") {
secondStepPromises.push(CommonUtilities.runAzCommand(`az webapp config appsettings set \
--name ${this.webAppDNSName} \
--resource-group ${this.resourceGroup.resourceGroupName} \
--settings ${environmentalVariables}`));
}
yield Promise.all(secondStepPromises);
yield CommonUtilities.runAzCommand(`az webapp deployment source config-local-git \
--name ${this.webAppDNSName} --resource-group ${resourceGroupName} \
--query url --output tsv`, CommonUtilities.azCommandOutputs.string);
yield this.deployToWebApp(developmentDeploy);
});
}
/**
* Handles copying the local web app code to Azure
* @developmentDeploy This is only used for development of sleeveforarm,
* it lets us know we need to deploy to the webapp a development version
* of sleeveforarm.
*/
deployToWebApp(developmentDeploy = false) {
return __awaiter(this, void 0, void 0, function* () {
const resourceGroupName = this.resourceGroup.resourceGroupName;
const profiles = yield CommonUtilities.runAzCommand(`az webapp deployment list-publishing-profiles --name ${this.webAppDNSName} \
--resource-group ${resourceGroupName}`);
const msDeployProfile = profiles.find((profile) => profile.publishMethod === "MSDeploy");
if (msDeployProfile === undefined) {
throw new Error("We didn't find the MSDeploy profile, huh?");
}
const username = msDeployProfile.userName;
const password = msDeployProfile.userPWD;
const gitURL =
// tslint:disable-next-line:max-line-length
`https://${username}:${password}@${this.webAppDNSName}.scm.azurewebsites.net/${this.webAppDNSName}.git`;
const gitCloneDepotParentPath = CommonUtilities.localScratchDirectory(this.targetDirectoryPath);
const gitCloneDepotPath = Path.join(gitCloneDepotParentPath, this.webAppDNSName);
yield FS.emptyDir(gitCloneDepotParentPath);
yield CommonUtilities.retryAfterFailure(() => __awaiter(this, void 0, void 0, function* () {
return yield CommonUtilities.exec(`git clone ${gitURL}`, gitCloneDepotParentPath);
}), 60);
const directoryContents = yield FS.readdir(gitCloneDepotPath);
// It's a git depo so it always has a hidden .git file, hence there
// will be at least one file
if (directoryContents.length > 1) {
// This command fails if there isn't at least one file (other than
// .git) in the directory, hence why we have the check above.
yield CommonUtilities.exec("git rm -f -r -q *", gitCloneDepotPath);
}
const nodeModulesPath = Path.join(this.targetDirectoryPath, "node_modules");
const sleevePath = Path.join(this.targetDirectoryPath, ".sleeve");
yield FS.copy(this.targetDirectoryPath, gitCloneDepotPath, {
filter: (src) => (src !== nodeModulesPath && src !== sleevePath)
});
if (developmentDeploy) {
yield this.developDeployToWebApp(gitCloneDepotPath);
}
yield CommonUtilities.exec("git add -A", gitCloneDepotPath);
const result = yield CommonUtilities.exec("git status --porcelain=v2", gitCloneDepotPath);
if (result.stdout !== "") {
yield CommonUtilities.exec("git commit -am \"Prep for release\"", gitCloneDepotPath);
yield CommonUtilities.exec("git push", gitCloneDepotPath);
}
});
}
/**
* Deploys a development version of SleeveForArm
*/
developDeployToWebApp(gitCloneDepotPath) {
return __awaiter(this, void 0, void 0, function* () {
// We want to clone node_modules in this case so we need to
// get rid of .gitignore
yield FS.remove(Path.join(gitCloneDepotPath, ".gitignore"));
const sleeveForArmClonePath = Path.join(gitCloneDepotPath, "sleeveforarm");
yield FS.ensureDir(sleeveForArmClonePath);
const depotPath = Path.join(__dirname, "..");
const disposableTestFilesPath = Path.join(depotPath, "disposableTestFiles");
const nodeModulesPath = Path.join(depotPath, "node_modules");
yield FS.copy(depotPath, sleeveForArmClonePath, {
filter: (src) => (src !== disposableTestFilesPath) &&
(src !== nodeModulesPath)
});
// Need to make the node module files just look like regular files
// Otherwise the WebApp Git Repo will treat sleeveforarm as a
// sub-module and not properly copy it over.
yield FS.remove(Path.join(sleeveForArmClonePath, ".git"));
// Otherwise we won't check in any of the .js or other files we normally
// ignore.
yield FS.remove(Path.join(sleeveForArmClonePath, ".gitignore"));
});
}
}
exports.WebappNodeAzureInfrastructure = WebappNodeAzureInfrastructure;
//# sourceMappingURL=webapp-node-azureInfrastructure.js.map