I have this small hobby project called Overseer. It is a tool used to debug and troubleshoot microservices. I was planning to use it in production, but because that is the only prototype. I decided to use it with simulated services. This way it will be a lot easier to create many different ‘test’ scenarios.

The current solution for spawning those ‘tests services’ is implemented in Suave.io and F#. Why these technologies? I just like F# and Suave.io seems like a nice decent framework. Full code If you can, please review this code. I am not a F# expert and would love to learn how to make it better.

Test service graph definition

I want to iterate fast and create different networks of services. I need a simple way of defining the structure of the network. At the moment, I am using an array of tuples with a service name and collection of all the services it depends on. It looks like this.

let listOfServices = [ ( "GG.Web.Crowdfunding" , "ms" , [ "GG.Service.IdentityVerification" ; "GG.Service.Project" ; "GG.Service.Profile" ; "GG.Service.Crm" ; "GG.Service.Project.RiskAnalysis" ; "GG.Imaging.Read" ; "GG.Imaging.Write" ; "GG.Service.Project.Registration" ; "GG.Service.AB" ; "PayPal" ; "GG.Service.User" ]); ( "GG.Service.IdentityVerification" , "ms" , [ "VerifyIntegrity" ; "Unfido" ; "CreditCallService" ; "PayPal" ; "GG.Service.User" ]); ]

This array of tuples is changed to Suave web ‘servers’ with one endpoint.

let toTaskMicroServers endpoint = Task . Run ( fun () -> startWebServer ( serverConfig endpoint . port ) ( app endpoint )) let taskList = listOfServices |> createEndpoints |> List . map toTaskMicroServers |> List . toArray |> Task . WaitAll

The result is a list of Tasks running suave exposing one simple endpoint.

Endpoint list

type EndpointResponse = { port : uint16 ; url : string ; name : string ; serviceType : string ; status : bool ; dependancies : EndpointResponse list }

let createInitialEndpoints list = let startPort = 3000 list // fold used here to increment port |> List . fold ( fun ( array , port ) ( n , t , d ) -> ( createEndpoint n t d port ):: array , port + 1 ) ([], startPort ) // discarding port from tuple as it is not needed |> fs

To create a list of the endpoints, I am using fold function with an accumulator to increment port. Fold is a function that I tend to overuse too much. But in this case, it feels ok. Is there any other way to iterate through something with a maintnance of external state?

Suave config

It was a bit tricky as the documentation is not that good. After many try and errors and SO searching, I was able to create simple endpoints. All it does is returning JSON metadata describing ‘server’ (status, dependencies).

let app endpoint = choose [ GET >=> choose [ path "/status/health" >=> MimeJSON >=> setHeader "Access-Control-Allow-Origin" "*" >=> OK ( JsonConvert . SerializeObject ( endpoint )); path "/" >=> Redirection . redirect "/status/health" ] ]

Result

{ " port " : 3000 , " url " : " http://localhost:3000/status/health " , " name " : " GG.Web.Crowdfunding " , " serviceType " : " ms " , " status " : true , " dependancies " : [ { " port " : 3001 , " url " : " http://localhost:3001/status/health " , " name " : " GG.Service.IdentityVerification " , " serviceType " : " ms " , " status " : false , " dependancies " : [] }] }

All the dependencies, links to them, ports are here. This data is then used to create a graph app id d3.js, example.

This is still in the making. D3.js is really powerful but quite tricky to use. I am mostly experimenting and there are a lot of ‘wow’ moments. Kind of feels like winapi programming a long time ago.

The first test was done with 11 services and it ‘works’. Wonder how it will cope with 100+ services. That is a topic for a different blog post.