Example #1
0
        public async Task StartAsync(Application application)
        {
            var invoker = new HttpMessageInvoker(new ConnectionRetryHandler(new SocketsHttpHandler
            {
                AllowAutoRedirect      = false,
                AutomaticDecompression = DecompressionMethods.None,
                UseProxy = false
            }));

            foreach (var service in application.Services.Values)
            {
                var serviceDescription = service.Description;

                if (service.Description.RunInfo is IngressRunInfo runInfo)
                {
                    var builder = new WebApplicationBuilder();

                    builder.Services.AddSingleton <MatcherPolicy, IngressHostMatcherPolicy>();

                    builder.Logging.AddProvider(new ServiceLoggerProvider(service.Logs));

                    var addresses = new List <string>();

                    // Bind to the addresses on this resource
                    for (int i = 0; i < serviceDescription.Replicas; i++)
                    {
                        // Fake replicas since it's all running processes
                        var replica = service.Description.Name + "_" + Guid.NewGuid().ToString().Substring(0, 10).ToLower();
                        var status  = new IngressStatus(service, replica);
                        service.Replicas[replica] = status;

                        var ports = new List <int>();

                        foreach (var binding in serviceDescription.Bindings)
                        {
                            if (binding.Port == null)
                            {
                                continue;
                            }

                            var port = binding.ReplicaPorts[i];
                            ports.Add(port);
                            var url = $"{binding.Protocol}://localhost:{port}";
                            addresses.Add(url);
                        }

                        status.Ports = ports;

                        service.ReplicaEvents.OnNext(new ReplicaEvent(ReplicaState.Added, status));
                    }

                    builder.Server.UseUrls(addresses.ToArray());
                    var webApp = builder.Build();

                    _webApplications.Add(webApp);

                    // For each ingress rule, bind to the path and host
                    foreach (var rule in runInfo.Rules)
                    {
                        if (!application.Services.TryGetValue(rule.Service, out var target))
                        {
                            continue;
                        }

                        _logger.LogInformation("Processing ingress rule: Path:{Path}, Host:{Host}, Service:{Service}", rule.Path, rule.Host, rule.Service);

                        var targetServiceDescription = target.Description;
                        RegisterListener(target);

                        var uris = new List <(int Port, Uri Uri)>();

                        // HTTP before HTTPS (this might change once we figure out certs...)
                        var targetBinding = targetServiceDescription.Bindings.FirstOrDefault(b => b.Protocol == "http") ??
                                            targetServiceDescription.Bindings.FirstOrDefault(b => b.Protocol == "https");

                        if (targetBinding == null)
                        {
                            _logger.LogInformation("Service {ServiceName} does not have any HTTP or HTTPs bindings", targetServiceDescription.Name);
                            continue;
                        }

                        // For each of the target service replicas, get the base URL
                        // based on the replica port
                        for (int i = 0; i < targetServiceDescription.Replicas; i++)
                        {
                            var port = targetBinding.ReplicaPorts[i];
                            var url  = $"{targetBinding.Protocol}://localhost:{port}";
                            uris.Add((port, new Uri(url)));
                        }

                        _logger.LogInformation("Service {ServiceName} is using {Urls}", targetServiceDescription.Name, string.Join(",", uris.Select(u => u.ToString())));

                        // The only load balancing strategy here is round robin
                        long            count = 0;
                        RequestDelegate del   = async context =>
                        {
                            var next = (int)(Interlocked.Increment(ref count) % uris.Count);

                            // we find the first `Ready` port
                            for (int i = 0; i < uris.Count; i++)
                            {
                                if (_readyPorts.ContainsKey(uris[next].Port))
                                {
                                    break;
                                }

                                next = (int)(Interlocked.Increment(ref count) % uris.Count);
                            }

                            // if we've looped through all the port and didn't find a single one that is `Ready`, we return HTTP BadGateway
                            if (!_readyPorts.ContainsKey(uris[next].Port))
                            {
                                context.Response.StatusCode = (int)HttpStatusCode.BadGateway;
                                await context.Response.WriteAsync("Bad gateway");

                                return;
                            }
                            var uri = new UriBuilder(uris[next].Uri)
                            {
                                Path  = rule.PreservePath ? $"{context.Request.Path}{context.Request.RouteValues["path"]}" : (string)context.Request.RouteValues["path"] ?? "/",
                                Query = context.Request.QueryString.Value
                            };

                            await context.ProxyRequest(invoker, uri.Uri);
                        };

                        IEndpointConventionBuilder conventions =
                            ((IEndpointRouteBuilder)webApp).Map((rule.Path?.TrimEnd('/') ?? "") + "/{**path}", del);

                        if (rule.Host != null)
                        {
                            conventions.WithMetadata(new IngressHostMetadata(rule.Host));
                        }

                        conventions.WithDisplayName(rule.Service);
                    }

                    await webApp.StartAsync();

                    foreach (var replica in service.Replicas)
                    {
                        service.ReplicaEvents.OnNext(new ReplicaEvent(ReplicaState.Started, replica.Value));
                    }
                }
            }
        }
Example #2
0
        public async Task StartAsync(Application application)
        {
            var invoker = new HttpMessageInvoker(new ConnectionRetryHandler(new SocketsHttpHandler
            {
                AllowAutoRedirect      = false,
                AutomaticDecompression = DecompressionMethods.None,
                UseProxy = false
            }));

            foreach (var service in application.Services.Values)
            {
                var serviceDescription = service.Description;

                if (service.Description.RunInfo is IngressRunInfo runInfo)
                {
                    var builder = new WebApplicationBuilder();

                    builder.Services.AddSingleton <MatcherPolicy, IngressHostMatcherPolicy>();

                    builder.Logging.AddProvider(new ServiceLoggerProvider(service.Logs));

                    var addresses = new List <string>();

                    // Bind to the addresses on this resource
                    for (int i = 0; i < serviceDescription.Replicas; i++)
                    {
                        // Fake replicas since it's all running processes
                        var replica = service.Description.Name + "_" + Guid.NewGuid().ToString().Substring(0, 10).ToLower();
                        var status  = new IngressStatus(service, replica);
                        service.Replicas[replica] = status;

                        var ports = new List <int>();

                        foreach (var binding in serviceDescription.Bindings)
                        {
                            if (binding.Port == null)
                            {
                                continue;
                            }

                            var port = service.PortMap[binding.Port.Value][i];
                            ports.Add(port);
                            var url = $"{binding.Protocol}://localhost:{port}";
                            addresses.Add(url);
                        }

                        status.Ports = ports;

                        service.ReplicaEvents.OnNext(new ReplicaEvent(ReplicaState.Added, status));
                    }

                    builder.Server.UseUrls(addresses.ToArray());
                    var webApp = builder.Build();

                    _webApplications.Add(webApp);

                    // For each ingress rule, bind to the path and host
                    foreach (var rule in runInfo.Rules)
                    {
                        if (!application.Services.TryGetValue(rule.Service, out var target))
                        {
                            continue;
                        }

                        _logger.LogInformation("Processing ingress rule: Path:{Path}, Host:{Host}, Service:{Service}", rule.Path, rule.Host, rule.Service);

                        var targetServiceDescription = target.Description;

                        var uris = new List <Uri>();

                        // HTTP before HTTPS (this might change once we figure out certs...)
                        var targetBinding = targetServiceDescription.Bindings.FirstOrDefault(b => b.Protocol == "http") ??
                                            targetServiceDescription.Bindings.FirstOrDefault(b => b.Protocol == "https");

                        if (targetBinding == null)
                        {
                            _logger.LogInformation("Service {ServiceName} does not have any HTTP or HTTPs bindings", targetServiceDescription.Name);
                            continue;
                        }

                        // For each of the target service replicas, get the base URL
                        // based on the replica port
                        for (int i = 0; i < targetServiceDescription.Replicas; i++)
                        {
                            var port = target.PortMap[targetBinding.Port !.Value][i];
Example #3
0
        public async Task StartAsync(Application application)
        {
            var invoker = new HttpMessageInvoker(new ConnectionRetryHandler(new SocketsHttpHandler
            {
                AllowAutoRedirect      = false,
                AutomaticDecompression = DecompressionMethods.None,
                UseProxy = false
            }));

            foreach (var service in application.Services.Values)
            {
                var serviceDescription = service.Description;

                if (service.Description.RunInfo is IngressRunInfo runInfo)
                {
                    var builder = new WebApplicationBuilder();

                    builder.Services.AddSingleton <MatcherPolicy, IngressHostMatcherPolicy>();

                    builder.Logging.AddProvider(new ServiceLoggerProvider(service.Logs));

                    var addresses = new List <string>();

                    // Bind to the addresses on this resource
                    for (int i = 0; i < serviceDescription.Replicas; i++)
                    {
                        // Fake replicas since it's all running processes
                        var replica = service.Description.Name + "_" + Guid.NewGuid().ToString().Substring(0, 10).ToLower();
                        var status  = new IngressStatus(service, replica);
                        service.Replicas[replica] = status;

                        var ports = new List <int>();

                        foreach (var binding in serviceDescription.Bindings)
                        {
                            if (binding.Port == null)
                            {
                                continue;
                            }

                            var port = service.PortMap[binding.Port.Value][i];
                            ports.Add(port);
                            var url = $"{binding.Protocol ?? "http"}://localhost:{port}";
                            addresses.Add(url);
                        }

                        status.Ports = ports;

                        service.ReplicaEvents.OnNext(new ReplicaEvent(ReplicaState.Added, status));
                    }

                    builder.Server.UseUrls(addresses.ToArray());
                    var webApp = builder.Build();

                    _webApplications.Add(webApp);

                    // For each ingress rule, bind to the path and host
                    foreach (var rule in runInfo.Rules)
                    {
                        if (!application.Services.TryGetValue(rule.Service, out var target))
                        {
                            continue;
                        }

                        _logger.LogInformation("Processing ingress rule: Path:{Path}, Host:{Host}, Service:{Service}", rule.Path, rule.Host, rule.Service);

                        var targetServiceDescription = target.Description;

                        var uris = new List <Uri>();

                        // For each of the target service replicas, get the base URL
                        // based on the replica port
                        for (int i = 0; i < targetServiceDescription.Replicas; i++)
                        {
                            foreach (var binding in targetServiceDescription.Bindings)
                            {
                                if (binding.Port == null)
                                {
                                    continue;
                                }

                                var port = target.PortMap[binding.Port.Value][i];
                                var url  = $"{binding.Protocol ?? "http"}://localhost:{port}";
                                uris.Add(new Uri(url));
                            }
                        }

                        // The only load balancing strategy here is round robin
                        long            count = 0;
                        RequestDelegate del   = context =>
                        {
                            var next = (int)(Interlocked.Increment(ref count) % uris.Count);

                            var uri = new UriBuilder(uris[next])
                            {
                                Path = (string)context.Request.RouteValues["path"]
                            };

                            return(context.ProxyRequest(invoker, uri.Uri));
                        };

                        IEndpointConventionBuilder conventions = null !;

                        if (rule.Path != null)
                        {
                            conventions = ((IEndpointRouteBuilder)webApp).Map(rule.Path.TrimEnd('/') + "/{**path}", del);
                        }
                        else
                        {
                            conventions = webApp.MapFallback(del);
                        }

                        if (rule.Host != null)
                        {
                            conventions.WithMetadata(new IngressHostMetadata(rule.Host));
                        }

                        conventions.WithDisplayName(rule.Service);
                    }
                }
            }

            foreach (var app in _webApplications)
            {
                await app.StartAsync();
            }
        }