Skip to content

Commit

Permalink
Merge pull request #13 from arktronic/main
Browse files Browse the repository at this point in the history
Upgrade to .NET 8 and fix HttpClient memory leak
  • Loading branch information
arktronic-sep authored Mar 24, 2024
2 parents d562543 + 1710809 commit 378adf4
Show file tree
Hide file tree
Showing 21 changed files with 411 additions and 434 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ jobs:
name: Unit Tests on Linux
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- name: Set up dotnet
uses: actions/setup-dotnet@v1
uses: actions/setup-dotnet@v4
with:
dotnet-version: '6.x'
dotnet-version: '8'
- run: dotnet test
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build-env
WORKDIR /build

# Copy csproj and restore as distinct layers
Expand All @@ -10,7 +10,7 @@ COPY sama/ ./
RUN dotnet publish -c Release -o out --no-restore /p:MvcRazorCompileOnPublish=true

# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:6.0
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /opt/sama
COPY --from=build-env /build/out .
ENTRYPOINT ["dotnet", "sama.dll"]
Expand Down
4 changes: 2 additions & 2 deletions Dockerfile-buildx-amd64
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Forcing amd64 for buildx per https://github.com/dotnet/dotnet-docker/issues/1537#issuecomment-755351628
FROM mcr.microsoft.com/dotnet/sdk:6.0-bullseye-slim-amd64 AS build-env
FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim-amd64 AS build-env
WORKDIR /build

# Copy csproj and restore as distinct layers
Expand All @@ -11,7 +11,7 @@ COPY sama/ ./
RUN dotnet publish -c Release -o out --no-restore /p:MvcRazorCompileOnPublish=true

# Build runtime image
FROM mcr.microsoft.com/dotnet/aspnet:6.0
FROM mcr.microsoft.com/dotnet/aspnet:8.0
WORKDIR /opt/sama
COPY --from=build-env /build/out .
ENTRYPOINT ["dotnet", "sama.dll"]
Expand Down
2 changes: 1 addition & 1 deletion sama/Controllers/AccountController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ public async Task<IActionResult> Edit(Guid? id)
var isRemote = (user == null && userId[0] == 0 && userId[1] == 0 && userId[2] == 0 && userId[3] == 0);
if (isRemote)
{
ViewData["IsCurrentUser"] = (id == Guid.Parse(_userManager.GetUserId(User)));
ViewData["IsCurrentUser"] = (id == Guid.Parse(_userManager.GetUserId(User)!));
return View("EditRemote");
}

Expand Down
15 changes: 1 addition & 14 deletions sama/Controllers/EndpointsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,8 @@
namespace sama.Controllers
{
[Authorize]
public class EndpointsController : Controller
public class EndpointsController(ApplicationDbContext _context, StateService _stateService, UserManagementService _userService, AggregateNotificationService _notifier) : Controller
{
private readonly ApplicationDbContext _context;
private readonly StateService _stateService;
private readonly UserManagementService _userService;
private readonly AggregateNotificationService _notifier;

public EndpointsController(ApplicationDbContext context, StateService stateService, UserManagementService userService, AggregateNotificationService notifier)
{
_context = context;
_stateService = stateService;
_userService = userService;
_notifier = notifier;
}

[AllowAnonymous]
public async Task<IActionResult> IndexRedirect()
{
Expand Down
51 changes: 26 additions & 25 deletions sama/Extensions/EndpointHttpExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using sama.Models;
using sama.Models;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Text.Json.Nodes;

namespace sama.Extensions
{
public static class EndpointHttpExtensions
{
private static JsonSerializerSettings JsonSettings = new JsonSerializerSettings { ContractResolver = new DefaultContractResolver() };

public static string? GetHttpLocation(this Endpoint endpoint) =>
GetValue<string>(endpoint, "Location");

Expand Down Expand Up @@ -45,43 +41,48 @@ public static void SetHttpCustomTlsCert(this Endpoint endpoint, string? pemEncod

private static List<T>? GetValueList<T>(Endpoint endpoint, string name, List<T>? defaultValue = null)
{
var list = GetValue<List<object>>(endpoint, name);
if (list == null)
EnsureHttp(endpoint);
if (string.IsNullOrWhiteSpace(endpoint.JsonConfig)) return defaultValue;

var node = JsonNode.Parse(endpoint.JsonConfig);
var matches = node?.AsObject().Where(kvp => kvp.Key == name);
if (matches?.Any() ?? false)
{
return defaultValue;
if (matches.First().Value == null) return defaultValue;
var array = matches.First().Value!.AsArray();
if (array == null) return defaultValue;
return array.Select(n => n!.GetValue<T>()).ToList();
}
return list.Select(o => (T)Convert.ChangeType(o, typeof(T))).ToList();
// else
return defaultValue;
}

private static T? GetValue<T>(Endpoint endpoint, string name, T? defaultValue = default(T))
{
EnsureHttp(endpoint);
if (string.IsNullOrWhiteSpace(endpoint.JsonConfig)) return defaultValue;

var obj = JsonConvert.DeserializeObject<ExpandoObject>(endpoint.JsonConfig, JsonSettings) as IDictionary<string, object>;
if (obj == null)
var node = JsonNode.Parse(endpoint.JsonConfig);
var matches = node?.AsObject().Where(kvp => kvp.Key == name);
if (matches?.Any() ?? false)
{
throw new ArgumentException($"Unable to get value for '{name}'");
if (matches.First().Value == null) return defaultValue;
return matches.First().Value!.GetValue<T>();
}
if (!obj.ContainsKey(name))
{
return defaultValue;
}
return (T)obj[name];
// else
return defaultValue;
}

private static void SetValue<T>(Endpoint endpoint, string name, T? value)
{
EnsureHttp(endpoint);

var json = (string.IsNullOrWhiteSpace(endpoint.JsonConfig) ? "{}" : endpoint.JsonConfig);
var obj = JsonConvert.DeserializeObject<ExpandoObject>(json, JsonSettings) ?? throw new ArgumentException($"Unable to deserialize '{name}'");
obj!.Remove(name, out object _);
if (!obj.TryAdd(name, value))
{
throw new ArgumentException($"Unable to set value for '{name}'");
}
endpoint.JsonConfig = JsonConvert.SerializeObject(obj, JsonSettings);

var nodeObj = JsonNode.Parse(json)?.AsObject() ?? throw new ArgumentException($"Unable to deserialize '{name}'");
nodeObj.Remove(name);
nodeObj.Add(name, JsonValue.Create(value));
endpoint.JsonConfig = nodeObj.ToJsonString();
}

private static void EnsureHttp(Endpoint endpoint)
Expand Down
27 changes: 18 additions & 9 deletions sama/Extensions/EndpointIcmpExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,31 +1,40 @@
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using sama.Models;
using sama.Models;
using System;
using System.Dynamic;
using System.Linq;
using System.Text.Json.Nodes;

namespace sama.Extensions
{
public static class EndpointIcmpExtensions
{
private static JsonSerializerSettings JsonSettings = new JsonSerializerSettings { ContractResolver = new DefaultContractResolver() };
private const string IcmpAddressKey = "Address";

public static string? GetIcmpAddress(this Endpoint endpoint)
{
EnsureIcmp(endpoint);
if (string.IsNullOrWhiteSpace(endpoint.JsonConfig)) return null;

return JsonConvert.DeserializeObject<dynamic>(endpoint.JsonConfig, JsonSettings)?.Address?.ToObject<string>();
var node = JsonNode.Parse(endpoint.JsonConfig);
var matches = node?.AsObject().Where(kvp => kvp.Key == IcmpAddressKey);
if (matches?.Any() ?? false)
{
if (matches.First().Value == null) return null;
return matches.First().Value!.GetValue<string>();
}
// else
return null;
}

public static void SetIcmpAddress(this Endpoint endpoint, string? address)
{
EnsureIcmp(endpoint);

var json = (string.IsNullOrWhiteSpace(endpoint.JsonConfig) ? "{}" : endpoint.JsonConfig);
dynamic obj = JsonConvert.DeserializeObject<ExpandoObject>(json, JsonSettings) ?? throw new ArgumentException("Unable to deserialize JSON settings for endpoint");
obj.Address = address;
endpoint.JsonConfig = JsonConvert.SerializeObject(obj, JsonSettings);

var nodeObj = JsonNode.Parse(json)?.AsObject() ?? throw new ArgumentException($"Unable to deserialize Address");
nodeObj.Remove(IcmpAddressKey);
nodeObj.Add(IcmpAddressKey, JsonValue.Create(address));
endpoint.JsonConfig = nodeObj.ToJsonString();
}

private static void EnsureIcmp(Endpoint endpoint)
Expand Down
22 changes: 22 additions & 0 deletions sama/HttpHandlerFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;
using System.Net.Http;
using System.Net.Security;

namespace sama;

public class HttpHandlerFactory
{
public virtual HttpMessageHandler Create(bool allowAutoRedirect, SslClientAuthenticationOptions? sslOptions)
{
var handler = new SocketsHttpHandler
{
PooledConnectionLifetime = TimeSpan.Zero,
AllowAutoRedirect = allowAutoRedirect,
};
if (sslOptions != null)
{
handler.SslOptions = sslOptions;
}
return handler;
}
}
2 changes: 1 addition & 1 deletion sama/Services/GraphiteNotificationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public virtual void NotifySingleResult(Endpoint endpoint, EndpointCheckResult re
}
catch (Exception ex)
{
_logger.LogError(0, "Unable to send Graphite notification", ex);
_logger.LogError(ex, "Unable to send Graphite notification");
}
}

Expand Down
Loading

0 comments on commit 378adf4

Please sign in to comment.