Skip to content

Commit 0c6b6f5

Browse files
Use script pod template custom resource to create pods and containers (#1136)
* Use script pod template custom resource to create pods and containers * Change to add range * Change to new merge method * Tweak how we create the pod details using new crd structure * Add clone * Update octopus home tp script * Fix compile * Handle all exceptions * Fix NRE --------- Co-authored-by: Liam Mackie <[email protected]>
1 parent 6609577 commit 0c6b6f5

File tree

7 files changed

+279
-91
lines changed

7 files changed

+279
-91
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
using k8s;
3+
using k8s.Models;
4+
5+
namespace Octopus.Tentacle.Kubernetes
6+
{
7+
public static class KubernetesContainerExtensionMethods
8+
{
9+
[return: NotNullIfNotNull(nameof(source))]
10+
public static V1Container? Clone(this V1Container? source)
11+
{
12+
if (source is null)
13+
{
14+
return null;
15+
}
16+
// Use JSON serialization for deep cloning
17+
var json = KubernetesJson.Serialize(source);
18+
return KubernetesJson.Deserialize<V1Container>(json);
19+
}
20+
}
21+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Net;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
using k8s;
8+
using k8s.Models;
9+
using Octopus.Tentacle.Core.Diagnostics;
10+
11+
namespace Octopus.Tentacle.Kubernetes
12+
{
13+
public interface IKubernetesCustomResourceService
14+
{
15+
Task<ScriptPodTemplateCustomResource?> GetOldestScriptPodTemplateCustomResource(CancellationToken cancellationToken);
16+
}
17+
18+
public class KubernetesCustomResourceService : KubernetesService, IKubernetesCustomResourceService
19+
{
20+
public KubernetesCustomResourceService(IKubernetesClientConfigProvider configProvider, ISystemLog log)
21+
: base(configProvider, log)
22+
{
23+
}
24+
25+
public async Task<ScriptPodTemplateCustomResource?> GetOldestScriptPodTemplateCustomResource(CancellationToken cancellationToken)
26+
{
27+
return await RetryPolicy.ExecuteAsync(async () =>
28+
{
29+
ScriptPodTemplateCustomResourceList resourceList;
30+
try
31+
{
32+
resourceList = await Client.CustomObjects.ListNamespacedCustomObjectAsync<ScriptPodTemplateCustomResourceList>(
33+
"agent.octopus.com",
34+
"v1beta1",
35+
KubernetesConfig.Namespace,
36+
"scriptpodtemplates",
37+
cancellationToken: cancellationToken);
38+
}
39+
catch (Exception ex)
40+
{
41+
// we are happy to handle all exceptions here and just fallback
42+
Log.WarnFormat(ex, "Failed to retrieve 'scriptpodtemplates' custom resource");
43+
return null;
44+
}
45+
46+
return resourceList.Items.OrderBy(r => r.Metadata.CreationTimestamp).FirstOrDefault();
47+
});
48+
}
49+
50+
//These are only ever deserialized from JSON
51+
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
52+
class ScriptPodTemplateCustomResourceList : KubernetesObject
53+
{
54+
public V1ListMeta Metadata { get; set; }
55+
public List<ScriptPodTemplateCustomResource> Items { get; set; }
56+
}
57+
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
58+
}
59+
}

source/Octopus.Tentacle/Kubernetes/KubernetesModule.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ protected override void Load(ContainerBuilder builder)
3434
builder.RegisterType<KubernetesPodContainerResolver>().As<IKubernetesPodContainerResolver>().SingleInstance();
3535
builder.RegisterType<KubernetesConfigMapService>().As<IKubernetesConfigMapService>().SingleInstance();
3636
builder.RegisterType<KubernetesSecretService>().As<IKubernetesSecretService>().SingleInstance();
37+
builder.RegisterType<KubernetesCustomResourceService>().As<IKubernetesCustomResourceService>().SingleInstance();
3738

3839
builder.RegisterType<KubernetesScriptPodCreator>().As<IKubernetesScriptPodCreator>().SingleInstance();
3940
builder.RegisterType<KubernetesRawScriptPodCreator>().As<IKubernetesRawScriptPodCreator>().SingleInstance();

source/Octopus.Tentacle/Kubernetes/KubernetesRawScriptPodCreator.cs

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
using Octopus.Tentacle.Core.Diagnostics;
1010
using Octopus.Tentacle.Core.Services.Scripts.Locking;
1111
using Octopus.Tentacle.Kubernetes.Crypto;
12-
using Octopus.Tentacle.Scripts;
1312

1413
namespace Octopus.Tentacle.Kubernetes
1514
{
@@ -25,6 +24,7 @@ public KubernetesRawScriptPodCreator(
2524
IKubernetesPodService podService,
2625
IKubernetesPodMonitor podMonitor,
2726
IKubernetesSecretService secretService,
27+
IKubernetesCustomResourceService customResourceService,
2828
IKubernetesPodContainerResolver containerResolver,
2929
IApplicationInstanceSelector appInstanceSelector,
3030
ISystemLog log,
@@ -33,31 +33,34 @@ public KubernetesRawScriptPodCreator(
3333
KubernetesPhysicalFileSystem kubernetesPhysicalFileSystem,
3434
IScriptPodLogEncryptionKeyProvider scriptPodLogEncryptionKeyProvider,
3535
ScriptIsolationMutex scriptIsolationMutex)
36-
: base(podService, podMonitor, secretService, containerResolver, appInstanceSelector, log, scriptLogProvider, homeConfiguration, kubernetesPhysicalFileSystem, scriptPodLogEncryptionKeyProvider, scriptIsolationMutex)
36+
: base(podService, podMonitor, secretService, customResourceService, containerResolver, appInstanceSelector, log, scriptLogProvider, homeConfiguration, kubernetesPhysicalFileSystem, scriptPodLogEncryptionKeyProvider, scriptIsolationMutex)
3737
{
3838
this.containerResolver = containerResolver;
3939
}
4040

41-
protected override async Task<IList<V1Container>> CreateInitContainers(StartKubernetesScriptCommandV1 command, string podName, string homeDir, string workspacePath, InMemoryTentacleScriptLog tentacleScriptLog)
41+
protected override async Task<IList<V1Container>> CreateInitContainers(StartKubernetesScriptCommandV1 command, string podName, string homeDir, string workspacePath, InMemoryTentacleScriptLog tentacleScriptLog, V1Container? containerSpec)
4242
{
43-
var container = new V1Container
44-
{
45-
Name = $"{podName}-init",
46-
Image = command.PodImageConfiguration?.Image ?? await containerResolver.GetContainerImageForCluster(),
47-
ImagePullPolicy = KubernetesConfig.ScriptPodPullPolicy,
48-
Command = new List<string> { "sh", "-c", GetInitExecutionScript("/nfs-mount", homeDir, workspacePath) },
49-
VolumeMounts = new List<V1VolumeMount> { new("/nfs-mount", "init-nfs-volume"), new(homeDir, "tentacle-home") },
50-
Resources = GetScriptPodResourceRequirements(tentacleScriptLog)
51-
};
43+
// Deep clone the container spec to avoid modifying the original
44+
var container = containerSpec.Clone() ??
45+
new V1Container
46+
{
47+
Resources = GetScriptPodResourceRequirements(tentacleScriptLog)
48+
};
49+
50+
container.Name = $"{podName}-init";
51+
container.Image = command.PodImageConfiguration?.Image ?? await containerResolver.GetContainerImageForCluster();
52+
container.ImagePullPolicy = KubernetesConfig.ScriptPodPullPolicy;
53+
container.Command = new List<string> { "sh", "-c", GetInitExecutionScript("/nfs-mount", homeDir, workspacePath) };
54+
container.VolumeMounts = Merge(container.VolumeMounts, new[] { new V1VolumeMount("/nfs-mount", "init-nfs-volume"), new V1VolumeMount(homeDir, "tentacle-home") });
5255

5356
return new List<V1Container> { container };
5457
}
55-
56-
protected override async Task<IList<V1Container>> CreateScriptContainers(StartKubernetesScriptCommandV1 command, string podName, string scriptName, string homeDir, string workspacePath, string[]? scriptArguments, InMemoryTentacleScriptLog tentacleScriptLog)
58+
59+
protected override async Task<IList<V1Container>> CreateScriptContainers(StartKubernetesScriptCommandV1 command, string podName, string scriptName, string homeDir, string workspacePath, string[]? scriptArguments, InMemoryTentacleScriptLog tentacleScriptLog, ScriptPodTemplateSpec? spec)
5760
{
5861
return new List<V1Container>
5962
{
60-
await CreateScriptContainer(command, podName, scriptName, homeDir, workspacePath, scriptArguments, tentacleScriptLog)
63+
await CreateScriptContainer(command, podName, scriptName, homeDir, workspacePath, scriptArguments, tentacleScriptLog, spec?.ScriptContainerSpec)
6164
};
6265
}
6366

@@ -82,7 +85,7 @@ protected override IList<V1Volume> CreateVolumes(StartKubernetesScriptCommandV1
8285
};
8386
}
8487

85-
string GetInitExecutionScript(string nfsVolumeDirectory, string homeDir, string workspacePath)
88+
static string GetInitExecutionScript(string nfsVolumeDirectory, string homeDir, string workspacePath)
8689
{
8790
var nfsWorkspacePath = Path.Combine(nfsVolumeDirectory, workspacePath);
8891
var homeWorkspacePath = Path.Combine(homeDir, workspacePath);

0 commit comments

Comments
 (0)