Skip to content

Commit ac4c93c

Browse files
authored
Add KubernetesAuthContext to Kubernetes commands (#1133)
* Add AuthContext to Kubernetes commands * Add labels and annotations * Remove IAuthContext * Add space * Nullable for backwards compatibility * Update k8s client to fix build * Tidy up labels * Review comments * Remove env var toggle
1 parent fe27faa commit ac4c93c

File tree

7 files changed

+105
-8
lines changed

7 files changed

+105
-8
lines changed

source/Octopus.Tentacle.Client/Scripts/KubernetesScriptServiceV1Executor.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ StartKubernetesScriptCommandV1 Map(ExecuteScriptCommand command)
6565
kubernetesScriptCommand.ScriptPodServiceAccountName,
6666
kubernetesScriptCommand.Scripts,
6767
kubernetesScriptCommand.Files.ToArray(),
68-
kubernetesScriptCommand.IsRawScript);
68+
kubernetesScriptCommand.IsRawScript,
69+
kubernetesScriptCommand.AuthContext);
6970
}
7071

7172
static ScriptOperationExecutionResult Map(KubernetesScriptStatusResponseV1 scriptStatusResponse)

source/Octopus.Tentacle.Client/Scripts/Models/Builders/ExecuteKubernetesScriptCommandBuilder.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public class ExecuteKubernetesScriptCommandBuilder : ExecuteScriptCommandBuilder
88
KubernetesImageConfiguration? configuration;
99
string? scriptPodServiceAccountName;
1010
bool isRawScript;
11+
KubernetesAgentAuthContext? authContext;
1112

1213
public ExecuteKubernetesScriptCommandBuilder(string taskId)
1314
: base(taskId, ScriptIsolationLevel.NoIsolation) //Kubernetes Agents don't need isolation since the scripts won't clash with each other (it won't clash more than Workers anyway)
@@ -32,6 +33,12 @@ public ExecuteKubernetesScriptCommandBuilder IsRawScript()
3233
return this;
3334
}
3435

36+
public ExecuteKubernetesScriptCommandBuilder WithAuthContext(KubernetesAgentAuthContext context)
37+
{
38+
authContext = context;
39+
return this;
40+
}
41+
3542
public override ExecuteScriptCommand Build()
3643
=> new ExecuteKubernetesScriptCommand(
3744
ScriptTicket,
@@ -43,7 +50,8 @@ public override ExecuteScriptCommand Build()
4350
Files.ToArray(),
4451
configuration,
4552
scriptPodServiceAccountName,
46-
isRawScript
53+
isRawScript,
54+
authContext
4755
);
4856
}
4957
}

source/Octopus.Tentacle.Client/Scripts/Models/ExecuteKubernetesScriptCommand.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ public class ExecuteKubernetesScriptCommand : ExecuteScriptCommand
1212

1313
public bool IsRawScript { get; }
1414

15+
public KubernetesAgentAuthContext? AuthContext { get; }
16+
1517
public ExecuteKubernetesScriptCommand(
1618
ScriptTicket scriptTicket,
1719
string taskId,
@@ -22,12 +24,14 @@ public ExecuteKubernetesScriptCommand(
2224
ScriptFile[] additionalFiles,
2325
KubernetesImageConfiguration? imageConfiguration,
2426
string? scriptPodServiceAccountName,
25-
bool isRawScript)
27+
bool isRawScript,
28+
KubernetesAgentAuthContext? authContext)
2629
: base(scriptTicket, taskId, scriptBody, arguments, isolationConfiguration, additionalScripts, additionalFiles)
2730
{
2831
ImageConfiguration = imageConfiguration;
2932
ScriptPodServiceAccountName = scriptPodServiceAccountName;
3033
IsRawScript = isRawScript;
34+
AuthContext = authContext;
3135
}
3236
}
3337
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System;
2+
3+
namespace Octopus.Tentacle.Contracts
4+
{
5+
public class KubernetesAgentAuthContext
6+
{
7+
public KubernetesAgentAuthContext(string spaceSlug, string projectSlug, string environmentSlug, string? tenantSlug, string stepSlug)
8+
{
9+
SpaceSlug = spaceSlug;
10+
ProjectSlug = projectSlug;
11+
EnvironmentSlug = environmentSlug;
12+
TenantSlug = tenantSlug;
13+
StepSlug = stepSlug;
14+
}
15+
16+
public string SpaceSlug { get; }
17+
public string ProjectSlug { get; }
18+
public string EnvironmentSlug { get; }
19+
public string? TenantSlug { get; }
20+
public string StepSlug { get; }
21+
}
22+
}

source/Octopus.Tentacle.Contracts/KubernetesScriptServiceV1/StartKubernetesScriptCommandV1.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ public StartKubernetesScriptCommandV1(
1818
string? scriptPodServiceAccountName,
1919
Dictionary<ScriptType, string>? additionalScripts,
2020
ScriptFile[]? additionalFiles,
21-
bool isRawScript)
21+
bool isRawScript,
22+
KubernetesAgentAuthContext? authContext)
2223
{
2324
Arguments = arguments;
2425
TaskId = taskId;
@@ -30,6 +31,7 @@ public StartKubernetesScriptCommandV1(
3031
PodImageConfiguration = podImageConfiguration;
3132
ScriptPodServiceAccountName = scriptPodServiceAccountName;
3233
IsRawScript = isRawScript;
34+
AuthContext = authContext;
3335

3436
if (additionalFiles != null)
3537
Files.AddRange(additionalFiles);
@@ -58,5 +60,7 @@ public StartKubernetesScriptCommandV1(
5860
public string[] Arguments { get; }
5961

6062
public string? ScriptPodServiceAccountName { get; }
63+
64+
public KubernetesAgentAuthContext? AuthContext { get; }
6165
}
6266
}

source/Octopus.Tentacle/Kubernetes/KubernetesConfig.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ public static class KubernetesConfig
7979

8080
public static string MetricsEnableVariableName => $"{EnvVarPrefix}__ENABLEMETRICSCAPTURE";
8181

82+
public static readonly string AgentLabelNamespace = "agent.octopus.com";
83+
8284
public static bool MetricsIsEnabled
8385
{
8486
get
@@ -118,5 +120,8 @@ static string GetRequiredEnvVar(string variable, string errorMessage)
118120

119121
static string GetEnvironmentVariableOrDefault(string variable, string defaultValue)
120122
=> Environment.GetEnvironmentVariable(variable) ?? defaultValue;
123+
124+
static bool GetBoolEnvironmentVariableOrDefault(string variable, bool defaultValue)
125+
=> bool.TryParse(Environment.GetEnvironmentVariable(variable), out var result) ? result : defaultValue;
121126
}
122127
}

source/Octopus.Tentacle/Kubernetes/KubernetesScriptPodCreator.cs

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using Newtonsoft.Json;
1313
using Octopus.Tentacle.Configuration;
1414
using Octopus.Tentacle.Configuration.Instances;
15+
using Octopus.Tentacle.Contracts;
1516
using Octopus.Tentacle.Contracts.KubernetesScriptServiceV1;
1617
using Octopus.Tentacle.Core.Diagnostics;
1718
using Octopus.Tentacle.Core.Services.Scripts.Locking;
@@ -205,7 +206,7 @@ async Task CreatePod(StartKubernetesScriptCommandV1 command, IScriptWorkspace wo
205206
Name = podName,
206207
NamespaceProperty = KubernetesConfig.Namespace,
207208
Labels = Merge(scriptPodTemplate?.Spec.PodMetadata?.Labels, GetScriptPodLabels(tentacleScriptLog, command)),
208-
Annotations = Merge(scriptPodTemplate?.Spec.PodMetadata?.Annotations, ParseScriptPodAnnotations(tentacleScriptLog))
209+
Annotations = Merge(scriptPodTemplate?.Spec.PodMetadata?.Annotations, GetScriptPodAnnotations(tentacleScriptLog, command))
209210
},
210211
//if the script pod template spec has been defined, use that
211212
Spec = scriptPodTemplate?.Spec.PodSpec ?? new V1PodSpec
@@ -380,7 +381,7 @@ protected V1ResourceRequirements GetScriptPodResourceRequirements(InMemoryTentac
380381
var message = $"Failed to deserialize env.{KubernetesConfig.PodResourceJsonVariableName} into valid pod resource requirements.{Environment.NewLine}JSON value: {json}{Environment.NewLine}Using default resource requests for script pod.";
381382
//if we can't parse the JSON, fall back to the defaults below and warn the user
382383
log.WarnFormat(e, message);
383-
//write a verbose message to the script log.
384+
//write a verbose message to the script log.
384385
tentacleScriptLog.Verbose(message);
385386
}
386387
}
@@ -433,7 +434,14 @@ V1Affinity ParseScriptPodAffinity(InMemoryTentacleScriptLog tentacleScriptLog)
433434
KubernetesConfig.PodAnnotationsJsonVariableName,
434435
"pod annotations");
435436

436-
Dictionary<string, string>? GetScriptPodLabels(InMemoryTentacleScriptLog tentacleScriptLog, StartKubernetesScriptCommandV1 command)
437+
Dictionary<string, string>? GetScriptPodAnnotations(InMemoryTentacleScriptLog tentacleScriptLog, StartKubernetesScriptCommandV1 command)
438+
{
439+
var annotations = ParseScriptPodAnnotations(tentacleScriptLog) ?? new Dictionary<string, string>();
440+
annotations.AddRange(GetAuthContext(command));
441+
return annotations;
442+
}
443+
444+
Dictionary<string, string> GetScriptPodLabels(InMemoryTentacleScriptLog tentacleScriptLog, StartKubernetesScriptCommandV1 command)
437445
{
438446
var labels = new Dictionary<string, string>
439447
{
@@ -451,9 +459,54 @@ V1Affinity ParseScriptPodAffinity(InMemoryTentacleScriptLog tentacleScriptLog)
451459
labels.AddRange(extraLabels);
452460
}
453461

462+
labels.Add($"{KubernetesConfig.AgentLabelNamespace}/permissions", "enabled");
463+
labels.AddRange(GetAuthContext(command, true));
464+
454465
return labels;
455466
}
456467

468+
static Dictionary<string, string> GetAuthContext(StartKubernetesScriptCommandV1 command, bool hash = false)
469+
{
470+
var dict = new Dictionary<string, string>();
471+
472+
if (command.AuthContext is null)
473+
{
474+
return dict;
475+
}
476+
477+
dict[$"{KubernetesConfig.AgentLabelNamespace}/project"] = hash
478+
? HashValue(command.AuthContext.ProjectSlug)
479+
: command.AuthContext.ProjectSlug;
480+
481+
dict[$"{KubernetesConfig.AgentLabelNamespace}/environment"] = hash
482+
? HashValue(command.AuthContext.EnvironmentSlug)
483+
: command.AuthContext.EnvironmentSlug;
484+
485+
if (command.AuthContext.TenantSlug is not null)
486+
{
487+
dict[$"{KubernetesConfig.AgentLabelNamespace}/tenant"] = hash
488+
? HashValue(command.AuthContext.TenantSlug)
489+
: command.AuthContext.TenantSlug;
490+
}
491+
492+
dict[$"{KubernetesConfig.AgentLabelNamespace}/step"] = hash
493+
? HashValue(command.AuthContext.StepSlug)
494+
: command.AuthContext.StepSlug;
495+
496+
dict[$"{KubernetesConfig.AgentLabelNamespace}/space"] = hash
497+
? HashValue(command.AuthContext.SpaceSlug)
498+
: command.AuthContext.SpaceSlug;
499+
500+
return dict;
501+
}
502+
503+
static string HashValue(string value)
504+
{
505+
using var sha1 = SHA1.Create();
506+
var bytes = sha1.ComputeHash(Encoding.UTF8.GetBytes(value));
507+
return BitConverter.ToString(bytes).Replace("-","");
508+
}
509+
457510
[return: NotNullIfNotNull("defaultValue")]
458511
T? ParseScriptPodJson<T>(InMemoryTentacleScriptLog tentacleScriptLog, string? json, string envVarName, string description, T? defaultValue = null) where T : class
459512
{
@@ -472,7 +525,7 @@ V1Affinity ParseScriptPodAffinity(InMemoryTentacleScriptLog tentacleScriptLog)
472525

473526
//if we can't parse the JSON, fall back to the defaults below and warn the user
474527
log.WarnFormat(e, message);
475-
//write a verbose message to the script log.
528+
//write a verbose message to the script log.
476529
tentacleScriptLog.Verbose(message);
477530
}
478531

0 commit comments

Comments
 (0)