Friday, October 22, 2021

Service decomposition and service design

Finding the boundaries of your system and decompose it into multiple services sounds easy, but it certainly isn’t.

If you are interested in this topic, check out the blog series by Vadim Samokhin:

Remark: After writing this post, I noticed that Vadim created a blog post linking to the series above and included also some other related posts.

Thursday, October 21, 2021

Azure AKS–Save some money using spot node pools

One of the ways you can save some money using Azure is by using spot node pools for your Azure Kubernetes Service cluster.

What’s a spot node pool?

Using a spot node pool allows you to take advantage of unused Azure capacity at a significant cost savings. At any point in time when Azure needs the capacity back, the Azure infrastructure will evict spot nodes. Therefore, Spot nodes are great for workloads that can handle interruptions like batch processing jobs, dev/test environments, large compute workloads, and more.

Remark: A spot node pool can't be the cluster's default node pool. A spot node pool can only be used for a secondary pool.

Pricing for a spot node pool

Pricing for spot instances is variable, based on region and SKU. For more information, see pricing for Linux and Windows. You do have the option to set a max price. In case the price is exceeded the spot node is evicted from your cluster.

Schedule a deployment to use the spot node pool

A spot node pool has the label kubernetes.azure.com/scalesetpriority:spot and the taint kubernetes.azure.com/scalesetpriority=spot:NoSchedule. We use this information to add a toleration in our deployment.yaml:

In case you have multiple spot node pools, you can use a nodeselector to select a specific pool:

More information

Winget–A package manager for Windows

I’ve been using Chocolatey for a long time as an easy way to get my Windows machine configured with all the software I need. With the release of version 1.1 of the Windows Package Manager(WinGet) I thought it was a good time to give it a try.

Installation

Chances are high that WinGet is already available on your machine. Open a terminal and type winget. If it is available you should see something like this:

If not, the Windows Package Manager is distributed with the App Installer from the Microsoft Store. You can also download and install the Windows Package Manager from GitHub, or just directly install the latest available released version.

Searching a package

The list of available packages is quite large(more than 2,600 packages in the Windows Package Manager app repository). Just run winget search <SomePackage> to see if the package you are looking for has available there.

For example let’s search for my favorite git client GitKraken:

PS C:\Users\bawu> winget search gitkraken
Naam      Id                Versie Bron
------------------------------------------
GitKraken Axosoft.GitKraken 8.1.0  winget

For packages inside the Microsoft store you don’t get  a readable id but a hash value instead:

PS C:\Users\bawu> winget search git
Name                                  Id                                         Version                    Source
-------------------------------------------------------------------------------------------------------------------
Learn Pro GIT                         9NHM1C45G44B                               Unknown                    msstore
My Git                                9NLVK2SL2SSP                               Unknown                    msstore
GitCup                                9NBLGGH4XFHP                               Unknown                    msstore
GitVine                               9P3BLC2GW78W                               Unknown                    msstore
GitFiend                              9NMNKLTSZNKC                               Unknown                    msstore
GitIt                                 9NBLGGH40HV7                               Unknown                    msstore
GitHub Zen                            9NBLGGH4RTK3                               Unknown                    msstore
GitLooker                             9PK6TGX9T87P                               Unknown                    msstore
Bhagavad Gita                         9WZDNCRFJCV5                               Unknown                    msstore
Git                                   Git.Git                                    2.33.1                     winget
GitNote                               zhaopengme.gitnote                         3.1.0         Tag: git     winget
Agent Git                             Xidicone.AgentGit                          1.85          Tag: Git     winget
TortoiseSVN                           TortoiseSVN.TortoiseSVN                    1.14.29085    Tag: git     winget
TortoiseGit                           TortoiseGit.TortoiseGit                    2.12.0.0      Tag: git     winget

Installing a package

After you have found the package you want, installing it is as easy as invoking the following command:

winget install --id <SomePackage>

Of course the real fun starts when you create a script that contains all the packages you need for you day-to-day work. Here is the script I’m using:

Tuesday, October 19, 2021

ASP.NET Core–Running Swagger behind a reverse proxy

Today I helped out a colleague who was struggling with the following use case:

We have an ASP.NET Core Web API with OpenAPI(Swagger) integration enabled. This ASP.NET Core Web API was running behind a reverse proxy(Yarp in case you want to know) and isn’t directly accessibel.

To explain the problem he had, let’s start from the following situation:

When browsing to the Swagger endpoint on https://localhost/proxy/swagger, we got the following:

Do you notice the application url in the address bar vs the url in the Servers dropdown?

If we try to invoke a specific endpoint through the Swagger UI, Swagger tries to do the call directly to http://internalname:5000 which results in 404 error as the service is not available directly on this address.

It seems that Swagger doesn’t respect the Forwarded headers as provided through Yarp.

We fixed it by explicitly reading out the X-Forwarded-Host value and using it to fill up the servers dropdown:

Monday, October 18, 2021

Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException–The system cannot find the file specified

After deploying an application to production, it failed with the following error message:

The system cannot find the file specified

Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: at Internal.Cryptography.Pal.CertificatePal.FilterPFXStore (System.Security.Cryptography.X509Certificates, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a) at Internal.Cryptography.Pal.CertificatePal.FromBlobOrFile (System.Security.Cryptography.X509Certificates, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a) at System.Security.Cryptography.X509Certificates.X509Certificate..ctor (System.Security.Cryptography.X509Certificates, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a) at System.Security.Cryptography.X509Certificates.X509Certificate2..ctor (System.Security.Cryptography.X509Certificates, Version=5.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a)

Let’s take a look at the code that caused this issue:

Not much I could do wrong with this code. It tries to load a PFX file from disk and open it using the specified password.

I checked if the file was there and that was indeed the case. So we have to search for the root cause somewhere else.

I took a look at the Application Pool settings and noticed that the Load User Profile setting was set to False.

Aha! After changing this back to True the error got away…

Friday, October 15, 2021

DaprCon is coming on October 19th-20th 2021

What is Dapr?

Dapr helps developers build event-driven, resilient distributed applications. Whether on-premises, in the cloud, or on an edge device, Dapr helps you tackle the challenges that come with building microservices and keeps your code platform agnostic.

The Dapr ecosystem keeps growing and now they’ll have their first virtual conference ‘DaprCon’ next week. DaprCon will include a variety of content including a keynote, technical sessions, panel discussions and real-world experiences of adopters building with Dapr

DaprCon is a fully virtual event that will be streamed on YouTube and attendance is free! To watch the live events just follow these two links:

More information: https://blog.dapr.io/posts/2021/10/05/join-us-for-daprcon-october-19th-20th-2021/

Thursday, October 14, 2021

MassTransit–Message versioning

MassTransit dedicated a whole documentation page to message versioning but it still wasn’t completely clear to me how it worked.

Let’s use this blog post to see what’s going on…

Publishing messages

Let’s first focus on the sending side.

Publishing a first version of our message contract

We’ll start with a first version of our message contract:

Let’s send this to RabbitMQ using:

Time to open the RabbitMQ Management portal and take a look how the message payload is constructed:

Creating a second version of our message contract

Let’s introduce a v2 version of our message contract:

If we send it to RabbitMQ in the same way:

There isn’t such a big difference when comparing the payloads:

The ‘messagetype’ value is different and of course the message itself. But that’s it.

Send a backwards compatible version

Let’s now construct a message that implements both contracts:

And send that one:

If we now check the payload, we see that 1 message is put on the queue with the following payload:

Take a look at the ‘messagetype’. You can see that it contains both the 2 messagecontracts AND the concrete message type we created:

"messageType": [

"urn:message:Sender:Program+SubmitOrderCommand",
"urn:message:Messages:SubmitOrder",
"urn:message:Messages:SubmitOrderV2"
],

Consuming messages

Now we have a good understanding on what is going on at the sending side, let’s move on to the consuming side.

Consuming v1 of our message contract

Let’s create a consumer for that consumes v1 of our message contract:

And subscribe this consumer:

After publishing a ‘SubmitOrder’ message, our consumer is called as expected.

> Old Order consumed

Consuming v2 of our message contract

Let’s create a consumer for that consumes v2 of our message contract:

And subscribe this consumer:

After publishing a ‘SubmitOrderV2’ message, our consumer is called as expected.

> New Order consumed

So far nothing special.

Consuming the backwards compatible version

The question is what happens when we send our ‘SubmitOrderCommand’ that implements both message contracts.

If we have only one consumer subscribed the behavior is completely the same as before and either the old or the new consumer is called.

But if we have both consumers subscribed:

Each one will get a copy of the message and be called:

> Old Order consumed
> New Order consumed

Ok, that is good to now. But what happens if one of the consumers now fail?

Althought the first consumer is called succesfully, the message will still end up on the error queue:

If we then move the message back to the original queue, both consumers will be called again.

Wednesday, October 13, 2021

.NET 6.0 - Migrate to ASP.NET Core 6

.NET 6 introduces a new hosting model for ASP.NET Core applications. This model is streamlined and reduces the amount of boilerplate code required to get a basic ASP.NET Core application up and running.

There are a lot of blog posts out there explaining this new hosting model, but I like to share the guide written by David Fowler, software architect on the ASP.NET team.

He walks you through the building blocks, explains the differences in the hosting model, shares a list of frequently asked questions and provides you a cheatsheet.

 

Tuesday, October 12, 2021

ASP.NET Core - InvalidOperationException: 'VaryByQueryKeys' requires the response cache middleware.

Yes, I’m still rewriting an existing ASP.NET Web API application to ASP.NET Core.

I ported an existing action method where I was using response caching. I previously ported other action methods that used caching but this one was a little bit different because I had to take the querystring values into account for the caching.

This is easily arranged by specifying the name of the query string parameter using the VaryByQueryKeys property on the ResponseCache attribute.

Small tip: If you want to take all query string parameters into account, you can use “*” as a single value.

When I tried to call this method I got a 500 error. Inside the logs I noticed the following error message:

InvalidOperationException: 'VaryByQueryKeys' requires the response cache middleware.

Although caching seemed to work for other scenario’s, when I used the VaryByQueryKeys property I had to add the response caching middleware.

Here is my Startup.ConfigureServices():

And my Startup.Configure()

Monday, October 11, 2021

ASP.NET Core–Route arguments are case sensitive

I’m currently rewriting an existing ASP.NET Web API application to ASP.NET Core. Along the way I encountered some issues, here is one specific lesson I learned…

After porting an ASP.NET Web API controller to .NET Core, a specific action method looked like this:

Looked OK to me. But when I tried to invoke this action method, I got a 400 error back:

{
  "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
  "title": "One or more validation errors occurred.",
  "status": 400,
  "traceId": "00-3c02d7d0541cbb49a1790b11a71d871a-885c9350d2c4864c-00",
  "errors": {
    "ID": [
      "The value '{id}' is not valid."
    ]
  }
}
It wasn’t immediatelly obvious to me what I did wrong. Maybe you spot my mistake?
Let’s have a look at the route attribute: [HttpGet(“{id}”)]
and at the method argument: GetAuthTypeById(int ID)
As you can see the ‘id’ argument in the route is lowercase where the method argument is uppercase. Turns out that this is the cause of the error above.
To make it work, I have to make sure that both the route attribute and method argument are using the exact same casing: 

Friday, October 8, 2021

ASP.NET Core - Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException: Ambiguous HTTP method for action

I’m currently rewriting an existing ASP.NET Web API application to ASP.NET Core.

I ported a controller from ASP.NET Web API to ASP.NET Core. Here is the end result:

Everything looked OK at first sight, but when I tried to run the application I got the following error message:

Swashbuckle.AspNetCore.SwaggerGen.SwaggerGeneratorException: Ambiguous HTTP method for action - Controllers.AuthTypeController.GetAll (VLM.IAM.Services). Actions require an explicit HttpMethod binding for Swagger/OpenAPI 3.0

   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperations(IEnumerable`1 apiDescriptions, SchemaRepository schemaRepository)

   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GeneratePaths(IEnumerable`1 apiDescriptions, SchemaRepository schemaRepository)

   at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwagger(String documentName, String host, String basePath)

   at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)

   at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT`1.ProcessRequestAsync()

The Swagger/OpenAPI middleware requires that you explicitly annotate all your action methods. So I had to update the GetAll action method and annotate it with a [HttpGet] attribute:

Thursday, October 7, 2021

ASP.NET Core - ActionResult doesn’t work with IList

I’m currently rewriting an existing ASP.NET Web API application to ASP.NET Core. Yesterday I blogged about the use of the ActionResult<T> to combine the type safety of typed controllers with the list of out-of-the-box actionresults.

While introducing the ActionResult<T> class everywhere, I stumbled over one use case where I got a compiler error. Here is the specific code:

When you try to compile this code in Visual Studio it fails with the following error message:

CS0029: Cannot implicitly convert type ‘System.Collection.Generic.IList<T> to Microsoft.AspNetCore.Mvc.ActionResult<System.Collection.Generic.IList<T>>.

The reason is because C# doesn't support implicit cast operators on interfaces. Consequently, we need to convert the interface to a concrete type if we want to use it as our type argument for ActionResult<T>.

An easy fix is to wrap the IList in a concrete type as I did in the example below:

Wednesday, October 6, 2021

ASP.NET Core - How to handle the ‘NotFound’ use case when using a typed controller

I’m currently rewriting an existing ASP.NET Web API application to ASP.NET Core. While doing that, I (re)discovered some ASP.NET Core features I forgot they existed.

One of the features that ASP.NET Core offers are ‘typed controllers’. I don’t think that is the official name but you’ll know what I mean when you take a look at the example below:

In the example above I created an action method GetTodoItem that returned a typed object of type ‘TodoItem’ (or to be even more correct a Task<TodoItem>).  This makes your life as a developer easy as you don’t have to think about the ins and outs of ASP.NET. It almost feels like that this action method is exactly the same as any other method you’ll find on a class.

But what if wanted to return a 404 message when no TodoItem could be found for the specified id. Should I throw a specific exception? In ASP.NET Web API this was possible through the use of the HttpResponseException.

In ASP.NET Core there is a better alternative through the usage of ActionResult<T>. By wrapping the returned object in an ActionResult I can keep most of my code(thanks to the magic of implicit cast operators) and start using ActionResult types for specific use cases.

Let’s rewrite our example:

Tuesday, October 5, 2021

GraphQL - Use @skip and @include on fragments

GraphQL has multiple built-in directives like @deprecated, @include, @skip and some others like @stream and @defer that are not part of the official spec (yet).

With the @include directive you can conditionally include fields based on a specified argument:

The @skip directive does exactly the opposite and excludes fields based on a specified argument:

But applying these directives for every field that should be included or excluded feels like a lot of repetition:

Here is a trick for you, the @skip and @include directives can be used on fields, fragment spreads, and inline fragments. This can help us to make our GraphQL queries more readable.

Here is the example rewritten using an inline fragment:

And here is the same example with the fragment spread into the query:

Monday, October 4, 2021

Azure DevOps–Emoji’s

Both in pull request comments and wiki pages in Azure DevOps, you can use emojis while documenting, adding comments or reviewing requests.

If you want to find out what emoji’s are supported; you can find the full list here.

 

Friday, October 1, 2021

Application Insights Telemetry enricher for Kubernetes

Quick tip if you are hosting your ASP.NET Core application in Kubernetes: have a look at the Microsoft Application Insights for Kubernetes nuget package.

Through this package you can enrich your Application Insights telemetry data with Kubernetes specific information:

After adding the nuget package you can register the enricher by adding the following code:

More information: https://github.com/microsoft/ApplicationInsights-Kubernetes