diff --git a/src/Tye.Core/BuildDockerImageStep.cs b/src/Tye.Core/BuildDockerImageStep.cs
index 95949c44..afd74813 100644
--- a/src/Tye.Core/BuildDockerImageStep.cs
+++ b/src/Tye.Core/BuildDockerImageStep.cs
@@ -29,6 +29,16 @@ namespace Tye
return;
}
+ if (!await DockerDetector.Instance.IsDockerInstalled.Value)
+ {
+ throw new CommandException($"Cannot generate a docker image for '{service.Service.Name}' because docker is not installed.");
+ }
+
+ if (!await DockerDetector.Instance.IsDockerConnectedToDaemon.Value)
+ {
+ throw new CommandException($"Cannot generate a docker image for '{service.Service.Name}' because docker is not running.");
+ }
+
await DockerContainerBuilder.BuildContainerImageAsync(output, application, service, project, container);
}
}
diff --git a/src/Tye.Core/DeployServiceYamlStep.cs b/src/Tye.Core/DeployServiceYamlStep.cs
index 65234765..e3e644c6 100644
--- a/src/Tye.Core/DeployServiceYamlStep.cs
+++ b/src/Tye.Core/DeployServiceYamlStep.cs
@@ -26,6 +26,16 @@ namespace Tye
return;
}
+ if (!await KubectlDetector.Instance.IsKubectlInstalled.Value)
+ {
+ throw new CommandException($"Cannot apply manifests for '{service.Service.Name}' because kubectl is not installed.");
+ }
+
+ if (!await KubectlDetector.Instance.IsKubectlConnectedToCluster.Value)
+ {
+ throw new CommandException($"Cannot apply manifests for '{service.Service.Name}' because kubectl is not connected to a cluster.");
+ }
+
using var tempFile = TempFile.Create();
output.WriteDebugLine($"Writing output to '{tempFile.FilePath}'.");
diff --git a/src/Tye.Core/Tye.Core.csproj b/src/Tye.Core/Tye.Core.csproj
index 4db4c849..ddb2ac90 100644
--- a/src/Tye.Core/Tye.Core.csproj
+++ b/src/Tye.Core/Tye.Core.csproj
@@ -10,6 +10,7 @@
+
@@ -24,8 +25,13 @@
-
-
+
+
+
+
+
+
+
diff --git a/src/Tye.Hosting/DockerRunner.cs b/src/Tye.Hosting/DockerRunner.cs
index 095dd069..69b82f9f 100644
--- a/src/Tye.Hosting/DockerRunner.cs
+++ b/src/Tye.Hosting/DockerRunner.cs
@@ -15,7 +15,6 @@ namespace Tye.Hosting
public class DockerRunner : IApplicationProcessor
{
private readonly ILogger _logger;
- private readonly Lazy> _dockerInstalled = new Lazy>(DetectDockerInstalled);
public DockerRunner(ILogger logger)
{
@@ -51,7 +50,7 @@ namespace Tye.Hosting
private async Task StartContainerAsync(Application application, Service service, DockerRunInfo docker)
{
- if (!await _dockerInstalled.Value)
+ if (!await DockerDetector.Instance.IsDockerInstalled.Value)
{
_logger.LogError("Unable to start docker container for service {ServiceName}, Docker is not installed.", service.Description.Name);
@@ -59,6 +58,14 @@ namespace Tye.Hosting
return;
}
+ if (!await DockerDetector.Instance.IsDockerConnectedToDaemon.Value)
+ {
+ _logger.LogError("Unable to start docker container for service {ServiceName}, Docker is not running.", service.Description.Name);
+
+ service.Logs.OnNext($"Unable to start docker container for service {service.Description.Name}, Docker is not running.");
+ return;
+ }
+
var serviceDescription = service.Description;
var environmentArguments = "";
diff --git a/src/Tye.Hosting/Tye.Hosting.csproj b/src/Tye.Hosting/Tye.Hosting.csproj
index 30066e2a..84851d4c 100644
--- a/src/Tye.Hosting/Tye.Hosting.csproj
+++ b/src/Tye.Hosting/Tye.Hosting.csproj
@@ -15,7 +15,10 @@
-
+
+
+
+
diff --git a/src/shared/DockerDetector.cs b/src/shared/DockerDetector.cs
new file mode 100644
index 00000000..294d6f44
--- /dev/null
+++ b/src/shared/DockerDetector.cs
@@ -0,0 +1,52 @@
+// 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;
+using System.Threading.Tasks;
+
+namespace Tye
+{
+ internal class DockerDetector
+ {
+ public static DockerDetector Instance { get; } = new DockerDetector();
+
+ private DockerDetector()
+ {
+ IsDockerInstalled = new Lazy>(DetectDockerInstalled);
+ IsDockerConnectedToDaemon = new Lazy>(DetectDockerConnectedToDaemon);
+ }
+
+ public Lazy> IsDockerInstalled { get; }
+
+ public Lazy> IsDockerConnectedToDaemon { get; }
+
+ private async Task DetectDockerInstalled()
+ {
+ try
+ {
+ await ProcessUtil.RunAsync("docker", "version", throwOnError: false);
+ return true;
+ }
+ catch (Exception)
+ {
+ // Unfortunately, process throws
+ return false;
+ }
+ }
+
+ private async Task DetectDockerConnectedToDaemon()
+ {
+ try
+ {
+ var result = await ProcessUtil.RunAsync("docker", "version", throwOnError: false);
+ return result.ExitCode == 0;
+ }
+ catch (Exception)
+ {
+ // Unfortunately, process throws
+ return false;
+ }
+ }
+ }
+}
diff --git a/src/shared/KubectlDetector.cs b/src/shared/KubectlDetector.cs
new file mode 100644
index 00000000..f968878b
--- /dev/null
+++ b/src/shared/KubectlDetector.cs
@@ -0,0 +1,54 @@
+// 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;
+using System.Threading.Tasks;
+
+namespace Tye
+{
+ internal class KubectlDetector
+ {
+ public static KubectlDetector Instance { get; } = new KubectlDetector();
+
+ private KubectlDetector()
+ {
+ IsKubectlInstalled = new Lazy>(DetectKubectlInstalled);
+ IsKubectlConnectedToCluster = new Lazy>(DetectKubectlConnectedToCluster);
+ }
+
+ public Lazy> IsKubectlInstalled { get; }
+
+ public Lazy> IsKubectlConnectedToCluster { get; }
+
+ private static async Task DetectKubectlInstalled()
+ {
+ try
+ {
+ // Ignoring the exit code and relying on Process to throw if kubectl is not found
+ // kubectl version will return non-zero if you're not connected to a cluster.
+ await ProcessUtil.RunAsync("kubectl", "version", throwOnError: false);
+ return true;
+ }
+ catch (Exception)
+ {
+ // Unfortunately, process throws
+ return false;
+ }
+ }
+
+ private static async Task DetectKubectlConnectedToCluster()
+ {
+ try
+ {
+ var result = await ProcessUtil.RunAsync("kubectl", "cluster-info", throwOnError: false);
+ return result.ExitCode == 0;
+ }
+ catch (Exception)
+ {
+ // Unfortunately, process throws
+ return false;
+ }
+ }
+ }
+}
diff --git a/src/tye/NameSanitizer.cs b/src/shared/NameSanitizer.cs
similarity index 100%
rename from src/tye/NameSanitizer.cs
rename to src/shared/NameSanitizer.cs
diff --git a/src/Tye.Hosting/Infrastructure/ProcessResult.cs b/src/shared/ProcessResult.cs
similarity index 96%
rename from src/Tye.Hosting/Infrastructure/ProcessResult.cs
rename to src/shared/ProcessResult.cs
index b83050e8..111bb87f 100644
--- a/src/Tye.Hosting/Infrastructure/ProcessResult.cs
+++ b/src/shared/ProcessResult.cs
@@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-namespace Tye.Hosting
+namespace Tye
{
internal class ProcessResult
{
diff --git a/src/Tye.Hosting/Infrastructure/ProcessUtil.cs b/src/shared/ProcessUtil.cs
similarity index 99%
rename from src/Tye.Hosting/Infrastructure/ProcessUtil.cs
rename to src/shared/ProcessUtil.cs
index 398314c0..3ac482bd 100644
--- a/src/Tye.Hosting/Infrastructure/ProcessUtil.cs
+++ b/src/shared/ProcessUtil.cs
@@ -11,7 +11,7 @@ using System.Threading;
using System.Threading.Tasks;
using Mono.Unix.Native;
-namespace Tye.Hosting
+namespace Tye
{
internal static class ProcessUtil
{
diff --git a/src/tye/TempDirectory.cs b/src/shared/TempDirectory.cs
similarity index 100%
rename from src/tye/TempDirectory.cs
rename to src/shared/TempDirectory.cs
diff --git a/src/tye/TempFile.cs b/src/shared/TempFile.cs
similarity index 100%
rename from src/tye/TempFile.cs
rename to src/shared/TempFile.cs
diff --git a/src/tye/Program.DeployCommand.cs b/src/tye/Program.DeployCommand.cs
index cd16a8ad..c75c13e4 100644
--- a/src/tye/Program.DeployCommand.cs
+++ b/src/tye/Program.DeployCommand.cs
@@ -161,6 +161,16 @@ namespace Tye
await ApplicationYamlWriter.WriteAsync(output, writer, application);
}
+ if (!await KubectlDetector.Instance.IsKubectlInstalled.Value)
+ {
+ throw new CommandException($"Cannot apply manifests because kubectl is not installed.");
+ }
+
+ if (!await KubectlDetector.Instance.IsKubectlConnectedToCluster.Value)
+ {
+ throw new CommandException($"Cannot apply manifests because kubectl is not connected to a cluster.");
+ }
+
output.WriteDebugLine("Running 'kubectl apply'.");
output.WriteCommandLine("kubectl", $"apply -f \"{tempFile.FilePath}\"");
var capture = output.Capture();
diff --git a/src/tye/tye.csproj b/src/tye/tye.csproj
index cb91d831..6c3225fb 100644
--- a/src/tye/tye.csproj
+++ b/src/tye/tye.csproj
@@ -10,12 +10,23 @@
true
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/E2ETest/E2ETest.csproj b/test/E2ETest/E2ETest.csproj
index 907ebb91..e3b3106e 100644
--- a/test/E2ETest/E2ETest.csproj
+++ b/test/E2ETest/E2ETest.csproj
@@ -9,7 +9,7 @@
-
+