feat: Kubernetes ServiceDiscoveryProvider

This commit is contained in:
geffzhang 2019-01-13 19:23:12 +08:00
parent 08c2ac1b05
commit 3525fda8ad
14 changed files with 215 additions and 5 deletions

View File

@ -56,6 +56,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Provider.Rafty", "sr
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Tracing.Butterfly", "src\Ocelot.Tracing.Butterfly\Ocelot.Tracing.Butterfly.csproj", "{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Ocelot.Tracing.Butterfly", "src\Ocelot.Tracing.Butterfly\Ocelot.Tracing.Butterfly.csproj", "{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}"
EndProject 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 Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU 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}.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.ActiveCfg = Release|Any CPU
{6045E23D-669C-4F27-AF8E-8EEE6DB3557F}.Release|Any CPU.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -132,6 +138,7 @@ Global
{1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E} = {5CFB79B7-C9DC-45A4-9A75-625D92471702} {1F6E5DCF-8A2E-4E24-A25D-064362DE8D0E} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
{AC153C67-EF18-47E6-A230-F0D3CF5F0A98} = {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} {6045E23D-669C-4F27-AF8E-8EEE6DB3557F} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
{72C8E528-B4F5-45CE-8A06-CD3787364856} = {5CFB79B7-C9DC-45A4-9A75-625D92471702}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {21476EFF-778A-4F97-8A56-D1AF1CEC0C48} SolutionGuid = {21476EFF-778A-4F97-8A56-D1AF1CEC0C48}

View 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);
}
}

View 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);
}
}
}

View 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>());
}
}
}

View 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; }
}
}

View 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;
};
}
}

View File

@ -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>

View 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;
}
}
}

View File

@ -8,6 +8,7 @@ namespace Ocelot.Configuration.Builder
private string _token; private string _token;
private string _configurationKey; private string _configurationKey;
private int _pollingInterval; private int _pollingInterval;
private string _namespace;
public ServiceProviderConfigurationBuilder WithHost(string serviceDiscoveryProviderHost) public ServiceProviderConfigurationBuilder WithHost(string serviceDiscoveryProviderHost)
{ {
@ -45,9 +46,15 @@ namespace Ocelot.Configuration.Builder
return this; return this;
} }
public ServiceProviderConfigurationBuilder WithNamesapce(string @namesapce)
{
_namespace = @namesapce;
return this;
}
public ServiceProviderConfiguration Build() public ServiceProviderConfiguration Build()
{ {
return new ServiceProviderConfiguration(_type, _serviceDiscoveryProviderHost, _serviceDiscoveryProviderPort, _token, _configurationKey, _pollingInterval); return new ServiceProviderConfiguration(_type, _serviceDiscoveryProviderHost, _serviceDiscoveryProviderPort, _token, _configurationKey, _pollingInterval, _namespace);
} }
} }
} }

View File

@ -13,6 +13,7 @@ namespace Ocelot.Configuration.Creator
? globalConfiguration?.ServiceDiscoveryProvider?.Type ? globalConfiguration?.ServiceDiscoveryProvider?.Type
: "consul"; : "consul";
var pollingInterval = globalConfiguration?.ServiceDiscoveryProvider?.PollingInterval ?? 0; var pollingInterval = globalConfiguration?.ServiceDiscoveryProvider?.PollingInterval ?? 0;
var k8snamesapce = globalConfiguration?.ServiceDiscoveryProvider?.Namespace ?? string.Empty;
return new ServiceProviderConfigurationBuilder() return new ServiceProviderConfigurationBuilder()
.WithHost(host) .WithHost(host)
@ -21,6 +22,7 @@ namespace Ocelot.Configuration.Creator
.WithToken(globalConfiguration?.ServiceDiscoveryProvider?.Token) .WithToken(globalConfiguration?.ServiceDiscoveryProvider?.Token)
.WithConfigurationKey(globalConfiguration?.ServiceDiscoveryProvider?.ConfigurationKey) .WithConfigurationKey(globalConfiguration?.ServiceDiscoveryProvider?.ConfigurationKey)
.WithPollingInterval(pollingInterval) .WithPollingInterval(pollingInterval)
.WithNamesapce(k8snamesapce)
.Build(); .Build();
} }
} }

View File

@ -8,5 +8,6 @@ namespace Ocelot.Configuration.File
public string Token { get; set; } public string Token { get; set; }
public string ConfigurationKey { get; set; } public string ConfigurationKey { get; set; }
public int PollingInterval { get; set; } public int PollingInterval { get; set; }
public string Namespace { get; set; }
} }
} }

View File

@ -2,7 +2,7 @@
{ {
public class ServiceProviderConfiguration 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; ConfigurationKey = configurationKey;
Host = host; Host = host;
@ -10,13 +10,21 @@
Token = token; Token = token;
Type = type; Type = type;
PollingInterval = pollingInterval; PollingInterval = pollingInterval;
Namesapce = @namespace;
} }
public string Host { get; } public string Host { get; }
public int Port { get; } public int Port { get; }
public string Type { get; } public string Type { get; }
public string Token { get; } public string Token { get; }
public string ConfigurationKey { get; } public string ConfigurationKey { get; }
public int PollingInterval { get; } public int PollingInterval { get; }
public string Namesapce { get; }
} }
} }

View File

@ -434,7 +434,8 @@
Host = "localhost", Host = "localhost",
Port = consulPort, Port = consulPort,
Type = "PollConsul", Type = "PollConsul",
PollingInterval = 0 PollingInterval = 0,
Namespace = string.Empty
} }
} }
}; };

View File

@ -30,7 +30,8 @@ namespace Ocelot.UnitTests.Configuration
Port = 1234, Port = 1234,
Type = "ServiceFabric", Type = "ServiceFabric",
Token = "testtoken", Token = "testtoken",
ConfigurationKey = "woo" ConfigurationKey = "woo",
Namespace ="default"
} }
}; };
@ -40,6 +41,7 @@ namespace Ocelot.UnitTests.Configuration
.WithType("ServiceFabric") .WithType("ServiceFabric")
.WithToken("testtoken") .WithToken("testtoken")
.WithConfigurationKey("woo") .WithConfigurationKey("woo")
.WithNamesapce("default")
.Build(); .Build();
this.Given(x => x.GivenTheFollowingGlobalConfig(globalConfig)) this.Given(x => x.GivenTheFollowingGlobalConfig(globalConfig))
@ -64,6 +66,7 @@ namespace Ocelot.UnitTests.Configuration
_result.Port.ShouldBe(expected.Port); _result.Port.ShouldBe(expected.Port);
_result.Token.ShouldBe(expected.Token); _result.Token.ShouldBe(expected.Token);
_result.Type.ShouldBe(expected.Type); _result.Type.ShouldBe(expected.Type);
_result.Namesapce.ShouldBe(expected.Namesapce);
_result.ConfigurationKey.ShouldBe(expected.ConfigurationKey); _result.ConfigurationKey.ShouldBe(expected.ConfigurationKey);
} }
} }