ARM automation done.

This commit is contained in:
Rodrigo Rodriguez (pragmatismo.io) 2018-10-25 18:13:51 -03:00
parent d030ed8278
commit b7f256b01f
7 changed files with 408 additions and 293 deletions

View file

@ -69,7 +69,7 @@ Notes:
1. Create/Edit the .env file and add the ADDITIONAL_DEPLOY_PATH key pointing to the .gbai local parent folder of .gbapp, .gbot, .gbtheme, .gbkb package directories. 1. Create/Edit the .env file and add the ADDITIONAL_DEPLOY_PATH key pointing to the .gbai local parent folder of .gbapp, .gbot, .gbtheme, .gbkb package directories.
2. Specify STORAGE_SYNC to TRUE so database sync is run when the server is run. 2. Specify STORAGE_SYNC to TRUE so database sync is run when the server is run.
3. In case of Microsoft SQL Server add the following keys: STORAGE_HOST, STORAGE_NAME, STORAGE_USERNAME, STORAGE_PASSWORD, STORAGE_DIALECT to `mssql`. 3. In case of Microsoft SQL Server add the following keys: STORAGE_SERVER, STORAGE_NAME, STORAGE_USERNAME, STORAGE_PASSWORD, STORAGE_DIALECT to `mssql`.
Note: Note:

View file

@ -42,12 +42,12 @@ import { SqlManagementClient } from "azure-arm-sql";
import { CognitiveServicesManagementClient } from "azure-arm-cognitiveservices"; import { CognitiveServicesManagementClient } from "azure-arm-cognitiveservices";
import { CognitiveServicesAccount } from "azure-arm-cognitiveservices/lib/models"; import { CognitiveServicesAccount } from "azure-arm-cognitiveservices/lib/models";
import { SearchManagementClient } from "azure-arm-search"; import { SearchManagementClient } from "azure-arm-search";
import { WebResource, ServiceClient } from "ms-rest-js"; import { WebResource, ServiceClient, HttpMethods } from "ms-rest-js";
import * as simplegit from "simple-git/promise"; import * as simplegit from "simple-git/promise";
import { AppServicePlan } from "azure-arm-website/lib/models"; import { AppServicePlan } from "azure-arm-website/lib/models";
import { GBConfigService } from "../../../deploy/core.gbapp/services/GBConfigService"; import { GBConfigService } from "../../../deploy/core.gbapp/services/GBConfigService";
const Spinner = require('cli-spinner').Spinner; const Spinner = require("cli-spinner").Spinner;
const scanf = require("scanf"); const scanf = require("scanf");
const msRestAzure = require("ms-rest-azure"); const msRestAzure = require("ms-rest-azure");
const git = simplegit(); const git = simplegit();
@ -56,6 +56,7 @@ const UrlJoin = require("url-join");
const PasswordGenerator = require("strict-password-generator").default; const PasswordGenerator = require("strict-password-generator").default;
const iconUrl = const iconUrl =
"https://github.com/pragmatismo-io/BotServer/blob/master/docs/images/generalbots-logo-squared.png"; "https://github.com/pragmatismo-io/BotServer/blob/master/docs/images/generalbots-logo-squared.png";
const publicIp = require("public-ip");
export class AzureDeployerService extends GBService { export class AzureDeployerService extends GBService {
instance: IGBInstance; instance: IGBInstance;
@ -70,9 +71,279 @@ export class AzureDeployerService extends GBService {
location: string; location: string;
public subscriptionId: string; public subscriptionId: string;
static apiVersion = "2017-12-01"; static apiVersion = "2017-12-01";
farmName: any;
public static async getSubscriptions(credentials) {
let subscriptionClient = new SubscriptionClient.default(credentials);
return subscriptionClient.subscriptions.list();
}
public async deployFarm(proxyAddress: string): Promise<IGBInstance> {
let culture = "en-us";
// Tries do get information from .env file otherwise asks in command-line.
let instance: IGBInstance = {};
instance = await this.ensureConfiguration(instance);
let spinner = new Spinner("%s");
spinner.start();
spinner.setSpinnerString("|/-\\");
let keys: any;
let name = instance.botId;
logger.info(`Deploying Deploy Group (It may take a few minutes)...`);
await this.createDeployGroup(name, instance.cloudLocation);
logger.info(`Deploying Bot Server...`);
let serverFarm = await this.createHostingPlan(
name,
`${name}-server-plan`,
instance.cloudLocation
);
await this.createServer(
serverFarm.id,
name,
`${name}-server`,
instance.cloudLocation
);
let administratorLogin = AzureDeployerService.getRndAdminAccount();
let administratorPassword = AzureDeployerService.getRndPassword();
logger.info(`Deploying Bot Storage...`);
let storageServer = `${name}-storage-server`;
let storageName = `${name}-storage`;
await this.createStorageServer(
name,
storageServer,
administratorLogin,
administratorPassword,
storageServer,
instance.cloudLocation
);
await this.createStorage(
name,
storageServer,
storageName,
instance.cloudLocation
);
instance.storageUsername = administratorLogin;
instance.storagePassword = administratorPassword;
instance.storageName = storageName;
instance.storageDialect = "mssql";
instance.storageServer = storageServer;
logger.info(`Deploying Search...`);
let searchName = `${name}-search`;
await this.createSearch(name, searchName, instance.cloudLocation);
let searchKeys = await this.searchClient.queryKeys.listBySearchService(
name,
searchName
);
instance.searchHost = `${searchName}.search.windows.net`;
instance.searchIndex = "azuresql-index";
instance.searchIndexer = "azuresql-indexer";
instance.searchKey = searchKeys[0].key;
logger.info(`Deploying Speech...`);
let speech = await this.createSpeech(
name,
`${name}-speech`,
instance.cloudLocation
);
keys = await this.cognitiveClient.accounts.listKeys(name, speech.name);
instance.speechKeyEndpoint = speech.endpoint;
instance.speechKey = keys.key1;
logger.info(`Deploying SpellChecker...`);
let spellChecker = await this.createSpellChecker(
name,
`${name}-spellchecker`,
instance.cloudLocation
);
keys = await this.cognitiveClient.accounts.listKeys(
name,
spellChecker.name
);
instance.spellCheckerKey = keys.key1;
instance.spellCheckerEndpoint = spellChecker.endpoint;
logger.info(`Deploying Text Analytics...`);
let textAnalytics = await this.createTextAnalytics(
name,
`${name}-textanalytics`,
instance.cloudLocation
);
keys = await this.cognitiveClient.accounts.listKeys(
name,
textAnalytics.name
);
instance.textAnalyticsEndpoint = textAnalytics.endpoint;
instance.textAnalyticsKey = keys.key1;
logger.info(`Deploying NLP...`);
let nlp = await this.createNLP(name, `${name}-nlp`, instance.cloudLocation);
keys = await this.cognitiveClient.accounts.listKeys(name, nlp.name);
let nlpAppId = await this.createLUISApp(
name,
name,
instance.cloudLocation,
culture,
instance.nlpAuthoringKey
);
let appId = msRestAzure.generateUuid();
instance.nlpEndpoint = nlp.endpoint;
instance.nlpKey = keys.key1;
instance.nlpAppId = nlpAppId;
logger.info(`Deploying Bot...`);
instance = await this.deployBootBot(
instance,
name,
`${proxyAddress}/api/messages/${name}`,
null,
null,
instance.cloudSubscriptionId,
appId
);
spinner.stop();
return instance;
}
public async openStorageFirewall(groupName, serverName) {
let username = GBConfigService.get("CLOUD_USERNAME");
let password = GBConfigService.get("CLOUD_PASSWORD");
let subscriptionId = GBConfigService.get("CLOUD_SUBSCRIPTIONID");
let credentials = await msRestAzure.loginWithUsernamePassword(
username,
password
);
let ip = await publicIp.v4();
let params = {
startIpAddress: ip,
endIpAddress: ip
};
let storageClient = new SqlManagementClient(credentials, subscriptionId);
await storageClient.firewallRules.createOrUpdate(
groupName,
serverName,
"gb",
params
);
}
private async ensureConfiguration(instance: IGBInstance) {
let username = GBConfigService.get("CLOUD_USERNAME");
let password = GBConfigService.get("CLOUD_PASSWORD");
let subscriptionId = GBConfigService.get("CLOUD_SUBSCRIPTIONID");
let location = GBConfigService.get("CLOUD_LOCATION");
let botId = GBConfigService.get("BOT_ID");
// No .env so asks for cloud credentials to start a new farm.
if (!username || !password || !subscriptionId || !location || !botId) {
process.stdout.write(
"A empty enviroment is detected. Please, enter details:"
);
}
let retriveUsername = () => {
if (!username) {
process.stdout.write("CLOUD_USERNAME:");
username = scanf("%s");
}
};
let retrivePassword = () => {
if (!password) {
process.stdout.write("CLOUD_PASSWORD:");
password = scanf("%s");
}
};
let retrieveBotId = () => {
if (!botId) {
process.stdout.write(
"Bot Id must only contain lowercase letters, digits or dashes, cannot start or end with or contain consecutive dashes and is limited to 60 characters.\n"
);
process.stdout.write("BOT_ID:");
botId = scanf("%s");
}
};
let authoringKey = GBConfigService.get("NLP_AUTHORING_KEY");
let retriveAuthoringKey = () => {
if (!authoringKey) {
process.stdout.write(
"Due to this opened issue: https://github.com/Microsoft/botbuilder-tools/issues/550\n"
);
process.stdout.write("Please enter your LUIS Authoring Key:");
authoringKey = scanf("%s");
}
};
while (!authoringKey) {
retriveAuthoringKey();
}
while (!botId) {
retrieveBotId();
}
while (!username) {
retriveUsername();
}
while (!password) {
retrivePassword();
}
// Connects to the cloud and retrives subscriptions.
let credentials = await msRestAzure.loginWithUsernamePassword(
username,
password
);
if (!subscriptionId) {
let map = {};
let index = 1;
let list = await AzureDeployerService.getSubscriptions(credentials);
list.forEach(element => {
console.log(
`${index}: ${element.displayName} (${element.subscriptionId})`
);
map[index++] = element;
});
let subscriptionIndex;
let retrieveSubscription = () => {
if (!subscriptionIndex) {
process.stdout.write("CLOUD_SUBSCRIPTIONID (type a number):");
subscriptionIndex = scanf("%d");
}
};
while (!subscriptionIndex) {
retrieveSubscription();
}
subscriptionId = map[subscriptionIndex].subscriptionId;
}
let retriveLocation = () => {
if (!location) {
process.stdout.write("CLOUD_LOCATION:");
location = scanf("%s");
}
};
while (!location) {
retriveLocation();
}
// Prepares the first instance on bot farm.
instance.botId = botId;
instance.cloudUsername = username;
instance.cloudPassword = password;
instance.cloudSubscriptionId = subscriptionId;
instance.cloudLocation = location;
instance.nlpAuthoringKey = authoringKey;
instance.adminPass = AzureDeployerService.getRndPassword();
constructor(credentials, subscriptionId, location) {
super();
this.resourceClient = new ResourceManagementClient.default( this.resourceClient = new ResourceManagementClient.default(
credentials, credentials,
subscriptionId subscriptionId
@ -86,134 +357,9 @@ export class AzureDeployerService extends GBService {
credentials, credentials,
subscriptionId subscriptionId
); );
this.searchClient = new SearchManagementClient(credentials, subscriptionId); this.searchClient = new SearchManagementClient(credentials, subscriptionId);
this.accessToken = credentials.tokenCache._entries[0].accessToken; this.accessToken = credentials.tokenCache._entries[0].accessToken;
this.location = location;
this.subscriptionId = subscriptionId;
}
public static async getSubscriptions(credentials) {
let subscriptionClient = new SubscriptionClient.default(credentials);
return subscriptionClient.subscriptions.list();
}
public async deployFarm(
name: string,
location: string,
proxyAddress: string
): Promise<IGBInstance> {
let instance: any = {};
let culture = "en-us";
let spinner = new Spinner('%s');
spinner.start();
spinner.setSpinnerString("⠁⠁⠉⠙⠚⠒⠂⠂⠒⠲⠴⠤⠄⠄⠤⠠⠠⠤⠦⠖⠒⠐⠐⠒⠓⠋⠉⠈⠈");
let keys: any;
logger.info(`Deploying Deploy Group...`);
await this.createDeployGroup(name, location);
logger.info(`Deploying Bot Server...`);
let serverFarm = await this.createHostingPlan(
name,
`${name}-server-plan`,
location
);
await this.createServer(serverFarm.id, name, `${name}-server`, location);
let administratorLogin = AzureDeployerService.getRndAdminAccount();
let administratorPassword = AzureDeployerService.getRndPassword();
logger.info(`Deploying Bot Storage...`);
let storageServerName = `${name}-storage-server`;
await this.createStorageServer(
name,
storageServerName,
administratorLogin,
administratorPassword,
storageServerName,
location
);
await this.createStorage(
name,
storageServerName,
`${name}-storage`,
location
);
instance.storageUsername = administratorLogin;
instance.storagePassword = administratorPassword;
instance.storageName = storageServerName;
instance.storageDialect = "mssql";
instance.storageServerName = storageServerName;
logger.info(`Deploying Search...`);
let searchName = `${name}-search`;
await this.createSearch(name, searchName, location);
let searchKeys = await this.searchClient.queryKeys.listBySearchService(
name,
searchName
);
instance.searchHost = `${searchName}.search.windows.net`;
instance.searchIndex = "azuresql-index";
instance.searchIndexer = "azuresql-indexer";
instance.searchKey = searchKeys[0].key;
logger.info(`Deploying NLP...`);
let nlp = await this.createNLP(name, `${name}-nlp`, location);
keys = await this.cognitiveClient.accounts.listKeys(name, nlp.name);
let nlpAppId = await this.createLUISApp(name, name, location, culture);
instance.nlpEndpoint = nlp.endpoint;
instance.nlpKey = keys.key1;
instance.nlpAppId = nlpAppId;
logger.info(`Deploying Speech...`);
let speech = await this.createSpeech(name, `${name}-speech`, location);
keys = await this.cognitiveClient.accounts.listKeys(name, speech.name);
instance.speechKeyEndpoint = speech.endpoint;
instance.speechKey = keys.key1;
logger.info(`Deploying SpellChecker...`);
let spellChecker = await this.createSpellChecker(
name,
`${name}-spellchecker`,
location
);
keys = await this.cognitiveClient.accounts.listKeys(
name,
spellChecker.name
);
instance.spellCheckerKey = keys.key1;
instance.spellCheckerEndpoint = spellChecker.endpoint;
logger.info(`Deploying Text Analytics...`);
let textAnalytics = await this.createTextAnalytics(
name,
`${name}-textanalytics`,
location
);
keys = await this.cognitiveClient.accounts.listKeys(
name,
textAnalytics.name
);
instance.textAnalyticsEndpoint = textAnalytics.endpoint;
instance.textAnalyticsKey = keys.key1;
let appId = msRestAzure.generateUuid();
logger.info(`Deploying Bot...`);
instance = await this.deployBootBot(
instance,
name,
`${proxyAddress}/api/messages/${name}`,
nlpAppId,
keys.key1,
this.subscriptionId,
appId
);
spinner.stop();
return instance; return instance;
} }
@ -227,10 +373,7 @@ export class AzureDeployerService extends GBService {
subscriptionId, subscriptionId,
appId appId
) { ) {
logger.info(`Deploying Bot...`);
let botId = name + AzureDeployerService.getRndBotId(); let botId = name + AzureDeployerService.getRndBotId();
[ [
instance.marketplacePassword, instance.marketplacePassword,
instance.webchatKey instance.webchatKey
@ -378,44 +521,59 @@ export class AzureDeployerService extends GBService {
name: string, name: string,
description: string, description: string,
location: string, location: string,
culture: string culture: string,
authoringKey: string
) { ) {
let parameters = { let parameters = {
name: name, name: name,
description: description, description: description,
culture: culture culture: culture
}; };
let requestUrl = `https://${location}.api.cognitive.microsoft.com/luis/api/v2.0/apps/`;
let req = new WebResource();
req.method = "POST"; let body = JSON.stringify(parameters);
req.url = requestUrl; let apps = await this.makeNlpRequest(
location,
authoringKey,
null,
"GET",
"apps"
);
let app = (apps.bodyAsJson as any).filter(x => x.name == name)[0];
let id: string;
if (!app) {
let res = await this.makeNlpRequest(
location,
authoringKey,
body,
"POST",
"apps"
);
id = res.bodyAsText;
} else {
id = app.id;
}
return id;
}
private async makeNlpRequest(
location: string,
authoringKey: string,
body: string,
method: HttpMethods,
resource: string
) {
let req = new WebResource();
req.method = method;
req.url = `https://${location}.api.cognitive.microsoft.com/luis/api/v2.0/${resource}`;
req.headers = {}; req.headers = {};
req.headers["Content-Type"] = "application/json"; req.headers["Content-Type"] = "application/json";
req.headers["accept-language"] = "*"; req.headers["accept-language"] = "*";
let authoringKey = GBConfigService.get("NLP_AUTHORING_KEY");
let retriveAuthoringKey = () => {
if (!authoringKey) {
process.stdout.write(
"Due to this opened issue: https://github.com/Microsoft/botbuilder-tools/issues/550\n"
);
process.stdout.write("Please enter your LUIS Authoring Key:");
authoringKey = scanf("%s");
}
};
while (!authoringKey) {
retriveAuthoringKey();
}
req.headers["Ocp-Apim-Subscription-Key"] = authoringKey; req.headers["Ocp-Apim-Subscription-Key"] = authoringKey;
req.body = JSON.stringify(parameters); req.body = body;
let httpClient = new ServiceClient(); let httpClient = new ServiceClient();
let res = await httpClient.sendRequest(req); let res = await httpClient.sendRequest(req);
return res;
return res.bodyAsJson;
} }
private async createSearch(group, name, location) { private async createSearch(group, name, location) {
@ -598,97 +756,13 @@ export class AzureDeployerService extends GBService {
lowerCaseAlpha: true, lowerCaseAlpha: true,
number: true, number: true,
specialCharacter: true, specialCharacter: true,
minimumLength: 8, minimumLength: 12,
maximumLength: 14 maximumLength: 14
}; };
let password = passwordGenerator.generatePassword(options); let password = passwordGenerator.generatePassword(options);
return password; return password;
} }
static async ensureDeployer() {
// Tries do get information from .env file otherwise asks in command-line.
let username = GBConfigService.get("CLOUD_USERNAME");
let password = GBConfigService.get("CLOUD_PASSWORD");
let subscriptionId = GBConfigService.get("CLOUD_SUBSCRIPTIONID");
let location = GBConfigService.get("CLOUD_LOCATION");
// No .env so asks for cloud credentials to start a new farm.
if (!username || !password || !subscriptionId || !location) {
process.stdout.write(
"FIRST RUN: A empty enviroment is detected. Please, enter credentials to create a new General Bots Farm."
);
}
let retriveUsername = () => {
if (!username) {
process.stdout.write("CLOUD_USERNAME:");
username = scanf("%s");
}
};
let retrivePassword = () => {
if (!password) {
process.stdout.write("CLOUD_PASSWORD:");
password = scanf("%s");
}
};
while (!username) {
retriveUsername();
}
while (!password) {
retrivePassword();
}
// Connects to the cloud and retrives subscriptions.
let credentials = await msRestAzure.loginWithUsernamePassword(
username,
password
);
if (!subscriptionId) {
let map = {};
let index = 1;
let list = await AzureDeployerService.getSubscriptions(credentials);
list.forEach(element => {
console.log(
`${index}: ${element.displayName} (${element.subscriptionId})`
);
map[index++] = element;
});
let subscriptionIndex;
let retrieveSubscription = () => {
if (!subscriptionIndex) {
process.stdout.write("CLOUD_SUBSCRIPTIONID (type a number):");
subscriptionIndex = scanf("%d");
}
};
while (!subscriptionIndex) {
retrieveSubscription();
}
subscriptionId = map[subscriptionIndex].subscriptionId;
}
let retriveLocation = () => {
if (!location) {
process.stdout.write("CLOUD_LOCATION:");
location = scanf("%s");
}
};
while (!location) {
retriveLocation();
}
return new AzureDeployerService(credentials, subscriptionId, location);
}
static getKBSearchSchema(indexName) { static getKBSearchSchema(indexName) {
return { return {
name: indexName, name: indexName,

View file

@ -121,9 +121,15 @@ export class GuaribasInstance extends Model<GuaribasInstance>
@Column @Column
cloudSubscriptionId: string; cloudSubscriptionId: string;
@Column
cloudUsername: string;
@Column @Column
cloudRegion: string; cloudPassword: string;
@Column
cloudLocation: string;
@Column @Column
whatsappBotKey: string; whatsappBotKey: string;
@ -180,6 +186,12 @@ export class GuaribasInstance extends Model<GuaribasInstance>
@Column({ type: DataType.STRING(512) }) @Column({ type: DataType.STRING(512) })
nlpEndpoint: string; nlpEndpoint: string;
@Column
nlpAuthoringKey: string;
@Column
deploymentPaths: string;
@Column @Column
searchHost: string; searchHost: string;
@ -210,6 +222,9 @@ export class GuaribasInstance extends Model<GuaribasInstance>
@Column @Column
storagePath: string; storagePath: string;
@Column
adminPass: string;
/* Settings section of bot.json */ /* Settings section of bot.json */
@Column(DataType.FLOAT) @Column(DataType.FLOAT)

View file

@ -50,12 +50,6 @@ export class GBConfigService {
} }
} }
public writeEntry(name, value) {
if (fs.exists) {
fs.appendFileSync(".env", `${name}=${value}`);
}
}
static get(key: string): string | undefined { static get(key: string): string | undefined {
let value = GBConfigService.tryGet(key); let value = GBConfigService.tryGet(key);
@ -77,7 +71,7 @@ export class GBConfigService {
value = undefined; value = undefined;
break; break;
case "STORAGE_DIALECT": case "STORAGE_DIALECT":
value = "sqlite"; value = undefined;
break; break;
case "STORAGE_STORAGE": case "STORAGE_STORAGE":
value = "./guaribas.sqlite"; value = "./guaribas.sqlite";

View file

@ -34,6 +34,7 @@
const logger = require("../../../src/logger"); const logger = require("../../../src/logger");
import { Sequelize } from "sequelize-typescript"; import { Sequelize } from "sequelize-typescript";
import * as fs from "fs";
import { GBConfigService } from "./GBConfigService"; import { GBConfigService } from "./GBConfigService";
import { IGBInstance, IGBCoreService } from "botlib"; import { IGBInstance, IGBCoreService } from "botlib";
import { GuaribasInstance } from "../models/GBModel"; import { GuaribasInstance } from "../models/GBModel";
@ -49,14 +50,6 @@ export class GBCoreService implements IGBCoreService {
return GBConfigService.tryGet("STORAGE_DIALECT"); return GBConfigService.tryGet("STORAGE_DIALECT");
} }
async ensureCloud(cloudDeployer) {
let instance = new GuaribasInstance();
await cloudDeployer.deploy(instance, "westus");
instance.save();
let content = `STORAGE_HOST = ${instance.storageServer}\n
STORAGE_NAME, STORAGE_USERNAME, STORAGE_PASSWORD, STORAGE_DIALECT`;
}
/** /**
* Data access layer instance. * Data access layer instance.
*/ */
@ -91,7 +84,6 @@ export class GBCoreService implements IGBCoreService {
* Constructor retrieves default values. * Constructor retrieves default values.
*/ */
constructor() { constructor() {
this.dialect = GBConfigService.get("STORAGE_DIALECT");
this.adminService = new GBAdminService(this); this.adminService = new GBAdminService(this);
} }
@ -101,6 +93,8 @@ export class GBCoreService implements IGBCoreService {
async initDatabase() { async initDatabase() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
try { try {
this.dialect = GBConfigService.get("STORAGE_DIALECT");
let host: string | undefined; let host: string | undefined;
let database: string | undefined; let database: string | undefined;
let username: string | undefined; let username: string | undefined;
@ -108,12 +102,14 @@ export class GBCoreService implements IGBCoreService {
let storage: string | undefined; let storage: string | undefined;
if (this.dialect === "mssql") { if (this.dialect === "mssql") {
host = GBConfigService.get("STORAGE_HOST"); host = GBConfigService.get("STORAGE_SERVER");
database = GBConfigService.get("STORAGE_NAME"); database = GBConfigService.get("STORAGE_NAME");
username = GBConfigService.get("STORAGE_USERNAME"); username = GBConfigService.get("STORAGE_USERNAME");
password = GBConfigService.get("STORAGE_PASSWORD"); password = GBConfigService.get("STORAGE_PASSWORD");
} else if (this.dialect === "sqlite") { } else if (this.dialect === "sqlite") {
storage = GBConfigService.get("STORAGE_STORAGE"); storage = GBConfigService.get("STORAGE_STORAGE");
} else {
reject(`Unknown dialect: ${this.dialect}.`);
} }
let logging = let logging =
@ -302,11 +298,29 @@ export class GBCoreService implements IGBCoreService {
return GuaribasInstance.findOne(options); return GuaribasInstance.findOne(options);
} }
public async writeEnv(instance: IGBInstance) {
let env =
`ADMIN_PASS=${instance.adminPass}\n` +
`ADDITIONAL_DEPLOY_PATH=\n` +
`STORAGE_DIALECT=${instance.storageDialect}\n` +
`STORAGE_SERVER=${instance.storageServer}.database.windows.net\n` +
`STORAGE_NAME=${instance.storageName}\n` +
`STORAGE_USERNAME=${instance.storageUsername}\n` +
`STORAGE_PASSWORD=${instance.storagePassword}\n`+
`CLOUD_USERNAME=${instance.cloudUsername}\n` +
`CLOUD_PASSWORD=${instance.cloudPassword}\n` +
`CLOUD_SUBSCRIPTIONID=${instance.cloudSubscriptionId}\n` +
`CLOUD_LOCATION=${instance.cloudLocation}\n` +
`CLOUD_GROUP=${instance.botId}\n` +
`NLP_AUTHORING_KEY=${instance.nlpAuthoringKey}`;
fs.writeFileSync(".env", env);
}
public async ensureProxy(): Promise<string> { public async ensureProxy(): Promise<string> {
let proxyAddress: string; let proxyAddress: string;
const ngrok = require('ngrok'); const ngrok = require("ngrok");
return await ngrok.connect(); return await ngrok.connect();
// let expiresOn = new Date( // let expiresOn = new Date(
// await this.adminService.getValue(0, "proxyExpiresOn") // await this.adminService.getValue(0, "proxyExpiresOn")

View file

@ -62,6 +62,7 @@
"express-promise-router": "3.0.3", "express-promise-router": "3.0.3",
"fs-extra": "7.0.0", "fs-extra": "7.0.0",
"fs-walk": "0.0.2", "fs-walk": "0.0.2",
"ip": "^1.1.5",
"localize": "0.4.7", "localize": "0.4.7",
"marked": "0.5.1", "marked": "0.5.1",
"mocha": "5.2.0", "mocha": "5.2.0",
@ -72,6 +73,7 @@
"ngrok": "^3.1.0", "ngrok": "^3.1.0",
"pragmatismo-io-framework": "1.0.17", "pragmatismo-io-framework": "1.0.17",
"process-exists": "^3.1.0", "process-exists": "^3.1.0",
"public-ip": "^2.4.0",
"reflect-metadata": "0.1.12", "reflect-metadata": "0.1.12",
"request-promise-native": "1.0.5", "request-promise-native": "1.0.5",
"scanf": "^1.0.2", "scanf": "^1.0.2",
@ -83,6 +85,7 @@
"swagger-client": "3.8.21", "swagger-client": "3.8.21",
"tedious": "2.6.4", "tedious": "2.6.4",
"ts-node": "7.0.1", "ts-node": "7.0.1",
"tslint": "^5.11.0",
"typedoc": "0.13.0", "typedoc": "0.13.0",
"typescript": "3.1.3", "typescript": "3.1.3",
"url-join": "4.0.0", "url-join": "4.0.0",

View file

@ -53,7 +53,7 @@ import { GBCustomerSatisfactionPackage } from "../deploy/customer-satisfaction.g
import { GBAdminService } from "../deploy/admin.gbapp/services/GBAdminService"; import { GBAdminService } from "../deploy/admin.gbapp/services/GBAdminService";
import { GuaribasInstance } from "../deploy/core.gbapp/models/GBModel"; import { GuaribasInstance } from "../deploy/core.gbapp/models/GBModel";
import { AzureDeployerService } from "../deploy/azuredeployer.gbapp/services/AzureDeployerService"; import { AzureDeployerService } from "../deploy/azuredeployer.gbapp/services/AzureDeployerService";
import { IGBPackage } from "botlib"; import { IGBPackage, IGBInstance } from "botlib";
let appPackages = new Array<IGBPackage>(); let appPackages = new Array<IGBPackage>();
@ -61,13 +61,10 @@ let appPackages = new Array<IGBPackage>();
* General Bots open-core entry point. * General Bots open-core entry point.
*/ */
export class GBServer { export class GBServer {
/** /**
* Program entry-point. * Program entry-point.
*/ */
static MASTERBOT_PREFIX = "generalbots-masterbot"
static run() { static run() {
// Creates a basic HTTP server that will serve several URL, one for each // Creates a basic HTTP server that will serve several URL, one for each
// bot instance. This allows the same server to attend multiple Bot on // bot instance. This allows the same server to attend multiple Bot on
@ -85,6 +82,7 @@ export class GBServer {
}) })
); );
let bootInstance: IGBInstance;
server.listen(port, () => { server.listen(port, () => {
(async () => { (async () => {
try { try {
@ -94,21 +92,29 @@ export class GBServer {
GBConfigService.init(); GBConfigService.init();
let core = new GBCoreService(); let core = new GBCoreService();
// Ensures cloud / on-premises infrastructure is setup. // Ensures cloud / on-premises infrastructure is setup.
logger.info(`Establishing a development local proxy...`); logger.info(`Establishing a development local proxy...`);
let proxyAddress = await core.ensureProxy(); let proxyAddress = await core.ensureProxy();
logger.info(`Ensuring services availability...`);
let cloudDeployer = await AzureDeployerService.ensureDeployer(); try {
let instance = await cloudDeployer.deployFarm('gbot', 'westus', proxyAddress); await core.initDatabase();
} catch (error) {
logger.info(`Deploying cognitive infrastructure...`);
let azureDeployer = new AzureDeployerService();
bootInstance = await azureDeployer.deployFarm(proxyAddress);
core.writeEnv(bootInstance);
logger.info(`File .env written, starting...`);
GBConfigService.init();
await core.initDatabase();
}
// TODO: Get .gb* templates from GitHub and download do additional deploy folder. // TODO: Get .gb* templates from GitHub and download do additional deploy folder.
await core.initDatabase();
// Check admin password. // Check admin password.
let conversationalService = new GBConversationalService(core); let conversationalService = new GBConversationalService(core);
let adminService = new GBAdminService(core); let adminService = new GBAdminService(core);
let password = GBConfigService.get("ADMIN_PASS"); let password = GBConfigService.get("ADMIN_PASS");
@ -138,28 +144,37 @@ export class GBServer {
// Loads all bot instances from object storage, if it's formatted. // Loads all bot instances from object storage, if it's formatted.
logger.info(`All instances are being now loaded...`); logger.info(`Loading instances from storage...`);
let instances: GuaribasInstance[]; let instances: GuaribasInstance[];
try { try {
instances = await core.loadInstances(); instances = await core.loadInstances();
} catch (error) { } catch (error) {
// Check if storage is empty and needs formatting. if (error.parent.code === "ELOGIN") {
let isInvalidObject = let azureDeployer = new AzureDeployerService();
error.parent.number == 208 || error.parent.errno == 1; // MSSQL or SQLITE. let group = GBConfigService.get("CLOUD_GROUP")
let serverName = GBConfigService.get("STORAGE_SERVER").split(".database.windows.net")[0]
await azureDeployer.openStorageFirewall(group,serverName )
if (isInvalidObject) {
if (GBConfigService.get("STORAGE_SYNC") != "true") {
throw `Operating storage is out of sync or there is a storage connection error. Try setting STORAGE_SYNC to true in .env file. Error: ${
error.message
}.`;
} else {
logger.info(
`Storage is empty. After collecting storage structure from all .gbapps it will get synced.`
);
}
} else { } else {
throw `Cannot connect to operating storage: ${error.message}.`; // Check if storage is empty and needs formatting.
let isInvalidObject =
error.parent.number == 208 || error.parent.errno == 1; // MSSQL or SQLITE.
if (isInvalidObject) {
if (GBConfigService.get("STORAGE_SYNC") != "true") {
throw `Operating storage is out of sync or there is a storage connection error. Try setting STORAGE_SYNC to true in .env file. Error: ${
error.message
}.`;
} else {
logger.info(
`Storage is empty. After collecting storage structure from all .gbapps it will get synced.`
);
}
} else {
throw `Cannot connect to operating storage: ${error.message}.`;
}
} }
} }
@ -172,11 +187,11 @@ export class GBServer {
// If instances is undefined here it's because storage has been formatted. // If instances is undefined here it's because storage has been formatted.
// Load all instances from .gbot found on deploy package directory. // Load all instances from .gbot found on deploy package directory.
if (!instances) { if (!instances) {
let saveInstance = new GuaribasInstance(bootInstance);
saveInstance.save();
instances = await core.loadInstances(); instances = await core.loadInstances();
} }
await core.ensureCloud(cloudDeployer);
// Setup server dynamic (per bot instance) resources and listeners. // Setup server dynamic (per bot instance) resources and listeners.
logger.info(`Building instances.`); logger.info(`Building instances.`);