Use a host header value for testing services on Kubernetes
Tags: csharp, testing, azure, azuredevops
Published: 2021-03-02 15:53:55

What is a [Host Header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Host)? Basically it's an address that the web server (or ingress service) will send a request to.   

At my company we are in the process of moving some of our services from Azure App Services to AKS. To run my C# API Integration tests against the Kubernetes pods our DevOps guys told me that I needed to use host header values to hit the internal K8S endpoints of the services. 

This was fairly easy to accomplish in C# as I just added a default parameter for the host header to my Post method (as well as my other REST methods).

```
/// <summary>
/// Helper method to add headers for PostAsync operation with no data.
/// </summary>
/// <param name="httpClient">HttpClient.</param>
/// <param name="url">url.</param>
/// <param name="hostHeader">Host header.</param>
/// <returns>HttpResponseMessage.</returns>
public static Task<HttpResponseMessage> PostAsJsonAsync(this HttpClient httpClient, string url, string hostHeader = "none")
{
    var stringContent = new StringContent(string.Empty);
    stringContent.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
    var dataAsString = stringContent.ToString();

    var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, url)
    {
      Content = new StringContent(dataAsString, Encoding.UTF8, "application/json"),
    };

    if (hostHeader != "none")
    {
        // Host name for K8S tests
        httpClient.DefaultRequestHeaders.Host = hostHeader;
    }

    return httpClient.PostAsync(url, stringContent);
}
```
By default the Host Header value is "none" for normal automation runs. When the tests are run against the AKS deployment we pass the host header variable value into the K8S yaml pipeline.

```
- stage: Test_Online
    pool:
      name: 'linux-agents'
    displayName: Test Online
    condition: and(succeeded(),eq(${{parameters.switch}}, true))
    variables:
      - ${{ if eq(parameters.envtype, 'prod') }}:
        - name: MyApiHostHeader
          value: ${{parameters.deployenv}}-${{parameters.service}}-online.${{parameters.region}}.${{parameters.deployenv}}.test.com
      - ${{ if eq(parameters.envtype, 'nonprod') }}:
        - ${{ if eq(parameters.deployenv, 'dev') }}:
          - name: MyApiHostHeader
            value: 
```
In my Visual Studio solution for the API automation I'm reading the hostHeader value from appSettings.json. In the K8S yaml pipeline we have to do a bunch of steps to run the job, including a FileTransform task to substitute the HostHeader variable we added earlier into the solution for the tests to run.

```
jobs:
      - job: Test_Offline
        steps:
        - ${{ if eq(parameters.skipTests, false) }}:
          - checkout: automation
            persistCredentials: true

          - script: |
              apt-get update
              apt-get install mono-complete
            displayName: Install Dotnet prerequisites

          - task: UseDotNet@2
            displayName: 'Use .NET Core'
            inputs:
              version: $(dotnet_core_version)
          - task: NuGetToolInstaller@1
            displayName: 'Use NuGet'
            inputs:
              versionSpec: $(nuget_version)
          - task: NuGetCommand@2
            displayName: 'NuGet restore'
            inputs:          
              restoreSolution: '**\MyAPITests.sln'

          #this task performs envvar substitution. we need to set the vars to override in lib var group or 
          #the env: block does NOT seem to work for passing in variable to task. we set these in global variables above
          - task: FileTransform@1
            inputs:      
              folderPath: '$(System.DefaultWorkingDirectory)'
              fileType: json
              targetFiles: '**/**/appSettings.json'

          - script: |
              cat MyAPITests/MyAPITests/appSettings.json
            displayName: show transform

          - task: DotNetCoreCLI@1
            displayName: 'Build solution: $(solution)'
            inputs:
              command: 'build'
              projects: |
                MyAPITests/$(solution)

          - task: DotNetCoreCLI@1
            displayName: 'Run API tests in solution: $(solution)'
            inputs:
              command: test
              projects: |
                HAPAPITests/**/*Tests/*.csproj
              arguments: '-c release -v d --filter Category=${{ parameters.testCategory }} --logger "trx;verbosity=detailed;LogFileName=DotNetApiAutomationTestResults.trx"'

          - task: PublishTestResults@2
            displayName: 'Publish Offline Test Results'
            inputs:
              searchFolder: MyAPITests
              testResultsFormat: 'VSTest'
              testResultsFiles: '**/*.trx'
              testRunTitle: '${{ parameters.service }} - Offline ApiAutomation ${{ parameters.deployenv }}'
              publishRunAttachments: true
              failTaskOnFailedTests: false
            condition: succeededOrFailed()

  ```
There's a lot of moving parts here with VS, Azure DevOps pipelines and AKS but we got it working!