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 Kubernetes Client package to v15 #1051

Merged
merged 4 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
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
@@ -1,18 +1,13 @@
using Halibut;
using Halibut.Diagnostics;
using Halibut.Diagnostics.LogCreators;
using Halibut.Logging;
using System;
using Halibut;
using Octopus.Tentacle.Client;
using Octopus.Tentacle.Client.Retries;
using Octopus.Tentacle.Client.Scripts;
using Octopus.Tentacle.CommonTestUtils;
using Octopus.Tentacle.Contracts.Observability;
using Octopus.Tentacle.Kubernetes.Tests.Integration.Setup;
using Octopus.Tentacle.Kubernetes.Tests.Integration.Tooling;
using Octopus.Tentacle.Tests.Integration.Common.Builders.Decorators;
using Octopus.Tentacle.Tests.Integration.Common.Logging;

namespace Octopus.Tentacle.Kubernetes.Tests.Integration;
namespace Octopus.Tentacle.Kubernetes.Tests.Integration.KubernetesAgent;

public abstract class KubernetesAgentIntegrationTest
{
Expand All @@ -23,9 +18,7 @@ public abstract class KubernetesAgentIntegrationTest
protected ILogger? Logger { get; private set; }

protected KubernetesAgentInstaller KubernetesAgentInstaller => kubernetesAgentInstaller ?? throw new InvalidOperationException("Expected kubernetesAgentInstaller to be set");

protected HalibutRuntime ServerHalibutRuntime { get; private set; } = null!;


protected TentacleClient TentacleClient { get; private set; } = null!;

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

protected readonly IDictionary<string, string> CustomHelmValues = new Dictionary<string, string>();

HalibutRuntime serverHalibutRuntime;

string? agentThumbprint;

[OneTimeSetUp]
Expand All @@ -54,12 +49,13 @@ public async Task OneTimeSetUp()
KubernetesTestsGlobalContext.Instance.Logger);

//create a new server halibut runtime
var listeningPort = BuildServerHalibutRuntimeAndListen();
serverHalibutRuntime = SetupHelpers.BuildServerHalibutRuntime();
var listeningPort = serverHalibutRuntime.Listen();

agentThumbprint = await kubernetesAgentInstaller.InstallAgent(listeningPort, KubernetesTestsGlobalContext.Instance.TentacleImageAndTag, CustomHelmValues);

//trust the generated cert thumbprint
ServerHalibutRuntime.Trust(agentThumbprint);
serverHalibutRuntime.Trust(agentThumbprint);
}

[SetUp]
Expand All @@ -76,7 +72,7 @@ public void SetUp()
CancellationToken = cancellationTokenSource.Token;

//each test should get its own tentacle client, so it gets its own builders
BuildTentacleClient();
TentacleClient = SetupHelpers.BuildTentacleClient(KubernetesAgentInstaller.SubscriptionId, agentThumbprint, serverHalibutRuntime, ConfigureTentacleServiceDecoratorBuilder);
}

[TearDown]
Expand All @@ -91,45 +87,14 @@ public async Task TearDown()
cancellationTokenSource?.Dispose();
}

protected virtual TentacleServiceDecoratorBuilder ConfigureTentacleServiceDecoratorBuilder(TentacleServiceDecoratorBuilder builder) => builder;

void BuildTentacleClient()
{
var endpoint = new ServiceEndPoint(KubernetesAgentInstaller.SubscriptionId, agentThumbprint, ServerHalibutRuntime.TimeoutsAndLimits);

var retrySettings = new RpcRetrySettings(true, TimeSpan.FromMinutes(2));
var clientOptions = new TentacleClientOptions(retrySettings);

TentacleClient.CacheServiceWasNotFoundResponseMessages(ServerHalibutRuntime);

var builder = new TentacleServiceDecoratorBuilder();
ConfigureTentacleServiceDecoratorBuilder(builder);

TentacleClient = new TentacleClient(
endpoint,
ServerHalibutRuntime,
new PollingTentacleScriptObserverBackoffStrategy(),
new NoTentacleClientObserver(),
clientOptions,
builder.Build());
}

int BuildServerHalibutRuntimeAndListen()
protected virtual void ConfigureTentacleServiceDecoratorBuilder(TentacleServiceDecoratorBuilder builder)
{
var serverHalibutRuntimeBuilder = new HalibutRuntimeBuilder()
.WithServerCertificate(TestCertificates.Server)
.WithHalibutTimeoutsAndLimits(HalibutTimeoutsAndLimits.RecommendedValues())
.WithLogFactory(new TestContextLogCreator("Server", LogLevel.Trace).ToCachingLogFactory());

ServerHalibutRuntime = serverHalibutRuntimeBuilder.Build();

return ServerHalibutRuntime.Listen();
}

[OneTimeTearDown]
public async Task OneTimeTearDown()
{
await ServerHalibutRuntime.DisposeAsync();
await serverHalibutRuntime.DisposeAsync();
kubernetesAgentInstaller?.Dispose();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using Octopus.Tentacle.Diagnostics;
using Octopus.Tentacle.Kubernetes.Diagnostics;

namespace Octopus.Tentacle.Kubernetes.Tests.Integration;
namespace Octopus.Tentacle.Kubernetes.Tests.Integration.KubernetesAgent;

public class KubernetesAgentMetricsIntegrationTest : KubernetesAgentIntegrationTest
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
using System;
using Octopus.Tentacle.Kubernetes.Tests.Integration.Setup;

namespace Octopus.Tentacle.Kubernetes.Tests.Integration.KubernetesAgent;

[SetUpFixture]
public class KubernetesClusterOneTimeSetUp
{
KubernetesClusterInstaller? installer;

[OneTimeSetUp]
public async Task OneTimeSetUp()
{
var toolDownloader = new RequiredToolDownloader(KubernetesTestsGlobalContext.Instance.TemporaryDirectory, KubernetesTestsGlobalContext.Instance.Logger);
var (kindExePath, helmExePath, kubeCtlPath) = await toolDownloader.DownloadRequiredTools(CancellationToken.None);

installer = new KubernetesClusterInstaller(KubernetesTestsGlobalContext.Instance.TemporaryDirectory, kindExePath, helmExePath, kubeCtlPath, KubernetesTestsGlobalContext.Instance.Logger);
await installer.Install();

KubernetesTestsGlobalContext.Instance.TentacleImageAndTag = SetupHelpers.GetTentacleImageAndTag(kindExePath, installer);
KubernetesTestsGlobalContext.Instance.SetToolExePaths(helmExePath, kubeCtlPath);
KubernetesTestsGlobalContext.Instance.KubeConfigPath = installer.KubeConfigPath;
}

[OneTimeTearDown]
public void OneTimeTearDown()
{
installer?.Dispose();
KubernetesTestsGlobalContext.Instance.Dispose();
}
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,28 @@
using System;
using FluentAssertions;
using FluentAssertions.Execution;
using Octopus.Client.Model.Endpoints;
using Octopus.Tentacle.Client.Scripts.Models;
using Octopus.Tentacle.Client.Scripts.Models.Builders;
using Octopus.Tentacle.CommonTestUtils;
using Octopus.Tentacle.CommonTestUtils.Diagnostics;
using Octopus.Tentacle.Contracts;
using Octopus.Tentacle.Contracts.Capabilities;
using Octopus.Tentacle.Contracts.ClientServices;
using Octopus.Tentacle.Contracts.KubernetesScriptServiceV1;
using Octopus.Tentacle.Tests.Integration.Common.Builders.Decorators;
using Octopus.Tentacle.Kubernetes.Tests.Integration.Util;
using Octopus.Tentacle.Tests.Integration.Common.Builders.Decorators;
using Octopus.Tentacle.Tests.Integration.Common.Builders.Decorators.Proxies;

namespace Octopus.Tentacle.Kubernetes.Tests.Integration;
namespace Octopus.Tentacle.Kubernetes.Tests.Integration.KubernetesAgent;

[TestFixture]
public class KubernetesScriptServiceV1IntegrationTest : KubernetesAgentIntegrationTest
{
IRecordedMethodUsages recordedMethodUsages = null!;

protected override TentacleServiceDecoratorBuilder ConfigureTentacleServiceDecoratorBuilder(TentacleServiceDecoratorBuilder builder)
protected override void ConfigureTentacleServiceDecoratorBuilder(TentacleServiceDecoratorBuilder builder)
{
builder.RecordMethodUsages<IAsyncClientKubernetesScriptServiceV1>(out var recordedUsages);

recordedMethodUsages = recordedUsages;

return builder;
}

[Test]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
using System;
using FluentAssertions;
using Halibut;
using Octopus.Tentacle.Client;
using Octopus.Tentacle.Client.Scripts.Models;
using Octopus.Tentacle.Client.Scripts.Models.Builders;
using Octopus.Tentacle.CommonTestUtils;
using Octopus.Tentacle.CommonTestUtils.Diagnostics;
using Octopus.Tentacle.Contracts;
using Octopus.Tentacle.Contracts.ClientServices;
using Octopus.Tentacle.Kubernetes.Tests.Integration.Setup;
using Octopus.Tentacle.Kubernetes.Tests.Integration.Util;
using Octopus.Tentacle.Tests.Integration.Common.Builders.Decorators;
using Octopus.Tentacle.Tests.Integration.Common.Builders.Decorators.Proxies;
using Octopus.Tentacle.Tests.Integration.Common.Logging;

namespace Octopus.Tentacle.Kubernetes.Tests.Integration;

[TestFixture]
public class KubernetesClientCompatibilityTests
{
static readonly object[] TestClusterVersions =
[
new object[] {new ClusterVersion(1, 31)},
new object[] {new ClusterVersion(1, 30)},
new object[] {new ClusterVersion(1, 29)},
new object[] {new ClusterVersion(1, 28)},
];

KubernetesTestsGlobalContext? testContext;
ILogger logger = null!;
TemporaryDirectory toolsTemporaryDirectory;
string kindExePath;
string helmExePath;
string kubeCtlPath;
KubernetesClusterInstaller? clusterInstaller;
KubernetesAgentInstaller? kubernetesAgentInstaller;
HalibutRuntime serverHalibutRuntime = null!;
string? agentThumbprint;
TraceLogFileLogger? traceLogFileLogger;
CancellationToken cancellationToken;
CancellationTokenSource? cancellationTokenSource;
TentacleClient tentacleClient = null!;
IRecordedMethodUsages recordedMethodUsages = null!;

[OneTimeSetUp]
public async Task OneTimeSetup()
{
logger = new SerilogLoggerBuilder().Build();
toolsTemporaryDirectory = new TemporaryDirectory();
var toolDownloader = new RequiredToolDownloader(toolsTemporaryDirectory, logger);
(kindExePath, helmExePath, kubeCtlPath) = await toolDownloader.DownloadRequiredTools(CancellationToken.None);
}

[OneTimeTearDown]
public void OneTimeTearDown()
{
toolsTemporaryDirectory.Dispose();
}

[TearDown]
public async Task TearDown()
{
if (traceLogFileLogger is not null) await traceLogFileLogger.DisposeAsync();
if (cancellationTokenSource is not null)
{
await cancellationTokenSource.CancelAsync();
cancellationTokenSource.Dispose();
}
clusterInstaller?.Dispose();
testContext?.Dispose();

traceLogFileLogger = null;
cancellationTokenSource = null;
clusterInstaller = null;
testContext = null;
}

[Test]
[TestCaseSource(nameof(TestClusterVersions))]
public async Task RunSimpleScript(ClusterVersion clusterVersion)
{
await SetUp(clusterVersion);

// Arrange
var logs = new List<ProcessOutput>();
var scriptCompleted = false;

var builder = new ExecuteKubernetesScriptCommandBuilder(LoggingUtils.CurrentTestHash())
.WithScriptBody(script => script
.Print("Hello World")
.PrintNTimesWithDelay("Yep", 30, TimeSpan.FromMilliseconds(100)));

var command = builder.Build();

// Act
var result = await tentacleClient.ExecuteScript(command, StatusReceived, ScriptCompleted, new InMemoryLog(), cancellationToken);

// Assert
logs.Should().Contain(po => po.Text.StartsWith("[POD EVENT]")); // Verify that we are receiving some pod events
logs.Should().Contain(po => po.Source == ProcessOutputSource.StdOut && po.Text == "Hello World");
scriptCompleted.Should().BeTrue();
result.ExitCode.Should().Be(0);
result.State.Should().Be(ProcessState.Complete);

recordedMethodUsages.For(nameof(IAsyncClientKubernetesScriptServiceV1.StartScriptAsync)).Started.Should().Be(1);
recordedMethodUsages.For(nameof(IAsyncClientKubernetesScriptServiceV1.GetStatusAsync)).Started.Should().BeGreaterThan(1);
recordedMethodUsages.For(nameof(IAsyncClientKubernetesScriptServiceV1.CompleteScriptAsync)).Started.Should().Be(1);
recordedMethodUsages.For(nameof(IAsyncClientKubernetesScriptServiceV1.CancelScriptAsync)).Started.Should().Be(0);

return;

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

Task ScriptCompleted(CancellationToken ct)
{
scriptCompleted = true;
return Task.CompletedTask;
}
}

async Task SetUp(ClusterVersion clusterVersion)
{
testContext = new KubernetesTestsGlobalContext(logger);

await SetupCluster(clusterVersion);

kubernetesAgentInstaller = new KubernetesAgentInstaller(
testContext.TemporaryDirectory,
testContext.HelmExePath,
testContext.KubeCtlExePath,
testContext.KubeConfigPath,
testContext.Logger);

//create a new server halibut runtime
serverHalibutRuntime = SetupHelpers.BuildServerHalibutRuntime();
var listeningPort = serverHalibutRuntime.Listen();

agentThumbprint = await kubernetesAgentInstaller.InstallAgent(listeningPort, testContext.TentacleImageAndTag, new Dictionary<string, string>());

//trust the generated cert thumbprint
serverHalibutRuntime.Trust(agentThumbprint);

traceLogFileLogger = new TraceLogFileLogger(LoggingUtils.CurrentTestHash());
logger = new SerilogLoggerBuilder()
.SetTraceLogFileLogger(traceLogFileLogger)
.Build()
.ForContext(GetType());

cancellationTokenSource = new CancellationTokenSource();
cancellationTokenSource.CancelAfter(TimeSpan.FromMinutes(5));
cancellationToken = cancellationTokenSource.Token;

tentacleClient = SetupHelpers.BuildTentacleClient(kubernetesAgentInstaller.SubscriptionId, agentThumbprint, serverHalibutRuntime, builder =>
{
builder.RecordMethodUsages<IAsyncClientKubernetesScriptServiceV1>(out var recordedUsages);
recordedMethodUsages = recordedUsages;
});
}

async Task SetupCluster(ClusterVersion clusterVersion)
{
clusterInstaller = new KubernetesClusterInstaller(testContext.TemporaryDirectory, kindExePath, helmExePath, kubeCtlPath, testContext.Logger);
await clusterInstaller.Install(clusterVersion);

testContext.TentacleImageAndTag = SetupHelpers.GetTentacleImageAndTag(kindExePath, clusterInstaller);
testContext.SetToolExePaths(helmExePath, kubeCtlPath);
testContext.KubeConfigPath = clusterInstaller.KubeConfigPath;
}
}
Loading