kubernetes provider (#772)

* feat: Kubernetes ServiceDiscoveryProvider

* 编写k8s测试例子

* feat:fix kube config

* feat: remove port

* feat : complete the k8s test

* feat :  add kubeserviceDiscovery test

* feat : add kube provider unittest

* feat :add kubetnetes docs

how to use ocelot with kubetnetes docs

* keep the configuration as simple as possible, no qos, no cache

* fix: use http

* add PollingKubeServiceDiscovery

* feat : refactor logger

* feat : add  pollkube docs

* feat:Remove unnecessary code

* feat : code-block json
This commit is contained in:
geffzhang
2019-01-31 18:19:32 +08:00
committed by Marcelo Castagna
parent 05ede70e62
commit 44dccf1fce
42 changed files with 1135 additions and 7 deletions

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,63 @@
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 Kube : IServiceDiscoveryProvider
{
private KubeRegistryConfiguration kubeRegistryConfiguration;
private IOcelotLogger logger;
private IKubeApiClient kubeApi;
public Kube(KubeRegistryConfiguration kubeRegistryConfiguration, IOcelotLoggerFactory factory, IKubeApiClientFactory kubeClientFactory)
{
this.kubeRegistryConfiguration = kubeRegistryConfiguration;
this.logger = factory.CreateLogger<Kube>();
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));
}
else
{
logger.LogWarning($"namespace:{kubeRegistryConfiguration.KubeNamespace }service:{kubeRegistryConfiguration.KeyOfServiceInK8s} Unable to use ,it is invalid. Address must contain host only e.g. localhost and port must be greater than 0");
}
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,38 @@
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>();
return GetkubeProvider(provider, config, name, factory);
};
private static ServiceDiscovery.Providers.IServiceDiscoveryProvider GetkubeProvider(IServiceProvider provider, Configuration.ServiceProviderConfiguration config, string name, IOcelotLoggerFactory factory)
{
var kubeClientFactory = provider.GetService<IKubeApiClientFactory>();
var k8sRegistryConfiguration = new KubeRegistryConfiguration()
{
ApiEndPoint = new Uri($"https://{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 Kube(k8sRegistryConfiguration, factory, kubeClientFactory);
if (config.Type?.ToLower() == "pollkube")
{
return new PollKube(config.PollingInterval, factory, k8sServiceDiscoveryProvider);
}
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.4" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Ocelot\Ocelot.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,15 @@
using Microsoft.Extensions.DependencyInjection;
using Ocelot.DependencyInjection;
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

@ -0,0 +1,50 @@
using Ocelot.Logging;
using Ocelot.ServiceDiscovery.Providers;
using Ocelot.Values;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Ocelot.Provider.Kubernetes
{
public class PollKube : IServiceDiscoveryProvider
{
private readonly IOcelotLogger _logger;
private readonly IServiceDiscoveryProvider _kubeServiceDiscoveryProvider;
private readonly Timer _timer;
private bool _polling;
private List<Service> _services;
public PollKube(int pollingInterval, IOcelotLoggerFactory factory, IServiceDiscoveryProvider kubeServiceDiscoveryProvider)
{
_logger = factory.CreateLogger<PollKube>();
_kubeServiceDiscoveryProvider = kubeServiceDiscoveryProvider;
_services = new List<Service>();
_timer = new Timer(async x =>
{
if (_polling)
{
return;
}
_polling = true;
await Poll();
_polling = false;
}, null, pollingInterval, pollingInterval);
}
public Task<List<Service>> Get()
{
return Task.FromResult(_services);
}
private async Task Poll()
{
_services = await _kubeServiceDiscoveryProvider.Get();
}
}
}

View File

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

View File

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

View File

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

View File

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