<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Partner Catalyst Team</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/partnercatalystteam/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/partnercatalystteam/feed.xml</link>
    <description>Our team causes or accelerates technology innovation in collaboration with our partners.  Our work takes us across industries, continents, and technologies.  In this blog, we use stories and code to share what we&amp;#039;re learning.</description>
    <lastBuildDate>Wed, 17 Apr 2019 04:06:51 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Y Combinator Collaboration: Deploying SailsJS to Azure Web Apps</title>
      <link>https://docs.microsoft.com/archive/blogs/partnercatalystteam/y-combinator-collaboration-deploying-sailsjs-to-azure-web-apps</link>
      <pubDate>Thu, 16 Jul 2015 07:38:00 GMT</pubDate>
      <dc:creator><![CDATA[sarasp@outlook.com]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/partnercatalystteam/2015/07/16/y-combinator-collaboration-deploying-sailsjs-to-azure-web-apps/</guid>
      <description><![CDATA[Y Combinator Collaboration: Deploying SailsJS to Azure Web Apps
by Felix Rieseberg, Partner...]]></description>
      <content:encoded><![CDATA[<h1>Y Combinator Collaboration: Deploying SailsJS to Azure Web Apps</h1>
<p class="MsoNormal">by Felix Rieseberg, Partner Catalyst Team</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">This case study describes the first open-source collaboration between TED and Y Combinator, enabling applications developed with SailsJS to be deployed directly to Azure.</p>
<p class="MsoNormal">&nbsp;</p>
<h1>Customer Problem</h1>
<p class="MsoNormal">SailsJS is an open-source real-time MVC framework for Node.js. It used widely by Node.js developers to develop enterprise-grade server solutions. In the Node world, SailsJS is to Node.js what Ruby on Rails is to Ruby. Its ensemble of small modules work together to provide simplicity, maintainability, and structural conventions to Node.js apps.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/1256.image001.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/1256.image001.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/1256.image001.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/1256.image001.png" alt="" border="0" /></a></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">The company behind SailsJS, Treeline.io, was a part of Y Combinator&rsquo;s Summer 2015 class.&nbsp; During the first meetings between Treeline.io and TED, it became obvious that there&rsquo;s great interest in SailsJS and equally great interest in Azure by companies using SailsJS, yet a reliable deployment path was not defined. The core of the issue was simple: How can Microsoft and Treeline.io best enable customers to deploy SailsJS apps as Azure Web Apps?</p>
<h1>&nbsp;</h1>
<h1>Overview of the Solution</h1>
<p class="MsoNormal">We began our investigation into a solution by looking at other cloud providers &ndash; many offered tutorials, but most of them seemed cumbersome. SailsJS already comes with a command line interface and we decided that the best user experience would explain itself: If the command <span style="font-size: 10.0pt; line-height: 107%; font-family: 'Courier New';">[sails run]</span>starts SailsJS, wouldn&rsquo;t the best deployment experience be a single command like <span style="font-size: 10.0pt; line-height: 107%; font-family: 'Courier New';">[sails deploy azure]?</span></p>
<h1 style="text-align: center;" align="center"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/5635.image002.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/5635.image002.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/5635.image002.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/5635.image002.png" alt="" border="0" /></a></h1>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">The solution provides exactly that: from within the SailsJS CLI, users can deploy any Sails app within a few seconds. The solution consists of three Node Modules &ndash; a Node Machine implementing Azure APIs, an npm module extending the SailsJS CLI with a<span style="font-family: terminal,monaco;"> [sails deploy]</span> command, and an npm module enabling [sails deploy] to directly deploy to Azure.</p>
<h1>&nbsp;</h1>
<h1>Implementation&nbsp;</h1>
<p class="MsoNormal">SailsJS makes heavy use of Node Machines, an open standard for JavaScript functions. They are essentially meta-data wrappers for APIs, allowing other applications to consume the packages in a standardized way. We started by building&nbsp;<a href="https://github.com/mikermcneil/machinepack-azure">a Node Machine for Azure</a>, which is capable of setting up the local Azure account, creating and updating websites and uploading files. In total, the machine pack implements 13 APIs:</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoListParagraphCxSpFirst" style="margin-left: .25in; text-indent: -.25in;"><span class="MsoHyperlink"><span style="font-family: Symbol; text-decoration: none;">&middot;<span style="font: 7.0pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><a href="https://github.com/mikermcneil/machinepack-azure/blob/master/machines/check-active-subscription.js">Checking for an active Azure subscription</a></p>
<p class="MsoListParagraphCxSpMiddle" style="margin-left: .25in; text-indent: -.25in;"><span class="MsoHyperlink"><span style="font-family: Symbol; text-decoration: none;">&middot;<span style="font: 7.0pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><a href="https://github.com/mikermcneil/machinepack-azure/blob/master/machines/list-azure-accounts.js">Listing azure subscriptions</a></p>
<p class="MsoListParagraphCxSpMiddle" style="margin-left: .25in; text-indent: -.25in;"><span class="MsoHyperlink"><span style="font-family: Symbol; text-decoration: none;">&middot;<span style="font: 7.0pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><a href="https://github.com/mikermcneil/machinepack-azure/blob/master/machines/register-azure-account.js">Registering an azure subscription</a></p>
<p class="MsoListParagraphCxSpMiddle" style="margin-left: .25in; text-indent: -.25in;"><span class="MsoHyperlink"><span style="font-family: Symbol; text-decoration: none;">&middot;<span style="font: 7.0pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><a href="https://github.com/mikermcneil/machinepack-azure/blob/master/machines/set-azure-account.js">Setting a new active azure subscription</a></p>
<p class="MsoListParagraphCxSpMiddle" style="margin-left: .25in; text-indent: -.25in;"><span class="MsoHyperlink"><span style="font-family: Symbol; text-decoration: none;">&middot;<span style="font: 7.0pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><a href="https://github.com/mikermcneil/machinepack-azure/blob/master/machines/exists-website.js">Checking if a website already exists for a given account</a></p>
<p class="MsoListParagraphCxSpMiddle" style="margin-left: .25in; text-indent: -.25in;"><span class="MsoHyperlink"><span style="font-family: Symbol; text-decoration: none;">&middot;<span style="font: 7.0pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><a href="https://github.com/mikermcneil/machinepack-azure/blob/master/machines/create-website.js">Creating a new website</a></p>
<p class="MsoListParagraphCxSpMiddle" style="margin-left: .25in; text-indent: -.25in;"><span class="MsoHyperlink"><span style="font-family: Symbol; text-decoration: none;">&middot;<span style="font: 7.0pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><a href="https://github.com/mikermcneil/machinepack-azure/blob/master/machines/set-deploy-credentials.js">Setting deployment credentials for a website</a></p>
<p class="MsoListParagraphCxSpMiddle" style="margin-left: .25in; text-indent: -.25in;"><span class="MsoHyperlink"><span style="font-family: Symbol; text-decoration: none;">&middot;<span style="font: 7.0pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><a href="https://github.com/mikermcneil/machinepack-azure/blob/master/machines/upload-file.js">Uploading files to a website</a></p>
<p class="MsoListParagraphCxSpMiddle" style="margin-left: .25in; text-indent: -.25in;"><span class="MsoHyperlink"><span style="font-family: Symbol; text-decoration: none;">&middot;<span style="font: 7.0pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><a href="https://github.com/mikermcneil/machinepack-azure/blob/master/machines/upload-webjob.js">Uploading webjobs to a website</a></p>
<p class="MsoListParagraphCxSpMiddle" style="margin-left: .25in; text-indent: -.25in;"><span class="MsoHyperlink"><span style="font-family: Symbol; text-decoration: none;">&middot;<span style="font: 7.0pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><a href="https://github.com/mikermcneil/machinepack-azure/blob/master/machines/trigger-webjob.js">Triggering webjobs</a></p>
<p class="MsoListParagraphCxSpMiddle" style="margin-left: .25in; text-indent: -.25in;"><span class="MsoHyperlink"><span style="font-family: Symbol; text-decoration: none;">&middot;<span style="font: 7.0pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><a href="https://github.com/mikermcneil/machinepack-azure/blob/master/machines/info-webjob.js">Fetching meta data information for a deployed webjob</a></p>
<p class="MsoListParagraphCxSpMiddle" style="margin-left: .25in; text-indent: -.25in;"><span class="MsoHyperlink"><span style="font-family: Symbol; text-decoration: none;">&middot;<span style="font: 7.0pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><a href="https://github.com/mikermcneil/machinepack-azure/blob/master/machines/log-webjob.js">Fetching stdout and stderr for a webjob</a></p>
<p class="MsoListParagraphCxSpMiddle" style="margin-left: .25in; text-indent: -.25in;"><span class="MsoHyperlink"><span style="font-family: Symbol; text-decoration: none;">&middot;<span style="font: 7.0pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span><a href="https://github.com/mikermcneil/machinepack-azure/blob/master/machines/list-images.js">Listing available VM images for an account</a></p>
<p class="MsoListParagraphCxSpLast" style="margin-left: .25in;">&nbsp;</p>
<p class="MsoNormal"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/8623.image003.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/8623.image003.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/8623.image003.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/8623.image003.png" alt="" border="0" /></a></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">The machine pack is available on <a href="https://github.com/mikermcneil/machinepack-azure">GitHub</a>, <a href="http://node-machine.org/machinepack-azure">node-machine.org</a> and <a href="https://www.npmjs.com/package/machinepack-azure">NPM</a> independently of this solution, as it might be useful in other scenarios.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">For the purpose of this solution, we consumed the Node Machine to implement a deployment strategy for SailsJS. Two Node modules had to be created to enable the scenario: The SailsJS CLI needed to be extended with a <span style="font-size: 10.0pt; line-height: 107%; font-family: Consolas;">[sails deploy</span><span style="font-size: 10.0pt; line-height: 107%; font-family: 'Courier New';">]</span> command. This proved to be surprisingly easy and only took<a href="https://github.com/mikermcneil/sails-deploy/blob/master/index.js"> about 50 lines of code</a>. Then, the <span style="font-size: 10.0pt; line-height: 107%; font-family: Consolas;">[sails deploy]</span>command needed a descriptive strategy for deploying apps to Azure, which we encapsulated in the Node Module <a href="https://github.com/mikermcneil/sails-deploy-azure">sails-deploy-azure</a>.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">The module sails-deploy-azure in detail implements the following flow.&nbsp; (<span class="MsoSubtleEmphasis">Note: The code is too long to include here, but each section is followed by links to relevant code locations.)</span></p>
<p class="MsoListParagraphCxSpFirst" style="margin-left: .25in; text-indent: -.25in;">1)<span style="font: 7.0pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>The command&nbsp;<span style="font-size: 10.0pt; line-height: 107%; font-family: Consolas;">sails deploy</span>&nbsp;accepts a website name and deployment username/password as optional parameters. If the command is run without parameters, a new website has to be created. We start by checking the local environment for an Azure account. Users who have the Node-powered Azure-CLI installed won't have to do anything, while users without it are asked to authenticate the local environment (they won't have to install the CLI though, since we wrap and abstract the CLI as a dependency).&nbsp; If the command is run with parameters, we don&rsquo;t have to create the Azure Website, and skip to step #3. [Code: <a href="https://github.com/mikermcneil/sails-deploy-azure/blob/master/index.js#L22-L51">L22-L51</a> in sails-deploy-azure/index.js, consuming machinepack-azure</p>
<p class="MsoListParagraphCxSpMiddle" style="margin-left: .25in; text-indent: -.25in;">2)<span style="font: 7.0pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Once we're authenticated, we check if a website for the current SailsJS app already exists in the current account by comparing namespaces. If so, we could just deploy to it - if not, we create one. [Code: <a href="https://github.com/mikermcneil/sails-deploy-azure/blob/master/index.js#L76-L158">L76-L152</a> in sails-deploy-azure/index.js, consuming machinepack-azure]</p>
<p class="MsoListParagraphCxSpMiddle" style="margin-left: .25in; text-indent: -.25in;">3)<span style="font: 7.0pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>We're ready to ship things up, so we zip the local folder, excluding unnecessary files and folders. We could have used Git to deploy, but Treeline.io preferred to not take a Git dependency. We use a Node-native ZIP implementation to compress the whole app into a single file. Next, we upload it to a temporary folder on the web app&rsquo;s virtual machine. [Code: <a href="https://github.com/mikermcneil/sails-deploy-azure/blob/master/index.js#L169-187">L169-187</a> and <a href="https://github.com/mikermcneil/sails-deploy-azure/blob/master/index.js#L263-L340">L263-L340</a> in sails-deploy-azure/index.js, consuming machinepack-azure]</p>
<p class="MsoListParagraphCxSpLast" style="margin-left: .25in; text-indent: -.25in;">4)<span style="font: 7.0pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>Now that the package is uploaded to Azure, we finish execution of local commands and commence remote execution. We upload a script containing commands to clean the site, unzip the package and run [npm install]&nbsp;to Azure, where it is automatically assigned a REST endpoint with API - allowing us to call the script, fetch its status and stderr/stdout output. Once the script is uploaded, we simply call its API, pipe through the output to the local machine and watch as Azure is setting up the SailsJS website. [Code: <a href="https://github.com/mikermcneil/sails-deploy-azure/blob/master/index.js#L188-L246">L188-246</a> in sails-deploy-azure/index.js, consuming machinepack-azure; <a href="https://github.com/mikermcneil/sails-deploy-azure/blob/master/payload/sailsdeploy.ps1">sailsdeploy.ps1</a>]</p>
<h1>&nbsp;</h1>
<h1>Running the Code</h1>
<p class="MsoNormal">To try this out, you'll need Node.js and NPM installed on your local machine. To try the actual deployment, you can either go the brave path of also setting up a SailsJS app or use a provided test app (http://1drv.ms/1A7r3hS). If you want to go with a provided test app, skip to step 5.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">1) Install the latest version of Sails by running the following command. At the time of writing this tutorial, commit 8747a77273c949455a8a89d79abfd36383d10e73 was used.</p>
<p class="MsoNormal" style="padding-left: 30px;">&nbsp;</p>
<p class="MsoNormal" style="padding-left: 30px;"><span style="font-size: 10.0pt; line-height: 107%; font-family: Consolas;">npm install -g balderdashy/sails</span></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">2) Create a new folder for your brand new SailsJS app and open up PowerShell/Terminal in said folder. Run the following command to scaffold the app:</p>
<p class="MsoNormal" style="padding-left: 30px;">&nbsp;</p>
<p class="MsoNormal" style="padding-left: 30px;"><span style="font-size: 10.0pt; line-height: 107%; font-family: Consolas;">sails new .</span></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">3) Update the configuration to use the correct port by opening up config/env/production.js and updating it to look like this:</p>
<p class="MsoNormal" style="margin-bottom: 0pt;">&nbsp;</p>
<p class="MsoNormal" style="padding-left: 30px; margin-bottom: 0pt;"><span style="font-size: 10.0pt; line-height: 107%; font-family: Consolas;">module.exports = {</span></p>
<p class="MsoNormal" style="padding-left: 60px; margin-bottom: 0pt;"><span style="font-size: 10.0pt; line-height: 107%; font-family: Consolas;">&nbsp; port: process.env.port,</span></p>
<p class="MsoNormal" style="padding-left: 30px; margin-bottom: 0pt;"><span style="font-size: 10.0pt; line-height: 107%; font-family: Consolas;">}</span></p>
<p class="MsoNormal" style="padding-left: 60px; margin-bottom: 0pt;">&nbsp;</p>
<p class="MsoNormal" style="margin-bottom: .0001pt;">&nbsp;</p>
<p class="MsoNormal">4) It's a good idea to disable Grunt for your website - it's extremely useful while in development mode, but shouldn't be part of a deployment. This step is not specific to Azure, but a good asset management practice for SailsJS. Open up package.json and remove everything that begins with "grunt-" <em>except </em>for Grunt itself.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">5) Log into the Azure Portal and create a new web app. Ideally, the website should have some power to run the Sails installation process without trouble. To ensure enough resources, create the website either in a "Basic" or a "Standard" plan. Make also sure to set deployment credentials for your website.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">6) Go back to your Sails app and create a file called ".sailsrc" in its root folder. Fill it with the following JSON. Make sure to set the sitename, deployment username, and deployment password to the right setting.</p>
<p class="MsoNormal" style="padding-left: 30px; margin-bottom: 0pt;">&nbsp;</p>
<p class="MsoNormal" style="padding-left: 30px; margin-bottom: 0pt;"><span style="font-size: 10.0pt; line-height: 107%; font-family: Consolas;">{</span></p>
<p class="MsoNormal" style="padding-left: 30px; margin-bottom: 0pt;"><span style="font-size: 10.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp; "commands": {</span></p>
<p class="MsoNormal" style="padding-left: 30px; margin-bottom: 0pt;"><span style="font-size: 10.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "deploy": {</span></p>
<p class="MsoNormal" style="padding-left: 30px; margin-bottom: 0pt;"><span style="font-size: 10.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "module": "sails-deploy-azure"</span></p>
<p class="MsoNormal" style="padding-left: 30px; margin-bottom: 0pt;"><span style="font-size: 10.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p class="MsoNormal" style="padding-left: 30px; margin-bottom: 0pt;"><span style="font-size: 10.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp; },</span></p>
<p class="MsoNormal" style="padding-left: 30px; margin-bottom: 0pt;"><span style="font-size: 10.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp; "azure": {</span></p>
<p class="MsoNormal" style="padding-left: 30px; margin-bottom: 0pt;"><span style="font-size: 10.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "sitename": "YOUR_SITENAME"</span></p>
<p class="MsoNormal" style="padding-left: 30px; margin-bottom: 0pt;"><span style="font-size: 10.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "username": "YOUR_USER",</span></p>
<p class="MsoNormal" style="padding-left: 30px; margin-bottom: 0pt;"><span style="font-size: 10.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "password": "YOUR_PASSWORD"</span></p>
<p class="MsoNormal" style="padding-left: 30px; margin-bottom: 0pt;"><span style="font-size: 10.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp; }</span></p>
<p class="MsoNormal" style="padding-left: 30px; margin-bottom: 0pt;"><span style="font-size: 10.0pt; line-height: 107%; font-family: Consolas;">}</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;">&nbsp;</p>
<p class="MsoNormal">7) You are ready for deployment! Run the following command from your app's root folder to let your local Sails app deploy to Azure:</p>
<p class="MsoNormal" style="padding-left: 30px;">&nbsp;</p>
<p class="MsoNormal" style="padding-left: 30px;"><span style="font-size: 10.0pt; line-height: 107%; font-family: Consolas;">node ./node_modules/sails/bin/sails.js deploy</span></p>
<h1>&nbsp;</h1>
<h1>Challenges</h1>
<p class="MsoNormal">Azure does not allow non-organizational accounts to set deployment credentials via command line and instead forces users to set those credentials via website. For users with organizational accounts, the user experience is therefore better, since we can automate the setting of credentials &ndash; whereas our only option for &lsquo;pay as you go&rsquo; customers is to point to the Azure Management Portal.</p>
<h1>&nbsp;</h1>
<h1>Opportunities for Reuse</h1>
<p class="MsoNormal">The same code can be reused in large portions to enable deployments of similar frameworks &ndash; it is obvious that the implemented flow is not specific to SailsJS. Practically speaking, the ZIP package being deployed to Azure could be filled with any framework or application. The script being uploaded to Azure to finish the installation is equally agnostic, but could also be extended to include more framework-specific commands. The Node Machine can be reused by any Node application implementing Node machines.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">Since SailsJS itself as well as all three modules developed during this customer engagement are open source and licensed with the MIT license, other developers are free to reuse the created code.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal"><em>Felix Rieseberg is&nbsp;an&nbsp;Open Source Engineer in Microsoft's Partner Catalyst team where he spends his&nbsp;time coding on the cutting edge of technology, trying to improve developer's lives by releasing quality code on GitHub and touring the world presenting the lessons we've learned.&nbsp; Read his blog at <a href="http://www.felixrieseberg.com/,">http://www.felixrieseberg.com/,</a> follow him on twitter @felixrieseberg, and check out his work on github at <a href="https://github.com/felixrieseberg">https://github.com/felixrieseberg.</a></em></p>
<p class="MsoNormal">&nbsp;</p>]]></content:encoded>
    </item>
    <item>
      <title>Using nginx and Docker Containers on Azure to migrate a Java/HTML/MongoDB Solution</title>
      <link>https://docs.microsoft.com/archive/blogs/partnercatalystteam/using-nginx-and-docker-containers-on-azure-to-migrate-a-javahtmlmongodb-solution</link>
      <pubDate>Wed, 01 Jul 2015 15:32:00 GMT</pubDate>
      <dc:creator><![CDATA[Shawn Cicoria]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/partnercatalystteam/2015/07/01/using-nginx-and-docker-containers-on-azure-to-migrate-a-javahtmlmongodb-solution/</guid>
      <description><![CDATA[Overview
By Shawn Cicoria, Partner Catalyst Team
This case study focuses on migrating existing...]]></description>
      <content:encoded><![CDATA[<div>
<h1>Overview</h1>
<p><em>By Shawn Cicoria</em><span>, Partner Catalyst Team</span></p>
<p>This case study focuses on migrating existing multi-tiered applications to Azure, using Docker containers and nginx to expedite rehydration and reduce the overall surface area for open ports for the main application. After reviewing this case study you&rsquo;ll have a basic understanding on a use case for nginx and dealing with a common scenario for on-premises applications and migrating them to the cloud &ndash; specifically, handling non-standard HTTP ports that are artifacts of developer choices and masked when on-premises.</p>
<h2><span>The Problem</span></h2>
<p>The problems presented and addressed are two areas that often happen when certain implementation choices are made and lack of communication between an organization&rsquo;s Development and Infrastructure teams exists.&nbsp; As teams progress towards a DevOps structure and culture, these issues are generally recognized and addressed early.</p>
<p>The example presented here is abstracted from a more complex solution and simplified to highlight the basic processes and core technologies that are applied to address some common issues.</p>
<h3><span>Rapid Environment Setup and Deployment of Tiered Solution</span></h3>
<p>Existing multi-tiered solutions are often hard to rehydrate in new environments and may take days, even weeks to get the right people focused on addressing the connections and linkage of all the moving parts.<br /><br />In a good, well established DevOps culture, the team can easily and quickly establish a full environment for development, test, staging, disaster recovery, even production with minimal intervention. Often however, if teams are disconnected or lack concern, motivation, or just lack focus until its tool late, these proper processes and approaches are not followed, and migration is slower and more expensive than it needs to be.</p>
<h3><span>Use of Non-Standard Ports</span></h3>
<p>One primary issue is that developers often implement solutions that make use of non-standard TCP ports (e.g. HTTP(s) over port 8000 or 44300 &ndash; not 80 or 443). Standard or &ldquo;well known ports&rdquo; are listed here: <a href="http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers">http://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers</a> and here <a href="http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml">http://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml</a>.</p>
<p>When solutions are deployed internally on-premises, these issues generally go unnoticed, since traffic within the corporate network is usually routed without concern as ports are not generally locked down on routers, switches, and internal bridges. Other than additional configuration that may happen on the server to open up these additional ports (which may be open by default &ndash; no firewall), the use of non-standard ports goes undetected or pushed to a low priority fix. However, moving to the cloud requires that we have more explicit control and knowledge of port usage.</p>
<h3><span>Existing On-Premise Solution Overview</span></h3>
<p>The architecture that is being used is shown in the diagram in this section (Figure 1 - On-Premises and Non-Standard Ports). The core aspects of the solution are:</p>
<ol>
<li>Browser based rich client<ol>
<li>HTML &amp; JavaScript</li>
</ol></li>
<li>Tomcat used for static content &ndash; port 80/443<ol>
<li>HTML / CSS / JavaScript / images</li>
<li>No direct calls from this tier to any other tier</li>
<li>&ldquo;serverConfig.js&rdquo; containing variable that identified the Spring Framework DNS name and Port &ndash; as example:</li>
</ol></li>
</ol>
<pre class="scroll"><code class="csharp">var baseAddress = 'http://apiserver:8080';</code></pre>
<ol>
<li>Spring Framework / Java &ndash; port 8080/44300<ol>
<li>Used for REST services</li>
</ol></li>
<li>Mongo DB &ndash; port 27017<ol>
<li>Document Storage / Database</li>
</ol></li>
</ol>
<p>NOTE: While static content was deployed to an IaaS instance running Docker, further determination should be done to identify if CDN is applicable and other application remediation approaches; further review of nginx modules, CDN requirements, and cache-ability of content is required.</p>
<h3><span>Non-Standard Ports</span></h3>
<p>Non-standard ports are an issue as we move the solution to the cloud. Generally edge gateways and proxy servers expect most traffic or permit only traffic to known ports. While getting an exception to be implemented in a firewall rule for specific applications is an option, this creates governance overhead, management, and support issues as the number of solutions in the enterprise increase with these exceptions.</p>
<h3><span>Rapid Environment Setup</span></h3>
<p>As new developers and testers ramp onto a team and a solution, it&rsquo;s important to be able to rapidly stand up a working environment so they can be productive right away. In addition, as new releases of the solution are implemented, testing the solution in a clean environments helps to diagnose situations where old code or remnants of prior solutions may remain. While production deployments may be different, using clean well known configurations and applying a release on that narrows the complexity and helps mitigate issues that occur as machines run over time and various configuration or environment changes are made and potentially not tracked.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/7120.ng1.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/7120.ng1.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/7120.ng1.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/7120.ng1.png" alt="" border="0" /></a></p>
<h2><span>Overview of the Solution</span></h2>
</div>
<div class="WordSection3">
<p>One approach is to use both Docker and nginx (<a href="http://nginx.org/">http://nginx.org</a>) along with the existing Spring-based Java &ldquo;middle-tier&rdquo; and keeping Mongo DB as the persistence tier.</p>
<p>Docker is a Linux container technology that provides lightweight runtime and packaging tool, and Docker Hub, allowing developers and IT Pros to automate their build pipeline and share artifacts with collaborators through public or private repositories. Please see&nbsp;<a href="http://docker.com/">http://docker.com</a>&nbsp;for extensive information on Docker.</p>
<h3><span>Rapid Environment Deployment and Repeatability</span></h3>
<p>Docker is utilized as a deployment tool for defining and creating several Dockerfile files and Builds that are built and deployed rapidly &ndash; and run across 1 or more Docker Host containers as required.</p>
<p>Docker offers up many attributes that assist in the repeatability. While this case study did not cover clustering approaches (as there are several in Docker), Docker offers Swarm as their native (in beta) approach for Docker clustering.</p>
<p>This solution also did not publish to the Docker Hub &ndash; it took advantage of creating a build in a local Docker host container and running direct from there. While Docker Hub is an option, there are approaches to running your own Docker repository:</p>
<p><a href="http://azure.microsoft.com/blog/2014/11/11/deploying-your-own-private-docker-registry-on-azure/">http://azure.microsoft.com/blog/2014/11/11/deploying-your-own-private-docker-registry-on-azure/</a></p>
<h3><span>Mongo DB and Docker</span></h3>
<p>Docker offers an official Mongo DB image; the following solution is based upon that official image, and includes some modifications to insert seed data and check for ready-state.&nbsp; This seed data is just for demonstration purposes and not intended for production. Note that the primary purpose of these Dockerfiles and the scripts was to rapidly establish a running Solution environment with all tiers working, so that developers and testers can validate a new build.</p>
<h4><span>Dockerfile for Mongo</span></h4>
<pre class="scroll"><code class="mysql"># Dockerizing MongoDB: Dockerfile for building MongoDB images<br /> # Based on ubuntu:latest, installs MongoDB following the instructions from:<br /> # http://docs.mongodb.org/manual/tutorial/install-mongodb-on-ubuntu/<br /> <br /> FROM ubuntu:latest<br /> MAINTAINER Shawn Cicoria shawn.cicoria@microsoft.com<br /> <br /> ADD mongorun.sh .<br /> # Installation:<br /> # Import MongoDB public GPG key AND create a MongoDB list file<br /> RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10<br /> RUN echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | tee /etc/apt/sources.list.d/10gen.list<br /> <br /> # Update apt-get sources AND install MongoDB<br /> RUN apt-get update &amp;&amp; apt-get install -y mongodb-org<br /> <br /> # Create the MongoDB data directory<br /> RUN mkdir -p -m 777 /data/db<br /> <br /> RUN mkdir -p -m 777 /tmp<br /> <br /> COPY mongodb.catalog.json /tmp/mongodb.catalog.json<br /> COPY mongodb.dealers.json /tmp/mongodb.dealers.json<br /> COPY mongodb.quotes.json /tmp/mongodb.quotes.json<br /> COPY mongorun.sh /tmp/mongorun.sh<br /> <br /> WORKDIR /tmp<br /> <br /> RUN chmod +x ./mongorun.sh<br /> <br /> # Expose port #27017 from the container to the host<br /> EXPOSE 27017<br /> <br /> VOLUME /data/db<br /> <br /> ENTRYPOINT ["./mongorun.sh"]<br /> </code></pre>
<p><span><br /></span></p>
<h4>Mongo Startup Script</h4>
<p>The script utilized as the entry point in the Dockerfile initializes a log file, and then starts MongoDB on a background process.&nbsp; It then waits for MongoDB to be ready by reading the log file for the message&nbsp;<strong>&ldquo;wating for connections on port&rdquo;</strong>.&nbsp; This represents one way to check for readiness.&nbsp; In the OrderService approach that follows in the next section, you will see another way.</p>
<pre class="scroll"><code class="cplusplus">#!/bin/bash<br /> <br /> # Initialize a mongo data folder and logfile<br /> sudo rm -r /data/db 1&gt;/dev/null 2&gt;/dev/null<br /> mkdir -p -m 777 /data/db <br /> touch /data/db/mongodb.log<br /> echo step 1<br /> # Start mongodb with logging<br /> # --logpath Without this mongod will output all log information to the standard output.<br /> # --logappend Ensure mongod appends new entries to the end of the logfile. We create it first so that the below tail always finds something<br /> /usr/bin/mongod --smallfiles --quiet --logpath /data/db/mongodb.log --logappend &amp;<br /> MONGO_PID=$!<br /> echo step 2<br /> # Wait until mongo logs that it's ready (or timeout after 60s)<br /> COUNTER=0<br /> grep -q 'waiting for connections on port' /data/db/mongodb.log<br /> while [[ $? -ne 0 &amp;&amp; $COUNTER -lt 90 ]] ; do<br /> sleep 2<br /> let COUNTER+=2<br /> echo "Waiting for mongo to initialize... ($COUNTER seconds so far)"<br /> grep -q 'waiting for connections on port' /data/db/mongodb.log<br /> done<br /> <br /> # Now we know mongo is ready and can continue with other commands<br /> echo now populate<br /> #some point do something to chedk if already run; but for this demo just do it.<br /> /usr/bin/mongoimport -d ordering -c catalog &lt; /tmp/mongodb.catalog.json<br /> /usr/bin/mongoimport -d ordering -c dealers &lt; /tmp/mongodb.dealers.json<br /> /usr/bin/mongoimport -d ordering -c quotes &lt; /tmp/mongodb.quotes.json<br /> <br /> wait $MONGO_PID</code></pre>
<h3>Java Spring and Docker</h3>
<p>For the Java Spring framework based application tier, which provides various REST services that the HTML front-end calls directly, the following Dockerfile was used</p>
<h4><span>OrderService Dockerfile</span></h4>
<pre class="scroll"><code class="cplusplus">FROM java:8-jre<br /> MAINTAINER Shawn Cicoria shawn.cicoria@microsoft.com<br /> <br /> ENV APP_HOME /usr/local/app<br /> ENV PATH $APP_HOME:$PATH<br /> RUN mkdir -p "$APP_HOME"<br /> <br /> WORKDIR $APP_HOME<br /> <br /> ADD ordering-service-0.1.0.jar $APP_HOME/<br /> ADD startService.sh $APP_HOME/<br /> <br /> RUN chmod +x startService.sh <br /> <br /> EXPOSE 8080<br /> #CMD ["java", "-jar", "ordering-service-0.1.0.jar"]<br /> CMD ["./startService.sh"]<br /> <br /> </code></pre>
<h4>StartService Script</h4>
<p>The OrderService Dockerfile references the following script, which polls MongoDB using a hostname of &lsquo;mongodb&rsquo; (you&rsquo;ll see that alias used in the &lsquo;docker run &ndash;link&rsquo; commands later) to check for ready state.</p>
<pre class="scroll"><code class="cplusplus">#!/bin/bash<br /> <br /> while ! curl http://mongodb:27017/<br /> do<br /> echo "$(date) - still trying"<br /> sleep 1<br /> done<br /> echo "$(date) - connected successfully"<br /> <br /> java -jar ordering-service-0.1.0.jar</code></pre>
<h3><span>nginx and Docker</span></h3>
<p>nginx [engine x] is an HTTP and reverse proxy server that is popular and used in this solution to provide the static file content needs along with providing a proxy to the OrderService, using simple rules. The primary benefit is squashing down to a single IP port for all traffic &ndash; however, nginx offers up other potential for distributed and rule based proxy routing and an abstraction of the OrderService endpoints &ndash; thus location transparency.</p>
<h4><span>Nginx Dockerfile</span></h4>
<p>Note that the Dockerfile uses the base image from Docker for nginx and extends that for custom configuration and content. There is a parameter of &ldquo;daemon off&rdquo; to nginx as without it the container would just exit as Docker requires the process to continually run in order for it to assume it&rsquo;s running and manageable.</p>
<pre class="scroll"><code class="cplusplus">FROM nginx:1.7.10<br /> MAINTAINER Shawn Cicoria shawn.cicoria@microsoft.com<br /> <br /> ENV WEB_HOME /usr/local/web<br /> <br /> ADD Web.tar $WEB_HOME/<br /> <br /> COPY nginx.conf /etc/nginx/nginx.conf<br /> <br /> EXPOSE 8000<br /> CMD ["nginx", "-g", "daemon off;"]</code></pre>
<h4><span>Nginx Configuration</span></h4>
<pre class="scroll"><code class="cplusplus">worker_processes 1;<br /> <br /> events {<br /> worker_connections 1024;<br /> }<br /> <br /> http {<br /> include mime.types;<br /> default_type application/octet-stream;<br /> <br /> sendfile on;<br /> keepalive_timeout 65;<br /> <br /> gzip on;<br /> <br /> server {<br /> listen 8000;<br /> server_name localhost;<br /> <br /> location / {<br /> root /usr/local/web/Web;<br /> index index.html index.htm;<br /> }<br /> <br /> error_page 500 502 503 504 /50x.html;<br /> <br /> location = /50x.html {<br /> root html;<br /> }<br /> <br /> location /catalog {<br /> proxy_pass http://orderservice:8080;<br /> }<br />&nbsp;</code></pre>
<pre class="scroll"><code class="cplusplus"> location /quotes {<br /> proxy_pass http://orderservice:8080;<br /> }<br /> location /shipments {<br /> proxy_pass http://orderservice:8080;<br /> }<br /> }<br /> }<br /> </code></pre>
<h2>Solution Architecture and Container Approach</h2>
<p>In the diagram at the end of this section, the solution takes advantage of Docker and can scale up to numerous Docker hosts (containers). Note that we&rsquo;ve also achieved use of a single IP port along with location transparency to the running containers and the calling clients.</p>
<p>In addition, for full clustering support you must ensure that any calls made between each tier leverages Docker&rsquo;s linking of containers as Docker provides dynamic IP addresses and container names for each running container.</p>
<h3><span>Docker Build Script</span></h3>
<p>The Docker build script is below and is fairly standard.</p>
<pre class="scroll"><code class="cplusplus">#!/bin/sh<br /> cd mongoseed<br /> docker build -t scicoria/mongoseed:0.1 .<br /> cd ../orderService<br /> docker build -t scicoria/orderservice:0.1 .<br /> cd ../staticsite<br /> docker build -t scicoria/staticsite:0.1 .<br /> cd ..</code></pre>
<h3><span>Docker Run Script</span></h3>
<p>For running the containers, it&rsquo;s important that the &ldquo;client&rdquo; is aware or &ldquo;linked&rdquo; to containers is needs to initiate requests to.&nbsp; In the following script the following links are in place:</p>
<p><strong>1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</strong><strong>StaticWeb -&gt; OrderService (DNS name OrderService) on Port 8000</strong></p>
<p><strong></strong><strong>2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</strong><strong>OrderService -&gt; MongoDB (DNS name mongodb) on Port 27017</strong></p>
<pre class="scroll"><code class="cplusplus">#!/bin/sh<br /> docker run -d -p 27017:27017 --name mongodb -v /data/db:/data/db scicoria/mongoseed:0.1<br /> docker run -d -p 8080:8080 --name orderservice --link mongodb:mongodb scicoria/orderservice:0.1<br /> docker run -d -p 8000:8000 --link orderservice:orderservice scicoria/staticsite:0.1</code></pre>
<p><strong><br /></strong></p>
<h3><span>Docker and Linking</span></h3>
<p>Docker provides virtual networking among containers and any communication among containers must be explicitly declared when running a container. While the ports that are identified on the command line with the&nbsp;<strong>&lsquo;-p&rsquo;&nbsp;</strong>parameters are for external communication into the container, Docker prevents that communication &ndash; unless it is explicitly linked.&nbsp; In addition you should be aware of the hostnames and dynamic IP addressing and Ports that are also used, as Docker effectively provides NAT (network address translation) services among containers.</p>
<h4><span>Eliminate use of Non-Standard Ports</span></h4>
<p>Finally, with this solution we can eliminate the use of non-standard ports and reduce configuration, support, and troubleshooting issues with regards to corporate firewalls, proxy servers, etc. that may prevent non-standard port traffic from traversing the network.</p>
<p>The final code used in the static web site for the JavaScript calls simply builds the endpoint name using the browser&rsquo;s location provider.&nbsp; Since all traffic is now being routed through the same nginx front-ends, and nginx is determining which traffic to proxy and send to&nbsp;<strong>OrderService</strong>, we&rsquo;ve reduced that complexity by a significant amount.</p>
<p>So, in updating the static web sites &ldquo;serverConfig.js&rdquo; &ndash; which executes in the Browser to the following, we have a minimal impact on the static web site and it&rsquo;s supporting JavaScript:</p>
<pre class="scroll"><code class="js">var baseAddress = window.location.protocol + '//' + window.location.hostname;</code></pre>
<pre class="scroll"><span style="font-size: 1.5em;"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/0624.ng2.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/0624.ng2.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/0624.ng2.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/0624.ng2.png" alt="" border="0" /></a><br /></span></pre>
<h2 class="scroll"><span style="font-size: 1.5em;">Code Artifacts</span></h2>
</div>
<div class="WordSection5">
<p>The source files are published to GitHub here &ndash; with the [<strong>nginix</strong>] branch represented here:</p>
<p><a href="https://github.com/cicorias/IgniteARMDocker/tree/nginix">https://github.com/cicorias/IgniteARMDocker/tree/nginix</a>&nbsp;[<strong>nginx</strong>]&nbsp;&nbsp; (note the different branch from &lsquo;master&rsquo;).</p>
<p>This solution and source is part of a Pre-MS Ignite Session on DevOps &ndash; with additional walk-through items here:&nbsp;<a href="https://github.com/Microsoft/PartsUnlimitedMRP">https://github.com/Microsoft/PartsUnlimitedMRP</a></p>
<h3><span>Opportunities for Reuse</span></h3>
<ol>
<li>The use of nginx for a Web front end provides an approach for layer 7 proxy and SSL termination. This example just brushes the surface of the full capabilities but they are on par with Application Request Routing (ARR) under IIS and nginx also runs on both Linux and Windows.</li>
<li>The various scripts and Dockefiles are representations of simple build and deployments and can be reused as is if published direct to Docker Hub.</li>
</ol>
<h3 class="WordSection5"><span>Further Research</span></h3>
<p>The pattern applied here is useful for many solutions. The concept of location transparency for service endpoints and the use of nginx provides a simple but effective and highly performant proxy based approach. In addition nginx is extensible allowing more static injection of routing of proxy calls to different endpoints &ndash; based upon various attributes such as load, time of day, versioning, etc.</p>
<p>&nbsp;</p>
<p><em>Shawn Cicoria has been working on distributed systems for nearly 2 decades after dealing his last deck of cards on the Blackjack table. Follow Shawn at&nbsp;<a href="http://bit.ly/cicorias">http://bit.ly/cicorias</a>&nbsp;or&nbsp;<a href="https://twitter.com/cicorias">@cicorias</a></em></p>
</div>]]></content:encoded>
    </item>
    <item>
      <title>Using io.js with Azure Web Apps &amp; Azure Websites</title>
      <link>https://docs.microsoft.com/archive/blogs/partnercatalystteam/using-io-js-with-azure-web-apps-azure-websites</link>
      <pubDate>Thu, 25 Jun 2015 06:42:00 GMT</pubDate>
      <dc:creator><![CDATA[sarasp@outlook.com]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/partnercatalystteam/2015/06/25/using-io-js-with-azure-web-apps-azure-websites/</guid>
      <description><![CDATA[Using io.js with Azure Web Apps &amp; Azure Websites
By Felix Rieseberg, Partner Catalyst Team,...]]></description>
      <content:encoded><![CDATA[<h1>Using io.js with Azure Web Apps &amp; Azure Websites</h1>
<p class="MsoNormal">By Felix Rieseberg, Partner Catalyst Team, with input from Domenic Denicola (Google, member of the ECMAScript/JavaScript technical committee).</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">This case study describes a solution that enables the use of io.js with Azure Web Apps (formerly known as Azure Websites).&nbsp; Any developer interested in using the io.js fork of Node.js on Azure can learn how in this case study.&nbsp; A more concise tutorial <a href="http://azure.microsoft.com/en-us/documentation/articles/web-sites-nodejs-iojs/">is available on Azure</a><span class="MsoHyperlink">.com</span>.</p>
<p class="MsoNormal">&nbsp;</p>
<h1>Customer Problem</h1>
<p class="MsoNormal">The Node.js fork io.js gained significant attention and popularity, since many Node.js core contributors declared it a more agile, forward-thinking and community-driven implementation of JavaScript I/O.&nbsp; Within days after the initial release, many popular leading companies announced that they were planning to switch to io.js for better performance.&nbsp; As a result, cloud providers hurried to support&nbsp; io.js with their Node.js infrastructure.&nbsp; Several JavaScript, Node.js and startup community members contacted Microsoft, indicating interest in running io.js on Azure Websites.&nbsp;&nbsp;&nbsp; Based on that input, we set to work ensuring that io.js runs well on Azure Web Apps.</p>
<p class="MsoNormal">&nbsp;</p>
<h1>&nbsp;Overview of the Solution</h1>
<p class="MsoNormal">Using io.js on Azure Web apps is enabled through a deployment script that a) downloads the latest available stable version of io.js for Windows during deployment and b) configures the runtime to use the downloaded binary as the system-wide Node executable.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/6758.image001.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/6758.image001.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/6758.image001.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/6758.image001.png" alt="" border="0" /></a></p>
<p class="MsoNormal">The resulting solution is available in three flavors:</p>
<ul>
<li>
<div class="MsoNormal">One-click installation via Azure Deploy (also known as Azure Resource Manager) on GitHub</div>
</li>
<li>
<div class="MsoNormal">Inclusion of the deployment payload with any project</div>
</li>
<li>
<div class="MsoNormal">Manual inclusion of an io.js binary, paired with the runtime configuration.</div>
</li>
</ul>
<p class="MsoNormal">&nbsp;</p>
<h1>Implementation</h1>
<p class="MsoNormal"><a href="http://github.com/projectkudu/kudu">Kudu</a>, a Microsoft-sponsored open source project, powers Azure Web App deployments. It allows for custom deployment scripts and can execute Bash, PowerShell and scripts in other languages during the deployment phase. IISNode, the IIS plugin that enables the use of Node.js, can be configured to use a specific binary as Node executable.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">Using Kudu and IISNode together allows for an elegant implementation.&nbsp; A <a href="https://github.com/felixrieseberg/iojs-azure/blob/master/deploy.cmd">script describing the deployment of the project creates a &lsquo;bin&rsquo; folder outside wwwroot</a> and downloads the latest available stable version of io.js. While the script also handles other required deployment tasks, it&rsquo;s merely 150 lines long. In detail, it starts by accepting the deployment command, copying the new deployment payload to the respective wwwroot folder. During this phase, the script also ensures that Node and npm are installed and accessible. Once deployed, the script moves on to download iojs (see below). Once that is done, Azure Website&rsquo;s default Node installation routine is executed &ndash; meaning that npm modules are downloaded and the environment is configured for IISNode.</p>
<p class="MsoNormal">&nbsp;</p>
<div style="border: solid #969696 1.0pt; padding: 8.0pt 8.0pt 8.0pt 8.0pt; background: #EEEEEE;">
<pre style="line-height: 15.6pt; background: #EEEEEE; border: none; padding: 0in;"><span style="font-size: 9.0pt; font-family: Consolas; color: black;">:: 2. Dowloading io.js</span></pre>
<pre style="line-height: 15.6pt; background: #EEEEEE; border: none; padding: 0in;"><span style="font-size: 9.0pt; font-family: Consolas; color: black;">if not exist "D:\home\site\bin" mkdir D:\home\site\bin</span></pre>
<pre style="line-height: 15.6pt; background: #EEEEEE; border: none; padding: 0in;"><span style="font-size: 9.0pt; font-family: Consolas; color: black;">if not exist "D:\home\site\bin\iojs" mkdir D:\home\site\bin\iojs</span></pre>
<pre style="line-height: 15.6pt; background: #EEEEEE; border: none; padding: 0in;"><span style="font-size: 9.0pt; font-family: Consolas; color: black;">call :ExecuteCmd curl -L -o D:\home\site\bin\iojs\iojs.exe https://iojs.org/dist/latest/win-x64/iojs.exe</span></pre>
<pre style="line-height: 15.6pt; background: #EEEEEE; border: none; padding: 0in;"><span style="font-size: 9.0pt; font-family: Consolas; color: black;">call :ExecuteCmd curl -L -o D:\home\site\bin\iojs\iojs.lib https://iojs.org/dist/latest/win-x64/iojs.lib</span></pre>
</div>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">Configration of IISNode itself is simple: A file named iisnode.yml in the wwwroot of a website is parsed by IISNode, allowing a text-based configuration of environment variables. One of those variables is &lsquo;nodeProcessCommandLine&rsquo;, allowing the specification of a binary to be used for Node calls.</p>
<p class="MsoNormal">&nbsp;</p>
<div style="border: solid #969696 1.0pt; padding: 8.0pt 8.0pt 8.0pt 8.0pt; background: #EEEEEE;">
<pre style="line-height: 15.6pt; background: #EEEEEE; border: none; padding: 0in;"><span style="font-size: 9.0pt; font-family: Consolas; color: black;">nodeProcessCommandLine: </span><span style="font-size: 9.0pt; font-family: Consolas; color: maroon;">"D:\home\site\wwwroot\bin\iojs\iojs.exe"</span></pre>
</div>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">io.js is npm-compatible, so no changes are required to enable package management.&nbsp; To get this running in your Azure Website, see the instructions at <a href="http://azure.microsoft.com/en-us/documentation/articles/web-sites-nodejs-iojs/">http://azure.microsoft.com/en-us/documentation/articles/web-sites-nodejs-iojs/</a>.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">To run this code yourself, see the <a href="http://azure.microsoft.com/en-us/documentation/articles/web-sites-nodejs-iojs">full tutorial on Azure.com</a> or <a href="https://github.com/felixrieseberg/iojs-azure/">directly to GitHub</a>, where clicking the &lsquo;Deploy to Azure&rsquo; button automates the process.</p>
<p class="MsoNormal">&nbsp;</p>
<h1>Opportunities for Reuse</h1>
<p class="MsoNormal">The code is <a href="https://github.com/felixrieseberg/iojs-azure/">available on GitHub</a> and referenced as the primary solution for <a href="http://azure.microsoft.com/en-us/documentation/articles/web-sites-nodejs-iojs/">io.js on Azure Web Apps on Azure.com</a>. It has been referenced in <a href="http://thenewstack.io/running-io-js-azure/">various blog posts</a> and <a href="https://t.co/X1y5a7UVVf">"cool technology on Azure" demos</a>.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">The quick support for io.js was received well by the community. Simon Bisson (ZDNet, Infoworld) wrote: &ldquo;<em>It&rsquo;s good to see Microsoft simplifying the process of working with new open Web technologies on Azure. Scripts like this, while not supported directly, make it possible for developers to experiment with new technologies without affecting production systems. Sharing scripts through Github is an important development, as it gives developers the option of modifying and updating scripts &ndash; and for the io.js team to fork the script and adopt it as part of their distribution tools.</em>&rdquo;</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/0743.image002.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/0743.image002.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/0743.image002.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/0743.image002.png" alt="" border="0" /></a></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">The code can be easily reused for different branches or forks of Node.js and is also the basis for <a href="https://github.com/felixrieseberg/node12-azure">Node 0.12 on Azure</a>.</p>
<p class="MsoNormal">&nbsp;</p>
<p><em>Felix Rieseberg is&nbsp;an&nbsp;Open Source Engineer in Microsoft's Partner Catalyst team where he spends his&nbsp;time coding on the cutting edge of technology, trying to improve developer's lives by releasing quality code on GitHub and touring the world presenting the lessons we've learned.&nbsp; Read his blog at <a href="http://www.felixrieseberg.com/,">http://www.felixrieseberg.com/,</a> follow him on twitter @felixrieseberg, and check out his work on github at <a href="https://github.com/felixrieseberg">https://github.com/felixrieseberg.</a></em></p>
<p>&nbsp;</p>]]></content:encoded>
    </item>
    <item>
      <title>Use of hierarchical clustering to predict the end location of a trip</title>
      <link>https://docs.microsoft.com/archive/blogs/partnercatalystteam/use-of-hierarchical-clustering-to-predict-the-end-location-of-a-trip</link>
      <pubDate>Tue, 23 Jun 2015 22:36:00 GMT</pubDate>
      <dc:creator><![CDATA[sarasp@outlook.com]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/partnercatalystteam/2015/06/23/use-of-hierarchical-clustering-to-predict-the-end-location-of-a-trip/</guid>
      <description><![CDATA[Use of Hierarchical Clustering to Predict the End Location of a Trip
By Beat Schwegler, Partner...]]></description>
      <content:encoded><![CDATA[<p class="MsoNormal"><strong><span style="font-size: x-large;">Use of Hierarchical Clustering to Predict the End Location of a Trip</span></strong></p>
<p class="MsoNormal"><em>By Beat Schwegler, Partner Catalyst Team</em></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">In this case study, our customer needed to predict the end location of a trip so that they could effectively manage their car-sharing fleet.&nbsp; We used Azure ML and Storm to build a model that predicts the end location of individual trips, which customer can then aggregate to most effectively reposition cars as needed to meet expected demand.&nbsp; If you are a developer interested in location hot spots or techniques for hierarchical geofencing, read this case study to understand both model and implementation of these techniques.</p>
<p class="MsoNormal">&nbsp;</p>
<h1>Customer Problem</h1>
<p class="MsoNormal">Our customer runs large scale car sharing fleets across multiple cities worldwide. They need to identify locations with high demand to ensure that enough cars are available for pick-up. To plan the fleet repositioning, it is important to predict possible drop-off locations. Together with their demand prediction, this allows the customer to take pre-emptive actions, such as repositioning cars (e.g. through limited time special offers). In this paper we discuss and implement an approach to predict drop-off locations based on one, real-time transmitted pick-up location.</p>
<p class="MsoNormal">&nbsp;</p>
<h1>Overview of the Solution</h1>
<p class="MsoNormal" style="page-break-after: avoid;">The overall architecture of the solution leverages a real-time data processing pipeline.</p>
<p class="MsoNormal" style="page-break-after: avoid;"><br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/8715.image001.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/8715.image001.png"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/8715.image001.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/8715.image001.png" alt="" border="0" /></a></p>
<p class="MsoCaption" style="text-align: center;" align="center"><em>Figure 1: real-time data processing pipeline</em></p>
<p class="MsoNormal">The car (or any other device) periodically sends its location which is ingested into the pipeline for further processing. In this solution, we use Storm (<a href="http://azure.microsoft.com/en-us/documentation/articles/hdinsight-storm-overview/">http://azure.microsoft.com/en-us/documentation/articles/hdinsight-storm-overview/</a>) to analyze the input stream and generate a summary for each trip, which we store in table storage. Each trip summary contains the following information:</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal" style="padding-left: 30px;"><span style="font-family: courier new,courier;">start location, end location, start time, trip duration, driver id</span></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">Our approach is based on the assumption that drivers follow certain patterns, such as driving from the train station to the office, or bringing kids to school each weekday morning. The idea is to identify the driver&rsquo;s most likely drop-off locations based on the current pick-up location.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal" style="text-align: left;">To implement the location clustering, we created a hierarchical model and computed the cluster that contains the highest number of locations. In such a model, each cluster is divided into four child clusters until we arrive at the required cluster size, the so-called leaf cluster. Each leaf cluster contains its list of locations. To limit the memory footprint and improve performance, we decided not to pre-create the clusters and leaves but to create them only when a location is added to the respective cluster/leaf. Figure 2 to Figure 4 show a representation of the clustering, and a visualization that only clusters containing actual locations are created. The red clusters in Figure 4 highlight the five top leaves (the leaves containing the most locations).</p>
<p class="MsoNormal" style="text-align: center;"><span style="line-height: 107%; font-family: 'Calibri',sans-serif; font-size: 11pt;"><br style="page-break-before: auto;" clear="all" />&nbsp;</span></p>
<p style="text-align: center;"><span style="line-height: 107%; font-family: 'Calibri',sans-serif; font-size: 11pt;"><img style="margin-right: auto; margin-left: auto; display: block;" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/8836.image002.jpg" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/8836.image002.jpg" alt="" border="0" /></span>&nbsp;Figure 2: Visualization of clusters (actual locations are in the Seattle area)&nbsp;&nbsp;</p>
<p style="text-align: center;">&nbsp;</p>
<div class="WordSection2">
<p class="MsoNormal" style="text-align: center; page-break-after: avoid;" align="center"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/7776.image003.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/7776.image003.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/7776.image003.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/7776.image003.png" alt="" border="0" /></a></p>
<p class="MsoNormal" style="text-align: center; page-break-after: avoid;" align="center">Figure 3: Zoomed visualization of clusters (green pin = start location, red pin = end location)</p>
<p class="MsoNormal" style="text-align: center; page-break-after: avoid;" align="center">&nbsp;</p>
<p class="MsoNormal" style="text-align: center; page-break-after: avoid;" align="center">&nbsp;<span style="line-height: 107%; font-family: 'Calibri',sans-serif; font-size: 11pt;"><br style="page-break-before: auto;" clear="all" /> <a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/1351.image004.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/1351.image004.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/1351.image004.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/1351.image004.png" alt="" border="0" /></a></span></p>
</div>
<p class="MsoCaption" style="text-align: center;" align="center">Figure 4: Zoomed visualization of the 5 top locations (the leaves are marked in red)</p>
<p class="MsoCaption" style="text-align: left;" align="center">&nbsp;</p>
<p class="MsoNormal" style="text-align: left;">One issue with a fixed hierarchical cluster is that the actual top location might be located in the intersection of leaves, and therefore won&rsquo;t be identified by the model (Figure 5).</p>
<p class="MsoNormal" style="text-align: left;">&nbsp;</p>
<p class="MsoNormal" style="text-align: center; page-break-after: avoid;" align="center"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/3404.image005.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/3404.image005.jpg"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/3404.image005.jpg" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/3404.image005.jpg" alt="" border="0" /></a>&nbsp;</p>
<p class="MsoCaption" style="text-align: center;" align="center">Figure 5: Top cluster is in the intersection of three leaves</p>
<p class="MsoCaption" style="text-align: center;" align="center">&nbsp;</p>
<p class="MsoNormal">To address this issue, we simply created a second model which is shifted by half of the leaf size, both vertically and horizontally. We then query the two models for their top locations and remove intersecting leaves by only taking the one with the highest number of locations (Figure 6).</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal" style="text-align: center; page-break-after: avoid;" align="center"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/5775.image006.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/5775.image006.jpg"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/5775.image006.jpg" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/5775.image006.jpg" alt="" border="0" /></a></p>
<p class="MsoCaption" style="text-align: center;" align="center">Figure 6: Using a second but shifted model returns the actual intersecting top cluster (the red square)</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">For the drop-off predictions based on the start location, we perform the following steps:</p>
<ol>
<li>
<p class="MsoListParagraphCxSpFirst" style="text-align: left; text-indent: -0.25in; padding-left: 30px;">We load all trips for the particular driver into the model and retrieve all trips that started in the same cluster. For improved accuracy, we can filter trips based on day or time before loading them into the model. The solution could also be extended to run filter queries as part of the top location retrieval, so basically passing a LINQ expression as an argument.&nbsp;</p>
</li>
<li>
<p class="MsoListParagraphCxSpFirst" style="text-align: left; text-indent: -0.25in; padding-left: 30px;">We&nbsp;retrieve all trips that started in the same leaf. In the case no previous trip started in the current leaf, we iteratively query the parent cluster, until we find a trip or reach a cluster level where the prediction becomes irrelevant.</p>
</li>
<li>
<p class="MsoListParagraphCxSpFirst" style="text-align: left; text-indent: -0.25in; padding-left: 30px;">We now iterate over all the trips that started in the same cluster and create a list of leaves where these trips ended. We then count how many of the trips end in a particular leaf and calculate the probability of the prediction.</p>
</li>
</ol>
<p>Limitations: It is clear that this route prediction only works for drivers who follow a specific pattern, such as frequently driving from the train station to the office. Figure 7 shows the performance of a driver which follows a certain pattern: The prediction accuracy progressively increases. After ~40 trips, the model is able to predict around ~60% of end locations with an accuracy of 200 meters.&nbsp;</p>
<p class="MsoNormal" style="text-align: center; page-break-after: avoid;" align="center"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/1258.image007.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/1258.image007.jpg"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/1258.image007.jpg" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/1258.image007.jpg" alt="" border="0" /></a></p>
<p class="MsoCaption" style="text-align: center;" align="center">Figure 7: Performance for a driver who follows a regular trip pattern</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">This is comparison to the second driver (see Figure 8): After only 10 trips, we&rsquo;re able to predict ~50% of end locations with an accuracy of 200 meters. After around 100 trips, it looks like the driving pattern changes and the accuracy decreases.&nbsp;</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal" style="text-align: center; page-break-after: avoid;" align="center"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/1220.image008.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/1220.image008.jpg"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/1220.image008.jpg" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/1220.image008.jpg" alt="" border="0" /></a></p>
<p class="MsoCaption" style="text-align: center;" align="center">Figure 8: Performance for a driver with changing trip pattern</p>
<h1 class="WordSection3">Implementation</h1>
<p class="MsoNormal">The solution consist of two algorithms: First the hierarchical clustering and second the actual route prediction algorithm (which leverages the clustering for its predictions).</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">The code for the two algorithms can be found on GitHub: <a href="https://github.com/cloudbeatsch/RoutePrediction">https://github.com/cloudbeatsch/RoutePrediction</a>.</p>
<h1 class="WordSection3">&nbsp;</h1>
<h1 class="WordSection3">Hierarchical clustering algorithm&nbsp;&nbsp;</h1>
<p class="MsoNormal">The hierarchical clustering algorithm is based on the composite pattern (Figure 9).</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal" style="text-align: center; page-break-after: avoid;" align="center"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/4812.image009.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/4812.image009.jpg"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/4812.image009.jpg" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/4812.image009.jpg" alt="" border="0" /></a></p>
<p class="MsoCaption" style="text-align: center;" align="center">Figure 9: Class diagram - hierarchical clustering algorithm</p>
<p class="MsoListParagraphCxSpFirst" style="text-indent: -0.25in; padding-left: 30px;"><span style="font-family: Symbol;">&middot;<span style="font: 7pt/normal 'Times New Roman'; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="line-height: 107%; font-family: 'Courier New'; font-size: 9pt;">LocationClustering</span> represents the service fa&ccedil;ade and implements the algorithm to query the top location</p>
<p class="MsoListParagraphCxSpMiddle" style="text-indent: -0.25in; padding-left: 30px;"><span style="font-family: Symbol;">&middot;<span style="font: 7pt/normal 'Times New Roman'; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>Each model consists of a single root cluster of type <span style="line-height: 107%; font-family: 'Courier New'; font-size: 9pt;">ClusterRoot</span></p>
<p class="MsoListParagraphCxSpMiddle" style="text-indent: -0.25in; padding-left: 30px;"><span style="font-family: Symbol;">&middot;<span style="font: 7pt/normal 'Times New Roman'; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>Each hierarchy level represents a different zoom level and contains one to four child clusters (which are either of type <span style="line-height: 107%; font-family: 'Courier New'; font-size: 9pt;">ClusterHierarchy</span> or <span style="line-height: 107%; font-family: 'Courier New'; font-size: 9pt;">ClusterLeaf</span> in the case that the minimal cluster size has been reached) (Figure 8)</p>
<p class="MsoListParagraphCxSpMiddle" style="text-indent: -0.25in; padding-left: 30px;"><span style="font-family: Symbol;">&middot;<span style="font: 7pt/normal 'Times New Roman'; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>Each leaf contains a collection of <span style="line-height: 107%; font-family: 'Courier New'; font-size: 9pt;">LocationInstance</span> objects (of which each contains a location object of type T)</p>
<p class="MsoListParagraphCxSpLast" style="text-indent: -0.25in; padding-left: 30px;"><span style="font-family: Symbol;">&middot;<span style="font: 7pt/normal 'Times New Roman'; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span>The cluster hierarchy can be traversed using a visitor which implements<span style="line-height: 107%; font-family: 'Courier New'; font-size: 9pt;"> IClusterVisitor</span></p>
<p class="MsoNormal" style="text-align: center; page-break-after: avoid;" align="center"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/2158.image010.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/2158.image010.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/2158.image010.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/2158.image010.png" alt="" border="0" /></a></p>
<p class="MsoCaption" style="text-align: center;" align="center">Figure 10: Sample cluster hierarchy</p>
<p class="MsoNormal">&nbsp;</p>
<h1 class="WordSection3">Clustering locations to identify top locations</h1>
<p class="MsoNormal">Using location clustering to identify top locations is straightforward:</p>
<p class="MsoListParagraphCxSpFirst" style="text-indent: -0.25in;">1.<span style="font: 7pt/normal 'Times New Roman'; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>1. Create an instance of <span style="line-height: 107%; font-family: 'Courier New'; font-size: 9pt;">LocationClustering&lt;T&gt;</span> by typifying the location object and specifying the size of the leaf in meters<br /> <span style="background: white; color: blue; line-height: 107%; font-family: Consolas; font-size: 9.5pt;"><br /> </span><span style="background: white; line-height: 107%; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var locations = new LocationClustering&lt;string&gt;(200);</span>&nbsp;&nbsp;</p>
<p class="MsoListParagraphCxSpMiddle" style="text-indent: -0.25in; padding-left: 30px;">2.&nbsp;Add locations using the <span style="line-height: 107%; font-family: 'Courier New'; font-size: 9pt;">AddLocation</span> method</p>
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; locations.AddLocation(latitude, longitude, &ldquo;&lt;tripId&gt;&rdquo;);<br />&nbsp;<br /> </span>Locations can be added at any time. This allows the model to learn even after the first top leaves have been queried.</p>
<p class="MsoListParagraphCxSpMiddle" style="line-height: normal; margin-bottom: 0pt;">&nbsp;</p>
<p class="MsoListParagraphCxSpLast" style="text-indent: -0.25in;">3.<span style="font: 7pt/normal 'Times New Roman'; font-size-adjust: none; font-stretch: normal;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>3. Once the locations have been added, just query the top clusters using the <span style="line-height: 107%; font-family: 'Courier New'; font-size: 9pt;">GetSortedLeafs </span>method. This returns an enumerable with the requested number of top leaves (the top clusters).</p>
<p class="MsoNormal" style="padding-left: 30px; margin-left: 1in;"><span style="font-family: courier new,courier;"><span style="background: white; line-height: 107%; font-size: 9pt;">foreach (var leaf in locations.</span><span style="line-height: 107%; font-size: 9pt;">GetSortedLeafs(maxNrTopClusters)) {<br /> &nbsp;&nbsp; foreach (var location in leaf.GetLocations())<br /> &nbsp;&nbsp; {<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var tripId = location.LocationObject;<br /> &nbsp;&nbsp; }<br /> }</span></span></p>
<p class="MsoNormal" style="margin-left: 1in;">&nbsp;</p>
<h1 class="WordSection3">Predict end locations based on start location</h1>
<p class="MsoNormal">To predict end locations, we build a single cluster <strong><span style="text-decoration: underline;">per driver</span></strong> using previous trips. We add the start locations and store the trip&rsquo;s end location as the <span style="line-height: 107%; font-family: 'Courier New'; font-size: 9pt;">LocationObject</span>. In the example below, we store objects of type <span style="line-height: 107%; font-family: 'Courier New'; font-size: 9pt;">TripSummary</span> (which contains latitude and longitude of the end location):</p>
<p class="MsoNormal" style="margin-left: 0.5in;">&nbsp;</p>
<p class="MsoNormal" style="padding-left: 30px; margin-left: 0.5in;"><span style="background: white; line-height: 107%; font-family: 'Courier New'; font-size: 9pt;">var locations = new LocationClustering&lt;TripSummary&gt;(200);</span></p>
<p class="MsoNormal" style="padding-left: 30px; margin-left: 0.5in;"><span style="background: white; line-height: 107%; font-family: 'Courier New'; font-size: 9pt;">locations.AddLocation(startLat, startLon, tripSummary);</span></p>
<p class="MsoNormal" style="margin-left: 0.5in;">&nbsp;</p>
<p>Once all the trips have been loaded, the model can be used to predict end locations based on start latitude and longitude.&nbsp;&nbsp; In its essence, this requires the following steps:&nbsp;</p>
<p class="MsoListParagraphCxSpFirst" style="text-indent: -0.25in; padding-left: 30px;">1. Retrieve the leaf for the start location. In the case the retrieved leaf contains no trip, we iterate over the parent cluster until we have a cluster which contains at least one trip.</p>
<p class="MsoListParagraphCxSpFirst" style="text-indent: -0.25in; padding-left: 30px;">2. For each trip in the cluster, we retrieve its end location leaf and count how many trips end in the same leaf.</p>
<p class="MsoListParagraphCxSpFirst" style="text-indent: -0.25in; padding-left: 30px;">3. To calculate the probability of our prediction, we divide the number of locations in a leaf by the total number of locations.</p>
<p class="MsoNormal"><span style="line-height: 107%; font-family: 'Courier New'; font-size: 9pt;">public List&lt;RoutePredictionItem&gt; PredictEndLocation(double startLat, double startLon)<span style="background: white;"><br /> {<br /> &nbsp;&nbsp; var cluster = startLocations.GetLeaf(startLat, startlon);</span></span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp; int clusterLevel = 0;<br /> </span><span style="font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp; // check how many start points we have within a cluster </span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp; // and iterate through the cluster hierarchy to get at least 1 location<span style="background: white;"><br /> &nbsp;&nbsp; while (cluster.GetNrOfLocations() == 0){</span></span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var parent = cluster.GetParentItem();</span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (parent != null){</span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (parent is ClusterItem&lt;LocationInstance&lt;TripSummary&gt;&gt;){</span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cluster = parent as ClusterItem&lt;LocationInstance&lt;TripSummary&gt;&gt;;</span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; clusterLevel++;</span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp; }</span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;</span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp; Dictionary&lt;ClusterLeaf&lt;TripSummary&gt;, RoutePredictionItem&gt; leafs = </span><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp; </span></p>
<p class="MsoNormal" style="line-height: normal; text-indent: 0.5in; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">new Dictionary&lt;ClusterLeaf&lt;TripSummary&gt;, RoutePredictionItem&gt;();</span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp; // create a list of endLocation leafs and count how many times an end location<br /> &nbsp;&nbsp; // is part of the leaf</span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp; foreach (var location in cluster.GetLocations()){</span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ClusterLeaf&lt;TripSummary&gt; leaf = <br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; location.LocationObject.GetParentItem() as ClusterLeaf&lt;TripSummary&gt;;</span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;"><br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!leafs.ContainsKey(leaf)){</span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; leafs[leaf] = new RoutePredictionItem() { </span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Lat = leaf.CenterLat, </span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;Lon = leaf.CenterLon, </span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; NrOfEndPoints = 0,</span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Probability = 0.0 };</span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; leafs[leaf].NrOfEndPoints++;<br /> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; leafs[leaf].Probability = </span><span style="font-family: 'Courier New'; font-size: 9pt;">leafs[leaf].NrOfEndPoints /cluster.</span> <span style="font-family: 'Courier New'; font-size: 9pt;">GetNrOfLocations();</span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp; }</span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp; var predictions = leafs.Values.ToList();<br /> &nbsp;&nbsp; // sort the predictions on NrOfEndPoints</span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp; predictions.Sort();</span></p>
<p class="MsoNormal" style="line-height: normal; margin-bottom: 0pt;"><span style="background: white; font-family: 'Courier New'; font-size: 9pt;">&nbsp;&nbsp; return predictions;</span></p>
<p class="MsoNormal"><span style="background: white; line-height: 107%; font-family: 'Courier New'; font-size: 9pt;">}</span></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">To test the model and measure its accuracy, we split the dataset into a training and a validation set. Now we only load the training data into the model and validate our algorithm by predicting the end locations of our validation dataset. We now simply compare the predictions with the actual values and calculate the distance between them.</p>
<h1 class="WordSection3">&nbsp;</h1>
<h1 class="WordSection3">Challenges</h1>
<p class="MsoNormal">One challenge we encountered was the creation of trip summaries, basically the extraction of the start and end location from the transmitted data stream. Since GPS data isn&rsquo;t always accurate, we have to clean the data using different filters to ensure accuracy. To do so, we built a data pipeline and applied different filters (for example, a filter to remove jittering or a filter to recognize one-off measurements). This work will be published in a separate paper.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">Another challenge presented was the <span lang="EN">decimal degrees format of </span>latitude and longitude: Scaling and traversing across the different hierarchy levels is much more intuitive and less error prone when using a projection of metric X and Y axes (e.g. in meters). That&rsquo;s why decimal degrees are only used at the service boundary, all internal operations are using the projection in meters. The transformation between the different projections have been implemented in a separate library. This geo transformation library can be found on GitHub: <a href="https://github.com/juergenpf/Geodesy">https://github.com/juergenpf/Geodesy</a>.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">An alternative approach to the proposed solution could be to use SQL Servers&rsquo; spatial data capabilities to count start and end locations in predefined geo-fences.</p>
<h1 class="WordSection3">&nbsp;</h1>
<h1 class="WordSection3">Opportunities for Reuse</h1>
<p class="MsoNormal">The hierarchical location clustering algorithm can be re-used for any solution that needs to identify location hot-spots or requires a hierarchical geo-fencing. The implemented Visitor allows to traverse the hierarchy and enables additional scenarios such as cluster based aggregation or visualization.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">The route prediction algorithm represents a simple but yet effective way to predict end points of frequently done trips, and can be used in a variety of different scenarios. The GitHub repository contains a test project which demonstrates route prediction and validation using sample location data.&nbsp;</p>
<p class="MsoNormal">&nbsp;</p>
<p><em>Beat Schwegler is a Principal Architect on the Partner Catalyst Team based in Switzerland. Beat enjoys working with organizations of all sizes to develop innovative solutions. He calls himself &ldquo;head in the cloud &ndash; feet on the ground&rdquo; and loves to share his experiences at conferences worldwide. He blogs at <a href="http://www.cloudbeatsch.com/">www.cloudbeatsch.com</a>.</em></p>]]></content:encoded>
    </item>
    <item>
      <title>Optimizing Ember and Ember-CLI on Windows</title>
      <link>https://docs.microsoft.com/archive/blogs/partnercatalystteam/optimizing-ember-and-ember-cli-on-windows</link>
      <pubDate>Fri, 19 Jun 2015 14:07:00 GMT</pubDate>
      <dc:creator><![CDATA[sarasp@outlook.com]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/partnercatalystteam/2015/06/19/optimizing-ember-and-ember-cli-on-windows/</guid>
      <description><![CDATA[Optimizing Ember and Ember-CLI on Windows
By Felix Rieseberg, Partner Catalyst Team
&nbsp;
Ember...]]></description>
      <content:encoded><![CDATA[<h1>Optimizing Ember and Ember-CLI on Windows</h1>
<p class="MsoNormal">By Felix Rieseberg, Partner Catalyst Team</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">Ember is one of the most popular MVC web frameworks available today - Yahoo, Vine, Groupon, LinkedIn, ZenDesk are only few of the websites built with it. This case study describes the development and usage of <em>ember-cli-windows, </em>a small command-line utility greatly improving the developer experience by reducing build times for Ember users running Windows 10, 8.1, 8, or 7. The tool has now become the official way of <a href="http://www.ember-cli.com/#windows">running Ember-CLI on Windows</a>.&nbsp; This technique can be more broadly applied to optimize any scenario involving lots of disk read/write operations.</p>
<h1>&nbsp;</h1>
<h1>Customer Problem</h1>
<p class="MsoNormal">A core component of Ember is its powerful tooling: Ember CLI is a command-line client that makes it easier to scaffold, test, develop and build complex web applications. One of the main features is the build process.&nbsp; Ember-CLI transpiles modern JavaScript code (ECMAScript 6) or JavaScript-like languages (TypeScript, CoffeeScript, etc.) and dependencies into optimized, backwards-compatible ECMAScript 5 JavaScript code. This process is run during development whenever a file change occurs (to render the live preview of the web app), during testing, and when the app is built for production.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">On Windows, the build process can be magnitudes slower than on Linux or Mac OS X. Since the build process is executed in response to any local file change during development, Windows developers quickly find themselves staring at a slow &ldquo;<em>Building&hellip;</em>&rdquo; multiple times in an hour, wondering why the same build only takes seconds on OS X and Linux</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">Why does this happen?&nbsp;&nbsp; It turns out that two services, Windows Defender and the Windows Search Index monitor new files and writes to existing files.&nbsp;&nbsp; These services are valuable and important &ndash; Windows Defender protects against viruses, and Windows Search Index ensures that you can find what you&rsquo;re looking for on your machine.&nbsp; But this good and valuable work has unintended consequences.&nbsp; During the build process, many temporary files get created by the various code transpilers. As this happens, Windows Defender and Windows Search Index examine and lock these temporary files. While the two services check thousands of small files, the developer sits and waits.</p>
<h1>&nbsp;</h1>
<h1>Overview of the Solution</h1>
<p class="MsoNormal">A solution exists: Since the temporary files are simply copies of existing code, the user can safely disable both the Windows Search Index and Windows Defender for the local <em>tmp </em>folder in an Ember application source folder.&nbsp;</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">Ember-CLI-Windows was developed in close collaboration with the Ember team and open source volunteers to automate the full configuration (including the disabling step mentioned above), enabling users to simply run <em>ember-cli-windows </em>in any source folder to reduce build times by up to 70%.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">The same practice can be used to optimize the performance of any scenario involving lots of disk read/write operations.&nbsp; See <a href="http://social.technet.microsoft.com/wiki/contents/articles/953.microsoft-anti-virus-exclusion-list.aspx"><span style="font-family: 'Segoe UI',sans-serif;">http://social.technet.microsoft.com/wiki/contents/articles/953.microsoft-anti-virus-exclusion-list.aspx</span></a><span style="font-family: 'Segoe UI',sans-serif; color: #262626;"> for more details.</span></p>
<h1>&nbsp;</h1>
<h1>Implementation</h1>
<p class="MsoNormal">At the core, Ember-CLI-Windows is a <a href="https://github.com/felixrieseberg/ember-cli-windows/blob/master/bin/ember-cli-windows">Node.js command-line tool that programmatically invokes PowerShell</a> scripts to determine the current version of Windows and the current user rights.&nbsp; Then it appropriately configures both Windows Defender and the Windows Search Index.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">function runPowershell(scriptPath, name, cb) {</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp; var child = spawn('powershell.exe', ['-NoProfile', '-NoLogo', "&amp; {&amp; '"+ scriptPath + "' -</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">path '" + dir + "'}"]), // jshint ignore:line</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; output = [];</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">&nbsp;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp; child.stdout.on('data', function (data) {</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; console.log(data.toString());</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; output.push(data.toString());</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp; });</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">&nbsp;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp; child.stderr.on('data', function (data) {</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; console.log('Configuration Error: ', data.toString());</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; output.push(data.toString());</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp; });</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">&nbsp;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp; child.on('exit', function () {</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; console.log(name + ' configured!');</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; cb(output);</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp; });</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">&nbsp;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">&nbsp;&nbsp;&nbsp; child.stdin.end();</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;"><span style="font-size: 9.0pt; line-height: 107%; font-family: Consolas;">}</span></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoCaption" style="text-align: center;"><em>Snippet 1: Running PowerShell from Node.js (backwards-compatible to Windows 7</em>&nbsp;</p>
<p class="MsoNormal">The two PowerShell scripts (<a href="https://github.com/felixrieseberg/ember-cli-windows/blob/master/powershell/setup-defender.ps1">setup-defender.ps14</a> and <a href="https://github.com/felixrieseberg/ember-cli-windows/blob/master/powershell/setup-search.ps1">setup-search.ps15</a>) were initially only written to support Windows 8.1 and later versions, but the open source community quickly stepped in to provide backwards compatibility for Windows 7 (<em>Thanks to bschuedzig, kellyselden, Jackbennet and petetnt!</em>).</p>
<h1>&nbsp;</h1>
<h1>Installing and using Ember-CLI-Windows</h1>
<p class="MsoNormal">Ember-CLI-Windows comes in two forms: a standalone command line client for users who are developing multiple applications, and a non-global addon to Ember-CLI. In both cases, Node.js and NPM are required (as they are for Ember-CLI).</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">To install and execute the standalone version, simply run:&nbsp;</p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; background: #F7F7F7;"><span style="font-size: 10.0pt; font-family: Consolas; color: #333333; border: none windowtext 1.0pt; padding: 0in;">npm install ember-cli-windows -g</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; background: #F7F7F7;"><span style="font-size: 10.0pt; font-family: Consolas; color: #333333; border: none windowtext 1.0pt; padding: 0in;">ember-cli-windows</span></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">Should you get a PSSecurityException, allow your PowerShell to execute the script. Run the following command as Administrator:&nbsp;</p>
<pre style="background: #F7F7F7;"><code><span style="font-family: Consolas; color: #333333; border: none windowtext 1.0pt; padding: 0in;">Set-ExecutionPolicy Unrestricted -scope Process</span></code></pre>
<pre style="margin-bottom: 12.0pt; background: #F7F7F7;"><code><span style="font-family: Consolas; color: #333333; border: none windowtext 1.0pt; padding: 0in;">ember-cli-windows</span></code></pre>
<h1>&nbsp;</h1>
<h1>Installing and using Ember-CLI-Windows as an Ember Addon</h1>
<p class="MsoNormal">Ember-CLI-Windows can be invoked from any Ember-CLI installation. If you&rsquo;re already in a scaffolded Ember-CLI application, simply install the addon by running the following command:</p>
<pre style="margin-bottom: 12.0pt; background: #F7F7F7;"><code><span style="font-family: Consolas; color: #333333; border: none windowtext 1.0pt; padding: 0in;">npm install --save-dev ember-cli-windows-addon</span></code></pre>
<p class="MsoNormal">To execute Ember-CLI-Windows, just run:</p>
<pre style="background: #F7F7F7;"><code><span style="font-family: Consolas; color: #333333; border: none windowtext 1.0pt; padding: 0in;">ember windows</span></code></pre>
<h1>&nbsp;</h1>
<h1>Performance Gained</h1>
<p class="MsoNormal">Mileage varies, but some users were kind enough to share their results. In the case of this Dutch company, the build time went from 41 seconds to 8 seconds.</p>
<p class="MsoNormal"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/7573.image001.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/7573.image001.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/7573.image001.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/7573.image001.png" alt="" border="0" /></a></p>
<h1>&nbsp;</h1>
<h1>Challenges</h1>
<p class="MsoNormal">Automating the configuration on Windows 8.1 and later versions was quite easy, while achieving the same goal on Windows 7 was much more difficult. The advances Windows has made in command line automation since Windows 8 were very clear. Before Windows 8.1, edge-cases like special user groups, group policies, folder names with special characters, etc. required some PowerShell magic. In the case of Windows Search, we bundled an Interop DLL to shim features missing in PowerShell v1.</p>
<h1>&nbsp;</h1>
<h1>Opportunities for Reuse</h1>
<p class="MsoNormal">The same challenge exists whenever code generators and other tools create and destroy a large number of temporary files in a non OS-level temp folder. While the use case sounds unique, it is common for open-source tools to not rely on the operating system&rsquo;s &ldquo;temp&rdquo; folder so that the tool will operate in a platform-agnostic way.&nbsp;</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal"><em>Felix Rieseberg is&nbsp;an&nbsp;Open Source Engineer in Microsoft's Partner Catalyst team where he spends his&nbsp;time coding on the cutting edge of technology, trying to improve developer's lives by releasing quality code on GitHub and touring the world presenting the lessons we've learned.&nbsp; Read his blog at <a href="http://www.felixrieseberg.com/,">http://www.felixrieseberg.com/,</a> follow him on twitter @felixrieseberg, and check out his work on github at <a href="https://github.com/felixrieseberg">https://github.com/felixrieseberg.</a></em></p>
<p class="MsoNormal">&nbsp;</p>]]></content:encoded>
    </item>
    <item>
      <title>Storing Data Securely in Azure Blob Storage with Azure Encryption Extensions</title>
      <link>https://docs.microsoft.com/archive/blogs/partnercatalystteam/storing-data-securely-in-azure-blob-storage-with-azure-encryption-extensions</link>
      <pubDate>Wed, 17 Jun 2015 07:48:00 GMT</pubDate>
      <dc:creator><![CDATA[Stefan Keir Gordon]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/partnercatalystteam/2015/06/17/storing-data-securely-in-azure-blob-storage-with-azure-encryption-extensions/</guid>
      <description><![CDATA[The Problem
When building applications which require data encrypted at rest and on-the-wire there...]]></description>
      <content:encoded><![CDATA[<h1>The Problem</h1>
<p>When building applications which require data encrypted at rest and on-the-wire there may be significant complexity added to the software development process.&nbsp; Organizations building these types of applications must not only invest in the additional resources to write the code, but they must also ensure the implementation is actually secure.</p>
<p>In this document you will learn how you can leverage the <a href="https://github.com/stefangordon/azure-encryption-extensions">Azure Encryption Extensions library</a> to easily encrypt data you store in Azure Blob Storage using Microsoft&rsquo;s proven <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa380245%28v=vs.85%29.aspx">Cryptographic Service Providers</a>.&nbsp; This solution also allows you to upgrade existing Azure Blob Storage code with minimal changes.</p>
<h1>The Solution</h1>
<p>Azure Encryption Extensions is a simple library designed to streamline the work required to encrypt data stored in Azure Blob Storage. Data is encrypted on-the-fly as it is uploaded to Azure, and decrypted as it is downloaded. Unencrypted data <strong>never leaves your machine</strong> and you can <strong>manage your keys however you'd like.</strong></p>
<p>The library provides a set of wrappers over the public Azure Storage SDK which closely mirror the existing storage methods.&nbsp; This allows developers to access blob storage in a standard way, and reference existing documentation, while still achieving industry-standard security.</p>
<h1>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/7563.aee.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/7563.aee.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/7563.aee.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/180x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/7563.aee.png" alt="" border="0" /></a></h1>
<h2>Choosing the Encryption Method</h2>
<p>Two core types of encryption are supported.&nbsp; Symmetric and Asymmetric.&nbsp; Here we will provide a high level description of how they work, but for more details on the implementation see the links at the bottom of this document.</p>
<p>The <strong>asymmetric mode</strong> provides encryption when you prefer to use a Public-Private Key pair, such as an X509 certificate. This provider allows you to encrypt data with the public key, and decrypt data with the private key. It is useful in scenarios where you want to let systems encrypt data but not give them access to decrypt it. <strong>You can share the public (encryption) key broadly while maintaining the security of your data by protecting the private (decryption) key.</strong></p>
<p>Asymmetric encryption is valued for the flexibility in managing encryption and decryption keys separately, however it can be very slow for larger datasets.&nbsp;</p>
<p>Azure Encryption Extensions solves this by implementing an industry standard pattern of first encrypting your data with a random symmetric key (very fast) and only using asymmetric encryption to protect the random key.&nbsp; This random key is then encrypted asymmetrically using your key-pair. The encrypted random key is prepended to the encrypted stream so that it can be recovered as part of the decryption process. This whole process happens on-the-fly during reads and writes with no buffering or disk access required.</p>
<p>The <strong>symmetric mode</strong> is designed for scenarios where you prefer to use the same key for encryption and decryption. This is often more simple to manage, and performs better than the asymmetric option. The provider performs industry standard AES encryption. &nbsp;</p>
<p>&nbsp;</p>
<h2>Storing your keys</h2>
<p>Azure Encryption Extensions is intentionally non-opinionated about key storage and permits a wide variety of options for key generation and use.&nbsp;</p>
<p>For all encryption types default constructors will generate cryptographically random and secure keys, which you can then export easily as a JSON string, or write to a file, and store as appropriate.</p>
<p>Alternatively you can provide your own keys in a variety of formats, including X509 Certificates, binary arrays, or JSON strings.</p>
<p>It is essential that you have an appropriate key management solution for your application, and this should be one of your first design decisions.&nbsp; Some scenarios may benefit from working <a href="http://azure.microsoft.com/en-us/services/key-vault/">with Azure Key Vault</a> while others may have custom key management strategies.</p>
<p>&nbsp;</p>
<h2>Using the Library</h2>
<p>Here we present a simple example of encrypting and uploading a file to Azure Blob Storage, and then downloading and decrypting it.&nbsp; In this example a typical X509 certificate is used for asymmetric encryption.</p>
<div>
<pre class="scroll"><code class="csharp"> CloudBlockBlob blob = container.GetBlockBlobReference("TestBlob"); <br /> <br /> // Create an Asymmetric provider from an X509Certificate2<br /> var provider = new AsymmetricBlobCryptoProvider(certificate); <br /> <br /> // Encrypt and upload the file to Azure, passing in our provider <br /> blob.UploadFromFileEncrypted(provider, path, FileMode.Open);<br /> <br /> // Download and decrypt the file<br /> blob.DownloadToFileEncrypted(provider, destinationPath, FileMode.Create);</code></pre>
</div>
<p>Note that the Upload and Download methods have signatures which are very similar to those from the underlying Azure Storage SDK.&nbsp; Simply append the word &ldquo;Encrypted&rdquo; to the end of the methods to leverage the encrypted version and include one additional parameter to reference the encryption provider.&nbsp; Defaults for all optional parameters will match the original methods.</p>
<h1>Code Artifacts</h1>
<p>Azure Encryption Extensions is available as a library released under a MIT license.&nbsp; It is fully open source on GitHub and compatible with .Net 3.5 or later.</p>
<p>You can install the library via <a href="https://www.nuget.org/packages/AzureEncryptionExtensions">NuGet</a>:</p>
<div>
<pre class="scroll"><code class="csharp">PM&gt; Install-Package AzureEncryptionExtensions</code></pre>
</div>
<p>&nbsp;</p>
<p>Or get the latest version by syncing the repository directly:</p>
<h1><a href="https://github.com/stefangordon/azure-encryption-extensions">https://github.com/stefangordon/azure-encryption-extensions</a></h1>
<p>&nbsp;</p>
<p>Extensive documentation on the encryption implementation and library usage is available in the <a href="https://github.com/stefangordon/azure-encryption-extensions">readme</a>.</p>
<h1>&nbsp;</h1>
<h1>Summary</h1>
<p>Azure Encryption Extensions is an easy way for teams to add encryption to their application while leveraging Azure Blob Storage.&nbsp; It helps ensure you implement your security in an industry standard way and permits easy retrofitting of legacy storage applications.&nbsp; These extensions can be used in client applications ranging from line-of-business desktop solutions to Xamarin based mobile apps.&nbsp; Additionally, the extensions can be used in server side solutions running in Azure or your own datacenter.</p>
<div>&nbsp;</div>
<div><em><span class="s1">Stefan Gordon is a Principal Software Engineer on the Partner Catalyst Team where he uses his distributed services background to help build innovative open source tools and platforms.&nbsp; You can follow his other work at&nbsp;<a title="stefangordon.com" href="http://www.stefangordon.com/">stefangordon.com</a></span></em></div>]]></content:encoded>
    </item>
    <item>
      <title>AllJoyn Cordova Plugin</title>
      <link>https://docs.microsoft.com/archive/blogs/partnercatalystteam/alljoyn-cordova-plugin</link>
      <pubDate>Thu, 11 Jun 2015 15:13:00 GMT</pubDate>
      <dc:creator><![CDATA[Stefan Keir Gordon]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/partnercatalystteam/2015/06/11/alljoyn-cordova-plugin/</guid>
      <description><![CDATA[The Problem
The AllJoyn protocol is an emerging leader in the device communications space. Think of...]]></description>
      <content:encoded><![CDATA[<h1>The Problem</h1>
<p>The AllJoyn protocol is an emerging leader in the device communications space. Think of it as &ldquo;Internet of Things&rdquo; on your local LAN &mdash; connecting your TV, lights, stereo and dishwasher to each other. See <a href="http://www.allseenalliance.org">www.allseenalliance.org</a> for details about the protocol and the alliance behind it.</p>
<p>With active support from most <a href="https://allseenalliance.org/about/members">major electronics manufacturers</a>, devices are starting to be released to the public. &nbsp;All new LG smart TVs have AllJoyn support, and LifX, an exciting new Kickstarter-funded smart lightbulb is quickly gaining traction. &nbsp;Qualcomm is building AllJoyn-enabled chipsets to power a wide assortment of upcoming consumer devices. &nbsp;</p>
<p>Unfortunately, despite significant support from manufacturers, software to actually manage these devices and leverage AllJoyn is almost nonexistent.&nbsp; OEMs still prefer propriety communications protocols for their applications and third party developers have mostly avoided participating.&nbsp;</p>
<p>Several barriers have prevented significant software investment in the space and limited consumer adoption:</p>
<ul>
<li>No direct cross-platform support in existing AllJoyn code
<ul>
<li>It is possible to leverage the existing code on a variety of devices, but each must be built independently and with little prior art.</li>
</ul>
</li>
<li>Lack of community involvement
<ul>
<li>The existing code and documentation are extremely complex and inaccessible to most developers.</li>
<li>Third-party samples and frameworks are rare.</li>
</ul>
</li>
<li>AllJoyn Routers are uncommon on most LANs.
<ul>
<li>AllJoyn requires a router or gateway on the local network to facilitate communications.&nbsp; These have been challenging to setup and out of reach of most consumers.</li>
</ul>
</li>
</ul>
<h1>Overview of the Solution</h1>
<p>To bring AllJoyn to a broader audience it is critical to unleash a broad developer community that can quickly build applications and frameworks to support the protocol.&nbsp; To do this we must lower the barriers to entry in the space by providing solutions that work with common tools and efficient workflows.</p>
<p><a href="http://www.stefangordon.com/wp-content/uploads/2015/02/alljoyn_cordova.jpg"><img style="max-width: 200px;" src="http://www.stefangordon.com/wp-content/uploads/2015/02/alljoyn_cordova.jpg" alt="" border="0" /></a></p>
<p>Microsoft Technical Evangelism &amp; Development (TED) took the approach of building a fully cross-platform solution that enables developers to rapidly build AllJoyn applications with modern web technologies which can be deployed to iOS, Android, Windows Phone and Windows Desktop from a single code base.</p>
<p>The core of this solution is the AllJoyn Plugin for Cordova.&nbsp; Apache Cordova is arguably the most robust option for cross platform mobile development in JavaScript and HTML. &nbsp;It powers PhoneGap, Ionic, AppGyver and many other&nbsp;popular platforms. &nbsp;This AllJoyn plugin can be used in any Cordova based framework to enable authoring of AllJoyn applications in JavaScript to run across all devices.</p>
<p>The AllJoyn Plugin for Cordova seamlessly ports and compiles the native C AllJoyn SDK for all platforms.&nbsp; It then wraps the SDK with platform-specific shimming layers, and finally unifies it across all platforms with a simplified JavaScript API.&nbsp; The plugin provides an abstraction layer that enables developers to simply start writing JavaScript and trust that their code will run across platforms, without worrying about challenges with native code.&nbsp;</p>
<p>The second facet of our solution is to help spur development by clearly demonstrating approaches to common scenarios.&nbsp;&nbsp; Three samples have been built using the plugin to illustrate approaches for AllJoyn lighting systems, televisions, and custom peer-to-peer implementations.</p>
<p>Finally, a major contribution from Windows is the inclusion of an AllJoyn Router in Windows 10 &ndash; ensuring that any LAN can trivially include the required router functionality by simply connecting a Windows PC or Xbox.</p>
<p>These tools, released in March 2015 at Mobile World Congress, are having an immediate impact on the AllJoyn landscape.&nbsp; Several partners are currently in development with them and plan to release applications this year to control and manage their devices.</p>
<h1>Additional Information</h1>
<h3><span style="font-size: 1.5em;">Find the Code</span></h3>
<p>The core plugin lives on Github<br /> <a href="https://github.com/AllJoyn-Cordova/cordova-plugin-alljoyn">https://github.com/AllJoyn-Cordova/cordova-plugin-alljoyn</a></p>
<p><br /> It can be installed from the Cordova plugin repository, or directly from the repo:</p>
<div>
<pre class="scroll"><code class="js">cordova plugin add org.allseen.alljoyn</code></pre>
</div>
<p>or</p>
<pre class="scroll"><code class="js">cordova plugin add https://github.com/AllJoyn-Cordova/cordova-plugin-alljoyn.git<br /> </code>&nbsp;</pre>
<h2>Learn</h2>
<p>We have put together several samples to help you get started. &nbsp;Each is a Cordova application based on the plugin. &nbsp;You can clone them; add the plugin; and build/deploy them right away.</p>
<h3>Controlling a TV</h3>
<p><a href="https://github.com/AllJoyn-Cordova/cordova-tv-alljoyn">https://github.com/AllJoyn-Cordova/cordova-tv-alljoyn</a></p>
<p>See the&nbsp;<a href="http://www.stefangordon.com/alljoyn-cordova-lg-tv-control-sample/">blog post covering the sample</a>.</p>
<p>As well as an&nbsp;<a href="https://andre.obsoleted.net/learn-alljoyn-tv/" target="_blank">in depth look</a>.</p>
<h3>Controlling a Lightbulb</h3>
<p><a href="https://github.com/AllJoyn-Cordova/cordova-lighting-alljoyn">https://github.com/AllJoyn-Cordova/cordova-lighting-alljoyn</a></p>
<p>See the&nbsp;<a href="http://www.stefangordon.com/lighting-control-sample-for-alljoyn-cordova-plugin/" target="_blank">blog post about the sample.</a></p>
<p>and a more&nbsp;<a href="http://www.phongcao.net/working-with-alljoyn-lifx-lightbulbs/" target="_blank">in depth overview</a>.</p>
<h3>Peer to Peer chat</h3>
<p><a href="https://github.com/AllJoyn-Cordova/cordova-chat-alljoyn">https://github.com/AllJoyn-Cordova/cordova-chat-alljoyn</a></p>
<p>&nbsp;</p>
<p class="p1"><em><span class="s1">Stefan Gordon is a Principal Software Engineer on the Partner Catalyst Team where he uses his distributed services background to help build innovative open source tools and platforms.&nbsp; You can follow his other work at <a title="stefangordon.com" href="http://www.stefangordon.com">stefangordon.com</a></span></em></p>]]></content:encoded>
    </item>
    <item>
      <title>Diagnosing Transient Network Issues and Deadlock on Azure Web Sites</title>
      <link>https://docs.microsoft.com/archive/blogs/partnercatalystteam/diagnosing-transient-network-issues-and-deadlock-on-azure-web-sites</link>
      <pubDate>Tue, 09 Jun 2015 18:58:00 GMT</pubDate>
      <dc:creator><![CDATA[Shawn Cicoria]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/partnercatalystteam/2015/06/09/diagnosing-transient-network-issues-and-deadlock-on-azure-web-sites/</guid>
      <description><![CDATA[Overview
Diagnosing distributed system failures and deadlock conditions can be difficult. This...]]></description>
      <content:encoded><![CDATA[<div>
<h1>Overview</h1>
<p>Diagnosing distributed system failures and deadlock conditions can be difficult. This case study provides insight on how to use existing tools from the Microsoft Azure platform. In addition utilities to assist in diagnosing and identification of root cause. With the information provided and the tools as a foundation, readers have additional guidance for identification of root cause, diagnosing and recommending solutions when complex issues arise on distributed solutions running in Microsoft Azure.</p>
<p><em>By Shawn Cicoria, <a href="http://blogs.msdn.com/b/partnercatalystteam">Partner Catalyst Team</a></em></p>
</div>
<h1>Customer Challenge</h1>
<p>The customer in this case was Jet.com which is utilizing a 3rd party storage engine that is an Open Source (OSS) platform providing persistence based upon an event-sourced application pattern. The provider exposes interfaces via REST and JSON in addition to providing client libraries for both .NET and Java; the .NET version is available through NuGet.</p>
<p><a title="Jet.com site" href="http://jet.com" target="_blank">Jet.com</a> is an e-commerce startup based in Hoboken, NJ. <a title="Jet.com site" href="http://Jet.com" target="_blank">Jet.com</a> offers a price club model for membership and provides optimization of pricing of shopping carts that looks to maximize savings for members by minimization of shipping and other relate charges. They are in beta period (as of May 2015) with limited insider release and have significant funding and backing.</p>
<p>What follows is an overview and a quick walkthrough of some of the Microsoft Azure tools that assist in diagnostics, along with an overview of a tool utilized to verify in a parallel path whether transient errors are occurring over time.</p>
<h2>Underlying Platform Transient Error</h2>
<p>During the diagnostics, it was apparent and confirm that there are small outages of a few seconds with Azure Web Sites and the current VNET support. The current implementation of VNET support in Azure WebSites is a Custom VPN layer developed by the Azure networking and WebSites team.</p>
<p>The custom VPN layer used by Azure WebSites, in this case, emitted a particular, and rare, Win32 exception. This error is <strong>WSAEADDRNOTAVAIL</strong> <em>(&ldquo;The requested address is not available in its context&rdquo;</em>) &ndash; an error message emitted by the VPN layer when a network becomes invalid. While this error is a defined Win32 error, it is emitted by the custom VPN layer that Azure Web Apps utilizes for VPN to customer VNETs.</p>
<p>For some background, Azure WebSites provide management, command line access, and file browsing on Azure Web Apps, a shared infrastructure component called Kudu is available, running in the browser. Kudu (https://github.com/projectkudu) connects to the web site through a custom implementation of virtual networking using a VPN software solution. Kudu can be accessed using a URL of the form:</p>
<pre class="scroll"><code class="html">http://{yourwebsitename}.scm.azurewebsites.net</code></pre>
<p>Its user interface includes file browsing and a choice of CMD or PowerShell command line access:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/6431.Picture1.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/6431.Picture1.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/6431.Picture1.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/6431.Picture1.png" alt="" border="0" /></a></p>
<p>Figure 1. Kudu UI</p>
<h2>Cloud and Transient Network Errors</h2>
<p>Distributed systems and especially those targeting cloud need to be resilient to transient network conditions that occasionally occur between tiers. As Virtual Networks (VNETs) in Azure help bridge varying instances of cloud services, Virtual Machines, and even Azure Web Apps, they are not as reliable as hardwired physical networks using switches.</p>
<p>VNETs can be thought of as software-defined networks that exist as virtual tunnels within physical networks. These virtual tunnels are created to isolate tenants and prevent intermingling of &lsquo;clear&rsquo; traffic. Ultimately, they translate to physical layer traffic that is all commingled, but at the IP layer, it is a secure encrypted tunnel. Thus, developers should expect failure at some point and need to design their code to be resilient to network failures.</p>
<h3>Provider Client Errors</h3>
<p>The symptom of the issue that occurred was that the provider&rsquo;s client library would eventually hang. Since all interactions with the Website were done through a singleton connection &ndash; which was hanging &ndash;the entire website stopped working. One recommendation is to provide a &ldquo;pool&rdquo; of objects; if one goes into error state, it is ejected from the pool and another is used in its place. However, the implementation didn&rsquo;t currently do this; additionally, while it may have minimized down time, it would have just masked a core issue in the provider library, not fixed it.</p>
<p>After creating a small repro MVC site and WebJobs[1] for testing the VNET and the Provider&rsquo;s client, all preliminary indications pointed to a deadlock condition in the provider&rsquo;s client library .NET code.</p>
<h2>Overview of the Solution</h2>
<h3>Finding the Problem</h3>
<p>In the end, the solution was for the Provider to deal with a deadlock situation in their client library managed code. However, given the obscurity of the error code, isolating this issue was initially challenging.</p>
<h3>Initial Diagnostics and Tools</h3>
<p>The first step was to establish a simple repro of the error. That was challenging to do as the VPN layer used in Azure Web Sites is a custom implementation. However, this was the first step.</p>
<p>A &ldquo;TCPPing&rdquo; diagnostic tool was created (see blog post) and the associated GitHub repo that contains the code:</p>
<p><a href="http://blogs.msdn.com/b/scicoria/archive/2015/02/15/troubleshooting-tool-azure-webjob-tcp-ping.aspx">http://blogs.msdn.com/b/scicoria/archive/2015/02/15/troubleshooting-tool-azure-webjob-tcp-ping.aspx </a></p>
<p><a href="https://github.com/cicorias/webjobtcpping">https://github.com/cicorias/webjobtcpping</a></p>
<h2>Diagnostics as a Service</h2>
<p>On Standard or Basic mode Azure Web Sites (not Shared), a feature called &ldquo;Diagnostics as a Service&rdquo; (DaaS) is available. This can be reached via the Web Site Kudu page:</p>
<pre class="scroll"><code class="html">http://{yourwebsitename}.scm.azurewebsites.net/DaaS</code></pre>
<p>From the page shown below, you can initiate a Memory Dump (other things too). We will then be able to leverage the Windows Debugger, or as done below using the &ldquo;automatic analyze&rdquo; feature of DaaS.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/3731.Picture2.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/3731.Picture2.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/3731.Picture2.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/3731.Picture2.png" alt="" border="0" /></a></p>
<p>Figure 2. Diagnostics as a Service</p>
<p>When running, it&rsquo;s easiest to choose the &lsquo;Schedule Analysis&rsquo; and select Live. To have the Analysis done, ensure you select it:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/8510.Picture3.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/8510.Picture3.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/8510.Picture3.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/8510.Picture3.png" alt="" border="0" /></a></p>
<p>Figure 3. Creating a Diagnostics Session</p>
<h3>Dump Analysis</h3>
<p>The analysis is provided as a single MHT file, readable in Internet Explorer. The first page indicates 2 errors, which are insignificant; however, the first warning, highlighted below is an indication of a blocked thread.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/7345.Picture4.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/7345.Picture4.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/7345.Picture4.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/7345.Picture4.png" alt="" border="0" /></a></p>
<p>Figure 4. Error Analysis</p>
<h3>Blocked Threads / Call Stack</h3>
<p>Generally a blocked thread with a Monitor.Wait occurs in multi-threading handling when the owner thread has not released an object. Examining the Stack Trace we see that the client code was awaiting error handling from a SocketError condition.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/8255.Picture5.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/8255.Picture5.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/8255.Picture5.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/8255.Picture5.png" alt="" border="0" /></a></p>
<p>Figure 5. Stack Trace</p>
<h2>Debugging Provider&rsquo;s Client Code</h2>
<p>In this case, the Provider&rsquo;s code is open sourced, so it was relatively easy to download the source and start debugging their code. While not finding the specific issue immediately the clues provided in the DaaS report were quick indications of areas to look int he code. After reviewing the code, we were able to identify an area of complex code that handles and wraps TCP Sockets.</p>
<h3>Collaboration with Engineering</h3>
<p>Instrumental in validation was working with one of the developers of Azure Web Sites VNET layer at Microsoft, we then acquired a development instance of Kudu and Azure Web Sites running on a dedicated PaaS instance. With this we were able to consistently reproduce the error condition. By working together, engineers from Azure, the Provider, and Jet.com were able to find and diagnose the problem, fix it, test the fix, and release a new build in a timeframe that enabled Jet.com to continue making progress on their upcoming release.</p>
<h3>Vendor Updates</h3>
<p>The Provider issued a GitHub update to their library, which was then tested and validated in our development environment.</p>
<h2>Code Artifacts</h2>
<h3>Azure Web Sites &ndash; Web Job Diagnostic Jobs</h3>
<p>This tool is a continuous WebJob that will TCP Ping (open a socket) to a remote address and report back via diagnostic logging if any failures are encountered. This was key in validating that the network was actually still working even when the customer&rsquo;s client code was not.</p>
<pre><a href="https://github.com/cicorias/webjobtcpping">https://github.com/cicorias/webjobtcpping </a></pre>
<h3>Web Jobs and Logging</h3>
<p>Azure Web Apps offers diagnostic logging &ndash; but it&rsquo;s important to understand where and how it works. Note that Azure Web Sites also will limit logging. Azure Web Sites limit log files to 100MB. Kudu&rsquo;s real-time console logging is limited based upon the # of lines, which appears to be around 270 lines (but isn&rsquo;t clearly defined in the documentation).</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/6011.Picture6.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/6011.Picture6.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/6011.Picture6.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/6011.Picture6.png" alt="" border="0" /></a></p>
<p>More information about setting up Diagnostics capabilities and limitations are in the following Azure articles.</p>
<ul>
<li>Monitor Web Apps in Azure App Service[2]</li>
<li>Troubleshoot a web app in Azure App Service using Visual Studio[3]</li>
<li>Enable diagnostics logging for web apps in Azure App Service [4]</li>
</ul>
<p>The Kudu console (Admin site) for Azure Web Sites gives access to the folders and directories of the site. It can be used to find and analyze the extensive pre-built logs that provide details regarding the operation of the site.</p>
<h3>Logging Helper Class</h3>
<p>In any distributed system logging is essential. There&rsquo;s nothing more helpful than logging that can be turned up for diagnosing what a running system is doing. Given we had limitations with running against a customer instance and initially running remote debugging sessions against the Kudu Development environment, logging provided much of what was needed.</p>
<p>While the Kudu Diagnostics Tool was helpful in identifying the deadlock, logging still provides key information that additionally identified and confirmed that no issues were encountered by the test tool when proper object handling during TCP Socket failures in code. The custom logging was applied within the test tool and test app. The recommendation here is to understand how Web Apps handle logging via Trace or even System.Console (standard output / error) and where that goes inside of Web Apps diagnostics (log files) and the Kudu file structure and limitations.</p>
<p>Here is an example of how to do custom logging with a helper class:</p>
<pre class="scroll"><code class="csharp">public class Helper<br />{<br /> <br /> public static void LogException(Exception ex)<br /> {<br /> if (ex is AggregateException)<br /> ((AggregateException)ex).Flatten();<br /> <br /> var msg1 = string.Format("Exception Occurred: {0}", ex.Message);<br /> var msg2 = string.Format("EX type: {0}", ex.GetType().ToString());<br /> <br /> Console.Error.WriteLine("ERROR: " + msg1);<br /> Console.Error.WriteLine("ERROR: " + msg2);<br /> Console.WriteLine("ERROR: " + msg1);<br /> Console.WriteLine("ERROR: " + msg2);<br /> Trace.TraceError(msg1);<br /> Trace.TraceError(msg2);<br /> }<br /> <br /> public static void LogMessage(string message)<br /> {<br /> var msg1 = string.Format("NOT AN ERROR: {0}", message);<br /> <br /> Console.WriteLine(msg1);<br /> Trace.TraceError(msg1);<br /> }<br /> }</code></pre>
<h2>Opportunities for Reuse</h2>
<p>Azure Web Sites provide many powerful tools for analyzing and monitoring a site&rsquo;s use and operations. In a system that uses a VPN, transient network conditions are to be expected, and can make isolating and fixing problems especially difficult. In this case study, we provided both an examination of the built-in tools as well as some tools we built to isolate problems caused by transient network issues. These approaches for identifying core underlying issues can be used in other systems that rely on VPNs, or in any situation where a more detailed understanding of the network status is required.</p>
<h2>Additional References and Links</h2>
<ul>
<li>[1] Azure Web Apps &ndash; WebJobs - <a href="http://azure.microsoft.com/en-us/documentation/articles/web-sites-create-web-jobs/">http://azure.microsoft.com/en-us/documentation/articles/web-sites-create-web-jobs/</a></li>
<li>[2] Monitor Web Apps in Azure App Service - <a href="http://azure.microsoft.com/en-us/documentation/articles/web-sites-monitor/">http://azure.microsoft.com/en-us/documentation/articles/web-sites-monitor/</a></li>
<li>[3] Troubleshoot a web app in Azure App Service using Visual Studio - <a href="http://azure.microsoft.com/en-us/documentation/articles/web-sites-dotnet-troubleshoot-visual-studio/">http://azure.microsoft.com/en-us/documentation/articles/web-sites-dotnet-troubleshoot-visual-studio/</a></li>
<li>[4] Enable diagnostics logging for web apps in Azure App Service - <a href="http://azure.microsoft.com/en-us/documentation/articles/web-sites-enable-diagnostic-log/">http://azure.microsoft.com/en-us/documentation/articles/web-sites-enable-diagnostic-log/</a></li>
</ul>
<div><em>Shawn Cicoria has been working on distributed systems for nearly 2 decades after dealing his last deck of cards on the Blackjack table. Follow Shawn at <a href="http://bit.ly/cicorias">http://bit.ly/cicorias</a> or <a href="https://twitter.com/cicorias">@cicorias</a></em></div>]]></content:encoded>
    </item>
    <item>
      <title>Data Anonymizing in Hadoop: A TED Case Study</title>
      <link>https://docs.microsoft.com/archive/blogs/partnercatalystteam/data-anonymizing-in-hadoop-a-ted-case-study</link>
      <pubDate>Thu, 04 Jun 2015 05:09:00 GMT</pubDate>
      <dc:creator><![CDATA[sarasp@outlook.com]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/partnercatalystteam/2015/06/04/data-anonymizing-in-hadoop-a-ted-case-study/</guid>
      <description><![CDATA[Data Anonymizing in Hadoop: A TED Case Study
by Beat Schwegler&nbsp;and Ivan Judson, Partner...]]></description>
      <content:encoded><![CDATA[<h1>Data Anonymizing in Hadoop: A TED Case Study</h1>
<p class="MsoNormal">by Beat Schwegler&nbsp;and Ivan Judson, Partner Catalyst Team</p>
<h1>&nbsp;</h1>
<h1>The Problem</h1>
<p class="MsoNormal">Our customer uses an on premise Cloudera Cluster to archive and mine customer related data. They wanted to use HDInsight for additional compute capacity and Microsoft Azure Machine Learning to create predictive models. However, their data contains PII (Personal Identifiable Information) and their company policy prevented them from uploading such data to a public cloud.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">One possible approach would be to simply remove all PII related data from the dataset. However this is not feasible because building the predictive models and its predictions are related to individual customers. Therefore we explored an approach in which we anonymize data before it is sent to the cloud and de-anonymize it, once the data is back on premise.</p>
<h1>Overview of the Solution</h1>
<p class="MsoNormal">We wanted to make the task of anonymizing/de-anonymizing feel like a native Hadoop capability. That&rsquo;s why we decided use Hadoop&rsquo;s Pig and extend its capabilities by implementing two user defined functions: ANONYMIZE and DEANONYMIZE. This allows us to embed anonymizing and de-anonymizing as part of an on premise PIG data transformation job, ensuring no PII goes off-premise:</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">Here an example anonymizing data (in this case the ownerId) as part of a Pig script:</p>
<p class="MsoNormal" style="margin-top: 0in; margin-right: 0in; margin-bottom: .0001pt; margin-left: 35.4pt;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New';">A = LOAD 'data/xyz_device' using PigStorage(';') AS (</span></p>
<p class="MsoNormal" style="margin-top: 0in; margin-right: 0in; margin-bottom: .0001pt; margin-left: 70.8pt;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New';">ownerId: chararray, <br /> specNr: chararray,</span></p>
<p class="MsoNormal" style="margin-top: 0in; margin-right: 0in; margin-bottom: .0001pt; margin-left: 70.8pt;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New';">senderId: chararray,</span></p>
<p class="MsoNormal" style="margin-top: 0in; margin-right: 0in; margin-bottom: .0001pt; margin-left: 70.8pt;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New';">deviceData: chararray);</span></p>
<p class="MsoNormal" style="margin-top: 0in; margin-right: 0in; margin-bottom: .0001pt; margin-left: 35.4pt;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New'; background: yellow;">B = FOREACH A GENERATE <strong>jaglion.ANONYMIZE(ownerId)</strong>, specNr, senderId, deviceData;</span></p>
<p class="MsoNormal" style="margin-top: 0in; margin-right: 0in; margin-bottom: .0001pt; margin-left: 35.4pt;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New';">jaglion.WASBSTORE B INTO 'data/devices/anonym/xyz_device' USING PigStorage (';');</span></p>
<p class="MsoNormal" style="margin-top: 0in; margin-right: 0in; margin-bottom: .0001pt; margin-left: 35.4pt;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New';">&nbsp;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt;">And here an example of de-anonymizing data:</p>
<p class="MsoNormal" style="margin-top: 0in; margin-right: 0in; margin-bottom: .0001pt; margin-left: 35.4pt;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New';">A = jaglion.WASBLOAD 'data/results/anonym/xyz_result' using PigStorage(';') AS (</span></p>
<p class="MsoNormal" style="margin-top: 0in; margin-right: 0in; margin-bottom: .0001pt; margin-left: 70.8pt;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New';">ownerId: chararray, <br /> resultData: chararray);</span></p>
<p class="MsoNormal" style="margin-top: 0in; margin-right: 0in; margin-bottom: .0001pt; margin-left: 35.4pt;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New'; background: yellow;">B = FOREACH A GENERATE <strong>jaglion.DEANONYMIZE(ownerId)</strong>, resultData;</span></p>
<p class="MsoNormal" style="margin-top: 0in; margin-right: 0in; margin-bottom: .0001pt; margin-left: 35.4pt;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New';">STORE B INTO 'data/xyz_result' USING PigStorage (';');</span></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">The anonymizing function uses Hadoop&rsquo;s HBASE as the persistent key/value store to retrieve and store the PII data correlation. This HBASE instance is part of the on premise Hadoop cluster.</p>
<p class="MsoNormal">We implemented two different correlation modes for different level of privacy:</p>
<h3>High privacy mode</h3>
<p class="MsoNormal">Each time the <span style="font-family: 'Courier New';">ANONYMIZE</span> function is called, it returns a unique id. In this mode, even if the <span style="font-family: 'Courier New';">customerId</span> is the same, the function will return two different anonymized ids. This mode ensures that off premise data can&rsquo;t be correlated across the anonymized dimension.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New';">ANONYMIZE(customerId) != ANONYMIZE(customerId)</span></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">High privacy mode can be used for independent jobs such as pattern recognition, large scale data transformation or model calculations. However it won&rsquo;t be useful for jobs which gain insight by correlating multiple dependent data items.</p>
<h3>Medium privacy mode</h3>
<p class="MsoNormal">The <span style="font-family: 'Courier New';">ANONYMIZE</span> function returns always the same anonymized id for the same request. This mode allows for off premise correlations across the same anonymized ids.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New';">ANONYMIZE(customerId) == ANONYMIZE(customerId)</span></p>
<h1>Code Artifacts</h1>
<p class="MsoNormal">The repository for the two user define functions (UDFs) can be found here: <a href="https://github.com/irjudson/jaglion">https://github.com/irjudson/jaglion</a>.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">To use the UDFs, the java package and its dependencies needs to be registered first:</p>
<p style="margin-top: 0in; margin-right: 0in; margin-bottom: .0001pt; margin-left: .5in;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New'; color: windowtext;">REGISTER /usr/lib/zookeeper/zookeeper.jar;</span></p>
<p style="margin-top: 0in; margin-right: 0in; margin-bottom: .0001pt; margin-left: .5in;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New'; color: windowtext;">REGISTER /usr/lib/hbase/hbase-client.jar;</span></p>
<p style="margin-top: 0in; margin-right: 0in; margin-bottom: .0001pt; margin-left: .5in;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New'; color: windowtext;">REGISTER /usr/lib/hbase/hbase-common.jar;</span></p>
<p style="margin-top: 0in; margin-right: 0in; margin-bottom: .0001pt; margin-left: .5in;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New'; color: windowtext;">REGISTER /usr/lib/hbase/hbase-protocol.jar;</span></p>
<p style="margin-top: 0in; margin-right: 0in; margin-bottom: .0001pt; margin-left: .5in;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New'; color: windowtext;">REGISTER /usr/lib/hbase/hbase-hadoop-compat.jar;</span></p>
<p style="margin-top: 0in; margin-right: 0in; margin-bottom: .0001pt; margin-left: .5in;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New'; color: windowtext;">REGISTER /usr/lib/hbase/lib/htrace-core.jar;</span></p>
<p style="margin-top: 0in; margin-right: 0in; margin-bottom: .0001pt; margin-left: .5in;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New'; color: windowtext;">&nbsp;</span></p>
<p style="margin-top: 0in; margin-right: 0in; margin-bottom: .0001pt; margin-left: .5in;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New'; color: windowtext;">REGISTER bin/jaglion.jar;<br /> <br /> </span></p>
<p class="MsoNormal">Now, two UDFs can be used within Pig. Assume we loaded the data into A using a statement similar to</p>
<p class="MsoNormal" style="text-indent: .5in;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New';">A = LOAD 'testdata';</span></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">Once A is loaded, the statement below anonymizes the first column in A using medium privacy (the same value will always generate the same anonymized value):</p>
<p class="MsoNormal" style="text-indent: .5in;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New';">B = FOREACH A GENERATE jaglion.ANONYMIZE($0, 0);</span></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">The following statement will generate a unique anonymized value for each value in A, regardless if the source values are the same:</p>
<p class="MsoNormal" style="text-indent: .5in;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New';">C = FOREACH A GENERATE jaglion.ANONYMIZE($0, 1);</span></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">To de-anonymize, we simply call the DEANONYMIZE function:</p>
<p class="MsoNormal" style="margin-bottom: .0001pt; text-indent: .5in;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New';">D = FOREACH B GENERATE jaglion.DEANONYMIZE($0);</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; text-indent: .5in;"><span style="font-size: 8.0pt; line-height: 107%; font-family: 'Courier New';">E = FOREACH C GENERATE jaglion.DEANONYMIZE($0);</span></p>
<h1>Opportunities for Reuse</h1>
<p class="MsoNormal">The anonymizing and de-anonymizing functions can be used in any Hadoop project that needs to remove PII data from a dataset, while still being able to correlate the anonymized data back to its original PII data.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">It can be leverage to enable hybrid cloud scenario or allow a dataset to be shared more broadly, either within the organization or among external partners.</p>
<p class="MsoNormal">&nbsp;</p>
<p><em>Ivan R. Judson is a software engineer on the Partner Catalyst Team where he leverages deep R&amp;D and startup experience to help partners (including startups!) solve their toughest problems. Passion for data and strong empathy for users are a common theme in his work, which he documents through talks at conferences and his blog at <a href="http://www.irjudson.org/">irjudson.org</a>.</em></p>
<p><em>Beat Schwegler is a Principal Architect on the Partner Catalyst Team based in Switzerland. Beat enjoys working with organizations of all sizes to develop innovative solutions. He calls himself &ldquo;head in the cloud &ndash; feet on the ground&rdquo; and loves to share his experiences at conferences worldwide. He blogs at <a href="http://www.cloudbeatsch.com">www.cloudbeatsch.com</a>.</em></p>]]></content:encoded>
    </item>
    <item>
      <title>DemocracyOS (YC-W15) and Deis on Azure: A TED Case Study</title>
      <link>https://docs.microsoft.com/archive/blogs/partnercatalystteam/democracyos-yc-w15-and-deis-on-azure-a-ted-case-study</link>
      <pubDate>Tue, 02 Jun 2015 10:06:00 GMT</pubDate>
      <dc:creator><![CDATA[Steven Edouard]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/partnercatalystteam/2015/06/02/democracyos-yc-w15-and-deis-on-azure-a-ted-case-study/</guid>
      <description><![CDATA[DemocracyOS (YC-W15) and Deis on Azure: A TED Case Study
Steven Edouard, Tim Park
The...]]></description>
      <content:encoded><![CDATA[<div class="WordSection1">
<h1>DemocracyOS (YC-W15) and Deis on Azure: A TED Case Study</h1>
<h1><span style="font-size: 12px;">Steven Edouard, Tim Park</span></h1>
<h1>The Problem<span style="font-size: 12px;">&nbsp;</span></h1>
<p class="MsoNormal"><strong style="mso-bidi-font-weight: normal;">Who is Democracy OS?</strong></p>
<p class="MsoNormal"><span class="SpellE">DemocracyOS</span> (DOS) is a Y-<span class="SpellE">Combinator</span> Winter 2015 cohort non-profit startup that aims to redefine how organizations make decisions, from government to academic organizations. DOS aims to redistribute decision making in organizations by making it easy for members to participate in policy-making, encouraging democracy within their group.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">DOS's product is completely open source and takes the form of an Express/Node.js application, backed by <span class="SpellE">MongoDB</span> for persistent data. You can think of the Democracy OS app similar to <span class="SpellE">WordPress</span> in that it's free and open to use; however, to monetize DOS intends to host instances of the application for organizations and charge a subscription fee.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal"><strong style="mso-bidi-font-weight: normal;">The Problem</strong></p>
<p class="MsoNormal">The high level problem that Democracy OS has is that they want to deploy their application on Azure with a highly productive developer workflow (like one that <span class="SpellE">Heroku</span> provides and similar to our Azure Websites workflow) on top of a Linux based infrastructure. This paper describes the work we did with them and the <span class="SpellE"><span class="GramE">Deis</span></span> open source project on Azure to enable this very common pattern we see across startup companies using open platforms on Azure.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">In their solution, Democracy OS must provision a new instance of the application as well as a new database to back the instance. Each application is hosted on a URL with the form:</p>
<p class="MsoNormal"><strong style="mso-bidi-font-weight: normal;">&lt;<span class="SpellE"><span class="GramE">organization</span>_name</span>&gt;</strong>.democracyos.com</p>
<p class="MsoNormal">Azure Websites didn't solve this problem for 2 reasons:</p>
<p class="MsoListParagraphCxSpFirst" style="text-indent: -.25in; mso-list: l0 level1 lfo1;"><span style="mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin;"><span style="mso-list: Ignore;">1)<span style="font: 7.0pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span>Azure Websites runs on Windows and we hit Windows-specific issues such as native modules not building on Windows or paths being too long which was not seen in their OSX &amp; Linux development environments.<span style="mso-spacerun: yes;">&nbsp; </span>This is a common problem with open stack oriented companies with our Azure Websites platform.</p>
<p class="MsoListParagraphCxSpLast" style="text-indent: -.25in; mso-list: l0 level1 lfo1;"><span style="mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin;"><span style="mso-list: Ignore;">2)<span style="font: 7.0pt 'Times New Roman';">&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span>DOS wanted to skip building their application on customer deployment and deploy only the prebuilt application bits per instance. At the same time they needed to easily push built versions of the application for each release and make those updates readily available for each instance. Out-of-the-box this wasn't as easy with Azure websites.</p>
<h1>Overview of the Solution</h1>
<p class="MsoNormal">What Democracy OS needed was infrastructure that would allow their customers to dynamically create Democracy OS instances quickly. It turns out that a Docker-based solution could solve their problem by distributing a prebuilt application via the Docker hub and using <span class="SpellE">Dockerfiles</span> to deploy their applications.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">However, DOS also needed container orchestration and HTTP traffic routing. Because they were already using <span class="SpellE">Heroku</span>, they loved the idea of <a href="http://deis.io"><span class="SpellE">Deis</span></a>, an open source cloud which aims to mimic <span class="SpellE">Heroku</span>-style deployments. Powered by Docker containers and the <span class="SpellE">NginX</span> web server, <span class="SpellE">Deis</span> allows easy implementation of the required deployment URL scheme and each customer instance can easily be scaled by any number of Docker containers making each instance horizontally scalable.￼</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">The figure below shows the application deployment workflow for a new customer <span class="SpellE">DemocracyOS</span> instance:</p>
<p class="MsoNormal" style="text-align: center;" align="center"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/0820.image002.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/0820.image002.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/71/24/0820.image002.png" original-url="http://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-71-24/0820.image002.png" alt="" border="0" /></a></p>
<p class="MsoNormal">The Democracy OS management application uses <span class="SpellE">Deis</span> controller API to automate the deployment of new DemocracyOS.com hosted <span class="SpellE">DemocracyOS</span> instances. They use the Docker hub to deploy the prebuilt version of their app and each customer app pulls the base image and is customized using deployment-specific configuration variables set through the controller <span class="SpellE">api</span>.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal"><span class="SpellE">Deis</span> <span class="GramE">runs</span> on <span class="SpellE">CoreOS</span>, a new Linux distribution which is very small, automatically patches itself with minimal downtime and most importantly, runs in a warehouse computing style over an entire cluster of virtual machines. A <span class="SpellE">CoreOS</span> cluster can power an entire Azure Cloud Service however, the <span class="SpellE"><span class="GramE">Deis</span></span> platform itself has had a few issues deploying to <span class="SpellE">CoreOS</span> on Azure and documentation &amp; support was still in its infancy.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">In particular, they ran into a number of <span class="GramE">clock</span> skew issues with <span class="SpellE">CoreOS</span> on Azure that caused the distributed <span class="SpellE">config</span> system (<span class="SpellE">etcd</span>) to think the cluster had fatally lost quorum and fail. We engaged the <span class="SpellE">CoreOS</span> team and partnered on a solution that resulted in a bug fix and pull requests to solve the NTP issue in <span class="SpellE">CoreOS</span> build 626. We verified the fix with <span class="SpellE">CoreOS</span> 626, load tested it, and chaos monkeyed the cluster and <span class="GramE">feel</span> confident in its performance now for future startups. As an aside, and as part of this work, we also migrated the nitrogen.io cluster we are using internally to <span class="SpellE"><span class="GramE">Deis</span></span> to get additional operational experience with the <span class="SpellE">Deis</span>/<span class="SpellE">CoreOS</span>/Docker stack for future customers and partners.</p>
<h1>How to Programmatically Deploy a '<span class="SpellE">Wordpress'</span>&nbsp;Style Application on <span class="SpellE">Deis</span></h1>
<p class="MsoNormal">Democracy OS used the <span class="SpellE">Deis</span> controller <span class="SpellE">api</span> to deploy a the democracy OS base image with deployment-specific variables to control which <span class="SpellE">monogdb</span> database backed the instance as well as other things such as the organization name.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">We created an example application which is what the actual Democracy OS instance manager implementation (found <a href="https://github.com/DemocracyOS/manager">here</a>) uses. This walk-through, with more detail can be found <a href="https://github.com/sedouard/deis-deployer/blob/master/README.md">here</a>.</p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">We want to use the <span class="SpellE">Deis</span> Controller API because we want the ability to plug the power of <span class="SpellE">deis</span>-scalable apps to any existing infrastructure we have.</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">&nbsp;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">For example - if you have a company that hosts an app for people such as </span><a href="http://wordpress.com/"><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #346eb7; text-decoration: none; text-underline: none;">Wordpress.com</span></a><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">, </span><a href="http://drupal.com/"><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #346eb7; text-decoration: none; text-underline: none;">Drupal</span></a><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;"> or </span><a href="http://democracyos.com/"><span class="SpellE"><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #346eb7; text-decoration: none; text-underline: none;">DemocracyOS</span></span></a><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;"> this would be super useful to plug into your user-facing application.</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">&nbsp;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">This repository has an example application using the </span><a href="https://github.com/aledbf/deis-api"><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #346eb7; text-decoration: none; text-underline: none;">node.js</span></a><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;"> <span class="SpellE"><span class="GramE">Deis</span></span> controller wrapper.</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">First you need to instantiate a client object and login to the cluster:</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;">&nbsp;</p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span class="SpellE"><span class="GramE"><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">var</span></span></span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"> <span class="SpellE">DeisAPI</span> </span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">=</span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #0e72a4;">require</span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">(</span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'<span class="SpellE">deis-api</span>'</span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">);</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span class="SpellE"><span class="GramE"><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">var</span></span></span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"> client </span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">=</span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">new</span><span class="SpellE"><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #654792;">DeisAPI</span></span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">({</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp; </span><span class="GramE">controller</span> </span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"> <span class="SpellE">nconf.get</span>(</span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'<span class="SpellE">deis_host</span>'</span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">),</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp; </span><span class="GramE">secure</span><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #0e72a4;">false</span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">,<span style="mso-spacerun: yes;">&nbsp; </span></span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #848684;">// Optional</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp; </span><span class="GramE">username</span><span style="mso-spacerun: yes;">&nbsp;&nbsp; </span></span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"> <span class="SpellE">nconf.get</span>(</span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'<span class="SpellE">deis_user</span>'</span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">),</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp; </span><span class="GramE">password</span><span style="mso-spacerun: yes;">&nbsp;&nbsp; </span></span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"> <span class="SpellE">nconf.get</span>(</span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'<span class="SpellE">deis_password</span>'</span><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">)</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">});</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="color: #262626; font-family: Consolas; font-size: 12pt;">&nbsp;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">We'll use the </span><a href="http://npmjs.com/inquirer"><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #346eb7; text-decoration: none; text-underline: none;">Inquirer</span></a><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;"> module to extract a few command-line parameters like the <strong>Organization Name</strong>, <strong>Background Color</strong> and <span class="SpellE"><span class="GramE">cpu</span></span> &amp; memory limits of the application. In a real-world integration you may replace this with a configuration from your database for the specific user or organization you're hosting the app for.</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 12.0pt; font-family: 'Helvetica Neue'; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">&nbsp;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span class="SpellE"><span class="GramE"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">inquirer.</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #0e72a4;">prompt</span></span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">([{</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span><span class="GramE">type</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'input'</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">,</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span><span class="GramE">name</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'<span class="SpellE">appName</span>'</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">,</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span><span class="GramE">message</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'Please specify a name for the application instance to deploy'</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">},</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">{</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span><span class="GramE">type</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'input'</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">,</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span><span class="GramE">name</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'<span class="SpellE">orgName</span>'</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">,</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span><span class="GramE">message</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'Please specify a name for the organization'</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">},</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">{</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span><span class="GramE">type</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'input'</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">,</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span><span class="GramE">name</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'<span class="SpellE">bgColor</span>'</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">,</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span><span class="GramE">message</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'Please specify a background color'</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">},</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">{</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span><span class="GramE">type</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'input'</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">,</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span><span class="GramE">name</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'memory'</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">,</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span><span class="GramE">message</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'Please specify the maximum memory allocated to the instance'</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">},</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">{</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span><span class="GramE">type</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'input'</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">,</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span><span class="GramE">name</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'<span class="SpellE">cpu</span>'</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">,</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span><span class="GramE">message</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'Please specify the maximum <span class="SpellE">cpu</span> time allocated to the instance'</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">}]</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">&nbsp;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><strong><span style="font-size: 14.0pt; font-family: 'Helvetica Neue'; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">Calling the API</span></strong></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><strong><span style="font-size: 14.0pt; font-family: 'Helvetica Neue'; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">&nbsp;</span></strong></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">Now that we have all of our information needed to call the <span class="SpellE">api</span> we'll go ahead and call the <span class="SpellE">api</span> wrapper functions, remember this uses a plain old REST API hosted by your <span class="SpellE">deis</span> cluster so you can do this from any language:</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><strong><span style="font-size: 24.0pt; font-family: 'Helvetica Neue'; mso-bidi-font-family: 'Helvetica Neue'; color: #346eb7;">&nbsp;</span></strong></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><strong><span style="font-size: 14.0pt; font-family: 'Helvetica Neue'; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">Authenticate</span></strong></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-family: 'Calibri,Helvetica Neue','serif'; mso-fareast-font-family: 'Calibri\,Helvetica Neue'; mso-bidi-font-family: 'Calibri\,Helvetica Neue'; color: #262626; mso-themecolor: text1; mso-themetint: 217;">Authentication is easy and this is the equivalent as doing</span><span class="SpellE"><span style="font-size: 10.0pt; font-family: Consolas; mso-fareast-font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626; mso-themecolor: text1; mso-themetint: 217;">deis</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-fareast-font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626; mso-themecolor: text1; mso-themetint: 217;"> login</span><span style="font-family: 'Helvetica Neue'; mso-fareast-font-family: 'Helvetica Neue'; mso-bidi-font-family: 'Helvetica Neue'; color: #262626; mso-themecolor: text1; mso-themetint: 217;"> on the cluster:</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 16.0pt; font-family: 'Helvetica Neue'; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">&nbsp;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #654792;"><span style="mso-spacerun: yes;">&nbsp; </span><span class="GramE">console<span style="color: #0e72a4;">.log</span></span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">(</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'creating app: '</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">+</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"> <span class="SpellE">answers.<span style="color: #0e72a4;">appName</span></span>);</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span></span><span class="SpellE"><span class="GramE"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">var</span></span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"> <span class="SpellE">appName</span> </span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">=</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"> <span class="SpellE">answers.<span style="color: #0e72a4;">appName</span></span>;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">&nbsp;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #848684;">// <span class="GramE">authenticate</span></span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span><span class="SpellE"><span class="GramE">client.auth.login</span></span>()</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><strong><span style="font-size: 10.0pt; font-family: 'Helvetica Neue'; mso-bidi-font-family: 'Helvetica Neue'; color: #346eb7;">&nbsp;</span></strong></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><strong><span style="font-size: 14.0pt; font-family: 'Helvetica Neue'; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">Create the App</span></strong></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">This registers the App on <span class="SpellE">deis</span> and allocates a virtual host for the app in the form of</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">&lt;<span class="SpellE">app_name</span>&gt;<span class="GramE">.&lt;</span><span class="SpellE">your_domain</span>&gt;.com</span><span style="font-size: 10.0pt; font-family: 'Helvetica Neue'; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">:</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 16.0pt; font-family: 'Helvetica Neue'; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">&nbsp;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span><span class="GramE">.then</span>(</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">function</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">() {</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp; </span></span><span class="GramE"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #654792;">console</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #0e72a4;">.log</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">(</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'<span class="SpellE">sucessfully</span> logged into <span class="SpellE">deis</span>'</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">);</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp; </span></span><span class="GramE"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">return</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"> <span class="SpellE">client.apps.create</span>(<span class="SpellE">appName</span>);</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span>})</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">&nbsp;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">However this step just allocates the app space on <span class="SpellE"><span class="GramE">Deis</span></span> but doesn't deploy anything. We'll do that in a later step.</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><strong><span style="font-size: 24.0pt; font-family: 'Helvetica Neue'; mso-bidi-font-family: 'Helvetica Neue'; color: #346eb7;">&nbsp;</span></strong></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><strong><span style="font-size: 14.0pt; font-family: 'Helvetica Neue'; mso-fareast-font-family: 'Helvetica Neue'; mso-bidi-font-family: 'Helvetica Neue'; color: #262626; mso-themecolor: text1; mso-themetint: 217;">Setting Deployment-specific Variables</span></strong><strong></strong></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">Now we'll use the questions from the user of the example <span class="SpellE">deployer</span> application to set environment variables for this specific deployment:</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #848684;"><span style="mso-spacerun: yes;">&nbsp; </span>// <span class="GramE">setup</span> instance-specific environment variables</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #848684;">// <span class="GramE">this</span> could be things such as the organization's name</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span><span class="GramE">.then</span>(</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">function</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">(results) {</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp; </span></span><span class="SpellE"><span class="GramE"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #654792;">console</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">.</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #0e72a4;">dir</span></span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">(results);</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp; </span></span><span class="GramE"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #654792;">console</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #0e72a4;">.log</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">(</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'successfully created app: '</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">+</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"> <span class="SpellE">answers.<span style="color: #0e72a4;">appName</span></span>);</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp; </span></span><span class="GramE"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #654792;">console</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #0e72a4;">.log</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">(</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'setting deployment specific variables'</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">);</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp; </span></span><span class="GramE"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">return</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"> <span class="SpellE">client.config.set</span>(results.</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #0e72a4;">id</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">, { </span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>ORGANIZATION_NAME</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"> <span class="SpellE">answers.orgName</span>, </span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>BG_COLOR</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"> <span class="SpellE">answers.<span style="color: #0e72a4;">bgColor</span></span></span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>},</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>{</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span class="GramE">memory</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"> { <span class="SpellE">cmd</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"> <span class="SpellE">answers.memory</span> },</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span class="SpellE"><span class="GramE">cpu</span></span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"> { <span class="SpellE">cmd</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">:</span><span class="SpellE"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #0e72a4;">parseInt</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">(<span class="SpellE">answers.cpu</span>) }</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>});</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span>})</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;">&nbsp;</p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">Notice how we're setting the environment variables </span><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: Consolas; color: #262626;">ORGANIZATION_NAME</span><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;"> as well as </span><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: Consolas; color: #262626;">BG_COLOR</span><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;"> to the variables collected from the user of this example app. This data however can come from a database, a <span class="SpellE">json</span> file, or anywhere else for your specific use case.</span></p>
<p class="MsoNormal"><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">&nbsp;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><strong><span style="font-size: 14.0pt; font-family: 'Helvetica Neue'; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">Deploying the Docker Image to the Application</span></strong></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-family: 'Calibri,Helvetica Neue','serif'; mso-fareast-font-family: 'Calibri\,Helvetica Neue'; mso-bidi-font-family: 'Calibri\,Helvetica Neue'; color: black; mso-themecolor: text1;">Finally to deploy the image, (in our case, our registry is the Docker hub) we simply specify the image name and <span class="SpellE"><span class="GramE">Deis</span></span> will automatically deploy the application. <span class="SpellE">Deis</span> knows when your Docker image exposes an endpoint and will automatically route port&nbsp;</span><span style="font-size: 10.0pt; font-family: Consolas; mso-fareast-font-family: Consolas; mso-bidi-font-family: Consolas; color: black; mso-themecolor: text1;">80&nbsp;</span><span style="font-family: 'Calibri,Helvetica Neue','serif'; mso-fareast-font-family: 'Calibri\,Helvetica Neue'; mso-bidi-font-family: 'Calibri\,Helvetica Neue'; color: black; mso-themecolor: text1;">or&nbsp;</span><span style="font-size: 10.0pt; font-family: Consolas; mso-fareast-font-family: Consolas; mso-bidi-font-family: Consolas;">443&nbsp;</span><span style="font-family: 'Calibri,Helvetica Neue','serif'; mso-fareast-font-family: 'Calibri\,Helvetica Neue'; mso-bidi-font-family: 'Calibri\,Helvetica Neue'; color: black; mso-themecolor: text1;">to the virtual host name:</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="mso-ascii-font-family: Calibri; mso-hansi-font-family: Calibri; mso-bidi-font-family: 'Helvetica Neue'; color: #262626;">&nbsp;</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span><span class="GramE">.then</span>(</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">function</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">(){</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp; </span></span><span class="GramE"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #654792;">console</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #0e72a4;">.log</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">(</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'successfully set deployment-specific variables'</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">);</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp; </span></span><span class="GramE"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">return</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"> <span class="SpellE">client.builds.create</span>(<span class="SpellE">answers.<span style="color: #0e72a4;">appName</span></span>, <span class="SpellE">nconf.get</span>(</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'<span class="SpellE">docker_hub_username</span>'</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">) </span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">+</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'/<span class="SpellE">simple-node:latest</span>'</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">);</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span>})</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span><span class="GramE">.then</span>(</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">function</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">(results) { </span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp; </span></span><span class="SpellE"><span class="GramE"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #654792;">console</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">.</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #0e72a4;">dir</span></span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">(results);</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp;&nbsp;&nbsp; </span></span><span class="GramE"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #94064b;">return</span></span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #654792;">console</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #0e72a4;">.log</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">(</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #13247e;">'successfully deployed node.js application'</span><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">);</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;"><span style="mso-spacerun: yes;">&nbsp; </span>})</span></p>
<p class="MsoNormal" style="margin-bottom: .0001pt; line-height: normal; mso-pagination: none; mso-layout-grid-align: none; text-autospace: none;"><span style="font-size: 10.0pt; font-family: Consolas; mso-bidi-font-family: Consolas; color: #262626;">&nbsp;</span></p>
<p class="MsoNormal"><span style="font-family: 'Calibri,Helvetica Neue','serif'; mso-fareast-font-family: 'Calibri\,Helvetica Neue'; mso-bidi-font-family: 'Calibri\,Helvetica Neue'; color: #262626; mso-themecolor: text1; mso-themetint: 217;">Now after a couple minutes (this application specifically deploys its <span class="SpellE">npm</span> packages on startup) you should see the customized <span class="GramE">app which</span> is setup to change the background color and display the organization name set in the app settings.</span></p>
<h1>Code Artifacts</h1>
<p class="MsoNormal">The primary artifacts are contributions to external open source projects. We made a <a href="https://github.com/deis/deis/tree/master/contrib/azure">contribution of tooling and documentation to the <span class="SpellE"><span class="GramE">Deis</span></span> project</a> about how to spin up a <span class="SpellE">Deis</span> cluster on Azure, continue to make enhancements to our <span class="SpellE">CoreOS</span> Linux agent, and helped diagnose issues with both on Azure.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">We also created a <a href="https://github.com/sedouard/deis-deployer/blob/master/README.md">generic example</a> on how to use the <span class="SpellE"><span class="GramE">Deis</span></span> controller <span class="SpellE">api</span> to do '<span class="SpellE">Wordpress'</span>&nbsp;style deployments on <span class="SpellE">Deis</span> using Docker hub.</p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal">Democracy OS also open-sourced their <a href="https://github.com/DemocracyOS/manager">instance manager</a> which handles deploying to <span class="SpellE">Deis</span> via controller <span class="SpellE"><span class="GramE">apis</span></span>.</p>
<h1>Opportunities for Reuse</h1>
<p class="MsoNormal">There are many opportunities for reuse by any company interested in moving workloads from <span class="SpellE">Heroku</span> (<span class="SpellE">Salesforce</span>) to Azure. <span class="SpellE">Deis</span> also provides a solid <span class="SpellE">devops</span> foundation for running open source workloads from scratch on Azure through its developer friendly '<span class="SpellE">git</span> push' development model. Developers can learn more about the <span class="SpellE"><span class="GramE">Deis</span></span> platform and how to deploy it on Azure at <a href="http://deis.io">http://deis.io</a><span class="MsoHyperlink">.</span></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal"><em>Steven Edouard is a full-time hacker &amp; Software Engineer on the Partner Catalyst Team in San Francisco. Most of what he does is open-sourced on github and you can learn more about him at <a href="http://stevenedouard.com">stevenedouard.com</a>.</em></p>
<p class="MsoNormal">&nbsp;</p>
<p class="MsoNormal"><em>Tim Park is a Principal Engineer and Silicon Valley site lead for Microsoft. &nbsp;Tim works in the&nbsp; Partner Catalyst&nbsp;Team, which works with customers,&nbsp;partners, and open source&nbsp;communities&nbsp;to get the platforms they use to running on Azure.</em></p>
</div>]]></content:encoded>
    </item>
  </channel>
</rss>