Microsoft MVP Logo

This week I'm presenting a session at the 2016 Boston installment of SPTechCon. At this show I'm doing two presentations, one of which shows how to build an Office Add-in for Outlook using Angular 2. As a developer, I find the most effective way to learn is to show someone what the goal or final result of a demo will be, then to go back and do it. A huge bonus is when I can walk away with the implementation & even replicate the demo quickly and easily myself. As a presenter, that's not always easy, especially in this loosely connected cloud world.

Interested in the session & attending SPTechCon in Boston this week? Check me out on Tuesday at 8:45am... the session title is Learning Angular 2 to Build Office Add-ins.

Let's take my session this week. An Office Add-in, in its most minimalist form, is an HTTPS secured website with an XML file that tells the Office client about the application & website. If I was attending my session here's what I'd want:

  • Lecture consisting of a few slides - really... just a few slides... because as we all know, power corrupts, and PowerPoint corrupts absolutely
  • Demo of the final working solution so I can see what we're trying to accomplish
  • Live coding of the demo from scratch or an acceptable starting point
  • Because live coding demos don't always go to plan, if the presenter was running out of time, they need a way to quickly jump to the working solution so you can walk through the code
  • Link to download the code so I can explore it later on my own time
  • A way to easily spin up the demo in my environment

Sound like a plan? Me too... so let me explain to you how I'm doing it. It's all in the magical beauty of Docker! The solution & presentation can be found in this repo: github.com / andrewconnell / pres-ng2-officeaddin.

What's Docker? Is is the company behind one of the most widely adopted container specifications & ecosystems out there. So what's a container? Go read my Docker & Containers 101 - What is Docker? post if you aren't familiar with Docker & containers. But if you want to just get the gist:

By now you know what a virtual machine (VM) is... it's a software implementation of an entire system... applications & operating system. A VM has no hardware... it shares that with the underlying computer it's on. VMs usually take a few minutes to be provisioned (because they have big virtual hard drives that are tens to hundreds of GB in size) and a few minutes to spin up (because they are full blown operating systems).

A container takes this abstraction one level further. Within the VM you have a thin layer installed, the Docker Engine. You then create an image which includes JUST the code your application needs to run, no OS code. This means that an image is much smaller and spins up much faster, usually in just a few milliseconds, because there's no booting of an OS. On the VM you then create an instance of the image, which is a container, with just a single command line statement.

Similar to differencing disks that we had with VMs, containers are based on another container. So your container may be based on the Node.js v5 container that's based on another container that's based on the Ubuntu 16.04 container. When you get an image for a container, it will download all the layers that your local machine doesn't already have. For instance, if you already got the Node.js v5 container (and all the ones it's based on), getting your container that's based on the Node.js v5 container may be a small download.

Building a Containerized Presentation

I started out by figuring out what the best starting point would be. Starting with the Angular 2 Quickstart sample, I cloned it locally and then killed the .git folder. Then I did a bunch of manual tweaks as I needed a webserver for my demo. This left me with a good starting point: a project that included a Node.js webserver hosting a REST API behind HTTPS and serving up the sample Angular 2 Quickstart application. At that point, I committed the changes and tagged them.

Grab this starting point from the tag in the repo here: pres-ng2-officeaddin / ng2-demo-start.

Next, I created the Office Add-in using Angular 2 and made sure everything worked.

The last step in the process was containerizing the application. This is the easiest step. To do this add a new text file to the root of the project named Dockerfile.

The comments in the file explain what's going on, but let me give you a little breakdown:

  • The image is based on the official image by the Node.js foundation with Node.js v5 installed.
  • Create two folders & a new group (nodejs) and user in that group (nodejs) and give the group ownership rights on the folders.
  • Create a new folder /ac/demo off the root of the system, copy the NPM package.json, Typings typings.json & TypeScript compiler configuration tsconfig.json into the folder and then run npm install to download all the NPM packages (this also runs the typings install step which downloads the type definitions for TypeScript).
  • Create a new folder src and copy the contents of the current working folder's src folder into it.
  • Set the user to nodejs and if no startup command is provided, run npm start when an instance of the image is started.

To build the image, run the following command (-t tells Docker what to name the image... the . on the end tells Docker where to find the Dockerfile.

$ docker build -t pres-ng2-officeaddin .

Try it for yourself... just install Docker for free for your platform from here: https://www.docker.com/products/overview.

What's cool about this scenario is that now, I have, and you have access to, a single package that contains the entire demo application. For this scenario, a webserver is started on port 3433 over HTTPS.

Running the Working Demo in a Container

To get the site working, all you have to do is run the following command on your machine:

$ docker run -it -p 443:3433 pres-ng2-officeaddin

Change the name to andrewconnell/pres-ng2-officeaddin:ng2-demo-start for the image to skip building the image & just download the image I built and available on my public Docker hub repo here: hub.docker.com/r/andrewconnell/pres-ng2-officeaddin

This will spin up a new container based on that image, mapping port 443 on your host machine to port 3433 in the container. It will also start it in interactive mode (-i) with a TTY command prompt (-t... combined with the -i).

Here's what it looks like when you run that without ever using docker before. Because containers are nested on top of other containers, be aware you might be about to download about 880MB of container layers. This first downloads all the layers for the container to run and then spins it up which you see about a minute into it. I then quit the container and do a few queries to see a list of all the images on my machine (docker images), the running containers (docker ps) and then all containers, even those run in the past who aren't running (docker ps -a).

I then installed the add-in into Outlook using the URL of the add-in's manifest file: https://localhost:443/app.xml & tada, it works!

Kill the container by hitting CTRL+C a few times in the command prompt / terminal window.

As the presenter, you want to make sure you grab the images before your session because as we know, conference internet can be problematic & the whole idea of using your tethered phone for a connection doesn't work so great in some big conference centers or hotel ballrooms, especially when you have an 880MB download to do. For my case, I'll grab my image without running it before the session using the following:

$ docker pull andrewconnell/pres-ng2-officeaddin:ng2-demo-start
  

Jump Back to Starting Point for Live Demo

This is the tricky part... you want to not destroy the work you did for the working solution while quickly jumping back in time to start doing the live coding demo and, at the same time, giving yourself a failsafe to jump back to the working solution quickly in case you're running out of time. This is where Docker sprinkled with a little Git saves the day.

I'm going to use the container you just ran as the core of my demo. Why? Because in a presentation room, I don't want to go through the process of downloading all the NPM packages my application uses (or, if I was using .NET, NuGet packages). Since the container already has all the NPM packages in it's node_modules folder & I've downloaded the container, I'll be good to go. The same is true for the typings folder for my TypeScript type definitions.

But what about the source code for the app? That's also in the container. Here's where the trick comes in: When I start the container I'll tell it to map the path to the source code in the container to a folder on the host laptop! Then all I have to do is get the local code back to the starting point. Git to the rescue!

# get the list of tags for the repo
$ git tag -l
ng2-demo-tag

# use the tag name to get the SHA of it's commit
$ git rev-list -n 1 ng2-demo-start
cb02f3b21038c6c304a93397afb79ebc09f99027

# reset the workspace back to the way it was at that tag
$ git reset [COMMIT-SHA] .

# undo the changes to the files that existing then, but have been edited in the final version
$ git checkout -- demos/ng2-outlook-minicrm/src/client/index.html
$ git checkout -- demos/ng2-outlook-minicrm/src/client/app/main.ts
$ git checkout -- demos/ng2-outlook-minicrm/src/client/app/app.component.ts

# remove all stuff that was added after this comment
$ git clean -fdx

At this point, the locally cloned repo for the application is back the way it was at the starting point. So now the trick is to get it running. To do this, I need to put my Docker hat back on and create a new file that will be used to spin up the container, but with some special parameters. This is a Docker Compose file:

The comments explain most of what's going on that you should understand from what I've already covered, but let me elaborate a bit.

  • Lines 12-13 says map the local folder ./src to the folder in the container located at /ac/demo/src. So now, when the container runs like it did last time in the working solution, everything is the same except the code is not coming from the container, but instead from my laptop. As you saw, in the last step I just reset the code on my laptop to the starting point so I just bypassed the final working solution!
  • Line 15 says instead of using the npm start as the startup command in the container as defined in the Dockerfile, use npm run dev. A quick peek at package.json shows that this does the same as the npm start command except it also starts the TypeScript compiler in watch mode so any changes to TypeScript will get automatically transpiled to JavaScript.

Now to spin up the container with this configuration, just run:

$docker-compose -f docker-compose.demo.yml up

Now hit the site at https://localhost and you get the starting point:

At this point, I can start doing the live coding demo.

For a little bonus credit, checkout the docker-compose.yml. That is what I'm using in the actual session to spin up the working solution... I'll just run docker-compose up instead of docker run ... and this will spin up two containers. One (service=webapp) will have the working solution & another (service=vorlonjs) that's hosting the slick debugging tool by Microsoft's DX team: VorlonJS.

Quickly Jump Back to Working Demo

What if you need to bailout and show the working version again? Again, Docker & git play a key role here.

First, reset the code to the final working version in the cloned repo so you can explain it to your attendees:

$ git reset --hard HEAD

Now that the code is set, kill the old running container and start it up using the original docker run command you used above. This time, it uses the working app in the container, not your mapped folder one.

Cleanup

How do you remove this stuff you just downloaded to your machine? You the container you ran is still there, just not in a running state. Then there's the 800MB collection of layers that makeup the image. Here's what you do:

  • remove the container using docker rm [container ID]
  • remove the image using docker rmi [image ID]

You can tell Docker to remove the container when it's finished running it automatically by adding --rm to the docker run command you ran above.

Conclusion

If you're new to Docker, this might be a bit much to bite off for a session. However if you've used Docker in any way previously, you'll agree that there's very little I had to do here to set this up as I only created two files (Dockerfile & docker-compose.demo.yml) and wrote down a few commands in git. All of this is easily repeatable in other demos.

Maybe this works for you as a presenter, maybe not... it works for me and I wanted to share it to give you a good use case of containers in the real world.

If you're at the SPTechCon conference this week and wonder this is all fine and good, but I don't do Node.js and I don't want to work in Linux (because today, production containers are a Linux only thing). Well for those of you in the .NET & Windows world, containers are coming to you too! Docker supports Windows containers, something that's coming in Windows Server 2016 (already working in the latest tech preview) so you can use classic .NET applications in containers. Or, you can leverage .NET Core / ASP.NET Core to build .NET applications that run on any platform.

Oh wait, I almost forgot...

There were two more steps I mentioned above in my presentation goal. One was a link to where you can get the presentation. Well for this one, go here: https://github.com/andrewconnell/pres-ng2-officeaddin.

The other was where you can get the working solution. Well you can build it yourself from the GitHub repo in the previous paragraph, or you can download it to your machine:

$ docker pull andrewconnell/pres-ng2-officeaddin

or just download & run it in one command:

$ docker run -it -p 433:3433 andrewconnell/pres-ng2-officeaddin:ng2-demo-start
Comments powered by Disqus