Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add mailcow status endpoint #144

Merged
merged 18 commits into from
Feb 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/dev_build_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ jobs:
DbConnect: "${{ secrets.DBCONNECT_TEST }}"
AZ_Storage_ContainerName: "${{ secrets.STORAGE_CONTAINER_NAME_TEST }}"
AZ_Storage_ConnectionString: "${{ secrets.STORAGE_CONNECTION_STRING }}"
Mailcow_API_Key: "${{ secrets.MAILCOW_API_KEY }}"
Mailcow_Status_Url: "${{ secrets.MAILCOW_STATUS_URL }}"

- name: Setup dotnet
uses: actions/setup-dotnet@v4
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/master_build_test_publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ jobs:
DbConnect: "${{ secrets.DBCONNECT_PROD }}"
AZ_Storage_ContainerName: "${{ secrets.STORAGE_CONTAINER_NAME_PROD }}"
AZ_Storage_ConnectionString: "${{ secrets.STORAGE_CONNECTION_STRING }}"
Mailcow_API_Key: "${{ secrets.MAILCOW_API_KEY }}"
Mailcow_Status_Url: "${{ secrets.MAILCOW_STATUS_URL }}"

- name: Production | Build Docker (prepare)
uses: azure/docker-login@v1.0.1
Expand All @@ -71,6 +73,7 @@ jobs:
- name: Production | Build Docker (execute with tests)
run: |
docker build . \
--build-arg "ENV_VALUE=Production" \
-t ${{ secrets.DOCKER_REGISTRY_SERVER_URL }}/${{ secrets.DOCKER_REGISTRY_SERVER_USERNAME }}:backend-production-${{ github.sha }}

- name: Production | Push Docker image (prepare)
Expand Down
7 changes: 6 additions & 1 deletion .github/workflows/stage_build_test_publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ jobs:
DbConnect: "${{ secrets.DBCONNECT_STAGE }}"
AZ_Storage_ContainerName: "${{ secrets.STORAGE_CONTAINER_NAME_STAGE }}"
AZ_Storage_ConnectionString: "${{ secrets.STORAGE_CONNECTION_STRING }}"
Mailcow_API_Key: "${{ secrets.MAILCOW_API_KEY }}"
Mailcow_Status_Url: "${{ secrets.MAILCOW_STATUS_URL }}"

- name: Staging | Build Docker (prepare)
uses: azure/docker-login@v1.0.1
Expand All @@ -32,7 +34,10 @@ jobs:
password: ${{ secrets.DOCKER_REGISTRY_SERVER_PASSWORD }}

- name: Staging | Build Docker (execute with tests)
run: docker build . -t ${{ secrets.DOCKER_REGISTRY_SERVER_URL }}/${{ secrets.DOCKER_REGISTRY_SERVER_USERNAME }}:backend-staging-${{ github.sha }}
run: |
docker build . \
--build-arg "ENV_VALUE=Staging" \
-t ${{ secrets.DOCKER_REGISTRY_SERVER_URL }}/${{ secrets.DOCKER_REGISTRY_SERVER_USERNAME }}:backend-staging-${{ github.sha }}

- name: Staging | Push Docker image (prepare)
uses: azure/docker-login@v1.0.1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

<ItemGroup>
<ProjectReference Include="..\..\EmailSender.Persistence\EmailSender.Persistence.Database\EmailSender.Persistence.Database.csproj" />
<ProjectReference Include="..\..\EmailSender.Services\EmailSender.Services.HttpClientService\EmailSender.Services.HttpClientService.csproj" />
<ProjectReference Include="..\EmailSender.Backend.Domain\EmailSender.Backend.Domain.csproj" />
<ProjectReference Include="..\EmailSender.Backend.Shared\EmailSender.Backend.Shared.csproj" />
<ProjectReference Include="..\..\EmailSender.Services\EmailSender.Services.SenderService\EmailSender.Services.SenderService.csproj" />
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
using MediatR;

namespace EmailSender.Backend.Application.Mailcow;

public class GetMailcowStatusQuery : IRequest<GetMailcowStatusQueryResult> { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
using System.Reflection;
using EmailSender.Backend.Application.Mailcow.Models;
using EmailSender.Backend.Core.Utilities.LoggerService;
using EmailSender.Services.HttpClientService.Abstractions;
using EmailSender.Services.HttpClientService.Models;
using Microsoft.Extensions.Configuration;

namespace EmailSender.Backend.Application.Mailcow;

public class GetMailcowStatusQueryHandler : RequestHandler<GetMailcowStatusQuery, GetMailcowStatusQueryResult>
{
private readonly IHttpClientServiceFactory _httpClientServiceFactory;

private readonly ILoggerService _loggerService;

private readonly IConfiguration _configuration;

private const BindingFlags Flags = BindingFlags.IgnoreCase | BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetField;

public GetMailcowStatusQueryHandler(IHttpClientServiceFactory httpClientServiceFactory, ILoggerService loggerService, IConfiguration configuration)
{
_httpClientServiceFactory = httpClientServiceFactory;
_loggerService = loggerService;
_configuration = configuration;
}

public override async Task<GetMailcowStatusQueryResult> Handle(GetMailcowStatusQuery request, CancellationToken cancellationToken)
{
var headers = new Dictionary<string, string>
{
["X-API-Key"] = _configuration.GetValue<string>("Mailcow_API_Key")
};

var configuration = new Configuration
{
Url = _configuration.GetValue<string>("Mailcow_Status_Url"),
Method = "GET",
Headers = headers
};

var client = _httpClientServiceFactory.Create(false, _loggerService);
var result = await client.Execute<MailcowStatus>(configuration, cancellationToken);

var healthyCount = 0;
var unhealthyCount = 0;
var data = new List<StatusItem>();

var resultType = result.GetType();
var resultProps = resultType.GetProperties(Flags);

foreach (var prop in resultProps)
{
var item = prop.GetValue(result, null);
if (item is null)
continue;

var statusItem = item as StatusItem;
data.Add(statusItem!);

if (statusItem?.State == "running")
{
healthyCount += 1;
}
else
{
unhealthyCount += 1;
}
}

if (healthyCount == data.Count)
{
return new GetMailcowStatusQueryResult
{
Status = StatusTypes.Healthy,
Results = data
};
}

if (healthyCount != unhealthyCount)
{
return new GetMailcowStatusQueryResult
{
Status = StatusTypes.Degraded,
Results = data
};
}

if (healthyCount == 0)
{
return new GetMailcowStatusQueryResult
{
Status = StatusTypes.Unhealthy,
Results = data
};
}

return new GetMailcowStatusQueryResult
{
Status = StatusTypes.Unknown,
Results = data
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using EmailSender.Backend.Application.Mailcow.Models;

namespace EmailSender.Backend.Application.Mailcow;

public class GetMailcowStatusQueryResult
{
public StatusTypes Status { get; set; }

public IList<StatusItem>? Results { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using Newtonsoft.Json;

namespace EmailSender.Backend.Application.Mailcow.Models;

public class MailcowStatus
{
[JsonProperty("ipv6nat-mailcow")]
public StatusItem? Ipv6Nat { get; set; }

[JsonProperty("watchdog-mailcow")]
public StatusItem? Watchdog { get; set; }

[JsonProperty("acme-mailcow")]
public StatusItem? Acme { get; set; }

[JsonProperty("ofelia-mailcow")]
public StatusItem? Ofelia { get; set; }

[JsonProperty("rspamd-mailcow")]
public StatusItem? Rspamd { get; set; }

[JsonProperty("nginx-mailcow")]
public StatusItem? Nginx { get; set; }

[JsonProperty("postfix-mailcow")]
public StatusItem? Postfix { get; set; }

[JsonProperty("dovecot-mailcow")]
public StatusItem? DoveCot { get; set; }

[JsonProperty("php-fpm-mailcow")]
public StatusItem? PhpFpm { get; set; }

[JsonProperty("mysql-mailcow")]
public StatusItem? MySql { get; set; }

[JsonProperty("redis-mailcow")]
public StatusItem? Redis { get; set; }

[JsonProperty("solr-mailcow")]
public StatusItem? Solr { get; set; }

[JsonProperty("clamd-mailcow")]
public StatusItem? Clamd { get; set; }

[JsonProperty("dockerapi-mailcow")]
public StatusItem? DockerApi { get; set; }

[JsonProperty("memcached-mailcow")]
public StatusItem? MemCached { get; set; }

[JsonProperty("sogo-mailcow")]
public StatusItem? SoGo { get; set; }

[JsonProperty("unbound-mailcow")]
public StatusItem? Unbound { get; set; }

[JsonProperty("netfilter-mailcow")]
public StatusItem? NetFilter { get; set; }

[JsonProperty("olefy-mailcow")]
public StatusItem? Olefy { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using Newtonsoft.Json;

namespace EmailSender.Backend.Application.Mailcow.Models;

public class StatusItem
{
[JsonProperty("type")]
public string? Type { get; set; }

[JsonProperty("container")]
public string? Container { get; set; }

[JsonProperty("state")]
public string? State { get; set; }

[JsonProperty("started_at")]
public DateTime? StartedAt { get; set; }

[JsonProperty("image")]
public string? Image { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace EmailSender.Backend.Application.Mailcow.Models;

public enum StatusTypes
{
Unknown,
Healthy,
Degraded,
Unhealthy
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading