зеркало из https://github.com/dotnet/tye.git
Specify ingress IP in Debug (#1048)
* Specify ingress IP in Debug * Quotes yaml values * Stop auto-defaulting specific looback addresses * Update tye YAML schema * Format whitespace
This commit is contained in:
Родитель
bbf5475806
Коммит
7ea78aaddd
|
@ -526,6 +526,11 @@ The port of the binding.
|
|||
|
||||
The protocol (`http` or `https`).
|
||||
|
||||
#### `ip` (`string`)
|
||||
|
||||
The optional IP adress to bind to. Can be '*' for all addresses.
|
||||
Default is localhost.
|
||||
|
||||
## IngressRule
|
||||
|
||||
`IngressRule` elements appear in an array within the `rules` property of the `Ingress` element. Rules configure the routing behavior of the ingress proxy.
|
||||
|
|
|
@ -461,6 +461,7 @@ namespace Microsoft.Tye
|
|||
Name = configBinding.Name,
|
||||
Port = configBinding.Port,
|
||||
Protocol = configBinding.Protocol ?? "http",
|
||||
IPAddress = configBinding.IPAddress,
|
||||
};
|
||||
ingress.Bindings.Add(binding);
|
||||
}
|
||||
|
|
|
@ -9,5 +9,6 @@ namespace Microsoft.Tye.ConfigModel
|
|||
public string? Name { get; set; }
|
||||
public int? Port { get; set; }
|
||||
public string? Protocol { get; set; } // HTTP or HTTPS
|
||||
public string? IPAddress { get; set; } // Can be * or any address to listen on
|
||||
}
|
||||
}
|
||||
|
|
|
@ -153,6 +153,9 @@
|
|||
<data name="MustBeAnInteger" xml:space="preserve">
|
||||
<value>"{value}" value must be an integer.</value>
|
||||
</data>
|
||||
<data name="MustBeAnIPAddress" xml:space="preserve">
|
||||
<value>"{value}" value must be an IP address, "*" or "localhost".</value>
|
||||
</data>
|
||||
<data name="MustBePositive" xml:space="preserve">
|
||||
<value>"{value}" value cannot be negative.</value>
|
||||
</data>
|
||||
|
|
|
@ -9,5 +9,6 @@ namespace Microsoft.Tye
|
|||
public string? Name { get; set; }
|
||||
public int? Port { get; set; }
|
||||
public string? Protocol { get; set; } // HTTP or HTTPS
|
||||
public string? IPAddress { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
// The .NET Foundation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Microsoft.Tye.ConfigModel;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
|
@ -144,6 +146,17 @@ namespace Tye.Serialization
|
|||
|
||||
binding.Port = port;
|
||||
break;
|
||||
case "ip":
|
||||
if (YamlParser.GetScalarValue(key, child.Value) is string ipString
|
||||
&& (IPAddress.TryParse(ipString, out var ip) || ipString == "*" || ipString.Equals("localhost", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
binding.IPAddress = ipString;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new TyeYamlException(child.Value.Start, CoreStrings.FormatMustBeAnIPAddress(key));
|
||||
}
|
||||
break;
|
||||
case "protocol":
|
||||
binding.Protocol = YamlParser.GetScalarValue(key, child.Value);
|
||||
break;
|
||||
|
|
|
@ -76,7 +76,8 @@ namespace Microsoft.Tye.Hosting
|
|||
|
||||
var port = binding.ReplicaPorts[i];
|
||||
ports.Add(port);
|
||||
var url = $"{binding.Protocol}://localhost:{port}";
|
||||
|
||||
var url = $"{binding.Protocol}://{binding.IPAddress ?? "localhost"}:{port}";
|
||||
urls.Add(url);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
||||
namespace Microsoft.Tye.Hosting.Model
|
||||
{
|
||||
|
@ -13,6 +14,7 @@ namespace Microsoft.Tye.Hosting.Model
|
|||
public int? Port { get; set; }
|
||||
public int? ContainerPort { get; set; }
|
||||
public string? Host { get; set; }
|
||||
public string? IPAddress { get; set; }
|
||||
public string? Protocol { get; set; }
|
||||
public List<int> ReplicaPorts { get; } = new List<int>();
|
||||
}
|
||||
|
|
|
@ -463,6 +463,10 @@
|
|||
"protocol": {
|
||||
"description": "The protocol used by the binding",
|
||||
"type": "string"
|
||||
},
|
||||
"ip": {
|
||||
"description": "The ip address the ingress listens on.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -212,6 +212,7 @@ namespace Microsoft.Tye
|
|||
Name = binding.Name,
|
||||
Port = binding.Port,
|
||||
Protocol = binding.Protocol,
|
||||
IPAddress = binding.IPAddress,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -45,4 +45,4 @@
|
|||
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -9,6 +9,8 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
@ -767,6 +769,101 @@ services:
|
|||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task IngressSpecificIPTest()
|
||||
{
|
||||
var allIps = GetLiveIPAddresses().ToList();
|
||||
var testIp = allIps[new Random().Next(allIps.Count)];
|
||||
await TestIngressIP($"'{testIp}'", new[] { testIp }, allIps.Where(ip => ip != testIp).Take(1));
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task IngressAllIPv6Test()
|
||||
{
|
||||
var ipV6 = GetLiveIPAddresses(AddressFamily.InterNetworkV6).FirstOrDefault();
|
||||
if (ipV6 == null) return;
|
||||
await TestIngressIP($"'{IPAddress.IPv6Any}'", ipV6);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task IngressAllIPv4Test()
|
||||
{
|
||||
var ipV4 = GetLiveIPAddresses(AddressFamily.InterNetwork).FirstOrDefault();
|
||||
if (ipV4 == null) return;
|
||||
var ipV6 = GetLiveIPAddresses(AddressFamily.InterNetworkV6).FirstOrDefault();
|
||||
var failIp = ipV6 == null ? Enumerable.Empty<IPAddress>() : new[] { ipV6 };
|
||||
await TestIngressIP($"'{IPAddress.Any}'", new[] { ipV4 }, failIp);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task IngressAllIPTest()
|
||||
{
|
||||
await TestIngressIP($"'*'", GetLiveIPAddresses().FirstOrDefault());
|
||||
}
|
||||
|
||||
|
||||
private static IEnumerable<IPAddress> GetLiveIPAddresses(AddressFamily? family = null)
|
||||
{
|
||||
return from ni in NetworkInterface.GetAllNetworkInterfaces()
|
||||
where ni.OperationalStatus == OperationalStatus.Up
|
||||
let prop = ni.GetIPProperties()
|
||||
from unicast in prop.UnicastAddresses
|
||||
let addr = unicast.Address
|
||||
where addr != IPAddress.IPv6Loopback && (family == null || addr.AddressFamily == family)
|
||||
select addr;
|
||||
}
|
||||
|
||||
private Task TestIngressIP(string ipSetting, params IPAddress[] mustAnswer) => TestIngressIP(ipSetting, mustAnswer, Enumerable.Empty<IPAddress>());
|
||||
private async Task TestIngressIP(string ipSetting, IEnumerable<IPAddress> mustAnswer, IEnumerable<IPAddress> mustFail)
|
||||
{
|
||||
#if !DEBUG
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
return; //disables running this test on windows as it stucks the test runner on the firewall open prompt
|
||||
#endif
|
||||
if (!mustAnswer.Any() && !mustFail.Any())
|
||||
return; // no IP to test against
|
||||
|
||||
using var projectDirectory = CopyTestProjectDirectory("apps-with-ingress");
|
||||
var projectFile = new FileInfo(Path.Combine(projectDirectory.DirectoryPath, "tye-ip_test.yaml"));
|
||||
File.WriteAllText(projectFile.FullName, File.ReadAllText(projectFile.FullName).Replace("__TEST_IP_STRING__", ipSetting));
|
||||
var outputContext = new OutputContext(_sink, Verbosity.Debug);
|
||||
var application = await ApplicationFactory.CreateAsync(outputContext, projectFile);
|
||||
|
||||
var handler = new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = (a, b, c, d) => true,
|
||||
AllowAutoRedirect = true
|
||||
};
|
||||
|
||||
var client = new HttpClient(new RetryHandler(handler));
|
||||
|
||||
await RunHostingApplication(application, new HostOptions(), async (app, uri) =>
|
||||
{
|
||||
foreach (var ip in mustAnswer.Concat(mustFail))
|
||||
{
|
||||
try
|
||||
{
|
||||
var ingressUri = await GetServiceUrl(client, uri, "ingress");
|
||||
var reqUri = new UriBuilder(ingressUri + "/index.html")
|
||||
{
|
||||
Host = ip.ToString()
|
||||
};
|
||||
|
||||
var htmlRequest = new HttpRequestMessage(HttpMethod.Get, reqUri.Uri);
|
||||
htmlRequest.Headers.Host = "ui.example.com";
|
||||
|
||||
var htmlResponse = await client.SendAsync(htmlRequest);
|
||||
htmlResponse.EnsureSuccessStatusCode();
|
||||
}
|
||||
catch (Exception) when (mustFail.Contains(ip))
|
||||
{
|
||||
// this is an expected failure
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
[ConditionalFact]
|
||||
[SkipIfDockerNotRunning]
|
||||
public async Task NginxIngressTest()
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
# tye application configuration file
|
||||
# read all about it at https://github.com/dotnet/tye
|
||||
#
|
||||
# when you've given us a try, we'd love to know what you think:
|
||||
# https://aka.ms/AA7q20u
|
||||
#
|
||||
name: apps-with-ingress-allip-ui
|
||||
ingress:
|
||||
- name: ingress
|
||||
bindings:
|
||||
- port: 8080
|
||||
ip: __TEST_IP_STRING__
|
||||
rules:
|
||||
- host: ui.example.com
|
||||
service: appC-ui
|
||||
|
||||
services:
|
||||
- name: appC-ui
|
||||
project: ApplicationC-UI/ApplicationC-UI.csproj
|
||||
replicas: 2
|
Загрузка…
Ссылка в новой задаче