Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Kubernetes HealthChecks Operator #418

Merged
merged 23 commits into from
Mar 11, 2020
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
0d5171e
Initial healthchecks kubernetes operator scaffolding
CarlosLanderas Jan 8, 2020
d99a504
Add Deployment and Service creation
CarlosLanderas Jan 30, 2020
1b2bf23
Implement controller delete for deployment and service
CarlosLanderas Jan 30, 2020
f04318b
Add deployment and services handlers
CarlosLanderas Jan 30, 2020
0662556
[WIP] Docker UI image hc push service
CarlosLanderas Jan 30, 2020
c27c050
Docker UI image push service
CarlosLanderas Jan 31, 2020
a48d68f
Discover UI Deployment service in push service
CarlosLanderas Jan 31, 2020
797f21b
Implement push service update feature
CarlosLanderas Jan 31, 2020
9101ac3
Propagate cancellation token and gracefully shutdown
CarlosLanderas Jan 31, 2020
e0eb3fa
SecretsHandler, Secure PushService and UI endpoint management updates
CarlosLanderas Jan 31, 2020
e198b75
Implement ILogger in all operator classes
CarlosLanderas Jan 31, 2020
d45614d
[WIP] Add initial Healthcheck custom resource definition
CarlosLanderas Feb 6, 2020
ea2491f
Dependencies.props and version for operator csproj
CarlosLanderas Feb 9, 2020
5278470
Set operator beta version in dependencies.props
CarlosLanderas Feb 13, 2020
bc7fde2
Remove warnings
CarlosLanderas Feb 13, 2020
02fdc2a
Update deployment yaml pull policy
CarlosLanderas Feb 13, 2020
1abb74d
Add docker k8s operator build and publish ps1 [skip ci]
CarlosLanderas Feb 13, 2020
a097217
Code review improvements
CarlosLanderas Mar 11, 2020
43eab8c
Add LoadBalancer and Nodeport address translation
CarlosLanderas Mar 11, 2020
220f4f7
Consider crd default scheme, annotations and default
CarlosLanderas Mar 11, 2020
b9d5db0
Remove duplicated package version
CarlosLanderas Mar 11, 2020
df687ef
Merge master and resolve conflicts
CarlosLanderas Mar 11, 2020
bb4f81f
Fix solution post merge
CarlosLanderas Mar 11, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/azds.yaml
**/bin
**/charts
**/docker-compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
17 changes: 17 additions & 0 deletions AspNetCore.Diagnostics.HealthChecks.sln
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HealthChecks.UI.Branding",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HealthChecks.Azure.IoTHub", "src\HealthChecks.Azure.IoTHub\HealthChecks.Azure.IoTHub.csproj", "{252BB504-B7CB-4581-8CD8-D7398CAA16F5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HealthChecks.UI.K8s.Operator", "src\HealthChecks.UI.K8s.Operator\HealthChecks.UI.K8s.Operator.csproj", "{692313D3-E947-494A-83B7-754E2FFAF348}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HealthChecks.UI.Image", "build\docker-images\HealthChecks.UI.Image\HealthChecks.UI.Image.csproj", "{737E4FD6-EA77-4608-A20F-767557FE3190}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docker-image", "docker-image", "{95119F6F-87C8-45B8-8D95-61736FBEBEDE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -282,6 +288,14 @@ Global
{252BB504-B7CB-4581-8CD8-D7398CAA16F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{252BB504-B7CB-4581-8CD8-D7398CAA16F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{252BB504-B7CB-4581-8CD8-D7398CAA16F5}.Release|Any CPU.Build.0 = Release|Any CPU
{692313D3-E947-494A-83B7-754E2FFAF348}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{692313D3-E947-494A-83B7-754E2FFAF348}.Debug|Any CPU.Build.0 = Debug|Any CPU
{692313D3-E947-494A-83B7-754E2FFAF348}.Release|Any CPU.ActiveCfg = Release|Any CPU
{692313D3-E947-494A-83B7-754E2FFAF348}.Release|Any CPU.Build.0 = Release|Any CPU
{737E4FD6-EA77-4608-A20F-767557FE3190}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{737E4FD6-EA77-4608-A20F-767557FE3190}.Debug|Any CPU.Build.0 = Debug|Any CPU
{737E4FD6-EA77-4608-A20F-767557FE3190}.Release|Any CPU.ActiveCfg = Release|Any CPU
{737E4FD6-EA77-4608-A20F-767557FE3190}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -329,6 +343,9 @@ Global
{18F9E412-646D-4751-9751-30AA7A0233DF} = {2A3FD988-2BB8-43CF-B3A2-B70E648259D4}
{B526834E-9392-4749-BAB2-7DF579F8F418} = {092533AB-7505-4EDC-8932-D40BF575D0D2}
{252BB504-B7CB-4581-8CD8-D7398CAA16F5} = {2A3FD988-2BB8-43CF-B3A2-B70E648259D4}
{692313D3-E947-494A-83B7-754E2FFAF348} = {2A3FD988-2BB8-43CF-B3A2-B70E648259D4}
{95119F6F-87C8-45B8-8D95-61736FBEBEDE} = {A5A8CE48-FF38-4A49-9E59-0EC1FC4474C0}
{737E4FD6-EA77-4608-A20F-767557FE3190} = {95119F6F-87C8-45B8-8D95-61736FBEBEDE}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2B8C62A1-11B6-469F-874C-A02443256568}
Expand Down
39 changes: 39 additions & 0 deletions build-operator-image.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
Param(
[parameter(Mandatory = $false)][bool]$PublishToDockerHub = $false
)


function Exec {
[CmdletBinding()]
param(
[Parameter(Position = 0, Mandatory = 1)][scriptblock]$cmd,
[Parameter(Position = 1, Mandatory = 0)][string]$errorMessage = ($msgs.error_bad_command -f $cmd)
)
& $cmd
if ($lastexitcode -ne 0) {
throw ("Exec: " + $errorMessage)
}
}

#Select the UI version from dependencies.props and use it as image version


$version = select-xml -Path .\build\dependencies.props -XPath "/Project/PropertyGroup[contains(@Label,'Health Checks Package Versions')]/HealthChecksUIK8sOperator"

$tag = $version.node.InnerXML

#Building docker image

echo "Building k8s operator docker image with tag: $tag"
echo "Publishing to Docker Hub : $PublishToDockerHub"

exec { & docker build . -f .\src\HealthChecks.UI.K8s.Operator\Dockerfile -t xabarilcoding/healthchecksui-k8s-operator:$tag }
exec { & docker tag xabarilcoding/healthchecksui-k8s-operator:$tag xabarilcoding/healthchecksui-k8s-operator:latest }

echo "Created docker image healthchecksui-k8s-operator:$tag. You can execute this image using docker run"

#Publish it
if ($PublishToDockerHub) {
docker push xabarilcoding/healthchecksui-k8s-operator:$tag
docker push xabarilcoding/healthchecksui-k8s-operator:latest
}
3 changes: 3 additions & 0 deletions build/dependencies.props
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
<MicrosoftAspNetCoreSignalRClient>3.0.0</MicrosoftAspNetCoreSignalRClient>
<MicrosoftAspNetCoreSignalR>1.0.0</MicrosoftAspNetCoreSignalR>
<MicrosoftSourceLinkGithub>1.0.0</MicrosoftSourceLinkGithub>
<MicrosoftExtensionsLoggingConsole>3.0.0</MicrosoftExtensionsLoggingConsole>
<FluentAssertions>5.9.0</FluentAssertions>
<xunit>2.4.1</xunit>
<xunitrunnervisualstudio>2.4.1</xunitrunnervisualstudio>
Expand Down Expand Up @@ -72,6 +73,7 @@
<KubernetesClient>1.5.25</KubernetesClient>
<DogStatsDCSharpClient>3.3.0</DogStatsDCSharpClient>
<MicrosoftAzureDevices>1.18.1</MicrosoftAzureDevices>
<KubernetesClient>1.6.10</KubernetesClient>
CarlosLanderas marked this conversation as resolved.
Show resolved Hide resolved
</PropertyGroup>

<PropertyGroup Label="CLI Tools Versions">
Expand Down Expand Up @@ -115,5 +117,6 @@
<HealthCheckSignalR>3.0.0</HealthCheckSignalR>
<HealthCheckCloudFirestore>3.0.0</HealthCheckCloudFirestore>
<HealthCheckIoTHub>3.0.0</HealthCheckIoTHub>
<HealthChecksUIK8sOperator>3.0.0-beta.1</HealthChecksUIK8sOperator>
</PropertyGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace HealthChecks.UI.Image.Configuration
{
public class PushServiceKeys
{
public const string Enabled = "enable_push_endpoint";
public const string PushEndpointSecret = "push_endpoint_secret";
public const int ServiceAdded = 0;
public const int ServiceUpdated = 1;
public const int ServiceRemoved = 2;
public const string AuthParameter = "key";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using HealthChecks.UI.Image.Configuration;
using Microsoft.AspNetCore.Http;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace HealthChecks.UI.Image.Extensions
{
public static class HttpRequestExtensions
{
public static bool IsAuthenticated(this HttpRequest request)
{
return request.Query.ContainsKey(PushServiceKeys.AuthParameter) &&
request.Query[PushServiceKeys.AuthParameter] == Environment.GetEnvironmentVariable(PushServiceKeys.PushEndpointSecret);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,78 @@
using HealthChecks.UI.Image.Configuration;
using HealthChecks.UI.Image;
using HealthChecks.UI.Image.Configuration;
using HealthChecks.UI.Image.Extensions;
using Microsoft.AspNetCore.Builder;
using HealthChecks.UI.Image.PushService;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.IO;
using System.Text.Json;

namespace Microsoft.AspNetCore.Builder
{
public static class IEndpointRouteBuilderExtensions
{
public static IEndpointConventionBuilder MapHealthChecksUI(this IEndpointRouteBuilder builder, IConfiguration configuration)
{
public static IEndpointConventionBuilder MapHealthChecksUI(this IEndpointRouteBuilder builder,
IConfiguration configuration)
{
if (bool.TryParse(configuration[PushServiceKeys.Enabled], out bool enabled) && enabled)
{
builder.MapHealthCheckPushEndpoint(configuration);
}

return builder.MapHealthChecksUI(setup =>
{
setup.ConfigureStylesheet(configuration);
setup.ConfigurePaths(configuration);

});
}
private static void MapHealthCheckPushEndpoint(this IEndpointRouteBuilder builder,
IConfiguration configuration)
{

var logger = builder.ServiceProvider.GetRequiredService<ILogger<Program>>();
logger.LogInformation("HealthChecks Push Endpoint Enabled");

builder.MapPost("/healthchecks/push", async context =>
{
if (context.Request.IsAuthenticated())
{
using var streamReader = new StreamReader(context.Request.Body);
var content = await streamReader.ReadToEndAsync();

var endpoint = JsonDocument.Parse(content);
var root = endpoint.RootElement;
var name = root.GetProperty("name").GetString();
var uri = root.GetProperty("uri").GetString();
var type = root.GetProperty("type").GetInt16();

if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(uri))
{
var pushService = context.RequestServices.GetService<HealthChecksPushService>();

if (type == PushServiceKeys.ServiceAdded)
{
await pushService.AddAsync(name, uri);
}
else if (type == PushServiceKeys.ServiceRemoved)
{
await pushService.RemoveAsync(name);
}
else if (type == PushServiceKeys.ServiceUpdated)
{
await pushService.UpdateAsync(name, uri);
}
}
}
else
{
context.Response.StatusCode = 401;
}

});
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
</PropertyGroup>

<ItemGroup>
<Folder Include="Properties\" />
<Folder Include="wwwroot\" />
</ItemGroup>

Expand Down
6 changes: 5 additions & 1 deletion build/docker-images/HealthChecks.UI.Image/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,11 @@ public static void Main(string[] args)

public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
return WebHost.CreateDefaultBuilder(args)
.ConfigureLogging(config =>
{
config.AddFilter(typeof(Program).Namespace, LogLevel.Information);
})
.ConfigureAppConfiguration(config =>
{
if (AzureAppConfiguration.Enabled)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using HealthChecks.UI.Core.Data;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace HealthChecks.UI.Image.PushService
{
internal class HealthChecksPushService
{
private readonly HealthChecksDb _db;
private readonly ILogger<HealthChecksPushService> _logger;

public HealthChecksPushService(HealthChecksDb db, ILogger<HealthChecksPushService> logger)
{
_db = db ?? throw new ArgumentNullException(nameof(db));
_logger = logger ?? throw new ArgumentException(nameof(logger));
}
public async Task AddAsync(string name, string uri)
{
if ((await Get(name)) == null)
{
await _db.Configurations.AddAsync(new HealthCheckConfiguration
{
Name = name,
Uri = uri,
DiscoveryService = "kubernetes"
});

await _db.SaveChangesAsync();

_logger.LogInformation("[Push] New service added: {name} with uri: {uri}", name, uri);
}
}

public async Task RemoveAsync(string name)
{
var endpoint = await Get(name);

if (endpoint != null)
{
_db.Configurations.Remove(endpoint);
await _db.SaveChangesAsync();

_logger.LogInformation("[Push] Service removed: {name}", name);
}
}

public async Task UpdateAsync(string name, string uri)
{
var endpoint = await Get(name);

if (endpoint != null)
{
endpoint.Uri = uri;
_db.Configurations.Update(endpoint);
await _db.SaveChangesAsync();

_logger.LogInformation("[Push] Service updated: {name} with uri {uri}", name, uri);
}
}

private Task<HealthCheckConfiguration> Get(string name)
{
return _db.Configurations.FirstOrDefaultAsync(c => c.Name == name);
}
}
}
18 changes: 14 additions & 4 deletions build/docker-images/HealthChecks.UI.Image/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using HealthChecks.UI.Image.Configuration;
using HealthChecks.UI.Image.PushService;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

using System;

namespace HealthChecks.UI.Image
{
public class Startup
Expand All @@ -17,12 +19,20 @@ public Startup(IConfiguration configuration)

public void ConfigureServices(IServiceCollection services)
{
services
services
.AddHealthChecksUI()
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0);

if (bool.TryParse(Configuration[PushServiceKeys.Enabled], out bool enabled) && enabled)
{
if(string.IsNullOrEmpty(Configuration[PushServiceKeys.PushEndpointSecret]))
{
throw new Exception($"{PushServiceKeys.PushEndpointSecret} environment variable has not been configured");
}
services.AddTransient<HealthChecksPushService>();
}
}

public void Configure(IApplicationBuilder app)
{
app.UseRouting()
Expand Down
Loading