├── README.md
├── versions.props
├── samples
└── discovery
│ ├── Time-UI
│ ├── Views
│ │ └── Home
│ │ │ └── Index.cshtml
│ ├── run.bat
│ ├── Services
│ │ ├── ITimeService.cs
│ │ └── TimeService.cs
│ ├── appsettings.Development.json
│ ├── appsettings.json
│ ├── Program.cs
│ ├── Controllers
│ │ └── HomeController.cs
│ ├── Time-UI.csproj
│ └── Startup.cs
│ └── Time-Service
│ ├── run.bat
│ ├── appsettings.Development.json
│ ├── Controllers
│ └── CurrentTimeController.cs
│ ├── appsettings.json
│ ├── Program.cs
│ ├── Time-Service.csproj
│ └── Startup.cs
├── test
└── Steeltoe.Discovery.ConsulBase.Test
│ ├── xunit.runner.json
│ ├── Steeltoe.Discovery.ConsulBase.Test.csproj
│ └── TempTest.cs
├── config
├── versions.props
├── versions-dev.props
└── versions-master.props
├── src
└── discovery
│ ├── Steeltoe.Discovery.ConsulBase
│ ├── ConsulOptions.cs
│ ├── Discovery
│ │ ├── HeartbeatOptions.cs
│ │ ├── ThisServiceInstance.cs
│ │ ├── ConsulServiceInstance.cs
│ │ ├── TtlScheduler.cs
│ │ ├── ConsulDiscoveryClient.cs
│ │ └── ConsulDiscoveryOptions.cs
│ ├── ServiceRegistry
│ │ ├── ConsulRegistration.cs
│ │ └── ConsulServiceRegistry.cs
│ ├── Steeltoe.Discovery.ConsulBase.csproj
│ └── Util
│ │ ├── DateTimeConversions.cs
│ │ └── ConsulServerUtils.cs
│ └── Steeltoe.Discovery.Consul.Client
│ ├── Steeltoe.Discovery.Consul.Client.csproj
│ ├── DiscoveryHostBuilderExtensions.cs
│ ├── DiscoveryServiceCollectionExtensions.cs
│ └── DiscoveryApplicationBuilderExtensions.cs
├── stylecop.json
├── .gitattributes
├── Steeltoe.Extensions.sln
├── .gitignore
└── LICENSE
/README.md:
--------------------------------------------------------------------------------
1 | # steeltoe-extensions
2 | steeltoe extensions
3 |
--------------------------------------------------------------------------------
/versions.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/samples/discovery/Time-UI/Views/Home/Index.cshtml:
--------------------------------------------------------------------------------
1 | @model DateTime
2 | @Model.ToString("yyyy-MM-dd HH:mm:ss")
--------------------------------------------------------------------------------
/samples/discovery/Time-UI/run.bat:
--------------------------------------------------------------------------------
1 | dotnet publish
2 | set ASPNETCORE_URLS=http://+:5001
3 | dotnet bin\Debug\netcoreapp2.0\publish\Time-UI.dll
--------------------------------------------------------------------------------
/test/Steeltoe.Discovery.ConsulBase.Test/xunit.runner.json:
--------------------------------------------------------------------------------
1 | {
2 | "maxParallelThreads": 1,
3 | "parallelizeTestCollections": false
4 | }
5 |
--------------------------------------------------------------------------------
/samples/discovery/Time-Service/run.bat:
--------------------------------------------------------------------------------
1 | dotnet publish
2 | set ASPNETCORE_URLS=http://+:5000
3 | dotnet bin\Debug\netcoreapp2.0\publish\Time-Service.dll
--------------------------------------------------------------------------------
/samples/discovery/Time-UI/Services/ITimeService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 |
4 | namespace Time_UI.Services
5 | {
6 | public interface ITimeService
7 | {
8 | Task GetNowAsync();
9 | }
10 | }
--------------------------------------------------------------------------------
/samples/discovery/Time-UI/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "IncludeScopes": false,
4 | "LogLevel": {
5 | "Default": "Debug",
6 | "System": "Information",
7 | "Microsoft": "Information"
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/samples/discovery/Time-Service/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "IncludeScopes": false,
4 | "LogLevel": {
5 | "Default": "Debug",
6 | "System": "Information",
7 | "Microsoft": "Information"
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/samples/discovery/Time-UI/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "IncludeScopes": false,
4 | "LogLevel": {
5 | "Default": "Information"
6 | }
7 | },
8 | "Consul": {
9 | "Discovery": {
10 | "Register": false,
11 | "QueryPassing": true
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/samples/discovery/Time-Service/Controllers/CurrentTimeController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using System;
3 |
4 | namespace Time_Service.Controllers
5 | {
6 | [Route("api/[controller]")]
7 | public class CurrentTimeController : Controller
8 | {
9 | [HttpGet]
10 | public string Get()
11 | {
12 | return DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/samples/discovery/Time-Service/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "IncludeScopes": false,
4 | "LogLevel": {
5 | "Default": "Information"
6 | }
7 | },
8 | "Consul": {
9 | "Host": "localhost",
10 | "Port": 8500
11 | },
12 | "Spring": {
13 | "Application": {
14 | "Name": "timeService"
15 | }
16 | },
17 | "Management": {
18 | "Endpoints": {
19 | "Path": "/actuator"
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/samples/discovery/Time-UI/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore;
2 | using Microsoft.AspNetCore.Hosting;
3 | using Steeltoe.Discovery.Consul.Client;
4 |
5 | namespace Time_UI
6 | {
7 | public class Program
8 | {
9 | public static void Main(string[] args)
10 | {
11 | BuildWebHost(args).Run();
12 | }
13 |
14 | public static IWebHost BuildWebHost(string[] args) =>
15 | WebHost.CreateDefaultBuilder(args)
16 | .UseStartup()
17 | .UseDiscoveryClient()
18 | .Build();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/samples/discovery/Time-Service/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore;
2 | using Microsoft.AspNetCore.Hosting;
3 | using Steeltoe.Discovery.Consul.Client;
4 |
5 | namespace Time_Service
6 | {
7 | public class Program
8 | {
9 | public static void Main(string[] args)
10 | {
11 | BuildWebHost(args).Run();
12 | }
13 |
14 | public static IWebHost BuildWebHost(string[] args) =>
15 | WebHost.CreateDefaultBuilder(args)
16 | .UseStartup()
17 | .UseDiscoveryClient()
18 | .Build();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/samples/discovery/Time-UI/Controllers/HomeController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using System.Threading.Tasks;
3 | using Time_UI.Services;
4 |
5 | namespace Time_UI.Controllers
6 | {
7 | public class HomeController : Controller
8 | {
9 | private readonly ITimeService _timeService;
10 |
11 | public HomeController(ITimeService timeService)
12 | {
13 | _timeService = timeService;
14 | }
15 |
16 | public async Task Index()
17 | {
18 | var now = await _timeService.GetNowAsync();
19 | return View(now);
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/samples/discovery/Time-UI/Time-UI.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/config/versions.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | $(STEELTOE_VERSION)
4 | $(STEELTOE_DASH_VERSION_SUFFIX)
5 | 2.0.0
6 | 2.0.0
7 | 2.0.0
8 | 2.0.0
9 | 2.0.0
10 | 4.4.0
11 | 1.0.2
12 | 10.0.3
13 | 15.5.0
14 | 2.3.1
15 | 2.3.1
16 | 0.7.2.4
17 |
18 |
--------------------------------------------------------------------------------
/config/versions-dev.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | $(STEELTOE_VERSION)
4 | $(STEELTOE_DASH_VERSION_SUFFIX)
5 | 2.1.0-dev-00062
6 | 2.1.0
7 | 2.1.0
8 | 2.1.0
9 | 2.1.0
10 | 4.4.0
11 | 1.0.2
12 | 10.0.3
13 | 15.7.0
14 | 2.3.1
15 | 2.3.1
16 | 0.7.2.4
17 |
18 |
--------------------------------------------------------------------------------
/config/versions-master.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | $(STEELTOE_VERSION)
4 | $(STEELTOE_DASH_VERSION_SUFFIX)
5 | 2.1.0-master-00053
6 | 2.0.0
7 | 2.0.0
8 | 2.0.0
9 | 2.0.0
10 | 4.4.0
11 | 1.0.2
12 | 10.0.3
13 | 15.5.0
14 | 2.3.1
15 | 2.3.1
16 | 0.7.2.4
17 |
18 |
--------------------------------------------------------------------------------
/samples/discovery/Time-Service/Time-Service.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp2.0
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 | PreserveNewest
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/discovery/Steeltoe.Discovery.ConsulBase/ConsulOptions.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2017 the original author or authors.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | namespace Steeltoe.Discovery.Consul
16 | {
17 | public class ConsulOptions
18 | {
19 | public string Host { get; set; } = "localhost";
20 |
21 | public string Scheme { get; set; } = "http";
22 |
23 | public int Port { get; set; } = 8500;
24 |
25 | public string Datacenter { get; set; }
26 |
27 | public string Token { get; set; }
28 |
29 | public string WaitTime { get; set; }
30 |
31 | public bool Enable { get; set; }
32 | }
33 | }
--------------------------------------------------------------------------------
/samples/discovery/Time-UI/Services/TimeService.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.Logging;
2 | using Steeltoe.Common.Discovery;
3 | using System;
4 | using System.Net.Http;
5 | using System.Threading.Tasks;
6 |
7 | namespace Time_UI.Services
8 | {
9 | public class TimeService : ITimeService
10 | {
11 | private readonly DiscoveryHttpClientHandler _handler;
12 | private const string CURRENT_TIME_URL = "http://timeService/api/currentTime";
13 |
14 | public TimeService(IDiscoveryClient discoveryClient, ILogger discoveryHandlerLogger)
15 | {
16 | _handler = new DiscoveryHttpClientHandler(discoveryClient, discoveryHandlerLogger);
17 | }
18 |
19 | #region Implementation of ITimeService
20 |
21 | public async Task GetNowAsync()
22 | {
23 | var client = GetClient();
24 | var result = await client.GetStringAsync(CURRENT_TIME_URL);
25 | return DateTime.Parse(result);
26 | }
27 |
28 | #endregion Implementation of ITimeService
29 |
30 | private HttpClient GetClient()
31 | {
32 | var client = new HttpClient(_handler, false);
33 | return client;
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/stylecop.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json",
3 | "settings": {
4 | "documentationRules": {
5 | "copyrightText": "Copyright {copyrightYear} the original author or authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.",
6 | "xmlHeader": false,
7 | "variables": {
8 | "copyrightYear": "2017"
9 | },
10 | "documentExposedElements": false,
11 | "documentInternalElements": false,
12 | "documentPrivateElements": false
13 | },
14 | "indentation": {
15 | "useTabs": false,
16 | "indentationSize": 4
17 | },
18 | "namingRules": {
19 | },
20 | "orderingRules": {
21 | "usingDirectivesPlacement": "outsideNamespace",
22 | "systemUsingDirectivesFirst": false
23 | },
24 | "readabilityRules": {
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/samples/discovery/Time-UI/Startup.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.AspNetCore.Hosting;
3 | using Microsoft.Extensions.Configuration;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using Time_UI.Services;
6 |
7 | namespace Time_UI
8 | {
9 | public class Startup
10 | {
11 | public Startup(IConfiguration configuration)
12 | {
13 | Configuration = configuration;
14 | }
15 |
16 | public IConfiguration Configuration { get; }
17 |
18 | // This method gets called by the runtime. Use this method to add services to the container.
19 | public void ConfigureServices(IServiceCollection services)
20 | {
21 | services.AddSingleton();
22 |
23 | services.AddMvc();
24 | }
25 |
26 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
27 | public void Configure(IApplicationBuilder app, IHostingEnvironment env)
28 | {
29 | if (env.IsDevelopment())
30 | {
31 | app.UseDeveloperExceptionPage();
32 | }
33 |
34 | app.UseMvc(routes =>
35 | {
36 | routes.MapRoute(
37 | name: "default",
38 | template: "{controller=Home}/{action=Index}/{id?}");
39 | });
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/samples/discovery/Time-Service/Startup.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.AspNetCore.Hosting;
3 | using Microsoft.Extensions.Configuration;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using Steeltoe.Management.Endpoint.CloudFoundry;
6 | using Steeltoe.Management.Endpoint.Health;
7 |
8 | namespace Time_Service
9 | {
10 | public class Startup
11 | {
12 | public Startup(IConfiguration configuration)
13 | {
14 | Configuration = configuration;
15 | }
16 |
17 | public IConfiguration Configuration { get; }
18 |
19 | // This method gets called by the runtime. Use this method to add services to the container.
20 | public void ConfigureServices(IServiceCollection services)
21 | {
22 | services.AddMvc();
23 |
24 | services.AddHealthActuator(Configuration);
25 | services.AddCloudFoundryActuator(Configuration);
26 | }
27 |
28 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
29 | public void Configure(IApplicationBuilder app, IHostingEnvironment env)
30 | {
31 | if (env.IsDevelopment())
32 | {
33 | app.UseDeveloperExceptionPage();
34 | }
35 |
36 | app.UseHealthActuator();
37 | app.UseCloudFoundryActuator();
38 |
39 | app.UseMvc(routes =>
40 | {
41 | routes.MapRoute(
42 | name: "default",
43 | template: "{controller=Home}/{action=Index}/{id?}");
44 | });
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/src/discovery/Steeltoe.Discovery.ConsulBase/Discovery/HeartbeatOptions.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2017 the original author or authors.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using System;
16 |
17 | namespace Steeltoe.Discovery.Consul.Discovery
18 | {
19 | public class HeartbeatOptions
20 | {
21 | public bool Enable { get; set; } = false;
22 |
23 | public int TtlValue { get; set; } = 30;
24 |
25 | public string TtlUnit { get; set; } = "s";
26 |
27 | public double IntervalRatio { get; set; } = 2.0 / 3.0;
28 |
29 | public TimeSpan ComputeHearbeatInterval()
30 | {
31 | // heartbeat rate at ratio * ttl, but no later than ttl -1s and, (under lesser priority),
32 | // no sooner than 1s from now
33 | var interval = TtlValue * IntervalRatio;
34 | var max = Math.Max(interval, 1);
35 | var ttlMinus1 = TtlValue - 1;
36 | var min = Math.Min(ttlMinus1, max);
37 | var heartbeatInterval = (int)Math.Round(1000 * min);
38 | return TimeSpan.FromMilliseconds(heartbeatInterval);
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/test/Steeltoe.Discovery.ConsulBase.Test/Steeltoe.Discovery.ConsulBase.Test.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | netcoreapp2.1;net461
7 |
8 |
9 |
10 | SA1101;SA1124;SA1201;SA1309;SA1310;SA1401;SA1600;SA1652;1591
11 |
12 |
13 |
14 |
15 | PreserveNewest
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | All
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | stylecop.json
39 | Always
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/discovery/Steeltoe.Discovery.Consul.Client/Steeltoe.Discovery.Consul.Client.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Neflix Consul Client
7 | $(SteeltoeVersion)
8 | $(VersionSuffix)
9 | Pivotal;majian
10 | netstandard2.0
11 | Steeltoe.Discovery.Consul.Client
12 | Steeltoe.Discovery.Consul.Client
13 | Consul, ASPNET Core, Spring, Spring Cloud
14 | https://steeltoe.io/images/transparent.png
15 | https://steeltoe.io
16 | http://www.apache.org/licenses/LICENSE-2.0
17 |
18 |
19 |
20 | SA1101;SA1124;SA1201;SA1309;SA1310;SA1401;SA1600;SA1652;1591
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | All
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | stylecop.json
39 | Always
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/discovery/Steeltoe.Discovery.ConsulBase/Discovery/ThisServiceInstance.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2017 the original author or authors.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using Steeltoe.Common.Discovery;
16 | using Steeltoe.Discovery.Consul.Util;
17 | using System;
18 | using System.Collections.Generic;
19 |
20 | namespace Steeltoe.Discovery.Consul.Discovery
21 | {
22 | internal class ThisServiceInstance : IServiceInstance
23 | {
24 | public ThisServiceInstance(ConsulDiscoveryOptions options)
25 | {
26 | ServiceId = options.ServiceName;
27 | Host = options.HostName;
28 | IsSecure = options.Scheme == "https";
29 | Port = options.Port ?? (IsSecure ? 443 : 80);
30 | var metadata = ConsulServerUtils.GetMetadata(options.Tags);
31 | Metadata = metadata;
32 | Uri = new Uri($"{options.Scheme}://{Host}:{Port}");
33 | }
34 |
35 | #region Implementation of IServiceInstance
36 |
37 | ///
38 | public string ServiceId { get; }
39 |
40 | ///
41 | public string Host { get; }
42 |
43 | ///
44 | public int Port { get; }
45 |
46 | ///
47 | public bool IsSecure { get; }
48 |
49 | ///
50 | public Uri Uri { get; }
51 |
52 | ///
53 | public IDictionary Metadata { get; }
54 |
55 | #endregion Implementation of IServiceInstance
56 | }
57 | }
--------------------------------------------------------------------------------
/src/discovery/Steeltoe.Discovery.ConsulBase/ServiceRegistry/ConsulRegistration.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2017 the original author or authors.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using Consul;
16 | using Steeltoe.Discovery.Consul.Discovery;
17 | using Steeltoe.Discovery.Consul.Util;
18 | using System;
19 | using System.Collections.Generic;
20 |
21 | namespace Steeltoe.Discovery.Consul.ServiceRegistry
22 | {
23 | public class ConsulRegistration
24 | {
25 | public ConsulRegistration(AgentServiceRegistration agentServiceRegistration, ConsulDiscoveryOptions options)
26 | {
27 | AgentServiceRegistration = agentServiceRegistration;
28 | InstanceId = agentServiceRegistration.ID;
29 | ServiceId = agentServiceRegistration.Name;
30 | Host = agentServiceRegistration.Address;
31 | Port = agentServiceRegistration.Port;
32 | IsSecure = options.Scheme == "https";
33 | Uri = new Uri($"{options.Scheme}://{Host}:{Port}");
34 | Metadata = ConsulServerUtils.GetMetadata(agentServiceRegistration.Tags);
35 | }
36 |
37 | public AgentServiceRegistration AgentServiceRegistration { get; }
38 |
39 | public string InstanceId { get; }
40 |
41 | public string ServiceId { get; }
42 |
43 | public string Host { get; }
44 |
45 | public int Port { get; }
46 |
47 | public bool IsSecure { get; }
48 |
49 | public Uri Uri { get; }
50 |
51 | public IDictionary Metadata { get; }
52 | }
53 | }
--------------------------------------------------------------------------------
/src/discovery/Steeltoe.Discovery.ConsulBase/Steeltoe.Discovery.ConsulBase.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Neflix Consul Client
7 | $(SteeltoeVersion)
8 | $(VersionSuffix)
9 | Pivotal;majian
10 | netstandard2.0
11 | Steeltoe.Discovery.ConsulBase
12 | Steeltoe.Discovery.ConsulBase
13 | Consul, ASPNET Core, Spring, Spring Cloud
14 | https://steeltoe.io/images/transparent.png
15 | https://steeltoe.io
16 | http://www.apache.org/licenses/LICENSE-2.0
17 |
18 |
19 |
20 | SA1101;SA1124;SA1201;SA1309;SA1310;SA1401;SA1600;SA1652;1591
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | All
37 |
38 |
39 |
40 |
41 |
42 | stylecop.json
43 | Always
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/src/discovery/Steeltoe.Discovery.ConsulBase/Discovery/ConsulServiceInstance.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2017 the original author or authors.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using Consul;
16 | using Steeltoe.Common.Discovery;
17 | using Steeltoe.Discovery.Consul.Util;
18 | using System;
19 | using System.Collections.Generic;
20 |
21 | namespace Steeltoe.Discovery.Consul.Discovery
22 | {
23 | public class ConsulServiceInstance : IServiceInstance
24 | {
25 | public ConsulServiceInstance(ServiceEntry healthService)
26 | {
27 | var metadata = ConsulServerUtils.GetMetadata(healthService);
28 |
29 | ServiceId = healthService.Service.Service;
30 | Host = ConsulServerUtils.FindHost(healthService);
31 | IsSecure = metadata.TryGetValue("secure", out var secureString) && bool.Parse(secureString);
32 | Port = healthService.Service.Port;
33 | Metadata = metadata;
34 |
35 | var scheme = IsSecure ? "https" : "http";
36 | Uri = new Uri($"{scheme}://{Host}:{Port}");
37 | }
38 |
39 | #region Implementation of IServiceInstance
40 |
41 | ///
42 | public string ServiceId { get; }
43 |
44 | ///
45 | public string Host { get; }
46 |
47 | ///
48 | public int Port { get; }
49 |
50 | ///
51 | public bool IsSecure { get; }
52 |
53 | ///
54 | public Uri Uri { get; }
55 |
56 | ///
57 | public IDictionary Metadata { get; }
58 |
59 | #endregion Implementation of IServiceInstance
60 | }
61 | }
--------------------------------------------------------------------------------
/src/discovery/Steeltoe.Discovery.ConsulBase/Util/DateTimeConversions.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2017 the original author or authors.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using System;
16 |
17 | namespace Steeltoe.Discovery.Consul.Util
18 | {
19 | public class DateTimeConversions
20 | {
21 | public static TimeSpan ToTimeSpan(string time)
22 | {
23 | if (string.IsNullOrWhiteSpace(time))
24 | {
25 | throw new ArgumentNullException(nameof(time));
26 | }
27 |
28 | time = time.ToLower();
29 |
30 | if (time.EndsWith("ms"))
31 | {
32 | return ToTimeSpan(int.Parse(time.Substring(0, time.Length - 2)), "ms");
33 | }
34 |
35 | if (time.EndsWith("s"))
36 | {
37 | return ToTimeSpan(int.Parse(time.Substring(0, time.Length - 1)), "s");
38 | }
39 |
40 | if (time.EndsWith("m"))
41 | {
42 | return ToTimeSpan(int.Parse(time.Substring(0, time.Length - 1)), "m");
43 | }
44 |
45 | if (time.EndsWith("h"))
46 | {
47 | return ToTimeSpan(int.Parse(time.Substring(0, time.Length - 1)), "h");
48 | }
49 |
50 | throw new InvalidOperationException("Incorrect format:" + time);
51 | }
52 |
53 | public static TimeSpan ToTimeSpan(int value, string unit)
54 | {
55 | if (string.IsNullOrWhiteSpace(unit))
56 | {
57 | throw new ArgumentNullException(nameof(unit));
58 | }
59 |
60 | switch (unit)
61 | {
62 | case "ms":
63 | return TimeSpan.FromMilliseconds(value);
64 |
65 | case "s":
66 | return TimeSpan.FromSeconds(value);
67 |
68 | case "m":
69 | return TimeSpan.FromMinutes(value);
70 |
71 | case "h":
72 | return TimeSpan.FromHours(value);
73 | }
74 |
75 | throw new InvalidOperationException("Incorrect unit:" + unit);
76 | }
77 | }
78 | }
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/src/discovery/Steeltoe.Discovery.ConsulBase/Util/ConsulServerUtils.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2017 the original author or authors.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using Consul;
16 | using System;
17 | using System.Collections.Generic;
18 | using System.Linq;
19 | using System.Net;
20 | using System.Net.Sockets;
21 |
22 | namespace Steeltoe.Discovery.Consul.Util
23 | {
24 | public class ConsulServerUtils
25 | {
26 | public static string FindHost(ServiceEntry healthService)
27 | {
28 | var service = healthService.Service;
29 | var node = healthService.Node;
30 |
31 | if (!string.IsNullOrWhiteSpace(service.Address))
32 | {
33 | return FixIPv6Address(service.Address);
34 | }
35 |
36 | if (!string.IsNullOrWhiteSpace(node.Address))
37 | {
38 | return FixIPv6Address(node.Address);
39 | }
40 |
41 | return node.Address;
42 | }
43 |
44 | public static string FixIPv6Address(string address)
45 | {
46 | var ip6 = Dns.GetHostAddresses(address)
47 | .FirstOrDefault(i => i.AddressFamily == AddressFamily.InterNetworkV6)?.ToString();
48 | return ip6 != null ? $"[{ip6}]" : address;
49 | }
50 |
51 | public static IDictionary GetMetadata(ServiceEntry healthService)
52 | {
53 | return GetMetadata(healthService.Service.Tags);
54 | }
55 |
56 | public static IDictionary GetMetadata(string[] tags)
57 | {
58 | var metadata = new Dictionary(StringComparer.OrdinalIgnoreCase);
59 | if (tags == null)
60 | {
61 | return metadata;
62 | }
63 |
64 | foreach (var tag in tags)
65 | {
66 | var index = tag.IndexOf('=');
67 | string key, value;
68 | if (index == -1 || Equals(index + 1, tag.Length))
69 | {
70 | key = value = tag;
71 | }
72 | else
73 | {
74 | key = tag.Substring(0, index);
75 | value = tag.Substring(index + 1);
76 | }
77 |
78 | metadata[key] = value;
79 | }
80 |
81 | return metadata;
82 | }
83 | }
84 | }
--------------------------------------------------------------------------------
/src/discovery/Steeltoe.Discovery.ConsulBase/Discovery/TtlScheduler.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2017 the original author or authors.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using Consul;
16 | using Microsoft.Extensions.Options;
17 | using System;
18 | using System.Collections.Concurrent;
19 | using System.Threading;
20 | using System.Threading.Tasks;
21 |
22 | namespace Steeltoe.Discovery.Consul.Discovery
23 | {
24 | public class TtlScheduler
25 | {
26 | private readonly ConcurrentDictionary _serviceHeartbeats = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase);
27 |
28 | private readonly IOptionsMonitor _heartbeatOptionsMonitor;
29 | private readonly ConsulClient _client;
30 |
31 | private HeartbeatOptions HeartbeatOptions => _heartbeatOptionsMonitor.CurrentValue;
32 |
33 | public TtlScheduler(IOptionsMonitor heartbeatOptionsMonitor, ConsulClient client)
34 | {
35 | _heartbeatOptionsMonitor = heartbeatOptionsMonitor;
36 | _client = client;
37 | }
38 |
39 | public void Add(string instanceId)
40 | {
41 | if (string.IsNullOrWhiteSpace(instanceId))
42 | {
43 | throw new ArgumentNullException(nameof(instanceId));
44 | }
45 |
46 | var interval = HeartbeatOptions.ComputeHearbeatInterval();
47 | var timer = new Timer(async s => { await PassTtl(s.ToString()); }, instanceId, TimeSpan.Zero, interval);
48 | _serviceHeartbeats.AddOrUpdate(instanceId, timer, (key, oldTimer) =>
49 | {
50 | oldTimer.Dispose();
51 | return timer;
52 | });
53 | }
54 |
55 | public void Remove(string instanceId)
56 | {
57 | if (string.IsNullOrWhiteSpace(instanceId))
58 | {
59 | throw new ArgumentNullException(nameof(instanceId));
60 | }
61 |
62 | if (_serviceHeartbeats.TryRemove(instanceId, out var timer))
63 | {
64 | timer.Dispose();
65 | }
66 | }
67 |
68 | private Task PassTtl(string serviceId)
69 | {
70 | if (!serviceId.StartsWith("service:"))
71 | {
72 | serviceId = "service:" + serviceId;
73 | }
74 |
75 | return _client.Agent.PassTTL(serviceId, "ttl");
76 | }
77 | }
78 | }
--------------------------------------------------------------------------------
/src/discovery/Steeltoe.Discovery.ConsulBase/ServiceRegistry/ConsulServiceRegistry.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2017 the original author or authors.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using Consul;
16 | using Microsoft.Extensions.Logging;
17 | using Microsoft.Extensions.Options;
18 | using Steeltoe.Discovery.Consul.Discovery;
19 | using System;
20 | using System.Threading.Tasks;
21 | using LogLevel = Microsoft.Extensions.Logging.LogLevel;
22 |
23 | namespace Steeltoe.Discovery.Consul.ServiceRegistry
24 | {
25 | public class ConsulServiceRegistry
26 | {
27 | private readonly ConsulClient _client;
28 | private readonly TtlScheduler _ttlScheduler;
29 | private readonly ILogger _logger;
30 | private readonly ConsulDiscoveryOptions _consulDiscoveryOptions;
31 | private readonly HeartbeatOptions _heartbeatOptions;
32 |
33 | public ConsulServiceRegistry(ConsulClient client, IOptions consulDiscoveryOptionsAccessor, TtlScheduler ttlScheduler, IOptions heartbeatOptionsAccessor, ILogger logger)
34 | {
35 | _client = client;
36 | _consulDiscoveryOptions = consulDiscoveryOptionsAccessor.Value;
37 | _ttlScheduler = ttlScheduler;
38 | _heartbeatOptions = heartbeatOptionsAccessor.Value;
39 | _logger = logger;
40 | }
41 |
42 | public async Task RegisterAsync(ConsulRegistration registration)
43 | {
44 | if (_logger.IsEnabled(LogLevel.Information))
45 | {
46 | _logger.LogInformation("Registering service with consul: " + registration.ServiceId);
47 | }
48 |
49 | try
50 | {
51 | await _client.Agent.ServiceRegister(registration.AgentServiceRegistration);
52 | if (_heartbeatOptions.Enable)
53 | {
54 | _ttlScheduler?.Add(registration.InstanceId);
55 | }
56 | }
57 | catch (Exception e)
58 | {
59 | if (_consulDiscoveryOptions.FailFast)
60 | {
61 | _logger.LogError(e, "Error registering service with consul: " + registration.ServiceId);
62 | throw;
63 | }
64 |
65 | _logger.LogWarning(e, "Failfast is false. Error registering service with consul: " + registration.ServiceId);
66 | }
67 | }
68 |
69 | public async Task DeregisterAsync(ConsulRegistration registration)
70 | {
71 | _ttlScheduler?.Remove(registration.InstanceId);
72 |
73 | if (_logger.IsEnabled(LogLevel.Information))
74 | {
75 | _logger.LogInformation("Deregistering service with consul: " + registration.ServiceId);
76 | }
77 |
78 | await _client.Agent.ServiceDeregister(registration.InstanceId);
79 | }
80 | }
81 | }
--------------------------------------------------------------------------------
/src/discovery/Steeltoe.Discovery.ConsulBase/Discovery/ConsulDiscoveryClient.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2017 the original author or authors.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using Consul;
16 | using Microsoft.Extensions.Options;
17 | using Steeltoe.Common.Discovery;
18 | using System;
19 | using System.Collections.Generic;
20 | using System.Linq;
21 | using System.Threading.Tasks;
22 |
23 | namespace Steeltoe.Discovery.Consul.Discovery
24 | {
25 | public class ConsulDiscoveryClient : IDiscoveryClient
26 | {
27 | private readonly ConsulClient _client;
28 | private readonly IOptionsMonitor _discoveryOptionsMonitor;
29 |
30 | private ConsulDiscoveryOptions ConsulDiscoveryOptions => _discoveryOptionsMonitor.CurrentValue;
31 |
32 | private IServiceInstance _thisServiceInstance;
33 |
34 | public ConsulDiscoveryClient(ConsulClient client, IOptionsMonitor discoveryOptionsMonitor)
35 | {
36 | _client = client;
37 | _discoveryOptionsMonitor = discoveryOptionsMonitor;
38 |
39 | _thisServiceInstance = new ThisServiceInstance(discoveryOptionsMonitor.CurrentValue);
40 | discoveryOptionsMonitor.OnChange(o => { _thisServiceInstance = new ThisServiceInstance(discoveryOptionsMonitor.CurrentValue); });
41 | }
42 |
43 | #region Implementation of IDiscoveryClient
44 |
45 | ///
46 | public IServiceInstance GetLocalServiceInstance()
47 | {
48 | return _thisServiceInstance;
49 | }
50 |
51 | ///
52 | public IList GetInstances(string serviceId)
53 | {
54 | var instances = new List();
55 | AddInstancesToListAsync(instances, serviceId).GetAwaiter().GetResult();
56 | return instances;
57 | }
58 |
59 | ///
60 | public Task ShutdownAsync()
61 | {
62 | return Task.CompletedTask;
63 | }
64 |
65 | ///
66 | public string Description { get; } = "HashiCorp Consul Client";
67 |
68 | ///
69 | public IList Services => GetServicesAsync().GetAwaiter().GetResult();
70 |
71 | #endregion Implementation of IDiscoveryClient
72 |
73 | private static ConsulClient CreateConsulClient(ConsulOptions options)
74 | {
75 | return new ConsulClient(s =>
76 | {
77 | s.Address = new Uri($"{options.Scheme}://{options.Host}:{options.Port}");
78 | });
79 | }
80 |
81 | private async Task AddInstancesToListAsync(ICollection instances, string serviceId)
82 | {
83 | var result = await _client.Health.Service(serviceId, ConsulDiscoveryOptions.DefaultQueryTag, ConsulDiscoveryOptions.QueryPassing);
84 | var response = result.Response;
85 |
86 | foreach (var instance in response.Select(s => new ConsulServiceInstance(s)))
87 | {
88 | instances.Add(instance);
89 | }
90 | }
91 |
92 | private async Task> GetServicesAsync()
93 | {
94 | var result = await _client.Catalog.Services();
95 | var response = result.Response;
96 | return response.Keys.ToList();
97 | }
98 | }
99 | }
--------------------------------------------------------------------------------
/test/Steeltoe.Discovery.ConsulBase.Test/TempTest.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2017 the original author or authors.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using System.Threading.Tasks;
16 | using Xunit;
17 |
18 | namespace Steeltoe.Discovery.ConsulBase.Test
19 | {
20 | public class TempTest
21 | {
22 | [Fact]
23 | public async Task Test()
24 | {
25 | // todo:impl
26 | // leaseRenewalIntervalInSeconds leaseExpirationDurationInSeconds
27 | /*var configuration = new ConfigurationBuilder()
28 | .AddInMemoryCollection(new[]
29 | {
30 | new KeyValuePair(
31 | "spring:application:name",
32 | "ConsoleApp"),
33 | new KeyValuePair(
34 | "spring:application:instance_id",
35 | "ConsoleApp1"),
36 | new KeyValuePair(
37 | "consul:client:serviceUrl",
38 | "http://localhost:8500"),
39 | new KeyValuePair(
40 | "consul:client:registryFetchIntervalSeconds",
41 | "2"),
42 | new KeyValuePair(
43 | "consul:instance:port",
44 | "5000"),
45 | new KeyValuePair(
46 | "consul:instance:host",
47 | "192.168.1.100"),
48 | new KeyValuePair(
49 | "consul:instance:leaseRenewalIntervalInSeconds",
50 | "6"),
51 | new KeyValuePair(
52 | "consul:instance:leaseExpirationDurationInSeconds",
53 | "4")
54 | })
55 | .Build();
56 | var services = new ServiceCollection()
57 | .AddOptions()
58 | .AddConsulDiscoveryClient(configuration)
59 | .BuildServiceProvider();
60 |
61 | var discoveryClient = services.GetService();
62 |
63 | var localServiceInstance = discoveryClient.GetLocalServiceInstance();
64 | foreach (var service in discoveryClient.Services)
65 | {
66 | var instances = discoveryClient.GetInstances(service);
67 | Assert.NotEmpty(instances);
68 | instances = discoveryClient.GetInstances(service.ToUpper());
69 | Assert.NotEmpty(instances);
70 | }
71 |
72 | Assert.Equal("192.168.1.100", localServiceInstance.Host);
73 | Assert.Equal(5000, localServiceInstance.Port);
74 | Assert.False(localServiceInstance.IsSecure);
75 | Assert.Equal("ConsoleApp", localServiceInstance.ServiceId);
76 | Assert.Equal(new Uri("http://192.168.1.100:5000"), localServiceInstance.Uri);
77 |
78 | await Task.Delay(TimeSpan.FromSeconds(5));
79 |
80 | Assert.Empty(discoveryClient.GetInstances(localServiceInstance.ServiceId));
81 |
82 | await discoveryClient.ShutdownAsync();
83 |
84 | await Task.Delay(TimeSpan.FromSeconds(3));
85 |
86 | Assert.False(discoveryClient.Services.Contains(localServiceInstance.ServiceId));*/
87 | }
88 | }
89 | }
--------------------------------------------------------------------------------
/src/discovery/Steeltoe.Discovery.Consul.Client/DiscoveryHostBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2017 the original author or authors.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using Microsoft.AspNetCore.Builder;
16 | using Microsoft.AspNetCore.Hosting;
17 | using Microsoft.Extensions.DependencyInjection;
18 | using Steeltoe.Discovery.Consul.Discovery;
19 | using System;
20 | using System.Linq;
21 | using System.Net;
22 |
23 | namespace Steeltoe.Discovery.Consul.Client
24 | {
25 | public static class DiscoveryHostBuilderExtensions
26 | {
27 | public static IWebHostBuilder UseDiscoveryClient(this IWebHostBuilder builder)
28 | {
29 | return builder
30 | .ConfigureServices((context, services) =>
31 | {
32 | services.PostConfigure(options =>
33 | {
34 | if (options.Port.HasValue)
35 | {
36 | return;
37 | }
38 |
39 | var urls = context.Configuration["urls"]?.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
40 | var availablePort = ResolveAvailablePort(urls);
41 |
42 | if (availablePort.HasValue)
43 | {
44 | options.Port = availablePort;
45 | }
46 | });
47 |
48 | services.AddConsulDiscoveryClient(context.Configuration);
49 | services.AddTransient();
50 | });
51 | }
52 |
53 | private static int? ResolveAvailablePort(string[] urls)
54 | {
55 | if (urls == null || !urls.Any())
56 | {
57 | return null;
58 | }
59 |
60 | var item = urls
61 | .Select(ResolveHostAndPort)
62 | .FirstOrDefault(i => IsAvailablePort(i.Host, i.Port));
63 |
64 | return item.Port;
65 | }
66 |
67 | private static bool IsAvailablePort(string host, int? port)
68 | {
69 | if (string.IsNullOrWhiteSpace(host) || !port.HasValue)
70 | {
71 | return false;
72 | }
73 |
74 | if (host == "+" || host == "*")
75 | {
76 | return true;
77 | }
78 |
79 | var ipAddresses = Dns.GetHostAddresses(host);
80 |
81 | return !ipAddresses.All(IPAddress.IsLoopback);
82 | }
83 |
84 | private static(string Host, int? Port) ResolveHostAndPort(string url)
85 | {
86 | string temp;
87 | if (url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
88 | {
89 | temp = url.Substring(7);
90 | }
91 | else if (url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
92 | {
93 | temp = url.Substring(8);
94 | }
95 | else
96 | {
97 | return (null, null);
98 | }
99 |
100 | var portStartIndex = temp.IndexOf(':');
101 |
102 | if (portStartIndex == -1 || portStartIndex + 1 == temp.Length)
103 | {
104 | return (null, null);
105 | }
106 |
107 | var host = temp.Substring(0, portStartIndex);
108 | var portString = temp.Substring(portStartIndex + 1);
109 |
110 | return (host, int.TryParse(portString, out var port) ? port as int? : null);
111 | }
112 |
113 | internal class StartupFilter : IStartupFilter
114 | {
115 | #region Implementation of IStartupFilter
116 |
117 | ///
118 | public Action Configure(Action next)
119 | {
120 | return builder =>
121 | {
122 | builder.UseDiscoveryClient();
123 | next(builder);
124 | };
125 | }
126 |
127 | #endregion Implementation of IStartupFilter
128 | }
129 | }
130 | }
--------------------------------------------------------------------------------
/src/discovery/Steeltoe.Discovery.Consul.Client/DiscoveryServiceCollectionExtensions.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2017 the original author or authors.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | using Consul;
16 | using Microsoft.AspNetCore.Hosting;
17 | using Microsoft.Extensions.Configuration;
18 | using Microsoft.Extensions.DependencyInjection;
19 | using Microsoft.Extensions.Options;
20 | using Steeltoe.Common.Discovery;
21 | using Steeltoe.Discovery.Consul.Discovery;
22 | using Steeltoe.Discovery.Consul.ServiceRegistry;
23 | using Steeltoe.Discovery.Consul.Util;
24 | using System;
25 | using System.Net;
26 | using System.Threading;
27 |
28 | namespace Steeltoe.Discovery.Consul.Client
29 | {
30 | public static class DiscoveryServiceCollectionExtensions
31 | {
32 | public static IServiceCollection AddConsulDiscoveryClient(this IServiceCollection services, IConfiguration configuration, IDiscoveryLifecycle lifecycle = null)
33 | {
34 | var consulConfigSection = configuration.GetSection("consul");
35 |
36 | services.Configure(consulConfigSection);
37 |
38 | var discoveryConfigurationSection = consulConfigSection.GetSection("discovery");
39 | services.Configure(discoveryConfigurationSection);
40 |
41 | services.Configure(discoveryConfigurationSection.GetSection("heartbeat"));
42 |
43 | services.PostConfigure(options =>
44 | {
45 | if (!options.PreferIpAddress && string.IsNullOrWhiteSpace(options.HostName))
46 | {
47 | options.HostName = Dns.GetHostName();
48 | }
49 |
50 | if (string.IsNullOrWhiteSpace(options.InstanceId))
51 | {
52 | options.InstanceId = configuration["spring:application:instance_id"];
53 | }
54 |
55 | if (string.IsNullOrWhiteSpace(options.InstanceId))
56 | {
57 | options.InstanceId = options.HostName + ":" + options.Port;
58 | }
59 |
60 | if (string.IsNullOrWhiteSpace(options.ServiceName))
61 | {
62 | options.ServiceName = configuration["spring:application:name"];
63 | }
64 |
65 | if (string.IsNullOrWhiteSpace(options.ServiceName))
66 | {
67 | options.ServiceName = configuration["applicationName"];
68 | }
69 | });
70 |
71 | AddConsulServices(services, lifecycle);
72 | return services;
73 | }
74 |
75 | private static void AddConsulServices(IServiceCollection services, IDiscoveryLifecycle lifecycle)
76 | {
77 | services.AddSingleton(s =>
78 | {
79 | var consulOptions = s.GetRequiredService>().Value;
80 | return new ConsulClient(options =>
81 | {
82 | options.Address = new Uri($"{consulOptions.Scheme}://{consulOptions.Host}:{consulOptions.Port}");
83 | options.Datacenter = consulOptions.Datacenter;
84 | options.Token = consulOptions.Token;
85 | if (!string.IsNullOrWhiteSpace(consulOptions.WaitTime))
86 | {
87 | options.WaitTime = DateTimeConversions.ToTimeSpan(consulOptions.WaitTime);
88 | }
89 | });
90 | });
91 | services.AddSingleton();
92 |
93 | services.AddSingleton();
94 |
95 | services.AddSingleton();
96 |
97 | if (lifecycle == null)
98 | {
99 | services.AddSingleton();
100 | }
101 | else
102 | {
103 | services.AddSingleton(lifecycle);
104 | }
105 |
106 | services.AddSingleton(p => p.GetService());
107 | }
108 |
109 | internal class ApplicationLifecycle : IDiscoveryLifecycle
110 | {
111 | public ApplicationLifecycle(IApplicationLifetime lifeCycle)
112 | {
113 | ApplicationStopping = lifeCycle.ApplicationStopping;
114 | }
115 |
116 | public CancellationToken ApplicationStopping { get; set; }
117 | }
118 | }
119 | }
--------------------------------------------------------------------------------
/Steeltoe.Extensions.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27703.2018
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{67E8AF17-1C29-4D0E-9E1B-AD94363BA700}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "discovery", "discovery", "{3B009DE0-3FE4-4F98-979B-58A630D3691E}"
9 | EndProject
10 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Discovery.ConsulBase", "src\discovery\Steeltoe.Discovery.ConsulBase\Steeltoe.Discovery.ConsulBase.csproj", "{135B3F3D-C0A8-471F-B2F0-BE36B026EFFA}"
11 | EndProject
12 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{13399DD8-B5A1-494A-B67B-14803BB0DC18}"
13 | EndProject
14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Discovery.ConsulBase.Test", "test\Steeltoe.Discovery.ConsulBase.Test\Steeltoe.Discovery.ConsulBase.Test.csproj", "{8519B808-8917-47D6-85F0-54DE1A849FBC}"
15 | EndProject
16 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{521252DE-906F-45AC-96EC-8B609A470245}"
17 | EndProject
18 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "discovery", "discovery", "{5B52BFEC-72C2-4262-AED1-7A0A70778F0C}"
19 | EndProject
20 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Time-Service", "samples\discovery\Time-Service\Time-Service.csproj", "{6E24B3FE-C1AA-4D23-9E8B-74C6A881D8C5}"
21 | EndProject
22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Time-UI", "samples\discovery\Time-UI\Time-UI.csproj", "{8EA0C49B-6F12-43E8-BB83-506FB96BCFBF}"
23 | EndProject
24 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "config", "config", "{09D7B600-14B4-4B79-AC91-84677B011669}"
25 | ProjectSection(SolutionItems) = preProject
26 | config\versions-dev.props = config\versions-dev.props
27 | config\versions-master.props = config\versions-master.props
28 | config\versions.props = config\versions.props
29 | EndProjectSection
30 | EndProject
31 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{A1E39C7B-535D-404A-9AF9-429E43288A32}"
32 | ProjectSection(SolutionItems) = preProject
33 | stylecop.json = stylecop.json
34 | versions.props = versions.props
35 | EndProjectSection
36 | EndProject
37 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Steeltoe.Discovery.Consul.Client", "src\discovery\Steeltoe.Discovery.Consul.Client\Steeltoe.Discovery.Consul.Client.csproj", "{EA4175BE-5724-4FBE-8EF8-453EDFCF5288}"
38 | EndProject
39 | Global
40 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
41 | Debug|Any CPU = Debug|Any CPU
42 | Release|Any CPU = Release|Any CPU
43 | EndGlobalSection
44 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
45 | {135B3F3D-C0A8-471F-B2F0-BE36B026EFFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
46 | {135B3F3D-C0A8-471F-B2F0-BE36B026EFFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
47 | {135B3F3D-C0A8-471F-B2F0-BE36B026EFFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
48 | {135B3F3D-C0A8-471F-B2F0-BE36B026EFFA}.Release|Any CPU.Build.0 = Release|Any CPU
49 | {8519B808-8917-47D6-85F0-54DE1A849FBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
50 | {8519B808-8917-47D6-85F0-54DE1A849FBC}.Debug|Any CPU.Build.0 = Debug|Any CPU
51 | {8519B808-8917-47D6-85F0-54DE1A849FBC}.Release|Any CPU.ActiveCfg = Release|Any CPU
52 | {8519B808-8917-47D6-85F0-54DE1A849FBC}.Release|Any CPU.Build.0 = Release|Any CPU
53 | {6E24B3FE-C1AA-4D23-9E8B-74C6A881D8C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
54 | {6E24B3FE-C1AA-4D23-9E8B-74C6A881D8C5}.Debug|Any CPU.Build.0 = Debug|Any CPU
55 | {6E24B3FE-C1AA-4D23-9E8B-74C6A881D8C5}.Release|Any CPU.ActiveCfg = Release|Any CPU
56 | {6E24B3FE-C1AA-4D23-9E8B-74C6A881D8C5}.Release|Any CPU.Build.0 = Release|Any CPU
57 | {8EA0C49B-6F12-43E8-BB83-506FB96BCFBF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
58 | {8EA0C49B-6F12-43E8-BB83-506FB96BCFBF}.Debug|Any CPU.Build.0 = Debug|Any CPU
59 | {8EA0C49B-6F12-43E8-BB83-506FB96BCFBF}.Release|Any CPU.ActiveCfg = Release|Any CPU
60 | {8EA0C49B-6F12-43E8-BB83-506FB96BCFBF}.Release|Any CPU.Build.0 = Release|Any CPU
61 | {EA4175BE-5724-4FBE-8EF8-453EDFCF5288}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
62 | {EA4175BE-5724-4FBE-8EF8-453EDFCF5288}.Debug|Any CPU.Build.0 = Debug|Any CPU
63 | {EA4175BE-5724-4FBE-8EF8-453EDFCF5288}.Release|Any CPU.ActiveCfg = Release|Any CPU
64 | {EA4175BE-5724-4FBE-8EF8-453EDFCF5288}.Release|Any CPU.Build.0 = Release|Any CPU
65 | EndGlobalSection
66 | GlobalSection(SolutionProperties) = preSolution
67 | HideSolutionNode = FALSE
68 | EndGlobalSection
69 | GlobalSection(NestedProjects) = preSolution
70 | {3B009DE0-3FE4-4F98-979B-58A630D3691E} = {67E8AF17-1C29-4D0E-9E1B-AD94363BA700}
71 | {135B3F3D-C0A8-471F-B2F0-BE36B026EFFA} = {3B009DE0-3FE4-4F98-979B-58A630D3691E}
72 | {8519B808-8917-47D6-85F0-54DE1A849FBC} = {13399DD8-B5A1-494A-B67B-14803BB0DC18}
73 | {5B52BFEC-72C2-4262-AED1-7A0A70778F0C} = {521252DE-906F-45AC-96EC-8B609A470245}
74 | {6E24B3FE-C1AA-4D23-9E8B-74C6A881D8C5} = {5B52BFEC-72C2-4262-AED1-7A0A70778F0C}
75 | {8EA0C49B-6F12-43E8-BB83-506FB96BCFBF} = {5B52BFEC-72C2-4262-AED1-7A0A70778F0C}
76 | {EA4175BE-5724-4FBE-8EF8-453EDFCF5288} = {3B009DE0-3FE4-4F98-979B-58A630D3691E}
77 | EndGlobalSection
78 | GlobalSection(ExtensibilityGlobals) = postSolution
79 | SolutionGuid = {5E5E5C4E-9DAF-453B-9629-5F2F62F0DAA0}
80 | EndGlobalSection
81 | EndGlobal
82 |
--------------------------------------------------------------------------------
/src/discovery/Steeltoe.Discovery.ConsulBase/Discovery/ConsulDiscoveryOptions.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2017 the original author or authors.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | namespace Steeltoe.Discovery.Consul.Discovery
16 | {
17 | public class ConsulDiscoveryOptions
18 | {
19 | public ConsulDiscoveryOptions()
20 | {
21 | Register = true;
22 | Deregister = true;
23 | RegisterHealthCheck = true;
24 | FailFast = true;
25 | HealthCheckPath = "/actuator/health";
26 | }
27 |
28 | ///
29 | /// Gets or sets Tags to use when registering service
30 | ///
31 | public string[] Tags { get; set; }
32 |
33 | ///
34 | /// Gets or sets Alternate server path to invoke for health checking
35 | ///
36 | public string HealthCheckPath { get; set; }
37 |
38 | ///
39 | /// Gets or sets Custom health check url to override default
40 | ///
41 | public string HealthCheckUrl { get; set; }
42 |
43 | ///
44 | /// Gets or sets How often to perform the health check (e.g. 10s), defaults to 10s.
45 | ///
46 | public string HealthCheckInterval { get; set; } = "10s";
47 |
48 | ///
49 | /// Gets or sets Timeout for health check (e.g. 10s).
50 | ///
51 | public string HealthCheckTimeout { get; set; } = "10s";
52 |
53 | ///
54 | /// Gets or sets Timeout to deregister services critical for longer than timeout(e.g. 30m).
55 | /// Requires consul version 7.x or higher.
56 | ///
57 | public string HealthCheckCriticalTimeout { get; set; } = "30m";
58 |
59 | ///
60 | /// Gets or sets IP address to use when accessing service (must also set preferIpAddress to use)
61 | ///
62 | public string IpAddress { get; set; }
63 |
64 | private string _hostName;
65 |
66 | ///
67 | /// Gets or sets Hostname to use when accessing server
68 | ///
69 | public string HostName
70 | {
71 | get => PreferIpAddress ? IpAddress : _hostName;
72 | set => _hostName = value;
73 | }
74 |
75 | ///
76 | /// Gets or sets Port to register the service under (defaults to listening port)
77 | ///
78 | public int? Port { get; set; }
79 |
80 | ///
81 | /// Gets or sets a value indicating whether gets or sets Use ip address rather than hostname
82 | /// during registration
83 | ///
84 | public bool PreferIpAddress { get; set; }
85 |
86 | ///
87 | /// Gets or sets Service name
88 | ///
89 | public string ServiceName { get; set; }
90 |
91 | ///
92 | /// Gets or sets Unique service instance id
93 | ///
94 | public string InstanceId { get; set; }
95 |
96 | private string _scheme = "http";
97 |
98 | ///
99 | /// Gets or sets Whether to register an http or https service
100 | ///
101 | public string Scheme
102 | {
103 | get => _scheme;
104 | set => _scheme = value?.ToLower();
105 | }
106 |
107 | ///
108 | /// Gets or sets Tag to query for in service list if one is not listed in serverListQueryTags.
109 | ///
110 | public string DefaultQueryTag { get; set; }
111 |
112 | ///
113 | /// Gets or sets a value indicating whether gets or sets Add the 'passing` parameter to
114 | /// /v1/health/service/serviceName. This pushes health check passing to the server.
115 | ///
116 | public bool QueryPassing { get; set; }
117 |
118 | ///
119 | /// Gets or sets a value indicating whether gets or sets Register as a service in consul.
120 | ///
121 | public bool Register { get; set; }
122 |
123 | ///
124 | /// Gets or sets a value indicating whether gets or sets Deregister automatic de-registration
125 | /// of service in consul.
126 | ///
127 | public bool Deregister { get; set; }
128 |
129 | ///
130 | /// Gets or sets a value indicating whether gets or sets RegisterHealthCheck in consul.
131 | /// Useful during development of a service.
132 | ///
133 | public bool RegisterHealthCheck { get; set; }
134 |
135 | ///
136 | /// Gets or sets a value indicating whether gets or sets FailFast Throw exceptions during
137 | /// service registration if true, otherwise, log warnings(defaults to true).
138 | ///
139 | public bool FailFast { get; set; }
140 | }
141 | }
--------------------------------------------------------------------------------
/src/discovery/Steeltoe.Discovery.Consul.Client/DiscoveryApplicationBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | // Copyright 2017 the original author or authors.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
4 | // in compliance with the License. You may obtain a copy of the License at
5 | //
6 | // http://www.apache.org/licenses/LICENSE-2.0
7 | //
8 | // Unless required by applicable law or agreed to in writing, software distributed under the License
9 | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10 | // or implied. See the License for the specific language governing permissions and limitations under
11 | // the License.
12 |
13 | using Consul;
14 | using Microsoft.AspNetCore.Builder;
15 | using Microsoft.Extensions.DependencyInjection;
16 | using Microsoft.Extensions.Options;
17 | using Steeltoe.Common.Discovery;
18 | using Steeltoe.Discovery.Consul.Discovery;
19 | using Steeltoe.Discovery.Consul.ServiceRegistry;
20 | using Steeltoe.Discovery.Consul.Util;
21 | using System;
22 |
23 | namespace Steeltoe.Discovery.Consul.Client
24 | {
25 | public static class DiscoveryApplicationBuilderExtensions
26 | {
27 | public static IApplicationBuilder UseDiscoveryClient(this IApplicationBuilder app)
28 | {
29 | var services = app.ApplicationServices;
30 |
31 | var consulDiscoveryOptions = services.GetService>()?.Value;
32 | var heartbeatOptions = services.GetService>()?.Value;
33 |
34 | if (consulDiscoveryOptions == null || !consulDiscoveryOptions.Register)
35 | {
36 | return app;
37 | }
38 |
39 | var registration = BuildRegistration(consulDiscoveryOptions, heartbeatOptions);
40 |
41 | var consulRegistration = new ConsulRegistration(registration, consulDiscoveryOptions);
42 |
43 | var consulServiceRegistry = services.GetRequiredService();
44 | consulServiceRegistry.RegisterAsync(consulRegistration).GetAwaiter().GetResult();
45 |
46 | if (consulDiscoveryOptions.Deregister)
47 | {
48 | var discoveryLifecycle = app.ApplicationServices.GetRequiredService();
49 | discoveryLifecycle.ApplicationStopping.Register(async () =>
50 | {
51 | await consulServiceRegistry.DeregisterAsync(consulRegistration);
52 | });
53 | }
54 |
55 | return app;
56 | }
57 |
58 | public static AgentServiceRegistration BuildRegistration(ConsulDiscoveryOptions options, HeartbeatOptions heartbeatOptions)
59 | {
60 | if (!options.Port.HasValue)
61 | {
62 | throw new ArgumentException("Port can not be empty.");
63 | }
64 |
65 | return new AgentServiceRegistration
66 | {
67 | Address = options.HostName,
68 | ID = options.InstanceId,
69 | Name = options.ServiceName,
70 | Port = options.Port.Value,
71 | Tags = options.Tags,
72 | Check = CreateCheck(options, heartbeatOptions)
73 | };
74 | }
75 |
76 | private static bool SetHeartbeat(AgentServiceCheck check, HeartbeatOptions heartbeatOptions)
77 | {
78 | if (!heartbeatOptions.Enable || heartbeatOptions.TtlValue <= 0 || string.IsNullOrEmpty(heartbeatOptions.TtlUnit))
79 | {
80 | return false;
81 | }
82 |
83 | check.Interval = null;
84 | check.HTTP = null;
85 |
86 | TimeSpan? ttl = DateTimeConversions.ToTimeSpan(heartbeatOptions.TtlValue + heartbeatOptions.TtlUnit);
87 | check.TTL = ttl;
88 |
89 | return true;
90 | }
91 |
92 | private static bool SetHttpCheck(AgentServiceCheck check, ConsulDiscoveryOptions options)
93 | {
94 | var healthCheckUrl = options.HealthCheckUrl;
95 |
96 | if (string.IsNullOrEmpty(healthCheckUrl))
97 | {
98 | var hostString = options.HostName;
99 | var port = options.Port;
100 | hostString += ":" + port;
101 |
102 | var healthCheckPath = options.HealthCheckPath;
103 | if (!healthCheckPath.StartsWith("/"))
104 | {
105 | healthCheckPath = "/" + healthCheckPath;
106 | }
107 |
108 | healthCheckUrl = $"{options.Scheme}://{hostString}{healthCheckPath}";
109 | }
110 |
111 | TimeSpan? interval = null;
112 |
113 | if (!string.IsNullOrWhiteSpace(options.HealthCheckInterval))
114 | {
115 | interval = DateTimeConversions.ToTimeSpan(options.HealthCheckInterval);
116 | }
117 |
118 | if (string.IsNullOrEmpty(healthCheckUrl) || interval == null)
119 | {
120 | return false;
121 | }
122 |
123 | check.HTTP = healthCheckUrl;
124 | check.Interval = interval;
125 |
126 | return true;
127 | }
128 |
129 | private static AgentServiceCheck CreateCheck(ConsulDiscoveryOptions options, HeartbeatOptions heartbeatOptions)
130 | {
131 | if (!options.RegisterHealthCheck)
132 | {
133 | return null;
134 | }
135 |
136 | TimeSpan? deregisterCriticalServiceAfter = null;
137 | TimeSpan? timeout = null;
138 |
139 | if (!string.IsNullOrWhiteSpace(options.HealthCheckTimeout))
140 | {
141 | timeout = DateTimeConversions.ToTimeSpan(options.HealthCheckTimeout);
142 | }
143 |
144 | if (!string.IsNullOrWhiteSpace(options.HealthCheckCriticalTimeout))
145 | {
146 | deregisterCriticalServiceAfter = DateTimeConversions.ToTimeSpan(options.HealthCheckCriticalTimeout);
147 | }
148 |
149 | var check = new AgentServiceCheck
150 | {
151 | Timeout = timeout,
152 | DeregisterCriticalServiceAfter = deregisterCriticalServiceAfter
153 | };
154 |
155 | if (heartbeatOptions.Enable)
156 | {
157 | return SetHeartbeat(check, heartbeatOptions) ? check : null;
158 | }
159 |
160 | return SetHttpCheck(check, options) ? check : null;
161 | }
162 | }
163 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015/2017 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # Visual Studio 2017 auto generated files
33 | Generated\ Files/
34 |
35 | # MSTest test Results
36 | [Tt]est[Rr]esult*/
37 | [Bb]uild[Ll]og.*
38 |
39 | # NUNIT
40 | *.VisualState.xml
41 | TestResult.xml
42 |
43 | # Build Results of an ATL Project
44 | [Dd]ebugPS/
45 | [Rr]eleasePS/
46 | dlldata.c
47 |
48 | # Benchmark Results
49 | BenchmarkDotNet.Artifacts/
50 |
51 | # .NET Core
52 | project.lock.json
53 | project.fragment.lock.json
54 | artifacts/
55 | **/Properties/launchSettings.json
56 |
57 | # StyleCop
58 | StyleCopReport.xml
59 |
60 | # Files built by Visual Studio
61 | *_i.c
62 | *_p.c
63 | *_i.h
64 | *.ilk
65 | *.meta
66 | *.obj
67 | *.iobj
68 | *.pch
69 | *.pdb
70 | *.ipdb
71 | *.pgc
72 | *.pgd
73 | *.rsp
74 | *.sbr
75 | *.tlb
76 | *.tli
77 | *.tlh
78 | *.tmp
79 | *.tmp_proj
80 | *.log
81 | *.vspscc
82 | *.vssscc
83 | .builds
84 | *.pidb
85 | *.svclog
86 | *.scc
87 |
88 | # Chutzpah Test files
89 | _Chutzpah*
90 |
91 | # Visual C++ cache files
92 | ipch/
93 | *.aps
94 | *.ncb
95 | *.opendb
96 | *.opensdf
97 | *.sdf
98 | *.cachefile
99 | *.VC.db
100 | *.VC.VC.opendb
101 |
102 | # Visual Studio profiler
103 | *.psess
104 | *.vsp
105 | *.vspx
106 | *.sap
107 |
108 | # Visual Studio Trace Files
109 | *.e2e
110 |
111 | # TFS 2012 Local Workspace
112 | $tf/
113 |
114 | # Guidance Automation Toolkit
115 | *.gpState
116 |
117 | # ReSharper is a .NET coding add-in
118 | _ReSharper*/
119 | *.[Rr]e[Ss]harper
120 | *.DotSettings.user
121 |
122 | # JustCode is a .NET coding add-in
123 | .JustCode
124 |
125 | # TeamCity is a build add-in
126 | _TeamCity*
127 |
128 | # DotCover is a Code Coverage Tool
129 | *.dotCover
130 |
131 | # AxoCover is a Code Coverage Tool
132 | .axoCover/*
133 | !.axoCover/settings.json
134 |
135 | # Visual Studio code coverage results
136 | *.coverage
137 | *.coveragexml
138 |
139 | # NCrunch
140 | _NCrunch_*
141 | .*crunch*.local.xml
142 | nCrunchTemp_*
143 |
144 | # MightyMoose
145 | *.mm.*
146 | AutoTest.Net/
147 |
148 | # Web workbench (sass)
149 | .sass-cache/
150 |
151 | # Installshield output folder
152 | [Ee]xpress/
153 |
154 | # DocProject is a documentation generator add-in
155 | DocProject/buildhelp/
156 | DocProject/Help/*.HxT
157 | DocProject/Help/*.HxC
158 | DocProject/Help/*.hhc
159 | DocProject/Help/*.hhk
160 | DocProject/Help/*.hhp
161 | DocProject/Help/Html2
162 | DocProject/Help/html
163 |
164 | # Click-Once directory
165 | publish/
166 |
167 | # Publish Web Output
168 | *.[Pp]ublish.xml
169 | *.azurePubxml
170 | # Note: Comment the next line if you want to checkin your web deploy settings,
171 | # but database connection strings (with potential passwords) will be unencrypted
172 | *.pubxml
173 | *.publishproj
174 |
175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
176 | # checkin your Azure Web App publish settings, but sensitive information contained
177 | # in these scripts will be unencrypted
178 | PublishScripts/
179 |
180 | # NuGet Packages
181 | *.nupkg
182 | # The packages folder can be ignored because of Package Restore
183 | **/[Pp]ackages/*
184 | # except build/, which is used as an MSBuild target.
185 | !**/[Pp]ackages/build/
186 | # Uncomment if necessary however generally it will be regenerated when needed
187 | #!**/[Pp]ackages/repositories.config
188 | # NuGet v3's project.json files produces more ignorable files
189 | *.nuget.props
190 | *.nuget.targets
191 |
192 | # Microsoft Azure Build Output
193 | csx/
194 | *.build.csdef
195 |
196 | # Microsoft Azure Emulator
197 | ecf/
198 | rcf/
199 |
200 | # Windows Store app package directories and files
201 | AppPackages/
202 | BundleArtifacts/
203 | Package.StoreAssociation.xml
204 | _pkginfo.txt
205 | *.appx
206 |
207 | # Visual Studio cache files
208 | # files ending in .cache can be ignored
209 | *.[Cc]ache
210 | # but keep track of directories ending in .cache
211 | !*.[Cc]ache/
212 |
213 | # Others
214 | ClientBin/
215 | ~$*
216 | *~
217 | *.dbmdl
218 | *.dbproj.schemaview
219 | *.jfm
220 | *.pfx
221 | *.publishsettings
222 | orleans.codegen.cs
223 |
224 | # Including strong name files can present a security risk
225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
226 | #*.snk
227 |
228 | # Since there are multiple workflows, uncomment next line to ignore bower_components
229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
230 | #bower_components/
231 |
232 | # RIA/Silverlight projects
233 | Generated_Code/
234 |
235 | # Backup & report files from converting an old project file
236 | # to a newer Visual Studio version. Backup files are not needed,
237 | # because we have git ;-)
238 | _UpgradeReport_Files/
239 | Backup*/
240 | UpgradeLog*.XML
241 | UpgradeLog*.htm
242 | ServiceFabricBackup/
243 | *.rptproj.bak
244 |
245 | # SQL Server files
246 | *.mdf
247 | *.ldf
248 | *.ndf
249 |
250 | # Business Intelligence projects
251 | *.rdl.data
252 | *.bim.layout
253 | *.bim_*.settings
254 | *.rptproj.rsuser
255 |
256 | # Microsoft Fakes
257 | FakesAssemblies/
258 |
259 | # GhostDoc plugin setting file
260 | *.GhostDoc.xml
261 |
262 | # Node.js Tools for Visual Studio
263 | .ntvs_analysis.dat
264 | node_modules/
265 |
266 | # Visual Studio 6 build log
267 | *.plg
268 |
269 | # Visual Studio 6 workspace options file
270 | *.opt
271 |
272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
273 | *.vbw
274 |
275 | # Visual Studio LightSwitch build output
276 | **/*.HTMLClient/GeneratedArtifacts
277 | **/*.DesktopClient/GeneratedArtifacts
278 | **/*.DesktopClient/ModelManifest.xml
279 | **/*.Server/GeneratedArtifacts
280 | **/*.Server/ModelManifest.xml
281 | _Pvt_Extensions
282 |
283 | # Paket dependency manager
284 | .paket/paket.exe
285 | paket-files/
286 |
287 | # FAKE - F# Make
288 | .fake/
289 |
290 | # JetBrains Rider
291 | .idea/
292 | *.sln.iml
293 |
294 | # CodeRush
295 | .cr/
296 |
297 | # Python Tools for Visual Studio (PTVS)
298 | __pycache__/
299 | *.pyc
300 |
301 | # Cake - Uncomment if you are using it
302 | # tools/**
303 | # !tools/packages.config
304 |
305 | # Tabs Studio
306 | *.tss
307 |
308 | # Telerik's JustMock configuration file
309 | *.jmconfig
310 |
311 | # BizTalk build output
312 | *.btp.cs
313 | *.btm.cs
314 | *.odx.cs
315 | *.xsd.cs
316 |
317 | # OpenCover UI analysis results
318 | OpenCover/
319 |
320 | # Azure Stream Analytics local run output
321 | ASALocalRun/
322 |
323 | # MSBuild Binary and Structured Log
324 | *.binlog
325 |
326 | # NVidia Nsight GPU debugger configuration file
327 | *.nvuser
328 |
329 | # MFractors (Xamarin productivity tool) working folder
330 | .mfractor/
331 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------