From 6269c10933bce589783e47c2a2abc578deca49d6 Mon Sep 17 00:00:00 2001 From: Liam Mackie Date: Thu, 30 Jan 2025 11:24:08 +1000 Subject: [PATCH] Add migration command for agent pre-installation --- .../scripts/configure-and-run.sh | 9 ++ ...talledKubernetesDeploymentTargetCommand.cs | 99 +++++++++++++++++++ source/Octopus.Tentacle/Program.cs | 1 + 3 files changed, 109 insertions(+) create mode 100644 source/Octopus.Tentacle/Commands/MigratePreInstalledKubernetesDeploymentTargetCommand.cs diff --git a/docker/kubernetes-agent-tentacle/scripts/configure-and-run.sh b/docker/kubernetes-agent-tentacle/scripts/configure-and-run.sh index 24a8933b2..41c4ae832 100644 --- a/docker/kubernetes-agent-tentacle/scripts/configure-and-run.sh +++ b/docker/kubernetes-agent-tentacle/scripts/configure-and-run.sh @@ -156,6 +156,14 @@ function validateWorkerVariables() { echo " - worker pools '$WorkerPools'" } +function migrateFromPreinstallScript() { + tentacle migrate-preinstalled-k8s-config \ + --source-config-map-name "tentacle-config-pre" \ + --source-secret-name "tentacle-secret-pre" \ + --destination-config-map-name "tentacle-config" \ + --destination-secret-name "tentacle-secret" +} + function configureTentacle() { tentacle create-instance --instance "$instanceName" --config "$configurationDirectory/tentacle.config" --home "$configurationDirectory" @@ -424,6 +432,7 @@ else echo "===============================================" + migrateFromPreinstallScript configureTentacle registerTentacle addAdditionalServerInstancesIfRequired diff --git a/source/Octopus.Tentacle/Commands/MigratePreInstalledKubernetesDeploymentTargetCommand.cs b/source/Octopus.Tentacle/Commands/MigratePreInstalledKubernetesDeploymentTargetCommand.cs new file mode 100644 index 000000000..39e5b657c --- /dev/null +++ b/source/Octopus.Tentacle/Commands/MigratePreInstalledKubernetesDeploymentTargetCommand.cs @@ -0,0 +1,99 @@ +using System; +using System.Net; +using Octopus.Diagnostics; +using Octopus.Tentacle.Startup; +using k8s; +using k8s.Autorest; +using Octopus.Tentacle.Kubernetes; + +namespace Octopus.Tentacle.Commands +{ + public class MigratePreInstalledKubernetesDeploymentTargetCommand : AbstractCommand + { + readonly ISystemLog log; + readonly IKubernetesClientConfigProvider configProvider; + + string? sourceConfigMapName; + string? sourceSecretName; + string? destinationConfigMapName; + string? destinationSecretName; + + public MigratePreInstalledKubernetesDeploymentTargetCommand(IKubernetesClientConfigProvider configProvider, ISystemLog log, ILogFileOnlyLogger logFileOnlyLogger) : base(logFileOnlyLogger) + { + this.log = log; + this.configProvider = configProvider; + + Options.Add("source-config-map-name=", "The name of the source config map (created by the pre-installation of the agent)", v => sourceConfigMapName = v); + Options.Add("source-secret-name=", "The name of the source secret (created by the pre-installation of the agent)", v => sourceSecretName = v); + Options.Add("destination-config-map-name=", "The name of the destination config map", v => destinationConfigMapName = v); + Options.Add("destination-secret-name=", "The name of the destination secret", v => destinationSecretName = v); + } + + // This command is only used as a way to programatically copy the config map and secret from the pre-installation hook to the new agent + // It does not access tentacle configuration so that it doesn't + protected override void Start() + { + // Check that the sources and destinations are different + if (sourceSecretName == destinationSecretName || sourceConfigMapName == destinationConfigMapName) + { + log.Error("Source and destination names must be different."); + return; + } + + var config = configProvider.Get(); + var client = new k8s.Kubernetes(config); + + // Check that the sources exist + var sourceConfigMap = TryGetCoreV1Object(() => client.CoreV1.ReadNamespacedConfigMap(sourceConfigMapName, KubernetesConfig.Namespace)); + var sourceSecret = TryGetCoreV1Object(() => client.CoreV1.ReadNamespacedSecret(sourceSecretName, KubernetesConfig.Namespace)); + if (sourceConfigMap is null || sourceSecret is null) + { + log.Info("Source config map or secret not found, skipping migration."); + return; + } + + // Check if the destinations exist + var destinationConfigMap = TryGetCoreV1Object(() => client.CoreV1.ReadNamespacedConfigMap(destinationConfigMapName, KubernetesConfig.Namespace)); + var destinationSecret = TryGetCoreV1Object(() => client.CoreV1.ReadNamespacedSecret(destinationSecretName, KubernetesConfig.Namespace)); + if (destinationConfigMap is null || destinationSecret is null) + { + log.Info("destination config map or secret not found, skipping migration."); + return; + } + + + // Check if the destination is already registered + if (destinationConfigMap.Data is not null && destinationConfigMap.Data.TryGetValue("Tentacle.Services.IsRegistered", out var isRegistered) && isRegistered == "True") + { + log.Info("Tentacle is already registered, skipping registration."); + return; + } + + // Copy the data from the source to the destination + destinationConfigMap.Data = sourceConfigMap.Data; + destinationSecret.Data = sourceSecret.Data; + client.CoreV1.ReplaceNamespacedConfigMap(destinationConfigMap, destinationConfigMapName, KubernetesConfig.Namespace); + client.CoreV1.ReplaceNamespacedSecret(destinationSecret, destinationSecretName, KubernetesConfig.Namespace); + + // Delete the sources (they are no longer needed) + client.CoreV1.DeleteNamespacedConfigMap(sourceConfigMapName, KubernetesConfig.Namespace); + client.CoreV1.DeleteNamespacedSecret(sourceSecretName, KubernetesConfig.Namespace); + + log.Info("Migration complete."); + } + + T? TryGetCoreV1Object(Func kubernetesFunc) where T : class + { + try + { + return kubernetesFunc(); + } + catch (HttpOperationException ex) when (ex.Response.StatusCode == HttpStatusCode.NotFound) + { + return null; + } + } + } + + +} \ No newline at end of file diff --git a/source/Octopus.Tentacle/Program.cs b/source/Octopus.Tentacle/Program.cs index a9a05a74f..a73d8fef8 100644 --- a/source/Octopus.Tentacle/Program.cs +++ b/source/Octopus.Tentacle/Program.cs @@ -79,6 +79,7 @@ public override IContainer BuildContainer(StartUpInstanceRequest startUpInstance #pragma warning restore CS0618 // Type or member is obsolete builder.RegisterCommand("register-k8s-target", "Registers this kubernetes agent as a deployment target with an Octopus Server"); builder.RegisterCommand("register-k8s-worker", "Registers this kubernetes agent as a worker with an Octopus Server"); + builder.RegisterCommand("migrate-preinstalled-k8s-config", "Migrates the configuration from the pre-install hook to the running agent instance"); builder.RegisterCommand("extract", "Extracts a NuGet package"); builder.RegisterCommand("deregister-from", "Deregisters this deployment target from an Octopus Server"); builder.RegisterCommand("deregister-worker", "Deregisters this worker from an Octopus Server");