Pull Image From Private Registry

In order to deploy an image into Kubernetes, the image must be available in a registry. I’m very much in the learning & experimenting phase of my Kubernetes journey, and I find myself using Docker Hub private registries for a lot of things. After using docker login I can docker push & docker pull images from a private registry just by naming them correctly with my Docker ID e.g. <my-docker-id>/some-image. However, kubectl doesn’t automatically inherit this knowledge and access, so what must be done to enable deployment of a locally-developed image into a locally-runing cluster? Let’s take a look!

The first thing we need to do is authenticate to the registry, which we can do using docker login. This will prompt for credentials and store an authorization token in ~/.docker/config.json. The lines below demonstrate how to do this and view the result.

docker login
cat ~/.docker/config.json

In order to make the authentication token accessible to the Kubernetes cluster, we can use Secrets. The following commands can be used to copy the credential from the Docker config.json into a Secret named regcred and inspect the result.

kubectl create secret generic regcred \
    --from-file=.dockerconfigjson=<path/to/.docker/config.json> \
    --type=kubernetes.io/dockerconfigjson
kubectl get secret regcred --output=yaml

Next, we’ll create a Deployment that includes our Secret. Create a new file my-app-deployment.yaml with the following contents:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-app
        image: <my-docker-id>/my-app:latest
        ports:
        - containerPort: 80
      imagePullSecrets:
      - name: regcred

And we’ll need to expose the deployment with a Service, so create another file my-app-service.yaml with this for its contents:

apiVersion: v1
kind: Service
metadata:
  name: my-app-svc
  labels:
    app: my-app
spec:
  type: NodePort
  ports:
  - port: 80
    nodePort: 30080
  selector:
    app: my-app

Now we use these files to create the Deployment and Service in Kubernetes:

kubectl create -f my-app-deployment.yaml
kubectl create -f my-app-service.yaml

That should do the trick. You can inspect the Deployment and Service using kubectl get pods and kubectl describe service my-app-service respectively. Assuming everything deployed correctly, you should be able to access your app at http://cluster-host-ip:30080.

Run an ASP.NET Core App on Raspberry Pi With Docker

In my last article, I wrote about how to create a single-page Angular app using the .NET Core CLI, create a Docker image, and run it as a container in about 4 steps that take just minutes to execute. By modifying a single line in your Dockerfile, you can target the 32-bit ARM architecture needed to run the image as a container on a Raspberry Pi.

Here’s the one line that needs to change in the Dockerfile to make it runnable on ARM32 (old line is commented for reference):

# FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1.2-buster-slim-arm32v7 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /src
COPY ["my-app.csproj", "./"]
RUN dotnet restore "./my-app.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "my-app.csproj" -c Release -o /app/build
RUN apt-get update && \
    apt-get install -y wget && \
    apt-get install -y gnupg2 && \
    wget -qO- https://deb.nodesource.com/setup_10.x | bash - && \
    apt-get install -y build-essential nodejs

FROM build AS publish
RUN dotnet publish "my-app.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "my-app.dll"]

Note that the official list of available tags for different architectures can be found here. Consult this list to determine if newer images are available.

That’s it, though. Create your image, push it to Docker Hub, pull it on your Raspberry Pi, and run it just as you would locally. If you haven’t used Docker Hub before, you’ll need to login and create a repository. Check out the Docker Hub Quickstart for help. You may also need to run docker login on both machines to access your new repo.

# dev machine
docker build -t <dockerID>/repo .
docker push <dockerID>/repo

# raspberry pi
docker pull <dockerID>/repo
docker run -d -p 5000:80 <dockerID>/repo
The default dotnet new Angular app running locally on Raspberry Pi.

10 Minutes to Create & Run .NET Core Angular SPA in Docker

In this article, I’ll demonstrate how to create an ASP.NET Core Angular single-page application using the .NET Core CLI, create a Docker image, and run it as a container. The entire process is just 4 steps, each of which takes about a minute to perform–although creating the Docker image takes a couple minutes to complete. Note, also, that these same steps work from both Linux and Windows.

  1. Create Angular application
  2. Create Dockerfile
  3. Create Docker image
  4. Run container

Before you start, make sure you have the following prerequisites installed.

  • .NET Core SDK
  • Visual Studio Code with Docker extension
  • Docker

Installation can be verified by running these commands:

dotnet --version
code --version
code --install-extension ms-azuretools.vscode-docker
docker --version

Create the Application

We begin by creating our Angular application using the dotnet new command and specifying the angular template. The -o argument will cause the new project to be created in a sub-folder named my-app. This will create a .NET Core ASP.NET project with an Angular app in its ClientApp folder. After running the command, go into the new directory and launch Visual Studio Code.

dotnet new angular -o my-app
cd my-app
code .

Add Docker Files

With the Docker extension for VS Code installed, you can add Docker files easily via the command palette. Open the command palette using ctrl+shift+P or View > Command Palette menu options, and run the command Docker: Add Docker Files to Workspace. (Tip: type “docker” to filter commands.)

Selecting the command will ask you a series of questions:

  • Application Platform: ASP.NET Core
  • Operating System: Linux
  • What port(s) does your app listen on? 80, 443

If you’re running Docker on Windows, it’s important to make sure Docker is configured to run the right type of containers (Windows vs Linux). I answered “Linux” above, and I can confirm that my Docker for Windows is configured to run Linux containers by clicking the Docker icon in my system tray, as shown in the screenshot below. It shows “Switch to Windows containers” which means it’s currently using Linux containers. It’s fine to use Windows containers, too, but you’ll need to adjust installation instructions for NodeJS in the Dockerfile further down.

Once you’ve answered the Docker extention’s questions, a Dockerfile is generated. One modification is needed to also install NodeJS for our client app. The lines needed to do this are highlighted in the complete Dockerfile below.

FROM mcr.microsoft.com/dotnet/core/aspnet:3.1 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS build
WORKDIR /src
COPY ["my-app.csproj", "./"]
RUN dotnet restore "./my-app.csproj"
COPY . .
WORKDIR "/src/."
RUN dotnet build "my-app.csproj" -c Release -o /app/build
RUN apt-get update && \
    apt-get install -y wget && \
    apt-get install -y gnupg2 && \
    wget -qO- https://deb.nodesource.com/setup_10.x | bash - && \
    apt-get install -y build-essential nodejs
    
FROM build AS publish
RUN dotnet publish "my-app.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "my-app.dll"]

Build the Image

When you’re done editing your Dockerfile in VS Code, save changes and head back to the command line. Run the following command to build your image:

docker build -t my-app .

Verify that the image was created by running another command:

docker image ls my-app

Run a Container

Now we’ll use the docker run command to run a container using our image. We’ll specify two arguments: -d to run in detached mode and -p to map ports on the local machine to the container.

docker run -d -p 5000:80 my-app

Verify that the app is running by browsing to the localhost:5000:

Moq Quick Reference for Rhino Mocks Users

Behold! A quick reference guide for Rhino Mocks (AAA syntax) users making the switch to Moq.

Rhino MocksMoq
MockRepository.GenerateMock<T>new Mock<T>
MockRepository.GeneratePartialMock<T>new Mock<T> { CallBase = True }
.Stub(…).Return(…)
.Stub(…).WhenCalled(…)
.Stub(…).Do(…)
.Setup(…).Returns(…)
.Expect(…).Return(…)
.Expect(…)
.Setup(…).Returns(…).Verifiable()
.Setup(…).Verifiable()
Arg<T>.Is.AnythingIt.IsAny<T>()
Arg<T>.Matches(…)It.Is<T>(…)
Arg<T>.Is.Equal(foo)It.Is<T>(x => x == foo)
.AssertWasCalled(…).Verify(…)
.AssertWasNotCalled(…).Verify(…, Times.Never)
.VerifyAllExpectations().Verify()

In addition to the syntax translations listed in the table above, a key difference is that you use the instantiated mock object via the mock’s Object property. For example:

var mock = new Mock<SomeClass>();
SomeMethod(mock.Object);

Parse Emails With Power Automate

Last week, I wrote about how to use Power Automate to add automation to everyday tasks like emails. One of the examples I gave was to notify myself when vacation requests arrive so I can approve them as quickly as possible. Emails like these frequently have links like “click here to view the request” that take you to the item needing attention. It sure would be nice if we could extract that link and do something useful with it, right?

Regular expressions are a great way to parse text like this to extract useful details. Unfortunately, Power Automate doesn’t seem to have native support for regular expressions, but there are connectors from a third-party provider Plumsail. I took it for a test run today, and it worked pretty well. So, let’s take a look at the steps.

In order to use the Plumsail connectors, you’ll need a Plumsail account & API key. You can register for an account here, and create an API key here. Keep your API key handy, and head over to Power Automate.

In my workflow, I added the Plumsail Regular Expression Match connector as a new action after my “When a new email arrives” trigger action. If it’s the first time using a Plumsail connector, it will prompt you for your API key and a connection name.

After you’ve specified your connection name & API key, the connector UI changes to what you’d expect for a regular expression: Pattern & Text. The output of this control is a collection of matches, and the dynamic content will adjust based on the capture groups (parentheses) specified in your pattern.

In the screenshot above, my regular expression will search the email body for the “please click here” hyperlink and capture the URL. I can then dump this URL into the message to myself in Slack. Works great!

Build a Linux .NET Core Dev Machine in Under an Hour

I have a complicated relationship with Linux. Every few years, I get nostalgic about my Solaris days back in college. So I build a machine, struggle through getting all the hardware & whatnot working, and invest tons of time trying to get the Windows apps I know & love running on it. Then I give up and go back to Windows.

And that’s about where I’m at now, in the nostalgia/fascination stage with fantasies of leaving my familiar Microsoft pastures. At work, we’re moving more & more toward containerization and Kubernetes with the goal of reducing cost by hosting services on Linux. Maybe its relevance to work will give the effort more legs this time?

So I embarked on the journey (again) and have been pleased with how much the experience has matured. Doing a clean install of Ubuntu on my old laptop was completely painless. I didn’t need to do any special setup or troubleshooting to get hardware running. In fact, it was 100% as easy as installing Windows, which I was forced to do a few weeks earlier presumably due to some botched updates that had me blue-screening every few minutes.

Keeping the momentum going, I jumped right into installing .NET Core and setting up Visual Studio Code. The whole thing–from beginning to end; from formatting and installing the OS to running my first console app–took about an hour.

It was easy enough that just about anybody capable of doing a clean OS install should be able to manage it. In fact, let’s go through all the steps, right now! I’ll walk you through what’s needed from installing the OS, .NET Core, and VS Code all the way through to creating and running a new project.

Getting the operating system up & running is obviously step 1. I followed the instructions on the Ubuntu website:

  1. Create a bootable USB drive
  2. Boot off the device
  3. Follow install directions

Alternatively, you could use a virtual machine. Since I’m retracing my own steps as I write this, I’m using VirtualBox which is freely available to everybody–so no excuses about not having a spare computer laying around! If you’re going this route, download VirtualBox, fire it up, and create a new virtual machine. For reference, I picked Linux/Ubuntu (64-bit) and used all defaults*. You still need to download the Ubuntu ISO, and select it as the boot media the first time you start the VM.

*My VM kept hanging during install until I increased memory from the default 1024 MB to 2048 MB.

Alright, now we’ve got our Linux machine running, and it’s time to install .NET Core. Follow Microsoft’s instructions to install the .NET Core SDK, or open a terminal and run the following commands:

wget -q https://packages.microsoft.com/config/ubuntu/19.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
sudo apt-get update
sudo apt-get install apt-transport-https
sudo apt-get install dotnet-sdk-3.1

We’re almost there now. Install Visual Studio Code along with the C# extensions by running a couple more commands:

sudo snap install --classic code
code --install-extension ms-vscode.csharp

That’s it! Who’s ready to run some code? Use the dotnet CLI to create a new console app, open the code in VS Code, and run it using F5 or Debug > Start Debugging. (If it’s your first run with the C# extensions installed, you may need to wait while it downloads dependencies.) VS Code will ask you which environment to use; pick .NET Core to add a launch.json file which tells VS Code how to run your app.

dotnet new console -o HelloConsole
cd HelloConsole
code .

And there you have it. Reproducing the setup on a VM, I was able to download the Ubuntu ISO, create a clean install, install .NET Core SDK & runtime along with VS Code, and create & run a new C# console app in under an hour. Not bad!

@mentions in Slack From Microsoft Power Automate

In my last post, I demonstrated how to create a basic workflow that posts to Slack when email is received that matches specified criteria. Pretty cool, right? Sometimes you want to make sure that somebody gets a notification about these important messages that you’re posting, though. Slack has a built-in way to do this with its mentioning system.

Unfortunately, mentioning a user or group from Power Automate isn’t as straightforward as you might expect. When you specify message text like @somegroup it just shows up in Slack as that text.

The reason it doesn’t work is because the text isn’t parsed to use special formatting required by Slack for things like mentioning. The good news is that the workflow widget will do this work for you if you enable Parse Mode=full in the advanced options.

The bummer about using Parse Mode=full is that you can’t include other object entities e.g. an email object’s subject. Enabling full parsing gives us a great hint at the solution if we look at the run results…

So if we know the “special formatting” values, we can use those in messages? Yep!

It’s not a perfect solution because there doesn’t seem to be a good, built-in way to retrieve the special formatting for a Slack user or group required to @mention them from Power Automate, but it does give you a couple ways to accomplish the goal of mentioning them in messages you post. You can enable full parsing in advanced options to mention from a static text message, or determine the special formatting for a user or group yourself to use in a dynamic message constructed from other entities in your workflow!

Everyday Automation With Microsoft Power Automate

One of my favorite tools these days is Microsoft Power Automate (formerly Microsoft Flows). Power Automate lets you visually construct workflows for all kinds of things, and I’ve found it to be an incredibly powerful way to automate tasks. Here are some examples:

  • Post in Slack when an email is sent to a shared mailbox
  • Post status tweets from a SaaS provider’s Twitter account in Slack
  • Send an SMS text message when a critical process fails

The thing I love most about Power Automate is that you can make sophisticated workflows very quickly, and they don’t require hosting or deployment. You just create, save, and it’s live.

Many of my workflows start by me realizing that I didn’t notice a certain category of emails quickly enough or that I need to give my team better awareness of an event. For example, I wanted to ensure that expense reports get approved right away so that employees get reimbursed, so I created a workflow to send a direct message in Slack whenever I get an approval request email from the expense system. Another common example has been to notify my team when applications report failures through various means.

Power Automate boasts a “low code” experience, but most of the workflows I’ve created have required coding skills and creative troubleshooting to figure out. That said, you can still do useful things with pretty much zero code as long as you’re not trying to get too fancy. In this post, we’ll build a simple workflow that posts a notification to Slack when an email comes in.

We begin by creating a new automated workflow. Automated workflows always start with a trigger, so I’m going to pick “When a new email arrives.” Another option that’s been useful to me is “When an email arrives in a shared mailbox,” but as you see in the screenshot below, there are many options to explore–and many that have nothing to do with email, as well.

If you’re automating something for an unmonitored mailbox, you may not want additional criteria, but I’m usually looking for something like an email from a specific sender with a certain subject. So, the next step in our workflow will be a Condition so we can specify which criteria we care about.

The last step in our workflow will be to define what happens when the condition is met or not met. We want our sample workflow to send a private message in Slack. To accomplish this, I’ll add the “Post message to Slack” step and enter my Slack username for the channel and some text for the message. Instead of hard-coded message text, you could also include attributes from the email message entity, similar to those used in the condition step.

The final step is simply to save it. That’s it. When an email arrives, the workflow checks it against the conditions you’ve defined, and performs the corresponding Yes/No actions–in this case send ourselves a message in Slack.

Blogging Makes You A Better Developer

Throughout my career, I’ve found that I’d figure things out, and several months or years later, I’d bump into someone else that was trying to do something similar, and I’d think or say: “Yea, I can’t remember the specifics, but I definitely remember dealing with that.” That was the original motivation for starting my blog: to build a personal catalog of things I’ve learned that I could refer back to should the things become relevant again.

It started as a bit of a hobby but eventually became part of my weekly professional life. I’d research my assignments, and I’d track the journey. Once I finished, I’d organize my list of steps into an article and publish it. The more I wrote, the more views my blog would see, and it became exciting to watch traffic grow and track statistics.

I was reading an article about the Feynman Technique and realized that this is similar to how I approached writing.

  • Start with a concept: a problem I’m trying to solve for an assignment
  • Teach it to a toddler: figure it out/make it work
  • Identify gaps: are there things I don’t understand about the solution or aspects that I am not able to articulate?
  • Review & simplify: write an article that’s concise and easy to understand

I was having a lot of fun transforming my work into articles and watching daily traffic grow, but what I didn’t realize was that this activity was actually giving me tremendous professional growth in a number of ways.

Writing Skills

Writing is an extremely underrated skill for software developers. Between email, team chats, direct messages, and documentation, I’d venture to say I spend more time writing than I do coding. In the digital age, I’m often represented by the things I type much more than the things I say or do.

The more important part of writing is how it affects people around you, though. Good writing is essential to sharing and communicating your ideas. It allows you to explain concepts to your bosses, teammates, and clients/customers. Maybe it allows a colleague to understand your perspective and snap to your approach, or maybe it helps them explain back to you where you’re wrong. It might enable you to implement a cool new feature or prevent the team from wasting time on something that’s not needed. Good things happen when you articulate thoughts effectively.

Technical Expertise

This is the big advantage that I didn’t realize until more recently. I thought I was learning things and just documenting them by writing articles, but the exercise of sharing it with the world forced me to take extra steps. I needed to fact-check claims I was making and double-check assertions. This correlates to the “identify gaps” step of the Feynman Technique. The act of publishing things I thought I knew forced me to make sure I actually knew them as best I could.

Similarly, knowing things a little better and having “practiced” explaining it makes you an excellent resource for your team. You know more things, you know them well, and you know how to explain them.

Shareable/Referenceable Content

My original goal! It’s awesome when somebody needs something, and you can just give them a link instead of explaining. It’s also fun when a co-worker’s researching something and they find you, which has happened a couple times. Sometimes you even turn up as an answer to your own questions!

Personal Brand

This is particularly good advice for people early in their career. If I get a resume from somebody with a blog that’s been active for several years, I get excited. This is someone that values learning and communication. This is someone that wants to share ideas and help others. This is someone that I think can provide a lift to the team.

Of course, if I look at the blog and it’s poorly written or has a lot of mistakes, it might work against you. This is where it’s important to follow the process. Start with your idea. Get it written. Fill the gaps. Simplify. With diligence and time, you’ll have lots of great content that represents you well.

Database-First Entity Framework in .NET Core

.NET Core and Entity Framework make connecting to an existing database really easy. This post will demonstrate how to generate models for an existing database using .NET Core Entity Framework. Note that you must have the .NET Core SDK installed.

First, let’s create a new console application. Open a new terminal and run the following commands:

/> dotnet new console -o MyConsoleApp
/> cd MyConsoleApp

If your app is using ASP.NET Core 2.1+, you’ll get all the Entity Framework packages you need. However, our console app is using .NET Core (not ASP.NET Core), so we need to install a couple more packages. Run the following commands:

/> dotnet add package Microsoft.EntityFrameworkCore.Design
/> dotnet add package Microsoft.EntityFrameworkCore.SqlServer

Now we’re ready to create our models. Again, we’ll do this by running a command in the terminal. Run the following command to generate models for all tables in your database along with a DbContext.

/> dotnet ef dbcontext scaffold "<connection string>" Microsoft.EntityFrameworkCore.SqlServer -o Models

Need help obtaining the connection string? If you’re using SQL Azure, you can browse to the database in the Azure Portal, and click the Connection Strings link.

With your DbContext and models created, you should now be able to write code to access tables. Add the following lines to your console app’s Program.cs:

using System;
using System.Linq;
using MyConsoleApp.Models;

namespace MyConsoleApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");

            using (var context = new YourDatabaseNameContext())
            {
                var foo = context.SomeTable.First();
            }
        }
    }
}