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

Move AzureContainerApps into src folder and minor cleanup #1901

Merged
merged 6 commits into from
Feb 8, 2023
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
28 changes: 14 additions & 14 deletions ProtoActor.sln
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Proto.Analyzers", "src\Prot
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Proto.Analyzers.Tests", "tests\Proto.Analyzers.Tests\Proto.Analyzers.Tests.csproj", "{E56413ED-8205-4AC1-A7CE-24A2C1711F54}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Proto.Cluster.AzureContainerApps", "Proto.Cluster.AzureContainerApps\Proto.Cluster.AzureContainerApps.csproj", "{4A8305DB-758B-4CAD-B8A8-146279A0729A}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Proto.Cluster.AzureContainerApps", "src\Proto.Cluster.AzureContainerApps\Proto.Cluster.AzureContainerApps.csproj", "{4DF9BBFF-C480-4550-B2BA-6603DAE6BC6F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -1348,18 +1348,18 @@ Global
{E56413ED-8205-4AC1-A7CE-24A2C1711F54}.Release|x64.Build.0 = Release|Any CPU
{E56413ED-8205-4AC1-A7CE-24A2C1711F54}.Release|x86.ActiveCfg = Release|Any CPU
{E56413ED-8205-4AC1-A7CE-24A2C1711F54}.Release|x86.Build.0 = Release|Any CPU
{4A8305DB-758B-4CAD-B8A8-146279A0729A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4A8305DB-758B-4CAD-B8A8-146279A0729A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4A8305DB-758B-4CAD-B8A8-146279A0729A}.Debug|x64.ActiveCfg = Debug|Any CPU
{4A8305DB-758B-4CAD-B8A8-146279A0729A}.Debug|x64.Build.0 = Debug|Any CPU
{4A8305DB-758B-4CAD-B8A8-146279A0729A}.Debug|x86.ActiveCfg = Debug|Any CPU
{4A8305DB-758B-4CAD-B8A8-146279A0729A}.Debug|x86.Build.0 = Debug|Any CPU
{4A8305DB-758B-4CAD-B8A8-146279A0729A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4A8305DB-758B-4CAD-B8A8-146279A0729A}.Release|Any CPU.Build.0 = Release|Any CPU
{4A8305DB-758B-4CAD-B8A8-146279A0729A}.Release|x64.ActiveCfg = Release|Any CPU
{4A8305DB-758B-4CAD-B8A8-146279A0729A}.Release|x64.Build.0 = Release|Any CPU
{4A8305DB-758B-4CAD-B8A8-146279A0729A}.Release|x86.ActiveCfg = Release|Any CPU
{4A8305DB-758B-4CAD-B8A8-146279A0729A}.Release|x86.Build.0 = Release|Any CPU
{4DF9BBFF-C480-4550-B2BA-6603DAE6BC6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4DF9BBFF-C480-4550-B2BA-6603DAE6BC6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4DF9BBFF-C480-4550-B2BA-6603DAE6BC6F}.Debug|x64.ActiveCfg = Debug|Any CPU
{4DF9BBFF-C480-4550-B2BA-6603DAE6BC6F}.Debug|x64.Build.0 = Debug|Any CPU
{4DF9BBFF-C480-4550-B2BA-6603DAE6BC6F}.Debug|x86.ActiveCfg = Debug|Any CPU
{4DF9BBFF-C480-4550-B2BA-6603DAE6BC6F}.Debug|x86.Build.0 = Debug|Any CPU
{4DF9BBFF-C480-4550-B2BA-6603DAE6BC6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4DF9BBFF-C480-4550-B2BA-6603DAE6BC6F}.Release|Any CPU.Build.0 = Release|Any CPU
{4DF9BBFF-C480-4550-B2BA-6603DAE6BC6F}.Release|x64.ActiveCfg = Release|Any CPU
{4DF9BBFF-C480-4550-B2BA-6603DAE6BC6F}.Release|x64.Build.0 = Release|Any CPU
{4DF9BBFF-C480-4550-B2BA-6603DAE6BC6F}.Release|x86.ActiveCfg = Release|Any CPU
{4DF9BBFF-C480-4550-B2BA-6603DAE6BC6F}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -1484,7 +1484,7 @@ Global
{B0F9003C-BA53-4948-8FB7-1F7F230F9310} = {3D12F5E5-9774-4D7E-8A5B-B1F64544925B}
{9D139291-31D9-4502-B5BD-EA522F54B008} = {3D12F5E5-9774-4D7E-8A5B-B1F64544925B}
{E56413ED-8205-4AC1-A7CE-24A2C1711F54} = {9AA2BCF0-19AB-4DD9-8D91-7D188E463806}
{4A8305DB-758B-4CAD-B8A8-146279A0729A} = {3D12F5E5-9774-4D7E-8A5B-B1F64544925B}
{4DF9BBFF-C480-4550-B2BA-6603DAE6BC6F} = {3D12F5E5-9774-4D7E-8A5B-B1F64544925B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {CD0D1E44-8118-4682-8793-6B20ABFA824C}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public static class ArmClientUtils
public static async Task<Member[]> GetClusterMembers(this ArmClient client, string resourceGroupName, string containerAppName)
{
var members = new List<Member>();

var containerApp = await (await client.GetResourceGroupByName(resourceGroupName)).Value.GetContainerAppAsync(containerAppName);

if (containerApp is null || !containerApp.HasValue)
Expand All @@ -36,7 +36,7 @@ public static async Task<Member[]> GetClusterMembers(this ArmClient client, stri
var replicasWithTraffic = containerAppRevisions.SelectMany(r => r.GetContainerAppReplicas());

var allTags = (await containerApp.Value.GetTagResource().GetAsync()).Value.Data.TagValues;

foreach (var replica in replicasWithTraffic)
{
var replicaNameTag = allTags.FirstOrDefault(kvp => kvp.Value == replica.Data.Name);
Expand Down Expand Up @@ -81,15 +81,16 @@ public static async Task AddMemberTags(this ArmClient client, string resourceGro
var resourceGroup = await client.GetResourceGroupByName(resourceGroupName);
var containerApp = await resourceGroup.Value.GetContainerAppAsync(containerAppName);
var tagResource = containerApp.Value.GetTagResource();

var existingTags = (await tagResource.GetAsync()).Value.Data.TagValues;
foreach (var tag in existingTags)
{
resourceTag.TagValues.Add(tag);
}

await tagResource.CreateOrUpdateAsync(WaitUntil.Completed, new TagResourceData(resourceTag));
}

public static async Task ClearMemberTags(this ArmClient client, string resourceGroupName, string containerAppName, string memberId)
{
var resourceGroup = await client.GetResourceGroupByName(resourceGroupName);
Expand All @@ -98,21 +99,21 @@ public static async Task ClearMemberTags(this ArmClient client, string resourceG

var resourceTag = new Tag();
var existingTags = (await tagResource.GetAsync()).Value.Data.TagValues;

foreach (var tag in existingTags)
{
if (!tag.Key.StartsWith(ResourceTagLabels.LabelPrefix(memberId)))
{
resourceTag.TagValues.Add(tag);
}
}

await tagResource.CreateOrUpdateAsync(WaitUntil.Completed, new TagResourceData(resourceTag));
}

public static async Task<Response<ResourceGroupResource>> GetResourceGroupByName(this ArmClient client, string resourceGroupName) =>
public static async Task<Response<ResourceGroupResource>> GetResourceGroupByName(this ArmClient client, string resourceGroupName) =>
await (await client.GetDefaultSubscriptionAsync()).GetResourceGroups().GetAsync(resourceGroupName);

private static IEnumerable<ContainerAppRevisionResource> GetActiveRevisionsWithTraffic(ContainerAppResource containerApp) =>
containerApp.GetContainerAppRevisions().Where(r => r.HasData && r.Data.Active.GetValueOrDefault(false) && r.Data.TrafficWeight > 0);
private static IEnumerable<ContainerAppRevisionResource> GetActiveRevisionsWithTraffic(ContainerAppResource containerApp) =>
containerApp.GetContainerAppRevisions().Where(r => r.HasData && (r.Data.IsActive ?? false) && r.Data.TrafficWeight > 0);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,55 +4,55 @@
using System.Threading.Tasks;
using Azure.ResourceManager;
using Azure.ResourceManager.AppContainers;
using Microsoft.Extensions.Configuration;
using JetBrains.Annotations;
using Microsoft.Extensions.Logging;
using Proto.Utils;

namespace Proto.Cluster.AzureContainerApps;

public class AzureContainerAppsProvider : IClusterProvider
[PublicAPI]
public class AzureContainerAppsProvider : IClusterProvider
{
public readonly string AdvertisedHost;

private readonly ArmClient _client;
private readonly string _resourceGroup;
private readonly string _containerAppName;
private readonly string _revisionName;
private readonly string _replicaName;
private readonly string _advertisedHost;

private string _memberId = null!;
private string _address = null!;
private Cluster _cluster = null!;
private string _clusterName = null!;
private string[] _kinds = null!;
private string _host = null!;
private int _port;

private readonly IConfiguration _configuration;
private static readonly ILogger Logger = Log.CreateLogger<AzureContainerAppsProvider>();
private static readonly TimeSpan PollIntervalInSeconds = TimeSpan.FromSeconds(5);

/// <summary>
/// Use this constructor to create a new instance.
/// </summary>
/// <param name="client">An existing <see cref="ArmClient"/></param> instance that you need to bring yourself.
/// <param name="resourceGroup">The resource group name containing your Azure Container App.</param>
/// <param name="containerAppName">The name of the container app. If not specified, the CONTAINER_APP_NAME environment variable is used.</param>
/// <param name="revisionName">The revisionName of the container app. If not specified, the CONTAINER_APP_REVISION environment variable is used.</param>
/// <param name="replicaName">The replica name of the container app. If not specified, the HOSTNAME environment variable is used.</param>
/// <param name="advertisedHost">The host or IP address of the container app. If not specified, will take the smallest local IP address (e.g. 127.0.0.1).</param>
public AzureContainerAppsProvider(
IConfiguration configuration,
ArmClient client,
string resourceGroup,
string containerAppName,
string revisionName,
string replicaName,
string advertisedHost = default)
string resourceGroup,
[CanBeNull] string containerAppName = default,
[CanBeNull] string revisionName = default,
[CanBeNull] string replicaName = default,
[CanBeNull] string advertisedHost = default)
{
_configuration = configuration;
_client = client;
_resourceGroup = resourceGroup;
_containerAppName = containerAppName;
_revisionName = revisionName;
_replicaName = replicaName;
AdvertisedHost = advertisedHost;

if (string.IsNullOrEmpty(AdvertisedHost))
{
AdvertisedHost = ConfigUtils.FindIpAddress().ToString();
}
_containerAppName = containerAppName ?? Environment.GetEnvironmentVariable("CONTAINER_APP_NAME") ?? throw new Exception("No app name provided");
_revisionName = revisionName ?? Environment.GetEnvironmentVariable("CONTAINER_APP_REVISION") ?? throw new Exception("No app revision provided");
_replicaName = replicaName ?? Environment.GetEnvironmentVariable("HOSTNAME") ?? throw new Exception("No replica name provided");
_advertisedHost = !string.IsNullOrEmpty(advertisedHost) ? advertisedHost : ConfigUtils.FindSmallestIpAddress().ToString();
}

public async Task StartMemberAsync(Cluster cluster)
Expand All @@ -64,38 +64,34 @@ public async Task StartMemberAsync(Cluster cluster)
_clusterName = clusterName;
_memberId = cluster.System.Id;
_port = port;
_host = host;
_kinds = kinds;
_address = $"{host}:{port}";

await RegisterMemberAsync();
StartClusterMonitor();
}

public Task StartClientAsync(Cluster cluster)
{
var clusterName = cluster.Config.ClusterName;
var (host, port) = cluster.System.GetAddress();
var (_, port) = cluster.System.GetAddress();
_cluster = cluster;
_clusterName = clusterName;
_memberId = cluster.System.Id;
_port = port;
_host = host;
_kinds = Array.Empty<string>();

StartClusterMonitor();
return Task.CompletedTask;
}

public async Task ShutdownAsync(bool graceful) => await DeregisterMemberAsync();

private async Task RegisterMemberAsync()
{
await Retry.Try(RegisterMemberInner, onError: OnError, onFailed: OnFailed, retryCount: Retry.Forever);

static void OnError(int attempt, Exception exception) =>
Logger.LogWarning(exception, "Failed to register service");
await Retry.Try(RegisterMemberInner, retryCount: Retry.Forever, onError: OnError, onFailed: OnFailed);

static void OnError(int attempt, Exception exception) => Logger.LogWarning(exception, "Failed to register service");
static void OnFailed(Exception exception) => Logger.LogError(exception, "Failed to register service");
}

Expand All @@ -106,19 +102,17 @@ private async Task RegisterMemberInner()
var revision = await containerApp.Value.GetContainerAppRevisionAsync(_revisionName);

if (revision.Value.Data.TrafficWeight.GetValueOrDefault(0) == 0)
{
return;
}


Logger.LogInformation(
"[Cluster][AzureContainerAppsProvider] Registering service {ReplicaName} on {IpAddress}",
"[Cluster][AzureContainerAppsProvider] Registering service {ReplicaName} on {IpAddress}",
_replicaName,
_address);

var tags = new Dictionary<string, string>
{
[ResourceTagLabels.LabelCluster(_memberId)] = _clusterName,
[ResourceTagLabels.LabelHost(_memberId)] = AdvertisedHost,
[ResourceTagLabels.LabelHost(_memberId)] = _advertisedHost,
[ResourceTagLabels.LabelPort(_memberId)] = _port.ToString(),
[ResourceTagLabels.LabelMemberId(_memberId)] = _memberId,
[ResourceTagLabels.LabelReplicaName(_memberId)] = _replicaName
Expand All @@ -145,7 +139,7 @@ private void StartClusterMonitor() =>
{
while (!_cluster.System.Shutdown.IsCancellationRequested)
{
Logger.LogInformation("Calling ECS API");
Logger.LogInformation("Calling ACS API");

try
{
Expand Down Expand Up @@ -184,7 +178,7 @@ static void OnError(int attempt, Exception exception) =>
private async Task DeregisterMemberInner()
{
Logger.LogInformation(
"[Cluster][AzureContainerAppsProvider] Unregistering member {ReplicaName} on {IpAddress}",
"[Cluster][AzureContainerAppsProvider] Unregistering member {ReplicaName} on {IpAddress}",
_replicaName,
_address);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,47 @@
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using JetBrains.Annotations;

namespace Proto.Cluster.AzureContainerApps;

public static class ConfigUtils
{
internal static IPAddress FindIpAddress(AddressFamily family = AddressFamily.InterNetwork)
public static IPAddress FindSmallestIpAddress(AddressFamily family = AddressFamily.InterNetwork)
{
var addressCandidates = NetworkInterface.GetAllNetworkInterfaces()
.Where(nif => nif.OperationalStatus == OperationalStatus.Up)
.SelectMany(nif => nif.GetIPProperties().UnicastAddresses.Select(a => a.Address))
.Where(addr => addr.AddressFamily == family && !IPAddress.IsLoopback(addr))
.ToList();

return PickSmallestIpAddress(addressCandidates);
}

private static IPAddress PickSmallestIpAddress(IEnumerable<IPAddress> candidates)
{
IPAddress result = null!;

foreach (var addr in candidates)
{
if (CompareIpAddresses(addr, result))
result = addr;
}

return result;

static bool CompareIpAddresses(IPAddress lhs, IPAddress rhs)
static bool CompareIpAddresses(IPAddress lhs, [CanBeNull] IPAddress rhs)
{
if (rhs == null)
return true;

var lbytes = lhs.GetAddressBytes();
var rbytes = rhs.GetAddressBytes();

if (lbytes.Length != rbytes.Length) return lbytes.Length < rbytes.Length;

for (var i = 0; i < lbytes.Length; i++)
{
if (lbytes[i] != rbytes[i])
{
return lbytes[i] < rbytes[i];
}
}

return false;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
<PropertyGroup>
<LangVersion>10</LangVersion>
<TargetFrameworks>netcoreapp3.1;net6.0;net7.0</TargetFrameworks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.ResourceManager.AppContainers" Version="1.0.0-beta.1" />
<PackageReference Include="Azure.ResourceManager.Resources" Version="1.3.1" />
<PackageReference Include="Azure.ResourceManager.AppContainers" Version="1.0.1"/>
<PackageReference Include="Azure.ResourceManager.Resources" Version="1.3.1"/>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\src\Proto.Cluster\Proto.Cluster.csproj" />
<ProjectReference Include="..\Proto.Cluster\Proto.Cluster.csproj"/>
</ItemGroup>

</Project>