mirror of
				https://github.com/nsnail/Ocelot.git
				synced 2025-11-04 15:10:50 +08:00 
			
		
		
		
	feat: Kubernetes ServiceDiscoveryProvider
This commit is contained in:
		@@ -56,6 +56,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Rafty", "sr
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Tracing.Butterfly", "src\Ocelot.Tracing.Butterfly\Ocelot.Tracing.Butterfly.csproj", "{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}"
 | 
			
		||||
EndProject
 | 
			
		||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ocelot.Provider.Kubernetes", "src\Ocelot.Provider.Kubernetes\Ocelot.Provider.Kubernetes.csproj", "{72C8E528-B4F5-45CE-8A06-CD3787364856}"
 | 
			
		||||
EndProject
 | 
			
		||||
Global
 | 
			
		||||
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 | 
			
		||||
		Debug|Any CPU = Debug|Any CPU
 | 
			
		||||
@@ -114,6 +116,10 @@ Global
 | 
			
		||||
		{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
		{72C8E528-B4F5-45CE-8A06-CD3787364856}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 | 
			
		||||
		{72C8E528-B4F5-45CE-8A06-CD3787364856}.Debug|Any CPU.Build.0 = Debug|Any CPU
 | 
			
		||||
		{72C8E528-B4F5-45CE-8A06-CD3787364856}.Release|Any CPU.ActiveCfg = Release|Any CPU
 | 
			
		||||
		{72C8E528-B4F5-45CE-8A06-CD3787364856}.Release|Any CPU.Build.0 = Release|Any CPU
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
	GlobalSection(SolutionProperties) = preSolution
 | 
			
		||||
		HideSolutionNode = FALSE
 | 
			
		||||
@@ -132,6 +138,7 @@ Global
 | 
			
		||||
		{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
 | 
			
		||||
		{AC153C67-EF18-47E6-A230-F0D3CF5F0A98} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
 | 
			
		||||
		{6045E23D-669C-4F27-AF8E-8EEE6DB3557F} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
 | 
			
		||||
		{72C8E528-B4F5-45CE-8A06-CD3787364856} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
 | 
			
		||||
	EndGlobalSection
 | 
			
		||||
	GlobalSection(ExtensibilityGlobals) = postSolution
 | 
			
		||||
		SolutionGuid = {21476EFF-778A-4F97-8A56-D1AF1CEC0C48}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12
									
								
								src/Ocelot.Provider.Kubernetes/IKubeApiClientFactory.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/Ocelot.Provider.Kubernetes/IKubeApiClientFactory.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
			
		||||
using KubeClient;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Provider.Kubernetes
 | 
			
		||||
{
 | 
			
		||||
    public interface IKubeApiClientFactory
 | 
			
		||||
    {
 | 
			
		||||
        IKubeApiClient Get(KubeRegistryConfiguration config);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								src/Ocelot.Provider.Kubernetes/KubeApiClientFactory.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/Ocelot.Provider.Kubernetes/KubeApiClientFactory.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
using KubeClient;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Provider.Kubernetes
 | 
			
		||||
{
 | 
			
		||||
    public class KubeApiClientFactory : IKubeApiClientFactory
 | 
			
		||||
    {
 | 
			
		||||
        public IKubeApiClient Get(KubeRegistryConfiguration config)
 | 
			
		||||
        {
 | 
			
		||||
            var option = new KubeClientOptions
 | 
			
		||||
            {
 | 
			
		||||
                ApiEndPoint = config.ApiEndPoint                
 | 
			
		||||
            };
 | 
			
		||||
            if(!string.IsNullOrEmpty(config?.AccessToken))
 | 
			
		||||
            {
 | 
			
		||||
                option.AccessToken = config.AccessToken;
 | 
			
		||||
                option.AuthStrategy = config.AuthStrategy;
 | 
			
		||||
                option.AllowInsecure = config.AllowInsecure;
 | 
			
		||||
            }
 | 
			
		||||
            return KubeApiClient.Create(option);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										59
									
								
								src/Ocelot.Provider.Kubernetes/KubeProvider.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/Ocelot.Provider.Kubernetes/KubeProvider.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
using KubeClient;
 | 
			
		||||
using KubeClient.Models;
 | 
			
		||||
using Ocelot.Logging;
 | 
			
		||||
using Ocelot.ServiceDiscovery.Providers;
 | 
			
		||||
using Ocelot.Values;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Threading.Tasks;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Provider.Kubernetes
 | 
			
		||||
{
 | 
			
		||||
    public class KubeProvider : IServiceDiscoveryProvider
 | 
			
		||||
    {
 | 
			
		||||
        private KubeRegistryConfiguration kubeRegistryConfiguration;
 | 
			
		||||
        private IOcelotLoggerFactory factory;
 | 
			
		||||
        private IKubeApiClient kubeApi;
 | 
			
		||||
        private IKubeApiClientFactory kubeClientFactory;
 | 
			
		||||
 | 
			
		||||
        public KubeProvider(KubeRegistryConfiguration kubeRegistryConfiguration, IOcelotLoggerFactory factory, IKubeApiClientFactory kubeClientFactory)
 | 
			
		||||
        {
 | 
			
		||||
            this.kubeRegistryConfiguration = kubeRegistryConfiguration;
 | 
			
		||||
            this.factory = factory;
 | 
			
		||||
            this.kubeApi = kubeClientFactory.Get(kubeRegistryConfiguration);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public async Task<List<Service>> Get()
 | 
			
		||||
        {
 | 
			
		||||
            var service = await kubeApi.ServicesV1().Get(kubeRegistryConfiguration.KeyOfServiceInK8s, kubeRegistryConfiguration.KubeNamespace);
 | 
			
		||||
            var services = new List<Service>();
 | 
			
		||||
            if (IsValid(service))
 | 
			
		||||
            {
 | 
			
		||||
                services.Add(BuildService(service));
 | 
			
		||||
            }
 | 
			
		||||
            return services;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private bool IsValid(ServiceV1 service)
 | 
			
		||||
        {
 | 
			
		||||
            if (string.IsNullOrEmpty(service.Spec.ClusterIP) || service.Spec.Ports.Count <= 0)
 | 
			
		||||
            {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private Service BuildService(ServiceV1 serviceEntry)
 | 
			
		||||
        {
 | 
			
		||||
            var servicePort = serviceEntry.Spec.Ports.FirstOrDefault();
 | 
			
		||||
            return new Service(
 | 
			
		||||
                serviceEntry.Metadata.Name,
 | 
			
		||||
                new ServiceHostAndPort(serviceEntry.Spec.ClusterIP, servicePort.Port),
 | 
			
		||||
                serviceEntry.Metadata.Uid,
 | 
			
		||||
                string.Empty,
 | 
			
		||||
                Enumerable.Empty<string>());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								src/Ocelot.Provider.Kubernetes/KubeRegistryConfiguration.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/Ocelot.Provider.Kubernetes/KubeRegistryConfiguration.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,22 @@
 | 
			
		||||
using KubeClient;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Provider.Kubernetes
 | 
			
		||||
{
 | 
			
		||||
    public class KubeRegistryConfiguration
 | 
			
		||||
    {
 | 
			
		||||
        public Uri ApiEndPoint { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string KubeNamespace { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string KeyOfServiceInK8s { get; set; }
 | 
			
		||||
 | 
			
		||||
        public KubeAuthStrategy AuthStrategy { get; set; }
 | 
			
		||||
 | 
			
		||||
        public string AccessToken { get; set; }
 | 
			
		||||
 | 
			
		||||
        public bool AllowInsecure { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										31
									
								
								src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/Ocelot.Provider.Kubernetes/KubernetesProviderFactory.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
using KubeClient;
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
using Ocelot.Logging;
 | 
			
		||||
using Ocelot.ServiceDiscovery;
 | 
			
		||||
using System;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Provider.Kubernetes
 | 
			
		||||
{
 | 
			
		||||
    public static class KubernetesProviderFactory
 | 
			
		||||
    {
 | 
			
		||||
        public static ServiceDiscoveryFinderDelegate Get = (provider, config, name) =>
 | 
			
		||||
        {
 | 
			
		||||
            var factory = provider.GetService<IOcelotLoggerFactory>();
 | 
			
		||||
            
 | 
			
		||||
            var kubeClientFactory = provider.GetService<IKubeApiClientFactory>();
 | 
			
		||||
 | 
			
		||||
            var k8sRegistryConfiguration = new KubeRegistryConfiguration() {
 | 
			
		||||
                ApiEndPoint = new Uri($"http://{config.Host}:{config.Port}"),
 | 
			
		||||
                KeyOfServiceInK8s = name,
 | 
			
		||||
                KubeNamespace = config.Namesapce,
 | 
			
		||||
                AuthStrategy = KubeAuthStrategy.BearerToken,
 | 
			
		||||
                AccessToken = config.Token,
 | 
			
		||||
                AllowInsecure = true // Don't validate server certificate
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            var k8sServiceDiscoveryProvider = new KubeProvider(k8sRegistryConfiguration, factory, kubeClientFactory);
 | 
			
		||||
 | 
			
		||||
            return k8sServiceDiscoveryProvider;
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,15 @@
 | 
			
		||||
<Project Sdk="Microsoft.NET.Sdk">
 | 
			
		||||
 | 
			
		||||
  <PropertyGroup>
 | 
			
		||||
    <TargetFramework>netstandard2.0</TargetFramework>
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <PackageReference Include="KubeClient" Version="2.2.2" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <ProjectReference Include="..\Ocelot\Ocelot.csproj" />
 | 
			
		||||
  </ItemGroup>
 | 
			
		||||
 | 
			
		||||
</Project>
 | 
			
		||||
							
								
								
									
										20
									
								
								src/Ocelot.Provider.Kubernetes/OcelotBuilderExtensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/Ocelot.Provider.Kubernetes/OcelotBuilderExtensions.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,20 @@
 | 
			
		||||
using KubeClient;
 | 
			
		||||
using Microsoft.Extensions.DependencyInjection;
 | 
			
		||||
using Ocelot.DependencyInjection;
 | 
			
		||||
using Ocelot.ServiceDiscovery;
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Text;
 | 
			
		||||
 | 
			
		||||
namespace Ocelot.Provider.Kubernetes
 | 
			
		||||
{
 | 
			
		||||
    public static class OcelotBuilderExtensions
 | 
			
		||||
    {
 | 
			
		||||
        public static IOcelotBuilder AddKubernetes(this IOcelotBuilder builder)
 | 
			
		||||
        {
 | 
			
		||||
            builder.Services.AddSingleton(KubernetesProviderFactory.Get);
 | 
			
		||||
            builder.Services.AddSingleton<IKubeApiClientFactory, KubeApiClientFactory>();
 | 
			
		||||
            return builder;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -8,6 +8,7 @@ namespace Ocelot.Configuration.Builder
 | 
			
		||||
        private string _token;
 | 
			
		||||
        private string _configurationKey;
 | 
			
		||||
        private int _pollingInterval;
 | 
			
		||||
        private string _namespace;
 | 
			
		||||
 | 
			
		||||
        public ServiceProviderConfigurationBuilder WithHost(string serviceDiscoveryProviderHost)
 | 
			
		||||
        {
 | 
			
		||||
@@ -45,9 +46,15 @@ namespace Ocelot.Configuration.Builder
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ServiceProviderConfigurationBuilder WithNamesapce(string @namesapce)
 | 
			
		||||
        {
 | 
			
		||||
            _namespace = @namesapce;
 | 
			
		||||
            return this;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public ServiceProviderConfiguration Build()
 | 
			
		||||
        {
 | 
			
		||||
            return new ServiceProviderConfiguration(_type, _serviceDiscoveryProviderHost, _serviceDiscoveryProviderPort, _token, _configurationKey, _pollingInterval);
 | 
			
		||||
            return new ServiceProviderConfiguration(_type, _serviceDiscoveryProviderHost, _serviceDiscoveryProviderPort, _token, _configurationKey, _pollingInterval, _namespace);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ namespace Ocelot.Configuration.Creator
 | 
			
		||||
                ? globalConfiguration?.ServiceDiscoveryProvider?.Type 
 | 
			
		||||
                : "consul";
 | 
			
		||||
            var pollingInterval = globalConfiguration?.ServiceDiscoveryProvider?.PollingInterval ?? 0;
 | 
			
		||||
            var k8snamesapce = globalConfiguration?.ServiceDiscoveryProvider?.Namespace ?? string.Empty;
 | 
			
		||||
 | 
			
		||||
            return new ServiceProviderConfigurationBuilder()
 | 
			
		||||
                .WithHost(host)
 | 
			
		||||
@@ -21,6 +22,7 @@ namespace Ocelot.Configuration.Creator
 | 
			
		||||
                .WithToken(globalConfiguration?.ServiceDiscoveryProvider?.Token)
 | 
			
		||||
                .WithConfigurationKey(globalConfiguration?.ServiceDiscoveryProvider?.ConfigurationKey)
 | 
			
		||||
                .WithPollingInterval(pollingInterval)
 | 
			
		||||
                .WithNamesapce(k8snamesapce)
 | 
			
		||||
                .Build();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -8,5 +8,6 @@ namespace Ocelot.Configuration.File
 | 
			
		||||
        public string Token { get; set; }
 | 
			
		||||
        public string ConfigurationKey { get; set; }
 | 
			
		||||
        public int PollingInterval { get; set; }
 | 
			
		||||
        public string Namespace { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
{
 | 
			
		||||
    public class ServiceProviderConfiguration
 | 
			
		||||
    {
 | 
			
		||||
        public ServiceProviderConfiguration(string type, string host, int port, string token, string configurationKey, int pollingInterval)
 | 
			
		||||
        public ServiceProviderConfiguration(string type, string host, int port, string token, string configurationKey, int pollingInterval, string @namespace = "")
 | 
			
		||||
        {
 | 
			
		||||
            ConfigurationKey = configurationKey;
 | 
			
		||||
            Host = host;
 | 
			
		||||
@@ -10,13 +10,21 @@
 | 
			
		||||
            Token = token;
 | 
			
		||||
            Type = type;
 | 
			
		||||
            PollingInterval = pollingInterval;
 | 
			
		||||
            Namesapce = @namespace;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public string Host { get; }
 | 
			
		||||
 | 
			
		||||
        public int Port { get; }
 | 
			
		||||
 | 
			
		||||
        public string Type { get; }
 | 
			
		||||
 | 
			
		||||
        public string Token { get; }
 | 
			
		||||
 | 
			
		||||
        public string ConfigurationKey { get; }
 | 
			
		||||
 | 
			
		||||
        public int PollingInterval { get; }
 | 
			
		||||
 | 
			
		||||
        public string Namesapce { get; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -434,7 +434,8 @@
 | 
			
		||||
                        Host = "localhost",
 | 
			
		||||
                        Port = consulPort,
 | 
			
		||||
                        Type = "PollConsul",
 | 
			
		||||
                        PollingInterval = 0
 | 
			
		||||
                        PollingInterval = 0,
 | 
			
		||||
                        Namespace = string.Empty
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 
 | 
			
		||||
@@ -30,7 +30,8 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                    Port = 1234,
 | 
			
		||||
                    Type = "ServiceFabric",
 | 
			
		||||
                    Token = "testtoken",
 | 
			
		||||
                    ConfigurationKey = "woo"
 | 
			
		||||
                    ConfigurationKey = "woo",
 | 
			
		||||
                    Namespace ="default"
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
@@ -40,13 +41,14 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
                .WithType("ServiceFabric")
 | 
			
		||||
                .WithToken("testtoken")
 | 
			
		||||
                .WithConfigurationKey("woo")
 | 
			
		||||
                .WithNamesapce("default")
 | 
			
		||||
                .Build();
 | 
			
		||||
 | 
			
		||||
            this.Given(x => x.GivenTheFollowingGlobalConfig(globalConfig))
 | 
			
		||||
                .When(x => x.WhenICreate())
 | 
			
		||||
                .Then(x => x.ThenTheConfigIs(expected))
 | 
			
		||||
                .BDDfy();
 | 
			
		||||
        }
 | 
			
		||||
        }     
 | 
			
		||||
 | 
			
		||||
        private void GivenTheFollowingGlobalConfig(FileGlobalConfiguration fileGlobalConfig)
 | 
			
		||||
        {
 | 
			
		||||
@@ -64,6 +66,7 @@ namespace Ocelot.UnitTests.Configuration
 | 
			
		||||
            _result.Port.ShouldBe(expected.Port);
 | 
			
		||||
            _result.Token.ShouldBe(expected.Token);
 | 
			
		||||
            _result.Type.ShouldBe(expected.Type);
 | 
			
		||||
            _result.Namesapce.ShouldBe(expected.Namesapce);
 | 
			
		||||
            _result.ConfigurationKey.ShouldBe(expected.ConfigurationKey);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user