Skip to content

Consider health checks that add some verification for setup #176

@twsouthwick

Description

@twsouthwick

Summary

We should provide some healthchecks that will ensure certain conditions are met and will provide users with actionable changes (i.e. identify that they are using the same virtual directory).

Motivation and goals

There are a number of conditions that need to be met in order to have a good experience:

  • Remote app is reachable
  • Virtual directory setup is the same
  • YARP setup
  • potentially more

Example

I have an example of what this looks like at https://github.com/twsouthwick/systemweb-adapters/tree/tasou/diagnostics. Public API surface-wise, it adds the following:

public static class DiagnosticsExtension
{
  public static ISystemWebAdapterBuilder AddDiagnostics(this ISystemWebAdapterBuilder builder);
}

If the full output is written out, it will look something like the following:

image

This is done by exposing (internally initially) two services:

internal interface IClientDiagnostic
{
    void Prepare(HttpRequestMessage request);

    DiagnosticResult Process(HttpResponseMessage response);
}

internal interface IServerDiagnostic
{
    void Process(HttpContextBase context);
}

An example diagnostic that verifies the virtual directory setup would look like this:

internal class VirtualDirectoryDiagnostic : IClientDiagnostic, IServerDiagnostic
{
    private const string HeaderName = "X-SystemWebAdapters-Diagnostic-VirtualDirectory";

    public string Name => "Virtual Directory Setup";

    void IClientDiagnostic.Prepare(HttpRequestMessage request)
    {
    }

    DiagnosticResult IClientDiagnostic.Process(HttpResponseMessage response)
    {
        if (response.Headers.TryGetValues(HeaderName, out var value) && value.FirstOrDefault() is { } serverAppPath)
        {
            var result = Result.Create(serverAppPath);

            return new DiagnosticResult(result.IsValid ? DiagnosticStatus.Healthy : DiagnosticStatus.Unhealthy, Name, result);
        }
        else
        {
            return new DiagnosticResult(DiagnosticStatus.Unhealthy, Name, Result.Unavailable);
        }
    }

    void IServerDiagnostic.Process(HttpContextBase context)
    {
        context.Response.Headers[HeaderName] = HttpRuntime.AppDomainAppVirtualPath;
    }

    private class Result
    {
        public static Result Unavailable = new(HttpRuntime.AppDomainAppVirtualPath, string.Empty);
        public static Result Same = new(HttpRuntime.AppDomainAppVirtualPath, HttpRuntime.AppDomainAppVirtualPath);

        public static Result Create(string server)
        {
            if (string.Equals(HttpRuntime.AppDomainAppVirtualPath, server, StringComparison.Ordinal))
            {
                return Same;
            }

            return new Result(HttpRuntime.AppDomainAppVirtualPath, server);
        }

        public Result(string client, string server)
        {
            Client = client;
            Server = server;
        }

        public bool IsValid => string.Equals(Client, Server, StringComparison.Ordinal);

        public string Client { get; }

        public string Server { get; }
    }
}

The flow would be something along the lines of:

  1. The health check creates an HttpRequestMessage
  2. This message is passed through the client diagnostics to prepare it
  3. It is then sent to the server
  4. The registered server diagnostics will process the request and write back what it needs to the response
  5. Upon receiving the response, the client diagnostic can process the incoming response and generate any health status it needs.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions