Compare commits

...

1 Commits

Author SHA1 Message Date
David Fowler 20c9bfeed2 WIP 6 years ago
  1. 6
      src/Microsoft.Tye.Core/ConfigModel/ConfigApplication.cs
  2. 54
      src/Microsoft.Tye.Hosting/AddressAssigner.cs
  3. 13
      src/Microsoft.Tye.Hosting/Dashboard/Pages/Index.razor
  4. 3
      src/Microsoft.Tye.Hosting/DockerRunner.cs
  5. 2
      src/Microsoft.Tye.Hosting/Model/Service.cs
  6. 8
      src/Microsoft.Tye.Hosting/ProcessRunner.cs
  7. 2
      src/Microsoft.Tye.Hosting/ProxyService.cs
  8. 2
      src/Microsoft.Tye.Hosting/TyeHost.cs

6
src/Microsoft.Tye.Core/ConfigModel/ConfigApplication.cs

@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Runtime.InteropServices;
using Tye; using Tye;
using Tye.Serialization; using Tye.Serialization;
using YamlDotNet.Serialization; using YamlDotNet.Serialization;
@ -106,7 +107,7 @@ namespace Microsoft.Tye.ConfigModel
throw new TyeYamlException(CoreStrings.FormatMultipleBindingWithSameName("service")); throw new TyeYamlException(CoreStrings.FormatMultipleBindingWithSameName("service"));
} }
if (service.Bindings.Count(o => o.Port != null && o.Port == binding.Port) > 1) if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && service.Bindings.Count(o => o.Port != null && o.Port == binding.Port) > 1)
{ {
throw new TyeYamlException(CoreStrings.FormatMultipleBindingWithSamePort("service")); throw new TyeYamlException(CoreStrings.FormatMultipleBindingWithSamePort("service"));
} }
@ -166,7 +167,8 @@ namespace Microsoft.Tye.ConfigModel
{ {
throw new TyeYamlException(CoreStrings.IngressBindingMustBeHttpOrHttps); throw new TyeYamlException(CoreStrings.IngressBindingMustBeHttpOrHttps);
} }
if (ingress.Bindings.Count(o => o.Port == binding.Port) > 1)
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && ingress.Bindings.Count(o => o.Port == binding.Port) > 1)
{ {
throw new TyeYamlException(CoreStrings.FormatMultipleBindingWithSamePort("ingress")); throw new TyeYamlException(CoreStrings.FormatMultipleBindingWithSamePort("ingress"));
} }

54
src/Microsoft.Tye.Hosting/PortAssigner.cs → src/Microsoft.Tye.Hosting/AddressAssigner.cs

@ -9,20 +9,23 @@ using System.Net.Sockets;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.Tye.Hosting.Model; using Microsoft.Tye.Hosting.Model;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using System.Runtime.InteropServices;
namespace Microsoft.Tye.Hosting namespace Microsoft.Tye.Hosting
{ {
public class PortAssigner : IApplicationProcessor public class AddressAssigner : IApplicationProcessor
{ {
private readonly ILogger _logger; private readonly ILogger _logger;
public PortAssigner(ILogger logger) public AddressAssigner(ILogger logger)
{ {
_logger = logger; _logger = logger;
} }
public Task StartAsync(Application application) public Task StartAsync(Application application)
{ {
// Nobody's going to have > 255 services right?
var octect = 0;
foreach (var service in application.Services.Values) foreach (var service in application.Services.Values)
{ {
if (service.Description.RunInfo == null) if (service.Description.RunInfo == null)
@ -30,7 +33,9 @@ namespace Microsoft.Tye.Hosting
continue; continue;
} }
static int GetNextPort() octect++;
static int GetNextPort(IPAddress address)
{ {
// Let the OS assign the next available port. Unless we cycle through all ports // Let the OS assign the next available port. Unless we cycle through all ports
// on a test run, the OS will always increment the port number when making these calls. // on a test run, the OS will always increment the port number when making these calls.
@ -38,16 +43,53 @@ namespace Microsoft.Tye.Hosting
// a given port, and a new test is able to bind to the same port due to port // a given port, and a new test is able to bind to the same port due to port
// reuse being enabled by default by the OS. // reuse being enabled by default by the OS.
using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); using var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(new IPEndPoint(IPAddress.Loopback, 0)); socket.Bind(new IPEndPoint(address, 0));
return ((IPEndPoint)socket.LocalEndPoint).Port; return ((IPEndPoint)socket.LocalEndPoint).Port;
} }
static bool IsPortAlreadyInUse(IPAddress address, int port)
{
var endpoint = new IPEndPoint(address, port);
try
{
using var socket = new Socket(endpoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(endpoint);
return false;
}
catch (SocketException e) when (e.SocketErrorCode == SocketError.AddressAlreadyInUse)
{
return true;
}
}
var httpUsed = false;
var httpsUsed = false;
// We need to bind to all interfaces on linux since the container -> host communication won't work
// if we use the IP address to reach out of the host. This works fine on osx and windows
// but doesn't work on linux.
var address = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? IPAddress.Any : IPAddress.Parse($"127.0.0.{octect}");
service.Address = address;
foreach (var binding in service.Description.Bindings) foreach (var binding in service.Description.Bindings)
{ {
// Auto assign a port // Auto assign a port
if (binding.Port == null) if (binding.Port == null)
{ {
binding.Port = GetNextPort(); if (!httpUsed && binding.Protocol == "http" && !IsPortAlreadyInUse(address, 80))
{
binding.Port = 80;
httpUsed = true;
}
else if (!httpsUsed && binding.Protocol == "https" && !IsPortAlreadyInUse(address, 443))
{
binding.Port = 443;
httpsUsed = true;
}
else
{
binding.Port = GetNextPort(address);
}
} }
if (service.Description.Replicas == 1) if (service.Description.Replicas == 1)
@ -60,7 +102,7 @@ namespace Microsoft.Tye.Hosting
for (var i = 0; i < service.Description.Replicas; i++) for (var i = 0; i < service.Description.Replicas; i++)
{ {
// Reserve a port for each replica // Reserve a port for each replica
var port = GetNextPort(); var port = GetNextPort(address);
binding.ReplicaPorts.Add(port); binding.ReplicaPorts.Add(port);
} }

13
src/Microsoft.Tye.Hosting/Dashboard/Pages/Index.razor

@ -44,12 +44,12 @@
{ {
if (b.Protocol == "http" || b.Protocol == "https") if (b.Protocol == "http" || b.Protocol == "https")
{ {
var url = GetUrl(b); var url = GetUrl(service, b);
<span><a href="@url" target="_blank">@url</a></span> <span><a href="@url" target="_blank">@url</a></span>
} }
else else
{ {
<span>@GetUrl(b)</span> <span>@GetUrl(service, b)</span>
} }
} }
else else
@ -77,9 +77,14 @@
private List<IDisposable> _subscriptions = new List<IDisposable>(); private List<IDisposable> _subscriptions = new List<IDisposable>();
string GetUrl(ServiceBinding b) string GetUrl(Service service, ServiceBinding b)
{ {
return $"{(b.Protocol ?? "tcp")}://{b.Host ?? "localhost"}:{b.Port}"; var url = $"{(b.Protocol ?? "tcp")}://{service.Address}";
if (b.Port != 80 && b.Port != 443)
{
url += $":{b.Port}";
}
return url;
} }
protected override void OnInitialized() protected override void OnInitialized()

3
src/Microsoft.Tye.Hosting/DockerRunner.cs

@ -244,6 +244,7 @@ namespace Microsoft.Tye.Hosting
status.Ports = ports.Select(p => p.Port); status.Ports = ports.Select(p => p.Port);
// These are the ports that the application should use for binding // These are the ports that the application should use for binding
var host = service.Address!.ToString();
// 1. Tell the docker container what port to bind to // 1. Tell the docker container what port to bind to
portString = docker.Private ? "" : string.Join(" ", ports.Select(p => $"-p {p.Port}:{p.ContainerPort ?? p.Port}")); portString = docker.Private ? "" : string.Join(" ", ports.Select(p => $"-p {p.Port}:{p.ContainerPort ?? p.Port}"));
@ -262,7 +263,7 @@ namespace Microsoft.Tye.Hosting
} }
// 3. For non-ASP.NET Core apps, pass the same information in the PORT env variable as a semicolon separated list. // 3. For non-ASP.NET Core apps, pass the same information in the PORT env variable as a semicolon separated list.
environment["PORT"] = string.Join(";", ports.Select(p => $"{p.ContainerPort ?? p.Port}")); environment["PORT"] = string.Join(";", ports.Select(p => $"{host}:{p.ContainerPort ?? p.Port}"));
// This the port for the container proxy (containerport:externalport) // This the port for the container proxy (containerport:externalport)
environment["PROXY_PORT"] = string.Join(";", ports.Select(p => $"{p.ContainerPort ?? p.Port}:{p.ExternalPort}")); environment["PROXY_PORT"] = string.Join(";", ports.Select(p => $"{p.ContainerPort ?? p.Port}:{p.ExternalPort}"));

2
src/Microsoft.Tye.Hosting/Model/Service.cs

@ -5,6 +5,7 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net;
using System.Reactive.Subjects; using System.Reactive.Subjects;
namespace Microsoft.Tye.Hosting.Model namespace Microsoft.Tye.Hosting.Model
@ -69,5 +70,6 @@ namespace Microsoft.Tye.Hosting.Model
public Subject<string> Logs { get; } = new Subject<string>(); public Subject<string> Logs { get; } = new Subject<string>();
public Subject<ReplicaEvent> ReplicaEvents { get; } = new Subject<ReplicaEvent>(); public Subject<ReplicaEvent> ReplicaEvents { get; } = new Subject<ReplicaEvent>();
public IPAddress? Address { get; set; }
} }
} }

8
src/Microsoft.Tye.Hosting/ProcessRunner.cs

@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -168,10 +169,7 @@ namespace Microsoft.Tye.Hosting
if (hasPorts) if (hasPorts)
{ {
// We need to bind to all interfaces on linux since the container -> host communication won't work var host = service.Address == IPAddress.Any ? "*" : service.Address!.ToString();
// if we use the IP address to reach out of the host. This works fine on osx and windows
// but doesn't work on linux.
var host = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "*" : "localhost";
// These are the ports that the application should use for binding // These are the ports that the application should use for binding
@ -235,7 +233,7 @@ namespace Microsoft.Tye.Hosting
{ {
if (hasPorts) if (hasPorts)
{ {
_logger.LogInformation("{ServiceName} running on process id {PID} bound to {Address}", replica, pid, string.Join(", ", ports.Select(p => $"{p.Protocol ?? "http"}://localhost:{p.Port}"))); _logger.LogInformation("{ServiceName} running on process id {PID} bound to {Address}", replica, pid, string.Join(", ", ports.Select(p => $"{p.Protocol ?? "http"}://{service.Address}:{p.Port}")));
} }
else else
{ {

2
src/Microsoft.Tye.Hosting/ProxyService.cs

@ -63,7 +63,7 @@ namespace Microsoft.Tye.Hosting
// We need to bind to all interfaces on linux since the container -> host communication won't work // We need to bind to all interfaces on linux since the container -> host communication won't work
// if we use the IP address to reach out of the host. This works fine on osx and windows // if we use the IP address to reach out of the host. This works fine on osx and windows
// but doesn't work on linux. // but doesn't work on linux.
var host = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? IPAddress.Any : IPAddress.Loopback; var host = service.Address;
sockets.Listen(host, binding.Port.Value, o => sockets.Listen(host, binding.Port.Value, o =>
{ {

2
src/Microsoft.Tye.Hosting/TyeHost.cs

@ -263,7 +263,7 @@ namespace Microsoft.Tye.Hosting
var processors = new List<IApplicationProcessor> var processors = new List<IApplicationProcessor>
{ {
new EventPipeDiagnosticsRunner(logger, diagnosticsCollector), new EventPipeDiagnosticsRunner(logger, diagnosticsCollector),
new PortAssigner(logger), new AddressAssigner(logger),
new ProxyService(logger), new ProxyService(logger),
new HttpProxyService(logger), new HttpProxyService(logger),
new DockerImagePuller(logger), new DockerImagePuller(logger),

Loading…
Cancel
Save