Skip to content
This repository has been archived by the owner on Nov 20, 2023. It is now read-only.

Commit

Permalink
Adding health checks (#432)
Browse files Browse the repository at this point in the history
  • Loading branch information
areller authored May 20, 2020
1 parent 87cd4f4 commit f27905b
Show file tree
Hide file tree
Showing 49 changed files with 2,337 additions and 70 deletions.
27 changes: 27 additions & 0 deletions src/Microsoft.Tye.Core/ApplicationFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ public static async Task<ApplicationBuilder> CreateAsync(OutputContext output, F
}
project.Replicas = configService.Replicas ?? 1;

project.Liveness = configService.Liveness != null ? GetProbeBuilder(configService.Liveness) : null;
project.Readiness = configService.Readiness != null ? GetProbeBuilder(configService.Readiness) : null;

// We don't apply more container defaults here because we might need
// to prompt for the registry name.
project.ContainerInfo = new ContainerInfo() { UseMultiphaseDockerfile = false, };
Expand All @@ -117,6 +120,9 @@ public static async Task<ApplicationBuilder> CreateAsync(OutputContext output, F
DockerFileContext = GetDockerFileContext(source, configService)
};
service = container;

container.Liveness = configService.Liveness != null ? GetProbeBuilder(configService.Liveness) : null;
container.Readiness = configService.Readiness != null ? GetProbeBuilder(configService.Readiness) : null;
}
else if (!string.IsNullOrEmpty(configService.Executable))
{
Expand All @@ -139,6 +145,9 @@ public static async Task<ApplicationBuilder> CreateAsync(OutputContext output, F
Replicas = configService.Replicas ?? 1
};
service = executable;

executable.Liveness = configService.Liveness != null ? GetProbeBuilder(configService.Liveness) : null;
executable.Readiness = configService.Readiness != null ? GetProbeBuilder(configService.Readiness) : null;
}
else if (!string.IsNullOrEmpty(configService.Include))
{
Expand Down Expand Up @@ -379,5 +388,23 @@ private static void AddToRootServices(ApplicationBuilder root, HashSet<string> d
}
}
}

private static ProbeBuilder GetProbeBuilder(ConfigProbe config) => new ProbeBuilder()
{
Http = config.Http != null ? GetHttpProberBuilder(config.Http) : null,
InitialDelay = config.InitialDelay,
Period = config.Period,
Timeout = config.Timeout,
SuccessThreshold = config.SuccessThreshold,
FailureThreshold = config.FailureThreshold
};

private static HttpProberBuilder GetHttpProberBuilder(ConfigHttpProber config) => new HttpProberBuilder()
{
Path = config.Path,
Headers = config.Headers,
Port = config.Port,
Protocol = config.Protocol
};
}
}
15 changes: 15 additions & 0 deletions src/Microsoft.Tye.Core/ConfigModel/ConfigApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,21 @@ public void Validate()
string.Join(Environment.NewLine, results.Select(r => r.ErrorMessage)));
}
}

var probes = new[] { (Name: "liveness", Probe: service.Liveness), (Name: "readiness", Probe: service.Readiness) }.Where(p => p.Probe != null).ToArray();
foreach (var probe in probes)
{
if (probe.Name == "liveness" && probe.Probe.SuccessThreshold != 1)
{
throw new TyeYamlException(CoreStrings.FormatSuccessThresholdMustBeOne(probe.Name));
}

// right now only http is supported, so it must be set
if (probe.Probe!.Http == null)
{
throw new TyeYamlException(CoreStrings.FormatProberRequired(probe.Name));
}
}
}

foreach (var ingress in config.Ingress)
Expand Down
18 changes: 18 additions & 0 deletions src/Microsoft.Tye.Core/ConfigModel/ConfigHttpProber.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Net.Http.Headers;

namespace Microsoft.Tye.ConfigModel
{
public class ConfigHttpProber
{
[Required] public string Path { get; set; } = default!;
public int? Port { get; set; }
public string? Protocol { get; set; }
public List<KeyValuePair<string, object>> Headers { get; set; } = new List<KeyValuePair<string, object>>();
}
}
16 changes: 16 additions & 0 deletions src/Microsoft.Tye.Core/ConfigModel/ConfigProbe.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace Microsoft.Tye.ConfigModel
{
public class ConfigProbe
{
public ConfigHttpProber? Http { get; set; }
public int InitialDelay { get; set; } = 0;
public int Period { get; set; } = 1;
public int Timeout { get; set; } = 1;
public int SuccessThreshold { get; set; } = 1;
public int FailureThreshold { get; set; } = 3;
}
}
2 changes: 2 additions & 0 deletions src/Microsoft.Tye.Core/ConfigModel/ConfigService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,7 @@ public class ConfigService
[YamlMember(Alias = "env")]
public List<ConfigConfigurationSource> Configuration { get; set; } = new List<ConfigConfigurationSource>();
public List<BuildProperty> BuildProperties { get; set; } = new List<BuildProperty>();
public ConfigProbe? Liveness { get; set; }
public ConfigProbe? Readiness { get; set; }
}
}
4 changes: 4 additions & 0 deletions src/Microsoft.Tye.Core/ContainerServiceBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,9 @@ public ContainerServiceBuilder(string name, string image)
public List<EnvironmentVariableBuilder> EnvironmentVariables { get; } = new List<EnvironmentVariableBuilder>();

public List<VolumeBuilder> Volumes { get; } = new List<VolumeBuilder>();

public ProbeBuilder? Liveness { get; set; }

public ProbeBuilder? Readiness { get; set; }
}
}
11 changes: 10 additions & 1 deletion src/Microsoft.Tye.Core/CoreStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,12 @@
<data name="MultipleBindingWithSamePort" xml:space="preserve">
<value>Cannot have multiple {0} bindings with the same port.</value>
</data>
<data name="ProberRequired" xml:space="preserve">
<value>A prober must be configured for the {0} probe.</value>
</data>
<data name="SuccessThresholdMustBeOne" xml:space="preserve">
<value>"successThreshold" for {0} probe must be set to "1".</value>
</data>
<data name="MustBeABoolean" xml:space="preserve">
<value>"{value}" must be a boolean value (true/false).</value>
</data>
Expand All @@ -150,6 +156,9 @@
<data name="MustBePositive" xml:space="preserve">
<value>"{value}" value cannot be negative.</value>
</data>
<data name="MustBeGreaterThanZero" xml:space="preserve">
<value>"{value}" value must be greater than zero.</value>
</data>
<data name="ProjectImageExecutableExclusive" xml:space="preserve">
<value>Cannot have both "{0}" and "{1}" set for a service. Only one of project, image, and executable can be set for a given service.</value>
</data>
Expand All @@ -162,4 +171,4 @@
<data name="UnrecognizedKey" xml:space="preserve">
<value>Unexpected key "{key}" in tye.yaml.</value>
</data>
</root>
</root>
4 changes: 4 additions & 0 deletions src/Microsoft.Tye.Core/ExecutableServiceBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,9 @@ public ExecutableServiceBuilder(string name, string executable)
public int Replicas { get; set; } = 1;

public List<EnvironmentVariableBuilder> EnvironmentVariables { get; } = new List<EnvironmentVariableBuilder>();

public ProbeBuilder? Liveness { get; set; }

public ProbeBuilder? Readiness { get; set; }
}
}
16 changes: 16 additions & 0 deletions src/Microsoft.Tye.Core/HttpProberBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.Generic;

namespace Microsoft.Tye
{
public class HttpProberBuilder
{
public string Path { get; set; } = default!;
public int? Port { get; set; }
public string? Protocol { get; set; }
public List<KeyValuePair<string, object>> Headers { get; set; } = new List<KeyValuePair<string, object>>();
}
}
68 changes: 68 additions & 0 deletions src/Microsoft.Tye.Core/KubernetesManifestGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,16 @@ public static KubernetesDeploymentOutput CreateDeployment(
}
}
}

if (project.Liveness != null)
{
AddProbe(project, container, project.Liveness!, "livenessProbe");
}

if (project.Readiness != null)
{
AddProbe(project, container, project.Readiness!, "readinessProbe");
}
}

foreach (var sidecar in project.Sidecars)
Expand Down Expand Up @@ -452,6 +462,64 @@ public static KubernetesDeploymentOutput CreateDeployment(
return new KubernetesDeploymentOutput(project.Name, new YamlDocument(root));
}

private static void AddProbe(ServiceBuilder service, YamlMappingNode container, ProbeBuilder builder, string name)
{
var probe = new YamlMappingNode();
container.Add(name, probe);

if (builder.Http != null)
{
var builderHttp = builder.Http;
var httpGet = new YamlMappingNode();

probe.Add("httpGet", httpGet);
httpGet.Add("path", builderHttp.Path);

if (builderHttp.Protocol != null)
{
httpGet.Add("scheme", builderHttp.Protocol.ToUpper());
}

if (builderHttp.Port != null)
{
httpGet.Add("port", builderHttp.Port.ToString()!);
}
else
{
// If port is not given, we pull default port
var binding = service.Bindings.First(b => builderHttp.Protocol == null || b.Protocol == builderHttp.Protocol);
if (binding.Port != null)
{
httpGet.Add("port", binding.Port.Value.ToString());
}

if (builderHttp.Protocol == null && binding.Protocol != null)
{
httpGet.Add("scheme", binding.Protocol.ToUpper());
}
}

if (builderHttp.Headers.Count > 0)
{
var headers = new YamlSequenceNode();
httpGet.Add("httpHeaders", headers);

foreach (var builderHeader in builderHttp.Headers)
{
var header = new YamlMappingNode();
header.Add("name", builderHeader.Key);
header.Add("value", builderHeader.Value.ToString()!);
headers.Add(header);
}
}
}

probe.Add("initialDelaySeconds", builder.InitialDelay.ToString());
probe.Add("periodSeconds", builder.Period.ToString()!);
probe.Add("successThreshold", builder.SuccessThreshold.ToString()!);
probe.Add("failureThreshold", builder.FailureThreshold.ToString()!);
}

private static void AddEnvironmentVariablesForComputedBindings(YamlSequenceNode env, ComputedBindings bindings)
{
foreach (var binding in bindings.Bindings.OfType<EnvironmentVariableInputBinding>())
Expand Down
16 changes: 16 additions & 0 deletions src/Microsoft.Tye.Core/ProbeBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace Microsoft.Tye
{
public class ProbeBuilder
{
public HttpProberBuilder? Http { get; set; }
public int InitialDelay { get; set; }
public int Period { get; set; }
public int Timeout { get; set; }
public int SuccessThreshold { get; set; }
public int FailureThreshold { get; set; }
}
}
4 changes: 4 additions & 0 deletions src/Microsoft.Tye.Core/ProjectServiceBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,9 @@ public ProjectServiceBuilder(string name, FileInfo projectFile)
public Dictionary<string, string> BuildProperties { get; } = new Dictionary<string, string>();

public List<SidecarBuilder> Sidecars { get; } = new List<SidecarBuilder>();

public ProbeBuilder? Liveness { get; set; }

public ProbeBuilder? Readiness { get; set; }
}
}
Loading

0 comments on commit f27905b

Please sign in to comment.