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

Update to Kubernetes SDK 14 #931

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Octopus.Tentacle.CommonTestUtils;
using Octopus.Tentacle.Contracts.Observability;
using Octopus.Tentacle.Kubernetes.Tests.Integration.Setup;
using Octopus.Tentacle.Kubernetes.Tests.Integration.Setup.Tooling;
using Octopus.Tentacle.Tests.Integration.Common.Builders.Decorators;
using Octopus.Tentacle.Tests.Integration.Common.Logging;

Expand All @@ -27,6 +28,7 @@ public abstract class KubernetesAgentIntegrationTest
protected CancellationToken CancellationToken { get; private set; }

protected TentacleServiceDecoratorBuilder? TentacleServiceDecoratorBuilder { get; set; }
protected KubeCtlTool KubeCtl { get; private set; }

[OneTimeSetUp]
public async Task OneTimeSetUp()
Expand All @@ -38,6 +40,13 @@ public async Task OneTimeSetUp()
KubernetesTestsGlobalContext.Instance.KubeConfigPath,
KubernetesTestsGlobalContext.Instance.Logger);

KubeCtl = new KubeCtlTool(
KubernetesTestsGlobalContext.Instance.TemporaryDirectory,
KubernetesTestsGlobalContext.Instance.KubeCtlExePath,
KubernetesTestsGlobalContext.Instance.KubeConfigPath,
kubernetesAgentInstaller.Namespace,
KubernetesTestsGlobalContext.Instance.Logger);

//create a new server halibut runtime
var listeningPort = BuildServerHalibutRuntimeAndListen();

Expand All @@ -49,6 +58,7 @@ public async Task OneTimeSetUp()
BuildTentacleClient(thumbprint);
}


[SetUp]
public void SetUp()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ public async Task OneTimeSetUp()
await installer.Install();

//if we are not running in TeamCity, then we need to find the latest local tag and use that if it exists
if (!TeamCityDetection.IsRunningInTeamCity())
if (!TeamCityDetection.IsRunningInTeamCity() && bool.TryParse(Environment.GetEnvironmentVariable("KubernetesAgentTests_UseLocalImage"), out var useLocal) && useLocal)
{
var imageLoader = new DockerImageLoader(KubernetesTestsGlobalContext.Instance.TemporaryDirectory, KubernetesTestsGlobalContext.Instance.Logger, kindExePath);
KubernetesTestsGlobalContext.Instance.TentacleImageAndTag = imageLoader.LoadMostRecentImageIntoKind(installer.ClusterName);
}
else
else if(TeamCityDetection.IsRunningInTeamCity())
APErebus marked this conversation as resolved.
Show resolved Hide resolved
{
var tag = Environment.GetEnvironmentVariable("KubernetesAgentTests_ImageTag");
KubernetesTestsGlobalContext.Instance.TentacleImageAndTag = $"docker.packages.octopushq.com/octopusdeploy/kubernetes-tentacle:{tag}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,66 @@ Task ScriptCompleted(CancellationToken ct)
return Task.CompletedTask;
}
}

[Test]
public async Task TentaclePodIsTerminatedDuringScriptExecution_ShouldRestartAndPickUpPodStatus()
{
// Arrange
var logs = new List<ProcessOutput>();
var scriptCompleted = false;
var count = 50;
var scriptBody = $@"echo ""Hello World""
for i in $(seq 1 {50});
do
echo Count: $i
sleep 0.1
done
";
var command = new ExecuteKubernetesScriptCommandBuilder(LoggingUtils.CurrentTestHash())
.WithScriptBody(scriptBody)
.Build();

var commandResult = await KubeCtl.ExecuteNamespacedCommand("get pods -l app.kubernetes.io/name=octopus-agent -o \"Name\"");
var initialPodName = commandResult.StdOut.Single();

//act
var scriptTask = TentacleClient.ExecuteScript(command, StatusReceived, ScriptCompleted, new InMemoryLog(), CancellationToken.None);
var killTask = KubeCtl.ExecuteNamespacedCommand("delete pods -l app.kubernetes.io/name=octopus-agent");

await Task.WhenAll(scriptTask, killTask);

var result = scriptTask.Result;

commandResult = await KubeCtl.ExecuteNamespacedCommand("get pods -l app.kubernetes.io/name=octopus-agent -o \"Name\"");
var finalPodName = commandResult.StdOut.Single();

//Assert
logs.Should().Contain(po => po.Source == ProcessOutputSource.StdOut && po.Text == "Hello World");
APErebus marked this conversation as resolved.
Show resolved Hide resolved

//verify that we are getting all the logs and that the tentacle has been killed
for (var i = 1; i <= count; i++)
{
var i1 = i;
logs.Should().Contain(po => po.Source == ProcessOutputSource.StdOut && po.Text == $"Count: {i1}");
}

scriptCompleted.Should().BeTrue();
result.ExitCode.Should().Be(0);
result.State.Should().Be(ProcessState.Complete);

finalPodName.Should().NotBe(initialPodName, because: "the tentacle pod should have been killed and restarted");

return;

void StatusReceived(ScriptExecutionStatus status)
{
logs.AddRange(status.Logs);
}

Task ScriptCompleted(CancellationToken ct)
{
scriptCompleted = true;
return Task.CompletedTask;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ namespace Octopus.Tentacle.Kubernetes.Tests.Integration;
public class KubernetesTestsGlobalContext : IDisposable
{
public static KubernetesTestsGlobalContext Instance { get; } = new();

public TemporaryDirectory TemporaryDirectory { get; }

public ILogger Logger { get; }

public string KubeConfigPath { get; set; } = "<unset>";

public string HelmExePath { get; private set; } = null!;
public string KubeCtlExePath { get; private set; }= null!;
public string KubeCtlExePath { get; private set; } = null!;
public string? TentacleImageAndTag { get; set; }

KubernetesTestsGlobalContext()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public KubernetesAgentInstaller(TemporaryDirectory temporaryDirectory, string he

public string AgentName { get; }

string Namespace => $"octopus-agent-{AgentName}";
public string Namespace => $"octopus-agent-{AgentName}";

public Uri SubscriptionId { get; } = PollingSubscriptionId.Generate();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System.Text;
using Octopus.Tentacle.CommonTestUtils;
using Octopus.Tentacle.CommonTestUtils.Logging;
using Octopus.Tentacle.Util;

namespace Octopus.Tentacle.Kubernetes.Tests.Integration.Setup.Tooling;

public class KubeCtlTool
{
readonly TemporaryDirectory temporaryDirectory;
readonly string kubeCtlExePath;
readonly string kubeConfigPath;
readonly string ns;
readonly ILogger logger;

public KubeCtlTool(TemporaryDirectory temporaryDirectory, string kubeCtlExePath, string kubeConfigPath, string ns, ILogger logger)
{
this.temporaryDirectory = temporaryDirectory;
this.kubeCtlExePath = kubeCtlExePath;
this.kubeConfigPath = kubeConfigPath;
this.ns = ns;
this.logger = logger;
}

public Task<KubeCtlCommandResult> ExecuteNamespacedCommand(string command, CancellationToken cancellationToken = default)
{
return Task.Run(() => ExecuteCommand($"{command} --namespace {ns}", cancellationToken), cancellationToken);
}

KubeCtlCommandResult ExecuteCommand(string command, CancellationToken cancellationToken = default)
{
var sb = new StringBuilder();
var sprLogger = new LoggerConfiguration()
.WriteTo.Logger(logger)
.WriteTo.StringBuilder(sb)
.MinimumLevel.Debug()
.CreateLogger();

var stdOut = new List<string>();
var stdErr = new List<string>();

var exitCode = SilentProcessRunner.ExecuteCommand(
kubeCtlExePath,
$"{command} --kubeconfig=\"{kubeConfigPath}\"",
temporaryDirectory.DirectoryPath,
sprLogger.Debug,
x =>
{
sprLogger.Information(x);
stdOut.Add(x);
},
y =>
{
sprLogger.Error(y);
stdErr.Add(y);
},
cancellationToken);

return new (exitCode, stdOut, stdErr);
}

public record KubeCtlCommandResult(int ExitCode, IEnumerable<string> StdOut, IEnumerable<string> StdError);
}
11 changes: 7 additions & 4 deletions source/Octopus.Tentacle/Octopus.Tentacle.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,7 @@
<DefineConstants>$(DefineConstants);HTTP_CLIENT_SUPPORTS_SSL_OPTIONS;REQUIRES_EXPLICIT_LOG_CONFIG;REQUIRES_CODE_PAGE_PROVIDER;USER_INTERACTIVE_DOES_NOT_WORK;DEFAULT_PROXY_IS_NOT_AVAILABLE;HAS_NULLABLE_REF_TYPES</DefineConstants>
</PropertyGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net48' ">
<PackageReference Include="KubernetesClient.Classic" Version="13.0.26" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0' Or '$(TargetFramework)' == 'net6.0-windows'">
<PackageReference Include="KubernetesClient" Version="13.0.26" />
<PackageReference Include="KubernetesClient.Classic" Version="14.0.2" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Octopus.Client" Version="14.3.1389" />
Expand Down Expand Up @@ -127,4 +124,10 @@
<EmbeddedResource Include="Startup\PathsToDeleteOnStartup.netfx.txt" />
<None Remove="Kubernetes\bootstrapRunner.sh" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageReference Include="KubernetesClient" Version="14.0.2" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0-windows'">
<PackageReference Include="KubernetesClient" Version="14.0.2" />
</ItemGroup>
</Project>