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

Add serviceName to NFS StatefulSet #229

Draft
wants to merge 11 commits into
base: release/kubernetes-agent/v1
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
5 changes: 5 additions & 0 deletions .changeset/dry-feet-add.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"kubernetes-agent": patch
---

Correctly set serviceName on NFS StatefulSet
5 changes: 5 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"yaml.schemas": {
"https://raw.githubusercontent.com/helm-unittest/helm-unittest/v0.5.1/schema/helm-testsuite.json": "file:///**/*_test.yaml"
}
}
4 changes: 2 additions & 2 deletions charts/kubernetes-agent/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"description": "The Octopus Kubernetes Agent",
"author": "Octopus Deploy Ptd Ltd",
"scripts": {
"test": "cross-env-shell docker run -ti --rm -v $INIT_CWD:/apps helmunittest/helm-unittest:latest .",
"update-test-snapshots": "cross-env-shell docker run -ti --rm -v $INIT_CWD:/apps helmunittest/helm-unittest:latest . -u",
"test": "cross-env-shell docker run -ti --rm -v $INIT_CWD:/apps helmunittest/helm-unittest:3.15.2-0.5.1 .",
"update-test-snapshots": "cross-env-shell docker run -ti --rm -v $INIT_CWD:/apps helmunittest/helm-unittest:3.15.2-0.5.1 . -u",
"generate-agent-docs": "docker run --rm --volume \".:/helm-docs\" jnorwood/helm-docs:latest"
},
"dependencies": {
Expand Down
14 changes: 11 additions & 3 deletions charts/kubernetes-agent/templates/nfs-statefulset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: {{ include "nfs.name" .}}
name: {{ include "nfs.name" . }}
namespace: {{ .Release.Namespace | quote }}
labels:
app.kubernetes.io/name: {{ include "nfs.name" .}}
app.kubernetes.io/name: {{ include "nfs.name" . }}
spec:
selector:
matchLabels:
app.kubernetes.io/name: {{ include "nfs.name" .}}
app.kubernetes.io/name: {{ include "nfs.name" . }}
{{- $ss := (lookup "apps/v1" "StatefulSet" .Release.Namespace (include "nfs.name" .)) }}
{{- if not $ss }}
{{/* If the stateful set doesn't exist */}}
serviceName: {{ include "nfs.name" . }}
{{- else if (index $ss "spec.serviceName" ) }}
{{/* Or it does exist and it already has a non-empty serviceName */}}
serviceName: {{ include "nfs.name" . }}
{{- end }}
template:
metadata:
{{- with .Values.persistence.nfs.metadata.annotations }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ spec:
{{- end }}
labels:
{{- include "kubernetes-agent.labels" . | nindent 8 }}
{{- with .Values.agent.metadata.labels }}
{{- with .Values.agent.metadata.labels }}
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ should match snapshot:
selector:
matchLabels:
app.kubernetes.io/name: octopus-agent-nfs
serviceName: octopus-agent-nfs
template:
metadata:
labels:
Expand Down
49 changes: 47 additions & 2 deletions charts/kubernetes-agent/tests/nfs-statefulset_test.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,27 @@
suite: "persistence"
templates:
- templates/nfs-statefulset.yaml
- templates/nfs-statefulset.yaml
kubernetesProvider:
scheme:
"apps/v1/StatefulSet":
gvr:
group: "apps"
version: "v1"
resource: "statefulsets"
namespaced: true
objects:
- kind: StatefulSet
apiVersion: "apps/v1"
metadata:
name: "empty-serviceName-nfs"
namespace: "NAMESPACE"
- kind: StatefulSet
apiVersion: "apps/v1"
metadata:
name: "populated-serviceName-nfs"
namespace: "NAMESPACE"
spec:
serviceName: "populated-serviceName-nfs"
tests:
- it: "is not created when storageClassName has a value"
set:
Expand All @@ -21,4 +42,28 @@ tests:
asserts:
- equal:
path: spec.template.spec.volumes[?(@.name == 'octopus-volume')].emptyDir.sizeLimit
value: 100Gi
value: 100Gi

- it: does not set serviceName if the StatefulSet exists and has an empty serviceName
set:
nameOverride: "empty-serviceName"
asserts:
- equal:
path: metadata.name
value: "empty-serviceName-nfs"
- notExists:
path: spec.serviceName

- it: sets serviceName if the StatefulSet doesn't exist
asserts:
- equal:
path: spec.serviceName
value: octopus-agent-nfs

- it: sets serviceName if the StatefulSet exists and is set to same service name
set:
nameOverride: "populated-ServiceName"
asserts:
- equal:
path: spec.serviceName
value: populated-ServiceName-nfs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ static string[] GetHelmChartPackageArguments(string version)
];
}

static DirectoryInfo GetChartsDirectory()
public static DirectoryInfo GetChartsDirectory()
{
var chartsDirectory = Path.Combine(AppContext.BaseDirectory);
var currentDirectory = new DirectoryInfo(chartsDirectory);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using Halibut;
using KubernetesAgent.Integration.Setup;
using KubernetesAgent.Integration.Setup.Common;
using Octopus.Tentacle.Client;
using Octopus.Tentacle.Client.Scripts.Models;
using Octopus.Tentacle.Client.Scripts.Models.Builders;
using Xunit.Abstractions;

namespace KubernetesAgent.Integration;

public class HelmInstallTests(ITestOutputHelper output) : IAsyncLifetime
{
readonly ITestOutputHelper output = output;
ILogger logger = null!;
readonly TemporaryDirectory workingDirectory = new(Directory.CreateTempSubdirectory());
KubernetesClusterInstaller clusterInstaller = null!;
KubernetesAgentInstaller agentInstaller = null!;
TentacleClient client = null!;
string kindExePath = null!;
string helmExePath = null!;
string kubeCtlPath = null!;

public async Task InitializeAsync()
{
logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.WriteTo.TestOutput(output)
.CreateLogger();

var requiredToolDownloader = new RequiredToolDownloader(workingDirectory, logger);
(kindExePath, helmExePath, kubeCtlPath) = await requiredToolDownloader.DownloadRequiredTools(CancellationToken.None);
clusterInstaller = new KubernetesClusterInstaller(workingDirectory, kindExePath, helmExePath, kubeCtlPath, logger);
await clusterInstaller.Install();
}

public async Task DisposeAsync()
{
clusterInstaller.Dispose();
workingDirectory.Dispose();
await Task.CompletedTask;
}

[Fact]
public async Task CanInstallAgentAndRunCommand()
{
var chartDirectory = HelmChartBuilder.GetChartsDirectory();

agentInstaller = new KubernetesAgentInstaller(workingDirectory , helmExePath, kubeCtlPath, clusterInstaller.KubeConfigPath, logger, chartDirectory.FullName);
client = await agentInstaller.InstallAgent();

var testLogger = new TestLogger(logger);

var runHelloWorldCommand = new ExecuteKubernetesScriptCommandBuilder(Guid.NewGuid().ToString())
.WithScriptBody("echo \"hello world\"")
.Build();
var result = await client.ExecuteScript(runHelloWorldCommand, OnScriptStatusResponseReceived, OnScriptCompleted, testLogger, CancellationToken.None);
if (result.ExitCode != 0)
{
throw new Exception($"Script failed with exit code {result.ExitCode}");
}

logger.Information("Script executed successfully");
return;

async Task OnScriptCompleted(CancellationToken t)
{
await Task.CompletedTask;
logger.Information("Script completed");
}

void OnScriptStatusResponseReceived(ScriptExecutionStatus res) => logger.Information("{Output}", res.ToString());
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
using System.Runtime.InteropServices;

namespace KubernetesAgent.Integration.Setup.Common;

public static class FileSystemInfoExtensionMethods
{
public static void MakeExecutable(this FileSystemInfo fsObject)
{
//if we are on windows, we don't need to do this
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return;
}

var result = ProcessRunner.Run("chmod", "-R", "+x", fsObject.FullName);
if (result.ExitCode != 0)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,21 @@ public class KubernetesAgentInstaller
readonly string kubeCtlExePath;
readonly TemporaryDirectory temporaryDirectory;
readonly ILogger logger;
readonly string? chartDirectoryName;
readonly string kubeConfigPath;
protected HalibutRuntime ServerHalibutRuntime { get; private set; } = null!;
protected TentacleClient TentacleClient { get; private set; } = null!;

bool isAgentInstalled;

public KubernetesAgentInstaller(TemporaryDirectory temporaryDirectory, string helmExePath, string kubeCtlExePath, string kubeConfigPath, ILogger logger)
public KubernetesAgentInstaller(TemporaryDirectory temporaryDirectory, string helmExePath, string kubeCtlExePath, string kubeConfigPath, ILogger logger, string? chartDirectoryName = null)
{
this.temporaryDirectory = temporaryDirectory;
this.helmExePath = helmExePath;
this.kubeCtlExePath = kubeCtlExePath;
this.kubeConfigPath = kubeConfigPath;
this.logger = logger;
this.chartDirectoryName = chartDirectoryName;

AgentName = Guid.NewGuid().ToString("N");
}
Expand Down Expand Up @@ -124,7 +126,8 @@ string BuildAgentInstallArguments(string valuesFilePath, bool hasRegistrySecret)
NamespaceFlag,
KubeConfigFlag,
AgentName,
ArtifactoryAgentOciRepository
//Use the local directory if it exists, otherwise pull from artifactory
chartDirectoryName ?? ArtifactoryAgentOciRepository
};

return string.Join(" ", args.WhereNotNull());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace KubernetesAgent.Integration.Setup;

public class KubernetesClusterInstaller : IDisposable
{
static int clusterCount = 0;
readonly string clusterName;
readonly string kubeConfigName;

Expand All @@ -25,7 +26,9 @@ public KubernetesClusterInstaller(TemporaryDirectory tempDirectory, string kindE
this.kubeCtlPath = kubeCtlPath;
this.logger = logger;

clusterName = $"helm-octopus-agent-int-{DateTime.Now:yyyyMMddhhmmss}";
//hack to get a unique time for multiple clusters
var count = Interlocked.Increment(ref clusterCount);
clusterName = $"octo-test-{DateTime.Now.AddSeconds(count):yyyyMMddhhmmss}";
kubeConfigName = $"{clusterName}.config";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace KubernetesAgent.Integration.Setup.Tooling;

public class HelmDownloader : ToolDownloader
{
const string LatestVersion = "v3.14.3";
const string LatestVersion = "v3.15.3";
public HelmDownloader( ILogger logger)
: base("helm", logger)
{
Expand Down
Loading