diff --git a/build/_build.csproj b/build/_build.csproj
index 8862ec4eb..6819c0d13 100644
--- a/build/_build.csproj
+++ b/build/_build.csproj
@@ -19,19 +19,19 @@
- all
- runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
-
+
diff --git a/source/Calamari.Common/Features/Processes/Semaphores/FileBasedSempahoreManager.cs b/source/Calamari.Common/Features/Processes/Semaphores/FileBasedSempahoreManager.cs
deleted file mode 100644
index 295d9c6f9..000000000
--- a/source/Calamari.Common/Features/Processes/Semaphores/FileBasedSempahoreManager.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using System;
-using Calamari.Common.Plumbing.Logging;
-
-namespace Calamari.Common.Features.Processes.Semaphores
-{
- //Originally based on https://github.com/markedup-mobi/file-lock (MIT license)
- public class FileBasedSempahoreManager : ISemaphoreFactory
- {
- readonly ILog log;
- readonly ICreateSemaphores semaphoreCreator;
- readonly int initialWaitBeforeShowingLogMessage;
-
- public FileBasedSempahoreManager()
- {
- log = ConsoleLog.Instance;
- initialWaitBeforeShowingLogMessage = (int)TimeSpan.FromSeconds(3).TotalMilliseconds;
- semaphoreCreator = new LockFileBasedSemaphoreCreator(log);
- }
-
- public FileBasedSempahoreManager(ILog log, TimeSpan initialWaitBeforeShowingLogMessage, ICreateSemaphores semaphoreCreator)
- {
- this.log = log;
- this.semaphoreCreator = semaphoreCreator;
- this.initialWaitBeforeShowingLogMessage = (int)initialWaitBeforeShowingLogMessage.TotalMilliseconds;
- }
-
- public IDisposable Acquire(string name, string waitMessage)
- {
- var semaphore = semaphoreCreator.Create(name, TimeSpan.FromMinutes(2));
-
- if (!semaphore.WaitOne(initialWaitBeforeShowingLogMessage))
- {
- log.Verbose(waitMessage);
- semaphore.WaitOne();
- }
-
- return new LockFileBasedSemaphoreReleaser(semaphore);
- }
-
- class LockFileBasedSemaphoreReleaser : IDisposable
- {
- readonly ISemaphore semaphore;
-
- public LockFileBasedSemaphoreReleaser(ISemaphore semaphore)
- {
- this.semaphore = semaphore;
- }
-
- public void Dispose()
- {
- semaphore.ReleaseLock();
- }
- }
- }
-}
\ No newline at end of file
diff --git a/source/Calamari.Common/Features/Processes/Semaphores/FileLock.cs b/source/Calamari.Common/Features/Processes/Semaphores/FileLock.cs
deleted file mode 100644
index 814e0f1c7..000000000
--- a/source/Calamari.Common/Features/Processes/Semaphores/FileLock.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.Runtime.Serialization;
-using System.Threading;
-
-namespace Calamari.Common.Features.Processes.Semaphores
-{
- public interface IFileLock
- {}
-
- [DataContract]
- public class FileLock : IFileLock
- {
- public FileLock(long processId, string processName, int threadId, long timestamp)
- {
- ProcessId = processId;
- ProcessName = processName;
- ThreadId = threadId;
- Timestamp = timestamp;
- }
-
- [DataMember]
- public long ProcessId { get; }
-
- [DataMember]
- public long Timestamp { get; internal set; }
-
- [DataMember]
- public string ProcessName { get; }
-
- [DataMember]
- public int ThreadId { get; }
-
- protected bool Equals(FileLock other)
- {
- return ProcessId == other.ProcessId &&
- string.Equals(ProcessName, other.ProcessName) &&
- ThreadId == other.ThreadId;
- }
-
- public override bool Equals(object? obj)
- {
- if (ReferenceEquals(null, obj))
- return false;
- if (ReferenceEquals(this, obj))
- return true;
- if (obj.GetType() != GetType())
- return false;
- return Equals((FileLock)obj);
- }
-
- public override int GetHashCode()
- {
- unchecked
- {
- var hashCode = ProcessId.GetHashCode();
- hashCode = (hashCode * 397) ^ (ProcessName?.GetHashCode() ?? 0);
- hashCode = (hashCode * 397) ^ ThreadId;
- return hashCode;
- }
- }
-
- public bool BelongsToCurrentProcessAndThread()
- {
- return ProcessId == Process.GetCurrentProcess().Id && ThreadId == Thread.CurrentThread.ManagedThreadId;
- }
- }
-}
\ No newline at end of file
diff --git a/source/Calamari.Common/Features/Processes/Semaphores/ICreateSemaphores.cs b/source/Calamari.Common/Features/Processes/Semaphores/ICreateSemaphores.cs
deleted file mode 100644
index 213e6b469..000000000
--- a/source/Calamari.Common/Features/Processes/Semaphores/ICreateSemaphores.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System;
-
-namespace Calamari.Common.Features.Processes.Semaphores
-{
- public interface ICreateSemaphores
- {
- ISemaphore Create(string name, TimeSpan lockTimeout);
- }
-}
\ No newline at end of file
diff --git a/source/Calamari.Common/Features/Processes/Semaphores/ILockIo.cs b/source/Calamari.Common/Features/Processes/Semaphores/ILockIo.cs
deleted file mode 100644
index f9c022744..000000000
--- a/source/Calamari.Common/Features/Processes/Semaphores/ILockIo.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-using System;
-
-namespace Calamari.Common.Features.Processes.Semaphores
-{
- public interface ILockIo
- {
- string GetFilePath(string lockName);
- bool LockExists(string lockFilePath);
- IFileLock ReadLock(string lockFilePath);
- bool WriteLock(string lockFilePath, FileLock fileLock);
- void DeleteLock(string lockFilePath);
- }
-}
\ No newline at end of file
diff --git a/source/Calamari.Common/Features/Processes/Semaphores/IProcessFinder.cs b/source/Calamari.Common/Features/Processes/Semaphores/IProcessFinder.cs
deleted file mode 100644
index 5e2efbe52..000000000
--- a/source/Calamari.Common/Features/Processes/Semaphores/IProcessFinder.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-using System;
-
-namespace Calamari.Common.Features.Processes.Semaphores
-{
- public interface IProcessFinder
- {
- bool ProcessIsRunning(int processId, string processName);
- }
-}
\ No newline at end of file
diff --git a/source/Calamari.Common/Features/Processes/Semaphores/LockFileBasedSemaphore.cs b/source/Calamari.Common/Features/Processes/Semaphores/LockFileBasedSemaphore.cs
deleted file mode 100644
index c9ee6b48f..000000000
--- a/source/Calamari.Common/Features/Processes/Semaphores/LockFileBasedSemaphore.cs
+++ /dev/null
@@ -1,159 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.Threading;
-using Calamari.Common.Plumbing.FileSystem;
-using Calamari.Common.Plumbing.Logging;
-
-namespace Calamari.Common.Features.Processes.Semaphores
-{
- public class LockFileBasedSemaphore : ISemaphore
- {
- public enum AcquireLockAction
- {
- DontAcquireLock,
- AcquireLock,
- ForciblyAcquireLock
- }
-
- readonly ILockIo lockIo;
- readonly IProcessFinder processFinder;
- readonly ILog log;
-
- public LockFileBasedSemaphore(string name, TimeSpan lockTimeout, ILog log)
- : this(name,
- lockTimeout,
- new LockIo(CalamariPhysicalFileSystem.GetPhysicalFileSystem()),
- new ProcessFinder(),
- log)
- {
- }
-
- public LockFileBasedSemaphore(string name,
- TimeSpan lockTimeout,
- ILockIo lockIo,
- IProcessFinder processFinder,
- ILog log)
- {
- if (string.IsNullOrEmpty(name))
- throw new ArgumentNullException(nameof(name), "name cannot be null or emtpy.");
-
- Name = name;
- LockTimeout = lockTimeout;
- LockFilePath = lockIo.GetFilePath(name);
- this.lockIo = lockIo;
- this.processFinder = processFinder;
- this.log = log;
- }
-
- TimeSpan LockTimeout { get; }
-
- public string Name { get; }
-
- string LockFilePath { get; }
-
- public AcquireLockAction ShouldAcquireLock(IFileLock fileLock)
- {
- if (lockIo.LockExists(LockFilePath))
- {
- //Someone else owns the lock
- if (fileLock is OtherProcessHasExclusiveLockOnFileLock)
- //we couldn't read the file as some other process has it open exlusively
- return AcquireLockAction.DontAcquireLock;
-
- if (fileLock is UnableToDeserialiseLockFile nonDeserialisedLockFile)
- {
- if ((DateTime.Now - nonDeserialisedLockFile.CreationTime).TotalSeconds > LockTimeout.TotalSeconds)
- {
- log.Warn("Lock file existed but was not readable, and has existed for longer than lock timeout. Taking lock.");
- return AcquireLockAction.AcquireLock;
- }
-
- return AcquireLockAction.DontAcquireLock;
- }
-
- //the file no longer exists
- if (fileLock is MissingFileLock)
- return AcquireLockAction.AcquireLock;
-
- var concreteFileLock = fileLock as FileLock;
- if (concreteFileLock == null)
- return AcquireLockAction.AcquireLock;
-
- //This lock belongs to this process - we can reacquire the lock
- if (concreteFileLock.BelongsToCurrentProcessAndThread())
- return AcquireLockAction.AcquireLock;
-
- if (!processFinder.ProcessIsRunning((int)concreteFileLock.ProcessId, concreteFileLock.ProcessName ?? ""))
- {
- log.Warn($"Process {concreteFileLock.ProcessId}, thread {concreteFileLock.ThreadId} had lock, but appears to have crashed. Taking lock.");
-
- return AcquireLockAction.AcquireLock;
- }
-
- var lockWriteTime = new DateTime(concreteFileLock.Timestamp);
- //The lock has not timed out - we can't acquire it
- if (!(Math.Abs((DateTime.Now - lockWriteTime).TotalSeconds) > LockTimeout.TotalSeconds))
- return AcquireLockAction.DontAcquireLock;
-
- log.Warn($"Forcibly taking lock from process {concreteFileLock.ProcessId}, thread {concreteFileLock.ThreadId} as lock has timed out. If this happens regularly, please contact Octopus Support.");
-
- return AcquireLockAction.ForciblyAcquireLock;
- }
-
- return AcquireLockAction.AcquireLock;
- }
-
- public bool TryAcquireLock()
- {
- var lockContent = lockIo.ReadLock(LockFilePath);
- var response = ShouldAcquireLock(lockContent);
- if (response == AcquireLockAction.ForciblyAcquireLock)
- lockIo.DeleteLock(LockFilePath);
-
- if (response == AcquireLockAction.AcquireLock || response == AcquireLockAction.ForciblyAcquireLock)
- return lockIo.WriteLock(LockFilePath, CreateLockContent());
-
- return false;
- }
-
- public void ReleaseLock()
- {
- //Need to own the lock in order to release it (and we can reacquire the lock inside the current process)
- if (lockIo.LockExists(LockFilePath) && TryAcquireLock())
- lockIo.DeleteLock(LockFilePath);
- }
-
- static FileLock CreateLockContent()
- {
- var process = Process.GetCurrentProcess();
- return new FileLock(process.Id,
- process.ProcessName,
- Thread.CurrentThread.ManagedThreadId,
- DateTime.Now.Ticks);
- }
-
- public bool WaitOne(int millisecondsToWait)
- {
- var stopwatch = new Stopwatch();
- stopwatch.Start();
- while (true)
- {
- if (TryAcquireLock())
- return true;
- if (stopwatch.ElapsedMilliseconds > millisecondsToWait)
- return false;
- Thread.Sleep(100);
- }
- }
-
- public bool WaitOne()
- {
- while (true)
- {
- if (TryAcquireLock())
- return true;
- Thread.Sleep(100);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/source/Calamari.Common/Features/Processes/Semaphores/LockFileBasedSemaphoreCreator.cs b/source/Calamari.Common/Features/Processes/Semaphores/LockFileBasedSemaphoreCreator.cs
deleted file mode 100644
index 083750b49..000000000
--- a/source/Calamari.Common/Features/Processes/Semaphores/LockFileBasedSemaphoreCreator.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using System;
-using Calamari.Common.Plumbing.Logging;
-
-namespace Calamari.Common.Features.Processes.Semaphores
-{
- public class LockFileBasedSemaphoreCreator : ICreateSemaphores
- {
- readonly ILog log;
-
- public LockFileBasedSemaphoreCreator(ILog log)
- {
- this.log = log;
- }
-
- public ISemaphore Create(string name, TimeSpan lockTimeout)
- {
- return new LockFileBasedSemaphore(name, lockTimeout, log);
- }
- }
-}
\ No newline at end of file
diff --git a/source/Calamari.Common/Features/Processes/Semaphores/LockIo.cs b/source/Calamari.Common/Features/Processes/Semaphores/LockIo.cs
deleted file mode 100644
index 03a4b3b50..000000000
--- a/source/Calamari.Common/Features/Processes/Semaphores/LockIo.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-using System;
-using System.IO;
-using Calamari.Common.Plumbing.FileSystem;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-
-namespace Calamari.Common.Features.Processes.Semaphores
-{
- public class LockIo : ILockIo
- {
- readonly ICalamariFileSystem fileSystem;
-
- public LockIo(ICalamariFileSystem fileSystem)
- {
- this.fileSystem = fileSystem;
- }
-
- public string GetFilePath(string lockName)
- {
- return Path.Combine(Path.GetTempPath(), lockName + ".lck");
- }
-
- public bool LockExists(string lockFilePath)
- {
- return fileSystem.FileExists(lockFilePath);
- }
-
- public IFileLock ReadLock(string lockFilePath)
- {
- try
- {
- using (var stream = fileSystem.OpenFileExclusively(lockFilePath, FileMode.Open, FileAccess.Read))
- {
- using (var streamReader = new StreamReader(stream))
- {
- var obj = JObject.Load(new JsonTextReader(streamReader));
- var lockContent = new FileLock(obj["ProcessId"].ToObject(),
- obj["ProcessName"].ToString(),
- obj["ThreadId"].ToObject(),
- obj["Timestamp"].ToObject());
- if (lockContent.BelongsToCurrentProcessAndThread())
- return lockContent;
- return new OtherProcessOwnsFileLock(lockContent);
- }
- }
- }
- catch (FileNotFoundException)
- {
- return new MissingFileLock();
- }
- catch (IOException)
- {
- return new OtherProcessHasExclusiveLockOnFileLock();
- }
- catch (JsonReaderException)
- {
- return new UnableToDeserialiseLockFile(fileSystem.GetCreationTime(lockFilePath));
- }
- catch (Exception) //We have no idea what went wrong - reacquire this lock
- {
- return new OtherProcessHasExclusiveLockOnFileLock();
- }
- }
-
- public bool WriteLock(string lockFilePath, FileLock fileLock)
- {
- try
- {
- var fileMode = FileMode.CreateNew;
- if (LockExists(lockFilePath))
- {
- var currentContent = ReadLock(lockFilePath);
- if (Equals(currentContent, fileLock))
- {
- if ((currentContent as FileLock)?.Timestamp == fileLock.Timestamp)
- return true;
- fileMode = FileMode.Create;
- }
- else if (currentContent.GetType() == typeof(UnableToDeserialiseLockFile))
- {
- DeleteLock(lockFilePath);
- }
- }
-
- var obj = new JObject
- {
- ["ProcessId"] = fileLock.ProcessId,
- ["ThreadId"] = fileLock.ThreadId,
- ["ProcessName"] = fileLock.ProcessName,
- ["Timestamp"] = fileLock.Timestamp
- };
- using (var stream = fileSystem.OpenFileExclusively(lockFilePath, fileMode, FileAccess.Write))
- {
- using (var streamWriter = new StreamWriter(stream))
- {
- obj.WriteTo(new JsonTextWriter(streamWriter));
- }
- }
-
- var writtenContent = ReadLock(lockFilePath);
- return Equals(writtenContent, fileLock);
- }
- catch (Exception)
- {
- return false;
- }
- }
-
- public void DeleteLock(string lockFilePath)
- {
- try
- {
- fileSystem.DeleteFile(lockFilePath);
- }
- catch (Exception)
- {
- // ignored - handled in create
- }
- }
- }
-}
\ No newline at end of file
diff --git a/source/Calamari.Common/Features/Processes/Semaphores/MissingFileLock.cs b/source/Calamari.Common/Features/Processes/Semaphores/MissingFileLock.cs
deleted file mode 100644
index 5218911bb..000000000
--- a/source/Calamari.Common/Features/Processes/Semaphores/MissingFileLock.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using System;
-
-namespace Calamari.Common.Features.Processes.Semaphores
-{
- public class MissingFileLock : IFileLock
- {
- }
-}
\ No newline at end of file
diff --git a/source/Calamari.Common/Features/Processes/Semaphores/OtherProcessHasExclusiveLockOnFileLock.cs b/source/Calamari.Common/Features/Processes/Semaphores/OtherProcessHasExclusiveLockOnFileLock.cs
deleted file mode 100644
index e54a6598d..000000000
--- a/source/Calamari.Common/Features/Processes/Semaphores/OtherProcessHasExclusiveLockOnFileLock.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using System;
-
-namespace Calamari.Common.Features.Processes.Semaphores
-{
- public class OtherProcessHasExclusiveLockOnFileLock : IFileLock
- {
- }
-}
\ No newline at end of file
diff --git a/source/Calamari.Common/Features/Processes/Semaphores/OtherProcessOwnsFileLock.cs b/source/Calamari.Common/Features/Processes/Semaphores/OtherProcessOwnsFileLock.cs
deleted file mode 100644
index af9592e07..000000000
--- a/source/Calamari.Common/Features/Processes/Semaphores/OtherProcessOwnsFileLock.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using System;
-
-namespace Calamari.Common.Features.Processes.Semaphores
-{
- public class OtherProcessOwnsFileLock : FileLock
- {
- public OtherProcessOwnsFileLock(FileLock fileLock) : base(fileLock.ProcessId, fileLock.ProcessName, fileLock.ThreadId, fileLock.Timestamp)
- {
- }
- }
-}
\ No newline at end of file
diff --git a/source/Calamari.Common/Features/Processes/Semaphores/ProcessFinder.cs b/source/Calamari.Common/Features/Processes/Semaphores/ProcessFinder.cs
deleted file mode 100644
index 897c43158..000000000
--- a/source/Calamari.Common/Features/Processes/Semaphores/ProcessFinder.cs
+++ /dev/null
@@ -1,49 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.Linq;
-using Calamari.Common.Plumbing;
-
-namespace Calamari.Common.Features.Processes.Semaphores
-{
- public class ProcessFinder : IProcessFinder
- {
- public bool ProcessIsRunning(int processId, string processName)
- {
- // borrowed from https://github.com/sillsdev/libpalaso/blob/7c7e5eed0a3d9c8a961b01887cbdebbf1b63b899/SIL.Core/IO/FileLock/SimpleFileLock.cs (Apache 2.0 license)
- Process process;
- try
- {
- // First, look for a process with this processId
- process = Process.GetProcesses().FirstOrDefault(x => x.Id == processId);
- }
- catch (NotSupportedException)
- {
- //FreeBSD does not support EnumProcesses
- //assume that the process is running
- return true;
- }
-
- // If there is no process with this processId, it is not running.
- if (process == null)
- return false;
-
- // Next, check for a match on processName.
- var isRunning = process.ProcessName == processName;
-
- // If a match was found or this is running on Windows, this is as far as we need to go.
- if (isRunning || CalamariEnvironment.IsRunningOnWindows)
- return isRunning;
-
- // We need to look a little deeper on Linux.
-
- // If the name of the process is not "mono" or does not start with "mono-", this is not
- // a mono application, and therefore this is not the process we are looking for.
- if (process.ProcessName.ToLower() != "mono" && !process.ProcessName.ToLower().StartsWith("mono-"))
- return false;
-
- // The mono application will have a module with the same name as the process, with ".exe" added.
- var moduleName = processName.ToLower() + ".exe";
- return process.Modules.Cast().Any(mod => mod.ModuleName.ToLower() == moduleName);
- }
- }
-}
\ No newline at end of file
diff --git a/source/Calamari.Common/Features/Processes/Semaphores/SemaphoreFactory.cs b/source/Calamari.Common/Features/Processes/Semaphores/SemaphoreFactory.cs
deleted file mode 100644
index 76bcf18de..000000000
--- a/source/Calamari.Common/Features/Processes/Semaphores/SemaphoreFactory.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-using System;
-using Calamari.Common.Plumbing;
-
-namespace Calamari.Common.Features.Processes.Semaphores
-{
- public static class SemaphoreFactory
- {
- public static ISemaphoreFactory Get()
- {
- if (CalamariEnvironment.IsRunningOnMono)
- return new FileBasedSempahoreManager();
- return new SystemSemaphoreManager();
- }
- }
-}
\ No newline at end of file
diff --git a/source/Calamari.Common/Features/Processes/Semaphores/UnableToDeserialiseLockFile.cs b/source/Calamari.Common/Features/Processes/Semaphores/UnableToDeserialiseLockFile.cs
deleted file mode 100644
index a3d59df7a..000000000
--- a/source/Calamari.Common/Features/Processes/Semaphores/UnableToDeserialiseLockFile.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using System;
-
-namespace Calamari.Common.Features.Processes.Semaphores
-{
- public class UnableToDeserialiseLockFile : IFileLock
- {
- public UnableToDeserialiseLockFile(DateTime creationTime)
- {
- CreationTime = creationTime;
- }
-
- public DateTime CreationTime { get; }
- }
-}
\ No newline at end of file
diff --git a/source/Calamari.Common/Features/Scripting/ScriptExecutor.cs b/source/Calamari.Common/Features/Scripting/ScriptExecutor.cs
index afbfac317..4d6268741 100644
--- a/source/Calamari.Common/Features/Scripting/ScriptExecutor.cs
+++ b/source/Calamari.Common/Features/Scripting/ScriptExecutor.cs
@@ -36,7 +36,7 @@ public CommandResult Execute(Script script,
try
{
if (execution.CommandLineInvocation.Isolate)
- using (SemaphoreFactory.Get()
+ using (new SystemSemaphoreManager()
.Acquire("CalamariSynchronizeProcess",
"Waiting for other process to finish executing script"))
{
diff --git a/source/Calamari.Common/Plumbing/CalamariEnvironment.cs b/source/Calamari.Common/Plumbing/CalamariEnvironment.cs
index 5b801ae6d..9ea1cf9b9 100644
--- a/source/Calamari.Common/Plumbing/CalamariEnvironment.cs
+++ b/source/Calamari.Common/Plumbing/CalamariEnvironment.cs
@@ -5,13 +5,6 @@ namespace Calamari.Common.Plumbing
{
public static class CalamariEnvironment
{
- ///
- /// If/When we try executing this on another runtime we can 'tweak' this logic.
- /// The reccomended way to determine at runtime if executing within the mono framework
- /// http://www.mono-project.com/docs/gui/winforms/porting-winforms-applications/#runtime-conditionals
- ///
- public static readonly bool IsRunningOnMono = Type.GetType("Mono.Runtime") != null;
-
public static bool IsRunningOnKubernetes =>
!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(KubernetesEnvironmentVariables.KubernetesAgentNamespace));
diff --git a/source/Calamari.Common/Plumbing/Deployment/Journal/DeploymentJournalWriter.cs b/source/Calamari.Common/Plumbing/Deployment/Journal/DeploymentJournalWriter.cs
index 3f248e01a..414dc4b35 100644
--- a/source/Calamari.Common/Plumbing/Deployment/Journal/DeploymentJournalWriter.cs
+++ b/source/Calamari.Common/Plumbing/Deployment/Journal/DeploymentJournalWriter.cs
@@ -28,7 +28,7 @@ public void AddJournalEntry(RunningDeployment deployment, bool wasSuccessful, st
{
if (deployment.SkipJournal)
return;
- var semaphore = SemaphoreFactory.Get();
+ var semaphore = new SystemSemaphoreManager();
var journal = new DeploymentJournal(fileSystem, semaphore, deployment.Variables);
var hasPackages = !string.IsNullOrWhiteSpace(packageFile) || deployment.Variables.GetIndexes(PackageVariables.PackageCollection).Any();
diff --git a/source/Calamari.Common/Plumbing/Extensions/ApplicationDirectory.cs b/source/Calamari.Common/Plumbing/Extensions/ApplicationDirectory.cs
index 863d69a6e..3cd256b65 100644
--- a/source/Calamari.Common/Plumbing/Extensions/ApplicationDirectory.cs
+++ b/source/Calamari.Common/Plumbing/Extensions/ApplicationDirectory.cs
@@ -9,7 +9,7 @@ namespace Calamari.Common.Plumbing.Extensions
{
public class ApplicationDirectory
{
- static readonly ISemaphoreFactory Semaphore = SemaphoreFactory.Get();
+ static readonly ISemaphoreFactory Semaphore = new SystemSemaphoreManager();
///
/// Returns the directory where the package will be installed.
diff --git a/source/Calamari.Common/Plumbing/Extensions/EnvironmentHelper.cs b/source/Calamari.Common/Plumbing/Extensions/EnvironmentHelper.cs
index e4ddfec90..9bfb271ce 100644
--- a/source/Calamari.Common/Plumbing/Extensions/EnvironmentHelper.cs
+++ b/source/Calamari.Common/Plumbing/Extensions/EnvironmentHelper.cs
@@ -42,8 +42,6 @@ static IEnumerable GetEnvironmentVars()
yield return SafelyGet(() => $"OperatingSystem: {Environment.OSVersion}");
yield return SafelyGet(() => $"OsBitVersion: {(Environment.Is64BitOperatingSystem ? "x64" : "x86")}");
yield return SafelyGet(() => $"Is64BitProcess: {Environment.Is64BitProcess}");
- if (CalamariEnvironment.IsRunningOnNix || CalamariEnvironment.IsRunningOnMac)
- yield return SafelyGet(() => $"Running on Mono: {CalamariEnvironment.IsRunningOnMono}");
if (CalamariEnvironment.IsRunningOnWindows)
yield return SafelyGet(() => $"CurrentUser: {WindowsIdentity.GetCurrent().Name}");
else
diff --git a/source/Calamari.Common/Plumbing/FileSystem/FreeSpaceChecker.cs b/source/Calamari.Common/Plumbing/FileSystem/FreeSpaceChecker.cs
index 2e98dcc14..e34d01911 100644
--- a/source/Calamari.Common/Plumbing/FileSystem/FreeSpaceChecker.cs
+++ b/source/Calamari.Common/Plumbing/FileSystem/FreeSpaceChecker.cs
@@ -42,12 +42,6 @@ ulong GetRequiredSpaceInBytes(out bool spaceOverrideSpecified)
public ulong GetSpaceRequiredToBeFreed(string directoryPath)
{
- if (CalamariEnvironment.IsRunningOnMono && CalamariEnvironment.IsRunningOnMac)
- {
- Log.Verbose("Unable to determine disk free space under Mono on macOS. Assuming there's enough.");
- return 0;
- }
-
var requiredSpaceInBytes = GetRequiredSpaceInBytes(out var spaceOverrideSpecified);
if (spaceOverrideSpecified)
@@ -70,15 +64,6 @@ public ulong GetSpaceRequiredToBeFreed(string directoryPath)
public void EnsureDiskHasEnoughFreeSpace(string directoryPath)
{
- if (CalamariEnvironment.IsRunningOnMono && CalamariEnvironment.IsRunningOnMac)
- {
- //After upgrading to macOS 10.15.2, and mono 5.14.0, drive.TotalFreeSpace and drive.AvailableFreeSpace both started returning 0.
- //see https://github.com/mono/mono/issues/17151, which was fixed in mono 6.4.xx
- //Rock and a hard place.
- Log.Verbose("Unable to determine disk free space under Mono on macOS. Assuming there's enough.");
- return;
- }
-
if (variables.GetFlag(SkipFreeDiskSpaceCheckVariable))
{
Log.Verbose($"{SkipFreeDiskSpaceCheckVariable} is enabled. The check to ensure that the drive containing the directory '{directoryPath}' on machine '{Environment.MachineName}' has enough free space will be skipped.");
diff --git a/source/Calamari.Common/Plumbing/Variables/DeploymentJournalVariableContributor.cs b/source/Calamari.Common/Plumbing/Variables/DeploymentJournalVariableContributor.cs
index 5cf5e67ba..35a16ffdf 100644
--- a/source/Calamari.Common/Plumbing/Variables/DeploymentJournalVariableContributor.cs
+++ b/source/Calamari.Common/Plumbing/Variables/DeploymentJournalVariableContributor.cs
@@ -14,7 +14,7 @@ public static void Contribute(ICalamariFileSystem fileSystem, IVariables variabl
if (string.IsNullOrWhiteSpace(policySet))
return;
- var journal = new DeploymentJournal(fileSystem, SemaphoreFactory.Get(), variables);
+ var journal = new DeploymentJournal(fileSystem, new SystemSemaphoreManager(), variables);
Previous(variables, journal, policySet);
PreviousSuccessful(variables, journal, policySet);
}
diff --git a/source/Calamari.FullFrameworkTools.Tests/Command/CommandRequestInvokerTests.cs b/source/Calamari.FullFrameworkTools.Tests/Command/CommandRequestInvokerTests.cs
index 80775e5f7..4a4ef4811 100644
--- a/source/Calamari.FullFrameworkTools.Tests/Command/CommandRequestInvokerTests.cs
+++ b/source/Calamari.FullFrameworkTools.Tests/Command/CommandRequestInvokerTests.cs
@@ -87,133 +87,4 @@ public MockRequest(int numberOne, int numberTwo)
public int NumberOne { get; }
public int NumberTwo { get; }
}
- /*public class CommandRequestInvokerTests
- {
- readonly ICommandLocator commandLocator;
-
- public CommandRequestInvokerTests()
- {
- commandLocator = Substitute.For();
- }
-
- [Test]
- public void SimpleHandlerReturnsSerializedResult()
- {
- var handler = new TestAdditionCommandHandler();
- commandLocator.GetCommand(Arg.Any()).Returns(handler);
- var requestObject = new TestAdditionRequest(4, 5);
- var jsonRequestObj = JsonSerializer.Serialize(requestObject, new JsonSerializerOptions());
-
- var invoker = new CommandRequestInvoker(commandLocator);
- var response = invoker.Run("TestCommand", jsonRequestObj);
-
- response.Should().BeEquivalentTo(new TestAdditionResponse(9));
- }
-
- [Test]
- public void SimpleHandlerEncrypted()
- {
- var handler = new TestAdditionCommandHandler();
- commandLocator.GetCommand(Arg.Any()).Returns(handler);
- var requestObject = new TestAdditionRequest(4, 5);
- var jsonRequestObj = JsonSerializer.Serialize(requestObject, new JsonSerializerOptions());
-
- var password = "pass23HJka";
- var enc = new AesEncryption(password);
- var encRequestObj = enc.Encrypt(jsonRequestObj);
-
- using (var temp = new TempFile(encRequestObj))
- {
- var invoker = new CommandRequestInvoker(commandLocator);
- var response = invoker.Run("TestCommand", password, temp.FilePath);
-
- response.Should().BeEquivalentTo(new TestAdditionResponse(9));
- }
- }
-
- public class TempFile : IDisposable
- {
- public TempFile(byte[] content)
- {
- FilePath = Path.GetTempFileName();
- File.WriteAllBytes(FilePath, content);
- }
-
- public string FilePath { get; }
-
- public void Dispose()
- {
- try
- {
- File.Delete(FilePath);
- }
- catch
- {
- // ignored
- }
- }
- }
-
- public class TestLog : ILog
- {
- public List Errors { get; } = new List();
- public List Infos { get; } = new List();
-
- public List Fatals { get; } = new List();
-
- public void Verbose(string message)
- {
- throw new NotImplementedException();
- }
-
- public void Error(string value)
- {
- Errors.Add(value);
- }
-
- public void Info(string value)
- {
- Infos.Add(value);
- }
-
- public void Fatal(Exception exception)
- {
- Fatals.Add(exception);
- }
-
- public void Result(object response)
- {
- }
- }
-
- class TestAdditionCommandHandler : FullFrameworkToolCommandHandler
- {
- protected override TestAdditionResponse Handle(TestAdditionRequest additionRequest)
- {
- return new TestAdditionResponse(additionRequest.NumberOne + additionRequest.NumberTwo);
- }
-
- }
-
- class TestAdditionRequest: IFullFrameworkToolRequest
- {
- public int NumberOne { get; }
- public int NumberTwo { get; }
-
- public TestAdditionRequest(int numberOne, int numberTwo)
- {
- NumberOne = numberOne;
- NumberTwo = numberTwo;
- }
- }
- class TestAdditionResponse: IFullFrameworkToolResponse
- {
- public int Result { get; }
-
- public TestAdditionResponse(int result)
- d {
- Result = result;
- }
- }
- }*/
}
\ No newline at end of file
diff --git a/source/Calamari.FullFrameworkTools/Command/CommandLocator.cs b/source/Calamari.FullFrameworkTools/Command/CommandLocator.cs
deleted file mode 100644
index f9d7af427..000000000
--- a/source/Calamari.FullFrameworkTools/Command/CommandLocator.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-/*using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using Calamari.FullFrameworkTools.Iis;
-
-namespace Calamari.FullFrameworkTools.Command
-{
- public interface ICommandLocator
- {
- IFullFrameworkToolCommandHandler GetCommand(string name);
- }
-
- public class CommandLocator: ICommandLocator
- {
- public IList Commands = new List()
- {
- new ImportCertificateToStoreHandler(),
- new OverwriteHomeDirectoryHandler()
- };
-
- public IFullFrameworkToolCommandHandler GetCommand(string name)
- {
- return Commands.FirstOrDefault(t => t.GetType().Name.Equals($"{name}Handler"));
- }
-
-
- public IFullFrameworkToolCommandHandler GetCommand()
- {
- return Commands.FirstOrDefault(t => GetAllTypes(t.GetType()).Contains(typeof(THandler)));
- }
-
- public IEnumerable GetAllTypes(Type type)
- {
- yield return type;
-
- if (type.BaseType == null)
- yield break;
-
- yield return type.BaseType;
- foreach (var b in GetAllTypes(type.BaseType))
- {
- yield return b;
- }
- }
- }
-}*/
\ No newline at end of file
diff --git a/source/Calamari.FullFrameworkTools/Iis/OverwriteHomeDirectoryHandler.cs b/source/Calamari.FullFrameworkTools/Iis/OverwriteHomeDirectoryHandler.cs
deleted file mode 100644
index 2da1a56e7..000000000
--- a/source/Calamari.FullFrameworkTools/Iis/OverwriteHomeDirectoryHandler.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-/*using System;
-using Calamari.FullFrameworkTools.Command;
-
-namespace Calamari.FullFrameworkTools.Iis
-{
- public class OverwriteHomeDirectoryHandler : FullFrameworkToolCommandHandler
- {
- public string Name { get; }
-
- protected override OverwriteHomeDirectoryResponse Handle(OverwriteHomeDirectoryRequest request)
- {
- var iis = new InternetInformationServer();
- var result = iis.OverwriteHomeDirectory(request.IisWebSiteName, request.Path, request.LegacySupport);
- return new OverwriteHomeDirectoryResponse(result);
- }
- }
-
- public class OverwriteHomeDirectoryRequest : IFullFrameworkToolRequest
- {
- public string IisWebSiteName { get; set; }
- public string Path { get; set; }
- public bool LegacySupport { get; set; }
- }
-
- public class OverwriteHomeDirectoryResponse : IFullFrameworkToolResponse
- {
- public OverwriteHomeDirectoryResponse(bool result)
- {
- Result = result;
- }
-
- public bool Result { get; set; }
- }
-}*/
\ No newline at end of file
diff --git a/source/Calamari.FullFrameworkTools/WindowsCertStore/PrivateKeyAccessRule.cs b/source/Calamari.FullFrameworkTools/WindowsCertStore/PrivateKeyAccessRule.cs
index d37d1c8a7..8ba42d8bf 100644
--- a/source/Calamari.FullFrameworkTools/WindowsCertStore/PrivateKeyAccessRule.cs
+++ b/source/Calamari.FullFrameworkTools/WindowsCertStore/PrivateKeyAccessRule.cs
@@ -9,6 +9,7 @@ namespace Calamari.FullFrameworkTools.WindowsCertStore;
public class PrivateKeyAccessRule
{
+ [JsonConstructor]
public PrivateKeyAccessRule(string identity, PrivateKeyAccess access)
:this(new NTAccount(identity), access)
{
diff --git a/source/Calamari.Shared/Integration/Certificates/WindowsX509CertificateStore.cs b/source/Calamari.Shared/Integration/Certificates/WindowsX509CertificateStore.cs
index 0bacce135..fde07eb9a 100644
--- a/source/Calamari.Shared/Integration/Certificates/WindowsX509CertificateStore.cs
+++ b/source/Calamari.Shared/Integration/Certificates/WindowsX509CertificateStore.cs
@@ -20,7 +20,7 @@ namespace Calamari.Integration.Certificates
{
public class WindowsX509CertificateStore: IWindowsX509CertificateStore
{
- public static readonly ISemaphoreFactory Semaphores = SemaphoreFactory.Get();
+ public static readonly ISemaphoreFactory Semaphores = new SystemSemaphoreManager();
public static readonly string SemaphoreName = nameof(WindowsX509CertificateStore);
const string IntermediateAuthorityStoreName = "CA";
diff --git a/source/Calamari.Tests/Fixtures/Deployment/CleanFixture.cs b/source/Calamari.Tests/Fixtures/Deployment/CleanFixture.cs
index 21540c0e5..2f7bdaac5 100644
--- a/source/Calamari.Tests/Fixtures/Deployment/CleanFixture.cs
+++ b/source/Calamari.Tests/Fixtures/Deployment/CleanFixture.cs
@@ -43,7 +43,7 @@ public void SetUp()
variables = new VariablesFactory(fileSystem).Create(new CommonOptions("test"));
- deploymentJournal = new DeploymentJournal(fileSystem, SemaphoreFactory.Get(), variables);
+ deploymentJournal = new DeploymentJournal(fileSystem, new SystemSemaphoreManager(), variables);
packagesDirectory = Path.Combine(Path.GetTempPath(), "CalamariTestPackages");
fileSystem.EnsureDirectoryExists(packagesDirectory);
diff --git a/source/Calamari.Tests/Fixtures/Integration/Process/Semaphores/FileBasedSempahoreManagerFixture.cs b/source/Calamari.Tests/Fixtures/Integration/Process/Semaphores/FileBasedSempahoreManagerFixture.cs
deleted file mode 100644
index fd3dd44c8..000000000
--- a/source/Calamari.Tests/Fixtures/Integration/Process/Semaphores/FileBasedSempahoreManagerFixture.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-using System;
-using Calamari.Common.Features.Processes.Semaphores;
-using Calamari.Common.Plumbing.Logging;
-using Calamari.Testing.Helpers;
-using Calamari.Tests.Helpers;
-using FluentAssertions;
-using NSubstitute;
-using NUnit.Framework;
-
-namespace Calamari.Tests.Fixtures.Integration.Process.Semaphores
-{
- [TestFixture]
- public class FileBasedSempahoreManagerFixture : SemaphoreFixtureBase
- {
- [Test]
- public void FileBasedSempahoreWaitsUntilFirstSemaphoreIsReleased()
- {
- SecondSemaphoreWaitsUntilFirstSemaphoreIsReleased(new FileBasedSempahoreManager());
- }
-
- [Test]
- public void FileBasedSempahoreShouldIsolate()
- {
- ShouldIsolate(new FileBasedSempahoreManager());
- }
-
- [Test]
- public void WritesMessageToLogIfCannotGetSemaphore()
- {
- var log = new InMemoryLog();
- var semaphoreCreator = Substitute.For();
- var name = Guid.NewGuid().ToString();
- var semaphore = Substitute.For();
- semaphore.WaitOne(200).Returns(false);
- semaphoreCreator.Create(name, TimeSpan.FromSeconds(60)).Returns(semaphore);
- var sempahoreManager = new FileBasedSempahoreManager(log, TimeSpan.FromMilliseconds(200), semaphoreCreator);
- using (sempahoreManager.Acquire(name, "wait message")) { }
-
- log.Messages.Should().Contain(m => m.Level == InMemoryLog.Level.Verbose && m.FormattedMessage == "wait message");
- }
-
- [Test]
- public void ReleasesLockOnDispose()
- {
- var semaphoreCreator = Substitute.For();
- var name = Guid.NewGuid().ToString();
- var semaphore = Substitute.For();
- semaphoreCreator.Create(name, TimeSpan.FromMinutes(2)).Returns(semaphore);
- var sempahoreManager = new FileBasedSempahoreManager(Substitute.For(), TimeSpan.FromMilliseconds(200), semaphoreCreator);
- using (sempahoreManager.Acquire(name, "wait message")) { }
- semaphore.Received().ReleaseLock();
- }
-
- [Test]
- public void AttemptsToGetLockWithTimeoutThenWaitsIndefinitely()
- {
- var semaphoreCreator = Substitute.For();
- var name = Guid.NewGuid().ToString();
- var semaphore = Substitute.For();
- semaphore.WaitOne(200).Returns(false);
- semaphoreCreator.Create(name, TimeSpan.FromMinutes(2)).Returns(semaphore);
- var sempahoreManager = new FileBasedSempahoreManager(Substitute.For(), TimeSpan.FromMilliseconds(200), semaphoreCreator);
- using (sempahoreManager.Acquire(name, "wait message")) { }
- semaphore.Received().WaitOne(200);
- semaphore.Received().WaitOne();
- }
- }
-}
\ No newline at end of file
diff --git a/source/Calamari.Tests/Fixtures/Integration/Process/Semaphores/FileLockFixture.cs b/source/Calamari.Tests/Fixtures/Integration/Process/Semaphores/FileLockFixture.cs
deleted file mode 100644
index bcdc22535..000000000
--- a/source/Calamari.Tests/Fixtures/Integration/Process/Semaphores/FileLockFixture.cs
+++ /dev/null
@@ -1,97 +0,0 @@
-using System;
-using System.Threading;
-using Calamari.Common.Features.Processes.Semaphores;
-using NUnit.Framework;
-
-namespace Calamari.Tests.Fixtures.Integration.Process.Semaphores
-{
- [TestFixture]
- public class FileLockFixture
- {
- [Test]
- [TestCase(123, 456, "ProcessName", 123, 456, "ProcessName", true)]
- [TestCase(123, 456, "ProcessName", 789, 456, "ProcessName", false)]
- [TestCase(123, 456, "ProcessName", 123, 789, "ProcessName", false)]
- [TestCase(123, 456, "ProcessName", 123, 456, "DiffProcessName", false)]
- [TestCase(123, 456, "ProcessName", 789, 246, "ProcessName", false)]
- [TestCase(123, 456, "ProcessName", 123, 246, "DiffProcessName", false)]
- [TestCase(123, 456, "ProcessName", 789, 456, "ProcessName", false)]
- [TestCase(123, 456, "ProcessName", 789, 246, "DiffProcessName", false)]
- public void EqualsComparesCorrectly(int processIdA, int threadIdA, string processNameA, int processIdB, int threadIdB, string processNameB, bool expectedResult)
- {
- var objectA = new FileLock(processIdA, processNameA, threadIdA, DateTime.Now.Ticks);
- var objectB = new FileLock(processIdB, processNameB, threadIdB, DateTime.Now.Ticks);
- Assert.That(Equals(objectA, objectB), Is.EqualTo(expectedResult));
- }
-
- [Test]
- public void EqualsIgnoresTimestamp()
- {
- var objectA = new FileLock(123, "ProcessName", 456, DateTime.Now.Ticks);
- var objectB = new FileLock(123, "ProcessName", 456, DateTime.Now.Ticks + 5);
- Assert.That(Equals(objectA, objectB), Is.True);
- }
-
- [Test]
- public void EqualsReturnsFalseIfOtherObjectIsNull()
- {
- var fileLock = new FileLock(123, "ProcessName", 456, DateTime.Now.Ticks);
- Assert.That(fileLock.Equals(null), Is.False);
- }
-
- [Test]
- public void EqualsReturnsFalseIfOtherObjectIsDifferentType()
- {
- var fileLock = new FileLock(123, "ProcessName", 456, DateTime.Now.Ticks);
- Assert.That(fileLock.Equals(new object()), Is.False);
- }
-
- [Test]
- public void EqualsReturnsTrueIfSameObject()
- {
- var fileLock = new FileLock(123, "ProcessName", 456, DateTime.Now.Ticks);
- // ReSharper disable once EqualExpressionComparison
- Assert.That(fileLock.Equals(fileLock), Is.True);
- }
-
- [Test]
- public void BelongsToCurrentProcessAndThreadMatchesOnCurrentProcessAndThread()
- {
- var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
- var fileLock = new FileLock(
- currentProcess.Id,
- currentProcess.ProcessName,
- Thread.CurrentThread.ManagedThreadId,
- DateTime.Now.Ticks
- );
- Assert.That(fileLock.BelongsToCurrentProcessAndThread(), Is.True);
- }
-
- [Test]
- public void BelongsToCurrentProcessAndThreadReturnsFalseIfIncorrectProcessId()
- {
- var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
- var fileLockWithIncorrectProcessId = new FileLock(
- -100,
- currentProcess.ProcessName,
- Thread.CurrentThread.ManagedThreadId,
- DateTime.Now.Ticks
- );
- Assert.That(fileLockWithIncorrectProcessId.BelongsToCurrentProcessAndThread(), Is.False);
- }
-
- [Test]
- public void BelongsToCurrentProcessAndThreadReturnsFalseIfIncorrectThreadId()
- {
- var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
- var fileLockWithIncorrectThreadId = new FileLock(
- currentProcess.Id,
- currentProcess.ProcessName,
- -200,
- DateTime.Now.Ticks
- );
- Assert.That(fileLockWithIncorrectThreadId.BelongsToCurrentProcessAndThread(), Is.False);
- }
-
- }
-}
diff --git a/source/Calamari.Tests/Fixtures/Integration/Process/Semaphores/LockFileBasedSemaphoreCreatorFixture.cs b/source/Calamari.Tests/Fixtures/Integration/Process/Semaphores/LockFileBasedSemaphoreCreatorFixture.cs
deleted file mode 100644
index cd2e2f2b4..000000000
--- a/source/Calamari.Tests/Fixtures/Integration/Process/Semaphores/LockFileBasedSemaphoreCreatorFixture.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Calamari.Common.Features.Processes.Semaphores;
-using Calamari.Testing.Helpers;
-using Calamari.Tests.Helpers;
-using NUnit.Framework;
-
-namespace Calamari.Tests.Fixtures.Integration.Process.Semaphores
-{
- [TestFixture]
- public class LockFileBasedSemaphoreCreatorFixture
- {
- [Test]
- public void ReturnsInstanceOfLockFileBasedSemaphore()
- {
- var creator = new LockFileBasedSemaphoreCreator(new InMemoryLog());
- var result = creator.Create("name", TimeSpan.FromSeconds(1));
- Assert.That(result, Is.InstanceOf());
- }
- }
-}
diff --git a/source/Calamari.Tests/Fixtures/Integration/Process/Semaphores/LockFileBasedSemaphoreFixture.cs b/source/Calamari.Tests/Fixtures/Integration/Process/Semaphores/LockFileBasedSemaphoreFixture.cs
deleted file mode 100644
index e07fd6fdb..000000000
--- a/source/Calamari.Tests/Fixtures/Integration/Process/Semaphores/LockFileBasedSemaphoreFixture.cs
+++ /dev/null
@@ -1,282 +0,0 @@
-using System;
-using System.Diagnostics;
-using System.Threading;
-using Calamari.Common.Features.Processes.Semaphores;
-using Calamari.Testing.Helpers;
-using Calamari.Tests.Helpers;
-using FluentAssertions;
-using NSubstitute;
-using NUnit.Framework;
-
-namespace Calamari.Tests.Fixtures.Integration.Process.Semaphores
-{
- [TestFixture]
- public class LockFileBasedSemaphoreFixture
- {
- [Test]
- public void AttemptsToAcquireLockIfLockFileDoesNotExist()
- {
- var lockIo = Substitute.For();
- var name = Guid.NewGuid().ToString();
- var semaphore = new LockFileBasedSemaphore(name, TimeSpan.FromSeconds(30), lockIo, Substitute.For(), new InMemoryLog());
- var fileLock = new FileLock(System.Diagnostics.Process.GetCurrentProcess().Id, Guid.NewGuid().ToString(), Thread.CurrentThread.ManagedThreadId, DateTime.Now.Ticks);
- lockIo.LockExists(Arg.Any()).Returns(false);
- var result = semaphore.ShouldAcquireLock(fileLock);
- Assert.That(result, Is.EqualTo(LockFileBasedSemaphore.AcquireLockAction.AcquireLock));
- }
-
- [Test]
- public void DoesNotAttemptToAcquireLockIfLockFileIsLockedByOtherProcess()
- {
- var lockIo = Substitute.For();
- var name = Guid.NewGuid().ToString();
- var semaphore = new LockFileBasedSemaphore(name, TimeSpan.FromSeconds(30), lockIo, Substitute.For(), new InMemoryLog());
- var fileLock = new OtherProcessHasExclusiveLockOnFileLock();
- lockIo.LockExists(Arg.Any()).Returns(true);
- var result = semaphore.ShouldAcquireLock(fileLock);
- Assert.That(result, Is.EqualTo(LockFileBasedSemaphore.AcquireLockAction.DontAcquireLock));
- }
-
- [Test]
- public void DoesNotAttemptToAcquireLockIfWeCantDeserialise()
- {
- var lockIo = Substitute.For();
- var name = Guid.NewGuid().ToString();
- var semaphore = new LockFileBasedSemaphore(name, TimeSpan.FromSeconds(30), lockIo, Substitute.For(), new InMemoryLog());
- var fileLock = new UnableToDeserialiseLockFile(DateTime.Now);
- lockIo.LockExists(Arg.Any()).Returns(true);
- var result = semaphore.ShouldAcquireLock(fileLock);
- Assert.That(result, Is.EqualTo(LockFileBasedSemaphore.AcquireLockAction.DontAcquireLock));
- }
-
- [Test]
- public void AttemptsToAcquireLockIfWeCantDeserialiseButFileIsOlderThanLockTimeout()
- {
- var log = new InMemoryLog();
- var lockIo = Substitute.For();
- var name = Guid.NewGuid().ToString();
- var semaphore = new LockFileBasedSemaphore(name, TimeSpan.FromSeconds(30), lockIo, Substitute.For(), log);
- var fileLock = new UnableToDeserialiseLockFile(DateTime.Now.Subtract(TimeSpan.FromMinutes(5)));
- lockIo.LockExists(Arg.Any()).Returns(true);
- var result = semaphore.ShouldAcquireLock(fileLock);
- Assert.That(result, Is.EqualTo(LockFileBasedSemaphore.AcquireLockAction.AcquireLock));
- log.Messages.Should().Contain(m => m.Level == InMemoryLog.Level.Warn && m.FormattedMessage == "Lock file existed but was not readable, and has existed for longer than lock timeout. Taking lock.");
- }
-
- [Test]
- public void AttemptsToAcquireLockIfWeCantFindLockFile()
- {
- var lockIo = Substitute.For();
- var name = Guid.NewGuid().ToString();
- var semaphore = new LockFileBasedSemaphore(name, TimeSpan.FromSeconds(30), lockIo, Substitute.For(), new InMemoryLog());
- var fileLock = new MissingFileLock();
- //when we check for the lock file, it exists
- lockIo.LockExists(Arg.Any()).Returns(true);
- //but when we read it, its been released
- var result = semaphore.ShouldAcquireLock(fileLock);
- Assert.That(result, Is.EqualTo(LockFileBasedSemaphore.AcquireLockAction.AcquireLock));
- }
-
- [Test]
- public void AttemptsToAcquireLockIfItBelongsToUs()
- {
- var lockIo = Substitute.For();
- var name = Guid.NewGuid().ToString();
- var semaphore = new LockFileBasedSemaphore(name, TimeSpan.FromSeconds(30), lockIo, Substitute.For(), new InMemoryLog());
- var fileLock = new FileLock(System.Diagnostics.Process.GetCurrentProcess().Id, Guid.NewGuid().ToString(), Thread.CurrentThread.ManagedThreadId, DateTime.Now.Ticks);
- lockIo.LockExists(Arg.Any()).Returns(true);
- var result = semaphore.ShouldAcquireLock(fileLock);
- Assert.That(result, Is.EqualTo(LockFileBasedSemaphore.AcquireLockAction.AcquireLock));
- }
-
- [Test]
- public void AttemptsToAcquireLockIfOtherProcessIsNoLongerRunning()
- {
- var log = new InMemoryLog();
- var lockIo = Substitute.For();
- var name = Guid.NewGuid().ToString();
- var processFinder = Substitute.For();
-
- processFinder.ProcessIsRunning(Arg.Any(), Arg.Any()).Returns(false);
- var semaphore = new LockFileBasedSemaphore(name, TimeSpan.FromSeconds(30), lockIo, processFinder, log);
- var fileLock = new FileLock(-1, Guid.NewGuid().ToString(), -2, DateTime.Now.Ticks);
- lockIo.LockExists(Arg.Any()).Returns(true);
- var result = semaphore.ShouldAcquireLock(fileLock);
- Assert.That(result, Is.EqualTo(LockFileBasedSemaphore.AcquireLockAction.AcquireLock));
- log.Messages.Should().Contain(m => m.Level == InMemoryLog.Level.Warn && m.FormattedMessage == "Process -1, thread -2 had lock, but appears to have crashed. Taking lock.");
- }
-
- [Test]
- public void DoesNotAttemptToAcquireLockIfOwnedBySomeoneElseAndLockHasNotTimedOut()
- {
- var lockIo = Substitute.For();
- var name = Guid.NewGuid().ToString();
- var processFinder = Substitute.For();
- processFinder.ProcessIsRunning(Arg.Any(), Arg.Any()).Returns(true);
- var semaphore = new LockFileBasedSemaphore(name, TimeSpan.FromSeconds(30), lockIo, processFinder, new InMemoryLog());
- var fileLock = new FileLock(0, Guid.NewGuid().ToString(), 0, DateTime.Now.Ticks);
- lockIo.LockExists(Arg.Any()).Returns(true);
- var result = semaphore.ShouldAcquireLock(fileLock);
- Assert.That(result, Is.EqualTo(LockFileBasedSemaphore.AcquireLockAction.DontAcquireLock));
- }
-
- [Test]
- public void AttemptsToForciblyAcquireLockIfOwnedBySomeoneElseAndLockHasTimedOut()
- {
- var log = new InMemoryLog();
- var lockIo = Substitute.For();
- var name = Guid.NewGuid().ToString();
- var processFinder = Substitute.For();
- processFinder.ProcessIsRunning(Arg.Any(), Arg.Any()).Returns(true);
- var semaphore = new LockFileBasedSemaphore(name, TimeSpan.FromSeconds(30), lockIo, processFinder, log);
- var fileLock = new FileLock(0, Guid.NewGuid().ToString(), 0, DateTime.Now.Subtract(TimeSpan.FromMinutes(5)).Ticks);
- lockIo.LockExists(Arg.Any()).Returns(true);
- var result = semaphore.ShouldAcquireLock(fileLock);
- Assert.That(result, Is.EqualTo(LockFileBasedSemaphore.AcquireLockAction.ForciblyAcquireLock));
- log.Messages.Should().Contain(m => m.Level == InMemoryLog.Level.Warn && m.FormattedMessage == "Forcibly taking lock from process 0, thread 0 as lock has timed out. If this happens regularly, please contact Octopus Support.");
- }
-
- [Test]
- public void DeletesLockIfOwnedBySomeoneElseAndLockHasTimedOut()
- {
- var lockIo = Substitute.For();
- var name = Guid.NewGuid().ToString();
- var processFinder = Substitute.For();
- processFinder.ProcessIsRunning(Arg.Any(), Arg.Any()).Returns(true);
- var semaphore = new LockFileBasedSemaphore(name, TimeSpan.FromSeconds(30), lockIo, processFinder, new InMemoryLog());
- //not setting processid/threadid, therefore its someone elses
- var fileLock = new FileLock(0, Guid.NewGuid().ToString(), 0, DateTime.Now.Subtract(TimeSpan.FromMinutes(5)).Ticks);
- lockIo.ReadLock(Arg.Any()).Returns(fileLock);
- lockIo.LockExists(Arg.Any()).Returns(true);
- lockIo.WriteLock(Arg.Any(), Arg.Any()).Returns(true);
- var result = semaphore.TryAcquireLock();
- Assert.That(result, Is.True);
- lockIo.Received().DeleteLock(Arg.Any());
- lockIo.Received().WriteLock(Arg.Any(), Arg.Any());
- }
-
- [Test]
- public void WritesLockFile()
- {
- var lockIo = Substitute.For();
- var name = Guid.NewGuid().ToString();
- var processFinder = Substitute.For();
- processFinder.ProcessIsRunning(Arg.Any(), Arg.Any()).Returns(true);
- var semaphore = new LockFileBasedSemaphore(name, TimeSpan.FromSeconds(30), lockIo, processFinder, new InMemoryLog());
- var fileLock = new FileLock(System.Diagnostics.Process.GetCurrentProcess().Id, Guid.NewGuid().ToString(), Thread.CurrentThread.ManagedThreadId, DateTime.Now.Ticks);
- lockIo.ReadLock(Arg.Any()).Returns(fileLock);
- lockIo.LockExists(Arg.Any()).Returns(true);
- lockIo.WriteLock(Arg.Any(), Arg.Any()).Returns(true);
- var result = semaphore.TryAcquireLock();
- Assert.That(result, Is.True);
- lockIo.DidNotReceive().DeleteLock(Arg.Any());
- lockIo.Received().WriteLock(Arg.Any(), Arg.Any());
- }
-
- [Test]
- public void CanReleaseLockIfWeCanAcquireIt()
- {
- var lockIo = Substitute.For();
- var name = Guid.NewGuid().ToString();
- var semaphore = new LockFileBasedSemaphore(name, TimeSpan.FromSeconds(30), lockIo, Substitute.For(), new InMemoryLog());
- var fileLock = new FileLock(System.Diagnostics.Process.GetCurrentProcess().Id, Guid.NewGuid().ToString(), Thread.CurrentThread.ManagedThreadId, DateTime.Now.Ticks);
- lockIo.ReadLock(Arg.Any()).Returns(fileLock);
- lockIo.LockExists(Arg.Any()).Returns(true);
- lockIo.WriteLock(Arg.Any(), Arg.Any()).Returns(true);
- semaphore.ReleaseLock();
- lockIo.Received().DeleteLock(Arg.Any());
- }
-
- [Test]
- public void CannotReleaseLockIfWeCannotAcquireIt()
- {
- var lockIo = Substitute.For();
- var name = Guid.NewGuid().ToString();
- var semaphore = new LockFileBasedSemaphore(name, TimeSpan.FromSeconds(30), lockIo, Substitute.For(), new InMemoryLog());
- var fileLock = new FileLock(System.Diagnostics.Process.GetCurrentProcess().Id, Guid.NewGuid().ToString(), Thread.CurrentThread.ManagedThreadId, DateTime.Now.Ticks);
- lockIo.ReadLock(Arg.Any()).Returns(fileLock);
- lockIo.LockExists(Arg.Any()).Returns(true);
- //failed to write the lock file (ie, someone else got in first)
- lockIo.WriteLock(Arg.Any(), Arg.Any()).Returns(false);
- semaphore.ReleaseLock();
- lockIo.DidNotReceive().DeleteLock(Arg.Any());
- }
-
- [Test]
- public void WaitOneWithTimeoutTimesOut()
- {
- var lockIo = Substitute.For();
- var name = Guid.NewGuid().ToString();
- var semaphore = new LockFileBasedSemaphore(name, TimeSpan.FromSeconds(30), lockIo, Substitute.For(), new InMemoryLog());
- var fileLock = new FileLock(System.Diagnostics.Process.GetCurrentProcess().Id, Guid.NewGuid().ToString(), Thread.CurrentThread.ManagedThreadId, DateTime.Now.Ticks);
- lockIo.ReadLock(Arg.Any()).Returns(fileLock);
- lockIo.LockExists(Arg.Any()).Returns(true);
- //failed to write the lock file (ie, someone else got in first)
- lockIo.WriteLock(Arg.Any(), Arg.Any()).Returns(false);
-
- var stopwatch = new Stopwatch();
- stopwatch.Start();
-
- var result = semaphore.WaitOne(300);
- Assert.That(result, Is.False);
- Assert.That(stopwatch.ElapsedMilliseconds, Is.GreaterThan(300));
- }
-
- [Test]
- public void WaitOneWithTimeoutDoesNotWaitIfCanAcquireLock()
- {
- var lockIo = Substitute.For();
- var name = Guid.NewGuid().ToString();
- var semaphore = new LockFileBasedSemaphore(name, TimeSpan.FromSeconds(30), lockIo, Substitute.For(), new InMemoryLog());
- var fileLock = new FileLock(System.Diagnostics.Process.GetCurrentProcess().Id, Guid.NewGuid().ToString(), Thread.CurrentThread.ManagedThreadId, DateTime.Now.Ticks);
- lockIo.ReadLock(Arg.Any()).Returns(fileLock);
- lockIo.LockExists(Arg.Any()).Returns(true);
- lockIo.WriteLock(Arg.Any(), Arg.Any()).Returns(true);
-
- var stopwatch = new Stopwatch();
- stopwatch.Start();
-
- var result = semaphore.WaitOne(300);
- Assert.That(result, Is.True);
- Assert.That(stopwatch.ElapsedMilliseconds, Is.LessThan(100));
- }
-
- [Test]
- public void WaitOneWithTimeoutReturnsAfterAquiringLock()
- {
- var lockIo = Substitute.For();
- var name = Guid.NewGuid().ToString();
- var semaphore = new LockFileBasedSemaphore(name, TimeSpan.FromSeconds(30), lockIo, Substitute.For(), new InMemoryLog());
- var fileLock = new FileLock(System.Diagnostics.Process.GetCurrentProcess().Id, Guid.NewGuid().ToString(), Thread.CurrentThread.ManagedThreadId, DateTime.Now.Ticks);
- lockIo.ReadLock(Arg.Any()).Returns(fileLock);
- lockIo.LockExists(Arg.Any()).Returns(true);
- lockIo.WriteLock(Arg.Any(), Arg.Any()).Returns(false, false, false, true);
-
- var stopwatch = new Stopwatch();
- stopwatch.Start();
-
- var result = semaphore.WaitOne(500);
- Assert.That(result, Is.True);
- Assert.That(stopwatch.ElapsedMilliseconds, Is.GreaterThan(200));
- }
-
- [Test]
- public void WaitOneReturnsAfterAquiringLock()
- {
- var lockIo = Substitute.For();
- var name = Guid.NewGuid().ToString();
- var semaphore = new LockFileBasedSemaphore(name, TimeSpan.FromSeconds(30), lockIo, Substitute.For(), new InMemoryLog());
- var fileLock = new FileLock(System.Diagnostics.Process.GetCurrentProcess().Id, Guid.NewGuid().ToString(), Thread.CurrentThread.ManagedThreadId, DateTime.Now.Ticks);
- lockIo.ReadLock(Arg.Any()).Returns(fileLock);
- lockIo.LockExists(Arg.Any()).Returns(true);
- lockIo.WriteLock(Arg.Any(), Arg.Any()).Returns(false, false, true);
-
- var stopwatch = new Stopwatch();
- stopwatch.Start();
-
- var result = semaphore.WaitOne();
- Assert.That(result, Is.True);
- Assert.That(stopwatch.ElapsedMilliseconds, Is.GreaterThan(175));
- }
- }
-}
\ No newline at end of file
diff --git a/source/Calamari.Tests/Fixtures/Integration/Process/Semaphores/LockIoTests.cs b/source/Calamari.Tests/Fixtures/Integration/Process/Semaphores/LockIoTests.cs
deleted file mode 100644
index 3a8913b60..000000000
--- a/source/Calamari.Tests/Fixtures/Integration/Process/Semaphores/LockIoTests.cs
+++ /dev/null
@@ -1,195 +0,0 @@
-using System;
-using System.IO;
-using System.Text;
-using System.Threading;
-using Calamari.Common.Features.Processes.Semaphores;
-using Calamari.Common.Plumbing.FileSystem;
-using Newtonsoft.Json;
-using NSubstitute;
-using NUnit.Framework;
-
-namespace Calamari.Tests.Fixtures.Integration.Process.Semaphores
-{
- [TestFixture]
- public class LockIoTests
- {
- [Test]
- public void ReadLockReturnsMissingFileLockIfFileNotFound()
- {
- var fileSystem = Substitute.For();
- var lockFilePath = "fake path";
- fileSystem.OpenFileExclusively(lockFilePath, FileMode.Open, FileAccess.Read)
- .Returns(x => { throw new FileNotFoundException(); } );
- var lockIo = new LockIo(fileSystem);
- var result = lockIo.ReadLock(lockFilePath);
- Assert.That(result, Is.InstanceOf());
- }
-
- [Test]
- public void ReadLockReturnsOtherProcessHasExclusiveLockIfIoException()
- {
- var fileSystem = Substitute.For();
- var lockFilePath = "fake path";
- fileSystem.OpenFileExclusively(lockFilePath, FileMode.Open, FileAccess.Read)
- .Returns(x => { throw new IOException("Sharing violation"); });
- var lockIo = new LockIo(fileSystem);
- var result = lockIo.ReadLock(lockFilePath);
- Assert.That(result, Is.InstanceOf());
- }
-
- [Test]
- public void ReadLockReturnsUnableToDeserialiseWhenDeserialisationFails()
- {
- var fileSystem = Substitute.For();
- var lockFilePath = "fake path";
- var fileCreationTime = DateTime.Now;
- fileSystem.GetCreationTime(lockFilePath).Returns(fileCreationTime);
-
- var lockIo = new LockIo(fileSystem);
- fileSystem.OpenFileExclusively(lockFilePath, FileMode.Open, FileAccess.Read)
- .Returns(x => { throw new JsonReaderException(); });
- var result = lockIo.ReadLock(lockFilePath);
- Assert.That(result, Is.InstanceOf());
- Assert.That(((UnableToDeserialiseLockFile)result).CreationTime, Is.EqualTo(fileCreationTime));
- }
-
- [Test]
- public void ReadLockReturnsOtherProcessHasExclusiveLockIfUnknownException()
- {
- var fileSystem = Substitute.For();
- var lockFilePath = "fake path";
- var lockIo = new LockIo(fileSystem);
- fileSystem.OpenFileExclusively(lockFilePath, FileMode.Open, FileAccess.Read)
- .Returns(x => { throw new ApplicationException(); });
- var result = lockIo.ReadLock(lockFilePath);
- Assert.That(result, Is.InstanceOf());
- }
-
- [Test]
- public void ReadLockReturnsLockDetailsIfLockBelongsToUs()
- {
- var fileSystem = Substitute.For();
- var lockFilePath = "fake path";
- var lockIo = new LockIo(fileSystem);
- var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
- var fileContent = $"{{\"__type\":\"FileLock:#Calamari.Integration.Processes.Semaphores\",\"ProcessId\":{currentProcess.Id},\"ProcessName\":\"{currentProcess.ProcessName}\",\"ThreadId\":{Thread.CurrentThread.ManagedThreadId},\"Timestamp\":636114372739676700}}";
- fileSystem.OpenFileExclusively(lockFilePath, FileMode.Open, FileAccess.Read)
- .Returns(x => new MemoryStream(Encoding.UTF8.GetBytes(fileContent)));
- var result = lockIo.ReadLock(lockFilePath) as FileLock;
- Assert.That(result, Is.InstanceOf());
- Assert.That(result.ProcessId, Is.EqualTo(currentProcess.Id));
- Assert.That(result.ProcessName, Is.EqualTo(currentProcess.ProcessName));
- Assert.That(result.ThreadId, Is.EqualTo(Thread.CurrentThread.ManagedThreadId));
- Assert.That(result.Timestamp, Is.EqualTo(636114372739676700));
- }
-
- [Test]
- public void ReadLockReturnsOtherProcessOwnsFileLockIfLockBelongsToSomeoneElse()
- {
- var fileSystem = Substitute.For();
- var lockFilePath = "fake path";
- var lockIo = new LockIo(fileSystem);
- var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
- var fileContent = $"{{\"__type\":\"FileLock:#Calamari.Integration.Processes.Semaphores\",\"ProcessId\":{currentProcess.Id + 1},\"ProcessName\":\"{currentProcess.ProcessName}\",\"ThreadId\":{Thread.CurrentThread.ManagedThreadId},\"Timestamp\":636114372739676700}}";
- fileSystem.OpenFileExclusively(lockFilePath, FileMode.Open, FileAccess.Read)
- .Returns(x => new MemoryStream(Encoding.UTF8.GetBytes(fileContent)));
- var result = lockIo.ReadLock(lockFilePath) as OtherProcessOwnsFileLock;
- Assert.That(result, Is.InstanceOf());
- Assert.That(result.ProcessId, Is.EqualTo(currentProcess.Id + 1));
- Assert.That(result.ProcessName, Is.EqualTo(currentProcess.ProcessName));
- Assert.That(result.ThreadId, Is.EqualTo(Thread.CurrentThread.ManagedThreadId));
- Assert.That(result.Timestamp, Is.EqualTo(636114372739676700));
- }
-
- [Test]
- public void DeleteLockSwallowsExceptions()
- {
- var fileSystem = Substitute.For();
- var lockFilePath = "fake path";
- var lockIo = new LockIo(fileSystem);
- fileSystem.When(x => x.DeleteFile(lockFilePath)).Do(x => { throw new Exception("failed to delete file"); });
- Assert.DoesNotThrow(() => lockIo.DeleteLock(lockFilePath));
- }
-
- [Test]
- public void WriteLockDoesNotOverwriteLockFileIfItsIdenticalToWhatWeAreWantingToWrite()
- {
- var fileSystem = Substitute.For();
- var lockFilePath = "fake path";
- var lockIo = new LockIo(fileSystem);
- fileSystem.FileExists(lockFilePath).Returns(true);
- var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
- var fileContent = $"{{\"__type\":\"FileLock:#Calamari.Integration.Processes.Semaphores\",\"ProcessId\":{currentProcess.Id},\"ProcessName\":\"{currentProcess.ProcessName}\",\"ThreadId\":{Thread.CurrentThread.ManagedThreadId},\"Timestamp\":636114372739676700}}";
- fileSystem.OpenFileExclusively(lockFilePath, FileMode.Open, FileAccess.Read)
- .Returns(x => new MemoryStream(Encoding.UTF8.GetBytes(fileContent)));
- var lockFile = (FileLock)lockIo.ReadLock(lockFilePath);
- lockIo.WriteLock(lockFilePath, lockFile);
- }
-
- [Test]
- public void WriteLockOverwritesLockFileIfTimestampIsDifferent()
- {
- var fileSystem = Substitute.For();
- var lockFilePath = "fake path";
- var lockIo = new LockIo(fileSystem);
- fileSystem.FileExists(lockFilePath).Returns(true);
- var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
- var fileContent = $"{{\"__type\":\"FileLock:#Calamari.Integration.Processes.Semaphores\",\"ProcessId\":{currentProcess.Id},\"ProcessName\":\"{currentProcess.ProcessName}\",\"ThreadId\":{Thread.CurrentThread.ManagedThreadId},\"Timestamp\":636114372739676700}}";
- fileSystem.OpenFileExclusively(lockFilePath, FileMode.Open, FileAccess.Read)
- .Returns(x => new MemoryStream(Encoding.UTF8.GetBytes(fileContent)));
- var lockFile = (FileLock)lockIo.ReadLock(lockFilePath);
- lockFile.Timestamp = lockFile.Timestamp + 1;
- fileSystem.OpenFileExclusively(lockFilePath, FileMode.Create, FileAccess.Write)
- .Returns(x => new MemoryStream());
- var result = lockIo.WriteLock(lockFilePath, lockFile);
- Assert.That(result, Is.True);
- }
-
- [Test]
- public void WriteLockDeletesLockIfUnableToDeserialise()
- {
- var fileSystem = Substitute.For();
- var lockFilePath = "fake path";
- var lockIo = new LockIo(fileSystem);
- fileSystem.FileExists(lockFilePath).Returns(true);
- var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
- var expectedFileContent = $"{{\"__type\":\"FileLock:#Calamari.Integration.Processes.Semaphores\",\"ProcessId\":{currentProcess.Id},\"ProcessName\":\"{currentProcess.ProcessName}\",\"ThreadId\":{Thread.CurrentThread.ManagedThreadId},\"Timestamp\":636114372739676700}}";
-
- fileSystem.OpenFileExclusively(lockFilePath, FileMode.Open, FileAccess.Read)
- .Returns(x => new MemoryStream(Encoding.UTF8.GetBytes("non deserialisable content")), x => new MemoryStream(Encoding.UTF8.GetBytes(expectedFileContent)));
- fileSystem.OpenFileExclusively(lockFilePath, FileMode.CreateNew, FileAccess.Write)
- .Returns(x => new MemoryStream());
- var result = lockIo.WriteLock(lockFilePath, new FileLock(currentProcess.Id, currentProcess.ProcessName, Thread.CurrentThread.ManagedThreadId, 636114372739676700));
- fileSystem.Received().DeleteFile(lockFilePath);
- Assert.That(result, Is.True);
- }
-
- [Test]
- public void WriteLockReturnsFalseIfIoException()
- {
- var fileSystem = Substitute.For();
- var lockFilePath = "fake path";
- var lockIo = new LockIo(fileSystem);
- fileSystem.FileExists(lockFilePath).Returns(true);
- fileSystem.OpenFileExclusively(lockFilePath, Arg.Any(), Arg.Any())
- .Returns(x => { throw new IOException("Sharing exception"); });
- var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
- var result = lockIo.WriteLock(lockFilePath, new FileLock(currentProcess.Id, currentProcess.ProcessName, Thread.CurrentThread.ManagedThreadId, 636114372739676700));
- Assert.That(result, Is.False);
- }
-
- [Test]
- public void WriteLockReturnsFalseIfUnknownException()
- {
- var fileSystem = Substitute.For();
- var lockFilePath = "fake path";
- var lockIo = new LockIo(fileSystem);
- fileSystem.FileExists(lockFilePath).Returns(true);
- fileSystem.OpenFileExclusively(lockFilePath, Arg.Any(), Arg.Any())
- .Returns(x => { throw new Exception("Unknown exception"); });
- var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
- var result = lockIo.WriteLock(lockFilePath, new FileLock(currentProcess.Id, currentProcess.ProcessName, Thread.CurrentThread.ManagedThreadId, 636114372739676700));
- Assert.That(result, Is.False);
- }
- }
-}
diff --git a/source/Calamari.Tests/Fixtures/Integration/Process/Semaphores/ProcessFinderFixture.cs b/source/Calamari.Tests/Fixtures/Integration/Process/Semaphores/ProcessFinderFixture.cs
deleted file mode 100644
index f3db0ce85..000000000
--- a/source/Calamari.Tests/Fixtures/Integration/Process/Semaphores/ProcessFinderFixture.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Calamari.Common.Features.Processes.Semaphores;
-using NUnit.Framework;
-
-namespace Calamari.Tests.Fixtures.Integration.Process.Semaphores
-{
- [TestFixture]
- public class ProcessFinderFixture
- {
- [Test]
- public void ProcessIsRunningReturnsTrueForCurrentProcess()
- {
- var processFinder = new ProcessFinder();
- var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
- var result = processFinder.ProcessIsRunning(currentProcess.Id, currentProcess.ProcessName);
- Assert.That(result, Is.True);
- }
-
- [Test]
- public void ProcessIsRunningReturnsFalseForNonExistantProcess()
- {
- var processFinder = new ProcessFinder();
- var result = processFinder.ProcessIsRunning(-1, Guid.NewGuid().ToString());
- Assert.That(result, Is.EqualTo(GetExpectedResult()));
- }
-
- private bool GetExpectedResult()
- {
- try
- {
- var processes = System.Diagnostics.Process.GetProcesses();
- return false;
- }
- catch (NotSupportedException)
- {
- //not supported on FreeBSD. Probably a nicer way to do this.
- return true;
- }
- }
- }
-}
diff --git a/source/Calamari.Tests/Fixtures/Integration/Process/Semaphores/SemaphoreFactoryFixture.cs b/source/Calamari.Tests/Fixtures/Integration/Process/Semaphores/SemaphoreFactoryFixture.cs
deleted file mode 100644
index 6aa8a0023..000000000
--- a/source/Calamari.Tests/Fixtures/Integration/Process/Semaphores/SemaphoreFactoryFixture.cs
+++ /dev/null
@@ -1,41 +0,0 @@
-using Calamari.Common.Features.Processes.Semaphores;
-using Calamari.Common.Plumbing;
-using Calamari.Testing.Helpers;
-using Calamari.Testing.Requirements;
-using Calamari.Tests.Helpers;
-using NUnit.Framework;
-
-namespace Calamari.Tests.Fixtures.Integration.Process.Semaphores
-{
- [TestFixture]
- public class SemaphoreFactoryFixture
- {
- [Test]
- public void ReturnsFileBasedSemaphoreManagerForMono()
- {
- if (!CalamariEnvironment.IsRunningOnMono)
- Assert.Ignore("This test is designed to run on mono");
- var result = SemaphoreFactory.Get();
- Assert.That(result, Is.InstanceOf());
- }
-
-#if NETFX
- [Test]
- [Category(TestCategory.CompatibleOS.OnlyWindows)]
- public void ReturnsSystemSemaphoreManagerForWindows()
- {
- if (!CalamariEnvironment.IsRunningOnWindows)
- Assert.Ignore("This test is designed to run on windows");
- var result = SemaphoreFactory.Get();
- Assert.That(result, Is.InstanceOf());
- }
-#else
- [Test]
- public void ReturnsSystemSemaphoreManagerForAllPlatformsUnderNetCore()
- {
- var result = SemaphoreFactory.Get();
- Assert.That(result, Is.InstanceOf());
- }
-#endif
- }
-}
\ No newline at end of file
diff --git a/source/Calamari.Tests/Fixtures/PackageDownload/PackageDownloadFixture.cs b/source/Calamari.Tests/Fixtures/PackageDownload/PackageDownloadFixture.cs
index cdea751c2..9bb7a85c0 100644
--- a/source/Calamari.Tests/Fixtures/PackageDownload/PackageDownloadFixture.cs
+++ b/source/Calamari.Tests/Fixtures/PackageDownload/PackageDownloadFixture.cs
@@ -99,9 +99,6 @@ public void ShouldDownloadPackage()
[RequiresNonFreeBSDPlatform]
public void ShouldDownloadMavenPackage()
{
- if (CalamariEnvironment.IsRunningOnMac && TestEnvironment.IsCI && !CalamariEnvironment.IsRunningOnMono)
- Assert.Inconclusive("As of November 2018, this test is failing under dotnet core on the cloudmac under teamcity - we were getting an error 'SSL connect error' when trying to download from 'https://repo.maven.apache.org/maven2/'. Marking as inconclusive so we can re-enable the build - it had been disabled for months :(");
-
var result = DownloadPackage(
MavenPublicFeed.PackageId,
MavenPublicFeed.Version.ToString(),
@@ -127,9 +124,6 @@ public void ShouldDownloadMavenPackage()
[RequiresNonFreeBSDPlatform]
public void ShouldDownloadMavenSnapshotPackage()
{
- if (CalamariEnvironment.IsRunningOnMac && TestEnvironment.IsCI && !CalamariEnvironment.IsRunningOnMono)
- Assert.Inconclusive("As of November 2018, this test is failing under dotnet core on the cloudmac under teamcity - we were getting an error 'SSL connect error' when trying to download from 'https://repo.maven.apache.org/maven2/'. Marking as inconclusive so we can re-enable the build - it had been disabled for months :(");
-
var result = DownloadPackage(
MavenPublicFeed.PackageId,
MavenPublicFeed.Version.ToString(),
@@ -193,9 +187,6 @@ public void ShouldUsePackageFromCache()
[RequiresNonFreeBSDPlatform]
public void ShouldUseMavenPackageFromCache()
{
- if (CalamariEnvironment.IsRunningOnMac && TestEnvironment.IsCI && !CalamariEnvironment.IsRunningOnMono)
- Assert.Inconclusive("As of November 2018, this test is failing under dotnet core on the cloudmac under teamcity - we were getting an error 'SSL connect error' when trying to download from 'https://repo.maven.apache.org/maven2/'. Marking as inconclusive so we can re-enable the build - it had been disabled for months :(");
-
DownloadPackage(MavenPublicFeed.PackageId,
MavenPublicFeed.Version.ToString(),
MavenPublicFeed.Id,
@@ -224,9 +215,6 @@ public void ShouldUseMavenPackageFromCache()
[RequiresNonFreeBSDPlatform]
public void ShouldUseMavenSnapshotPackageFromCache()
{
- if (CalamariEnvironment.IsRunningOnMac && TestEnvironment.IsCI && !CalamariEnvironment.IsRunningOnMono)
- Assert.Inconclusive("As of November 2018, this test is failing under dotnet core on the cloudmac under teamcity - we were getting an error 'SSL connect error' when trying to download from 'https://repo.maven.apache.org/maven2/'. Marking as inconclusive so we can re-enable the build - it had been disabled for months :(");
-
DownloadPackage(MavenPublicFeed.PackageId,
MavenPublicFeed.Version.ToString(),
MavenPublicFeed.Id,
@@ -276,9 +264,6 @@ public void ShouldByPassCacheAndDownloadPackage()
[RequiresNonFreeBSDPlatform]
public void ShouldByPassCacheAndDownloadMavenPackage()
{
- if (CalamariEnvironment.IsRunningOnMac && TestEnvironment.IsCI && !CalamariEnvironment.IsRunningOnMono)
- Assert.Inconclusive("As of November 2018, this test is failing under dotnet core on the cloudmac under teamcity - we were getting an error 'SSL connect error' when trying to download from 'https://repo.maven.apache.org/maven2/'. Marking as inconclusive so we can re-enable the build - it had been disabled for months :(");
-
var firstDownload = DownloadPackage(
MavenPublicFeed.PackageId,
MavenPublicFeed.Version.ToString(),
diff --git a/source/Calamari.Tests/KubernetesFixtures/Approved/KubernetesContextScriptWrapperLiveFixtureEks.DeployRawYaml_WithMultipleYamlFilesGlobPatterns_YamlFilesAppliedInCorrectBatches.ApplyingBatches.approved.txt b/source/Calamari.Tests/KubernetesFixtures/Approved/KubernetesContextScriptWrapperLiveFixtureEks.DeployRawYaml_WithMultipleYamlFilesGlobPatterns_YamlFilesAppliedInCorrectBatches.ApplyingBatches.approved.txt
index 68433ed41..ba716b1d1 100644
--- a/source/Calamari.Tests/KubernetesFixtures/Approved/KubernetesContextScriptWrapperLiveFixtureEks.DeployRawYaml_WithMultipleYamlFilesGlobPatterns_YamlFilesAppliedInCorrectBatches.ApplyingBatches.approved.txt
+++ b/source/Calamari.Tests/KubernetesFixtures/Approved/KubernetesContextScriptWrapperLiveFixtureEks.DeployRawYaml_WithMultipleYamlFilesGlobPatterns_YamlFilesAppliedInCorrectBatches.ApplyingBatches.approved.txt
@@ -11,7 +11,7 @@ Applying Batch #2 for YAML matching 'services/myapp-service.yml'
Matched file: services/myapp-service.yml
Created Resources:
- Service/nginx-service in namespace calamari-testing
-Resource Status Check: reported 8 updates, 0 removals
+Resource Status Check: reported 9 updates, 0 removals
Resource Status Check: 1 new resources have been added:
- Service/nginx-service in namespace calamari-testing
Applying Batch #3 for YAML matching 'configmaps/*.yml'
@@ -20,7 +20,7 @@ Matched file: configmaps/myapp-configmap1.yml
Created Resources:
- ConfigMap/game-demo in namespace calamari-testing
- ConfigMap/game-demo2 in namespace calamari-testing
-Resource Status Check: reported 10 updates, 0 removals
+Resource Status Check: reported 11 updates, 0 removals
Resource Status Check: 2 new resources have been added:
- ConfigMap/game-demo in namespace calamari-testing
- ConfigMap/game-demo2 in namespace calamari-testing
\ No newline at end of file
diff --git a/source/Calamari.Tests/KubernetesFixtures/Commands/Executors/GatherAndApplyRawYamlExecutorTests.cs b/source/Calamari.Tests/KubernetesFixtures/Commands/Executors/GatherAndApplyRawYamlExecutorTests.cs
index 9dd568053..d1f32a8c3 100644
--- a/source/Calamari.Tests/KubernetesFixtures/Commands/Executors/GatherAndApplyRawYamlExecutorTests.cs
+++ b/source/Calamari.Tests/KubernetesFixtures/Commands/Executors/GatherAndApplyRawYamlExecutorTests.cs
@@ -6,7 +6,6 @@
using Calamari.Common.Commands;
using Calamari.Common.Features.Processes;
using Calamari.Common.Plumbing.FileSystem;
-using Calamari.Common.Plumbing.ServiceMessages;
using Calamari.Common.Plumbing.Variables;
using Calamari.Kubernetes;
using Calamari.Kubernetes.Commands;
@@ -27,6 +26,7 @@ public class GatherAndApplyRawYamlExecutorTests
{
readonly ICalamariFileSystem fileSystem = TestCalamariPhysicalFileSystem.GetPhysicalFileSystem();
readonly ICommandLineRunner commandLineRunner = Substitute.For();
+ readonly IManifestReporter manifestReporter = Substitute.For();
InMemoryLog log;
List receivedCallbacks;
@@ -337,7 +337,8 @@ void SetupCommandLineRunnerMocks()
IRawYamlKubernetesApplyExecutor CreateExecutor(IVariables variables, ICalamariFileSystem fs)
{
var kubectl = new Kubectl(variables, log, commandLineRunner);
- return new GatherAndApplyRawYamlExecutor(log, fs, kubectl);
+
+ return new GatherAndApplyRawYamlExecutor(log, fs, manifestReporter, kubectl);
}
Task RecordingCallback(ResourceIdentifier[] identifiers)
diff --git a/source/Calamari.Tests/KubernetesFixtures/InstallTools.cs b/source/Calamari.Tests/KubernetesFixtures/InstallTools.cs
index d699ef83d..c286a6e13 100644
--- a/source/Calamari.Tests/KubernetesFixtures/InstallTools.cs
+++ b/source/Calamari.Tests/KubernetesFixtures/InstallTools.cs
@@ -164,7 +164,7 @@ await Download(Path.Combine(destinationDirectoryName, GetAWSCliFileName()),
$"/a {awsInstaller} /qn TARGETDIR={destinationDirectoryName}\\extract",
destinationDirectoryName);
}
- else if (CalamariEnvironment.IsRunningOnNix && !CalamariEnvironment.IsRunningOnMono)
+ else if (CalamariEnvironment.IsRunningOnNix)
{
ExecuteCommandAndReturnResult("sudo",
"apt-get install unzip",
diff --git a/source/Calamari.Tests/KubernetesFixtures/KubernetesContextScriptWrapperLiveFixture.cs b/source/Calamari.Tests/KubernetesFixtures/KubernetesContextScriptWrapperLiveFixture.cs
index 8b5cb8cd2..8ce52c24d 100644
--- a/source/Calamari.Tests/KubernetesFixtures/KubernetesContextScriptWrapperLiveFixture.cs
+++ b/source/Calamari.Tests/KubernetesFixtures/KubernetesContextScriptWrapperLiveFixture.cs
@@ -141,7 +141,7 @@ async Task RunTerraformInternal(string terraformWorkingFolder, Dictionar
TestContext.Error.WriteLine(e);
});
- result.ExitCode.Should().Be(0, because: $"`terraform {args[0]}` should run without error and exit cleanly during infrastructure setup");
+ result.ExitCode.Should().Be(0, because: $"`terraform {args[0]}` should run without error and exit cleanly during infrastructure setup. Error output: \\r\\n{{result.ErrorOutput}}\");");
return stdOut.ToString().Trim(Environment.NewLine.ToCharArray());
}
diff --git a/source/Calamari.Tests/KubernetesFixtures/KubernetesContextScriptWrapperLiveFixtureBase.cs b/source/Calamari.Tests/KubernetesFixtures/KubernetesContextScriptWrapperLiveFixtureBase.cs
index f86852bd5..588e34cc4 100644
--- a/source/Calamari.Tests/KubernetesFixtures/KubernetesContextScriptWrapperLiveFixtureBase.cs
+++ b/source/Calamari.Tests/KubernetesFixtures/KubernetesContextScriptWrapperLiveFixtureBase.cs
@@ -27,6 +27,7 @@ namespace Calamari.Tests.KubernetesFixtures
public abstract class KubernetesContextScriptWrapperLiveFixtureBase : CalamariFixture
{
protected const string TestNamespace = "calamari-testing";
+ protected const string StaticTestResourcePrefix = "calamari-testing-static";
protected IVariables variables;
protected string testFolder;
diff --git a/source/Calamari.Tests/KubernetesFixtures/KubernetesContextScriptWrapperLiveFixtureEks.cs b/source/Calamari.Tests/KubernetesFixtures/KubernetesContextScriptWrapperLiveFixtureEks.cs
index fec8b04d8..ad1b5fbcc 100644
--- a/source/Calamari.Tests/KubernetesFixtures/KubernetesContextScriptWrapperLiveFixtureEks.cs
+++ b/source/Calamari.Tests/KubernetesFixtures/KubernetesContextScriptWrapperLiveFixtureEks.cs
@@ -65,6 +65,10 @@ public class KubernetesContextScriptWrapperLiveFixtureEks: KubernetesContextScri
private const string SimpleConfigMap2ResourceName = "game-demo2";
private const string SimpleConfigMap2 =
"apiVersion: v1\nkind: ConfigMap\nmetadata:\n name: game-demo2\ndata:\n player_initial_lives: '1'\n ui_properties_file_name: 'user-interface.properties'\n game.properties: |\n enemy.types=blobs,foxes\n player.maximum-lives=10\n user-interface.properties: |\n color.good=orange\n color.bad=pink\n allow.textmode=false";
+
+ string awsAccessKey;
+ string awsSecretKey;
+
string eksClientID;
string eksSecretKey;
string eksClusterEndpoint;
@@ -81,7 +85,7 @@ public class KubernetesContextScriptWrapperLiveFixtureEks: KubernetesContextScri
protected override async Task PreInitialise()
{
- region = RegionRandomiser.GetARegion();
+ region = "ap-southeast-1";
await TestContext.Progress.WriteLineAsync($"Aws Region chosen: {region}");
}
@@ -112,12 +116,16 @@ protected override void ExtractVariablesFromTerraformOutput(JObject jsonOutput)
protected override async Task> GetEnvironmentVars(CancellationToken cancellationToken)
{
+ awsAccessKey = await ExternalVariables.Get(ExternalVariable.AwsCloudFormationAndS3AccessKey, cancellationToken);
+ awsSecretKey = await ExternalVariables.Get(ExternalVariable.AwsCloudFormationAndS3SecretKey, cancellationToken);
+
return new Dictionary
{
- { "AWS_ACCESS_KEY_ID", await ExternalVariables.Get(ExternalVariable.AwsCloudFormationAndS3AccessKey, cancellationToken) },
- { "AWS_SECRET_ACCESS_KEY", await ExternalVariables.Get(ExternalVariable.AwsCloudFormationAndS3SecretKey, cancellationToken) },
+ { "AWS_ACCESS_KEY_ID", awsAccessKey },
+ { "AWS_SECRET_ACCESS_KEY", awsSecretKey },
{ "AWS_DEFAULT_REGION", region },
- { "TF_VAR_tests_source_dir", testFolder }
+ { "TF_VAR_tests_source_dir", testFolder },
+ { "TF_VAR_static_resource_prefix", StaticTestResourcePrefix }
};
}
@@ -134,7 +142,7 @@ public void DeployRawYaml_WithRawYamlDeploymentScriptOrCommand_OutputShouldIndic
var objectStatusUpdates = Log.Messages.GetServiceMessagesOfType("k8s-status");
- objectStatusUpdates.Where(m => m.Properties["status"] == "Successful").Should().HaveCount(5);
+ objectStatusUpdates.Where(m => m.Properties["status"] == "Successful").Should().HaveCount(6);
rawLogs.Should().ContainSingle(m =>
m.Contains("Resource status check completed successfully because all resources are deployed successfully"));
@@ -383,8 +391,8 @@ public void DiscoverKubernetesClusterWithEnvironmentVariableCredentialsAndNoIamR
try
{
- Environment.SetEnvironmentVariable(accessKeyEnvVar, eksClientID);
- Environment.SetEnvironmentVariable(secretKeyEnvVar, eksSecretKey);
+ Environment.SetEnvironmentVariable(accessKeyEnvVar, awsAccessKey);
+ Environment.SetEnvironmentVariable(secretKeyEnvVar, awsSecretKey);
var authenticationDetails = new AwsAuthenticationDetails
{
@@ -428,8 +436,8 @@ public void DiscoverKubernetesClusterWithAwsAccountCredentialsAndNoIamRole()
{
Account = new AwsAccessKeyCredentials
{
- AccessKey = eksClientID,
- SecretKey = eksSecretKey
+ AccessKey = awsAccessKey,
+ SecretKey = awsSecretKey
},
AccountId = "Accounts-1",
Type = "account"
@@ -608,15 +616,15 @@ private void SetVariablesToAuthoriseWithAmazonAccount()
variables.Set(KubernetesSpecialVariables.EksClusterName, eksClusterName);
variables.Set("Octopus.Action.Aws.Region", region);
variables.Set("Octopus.Action.AwsAccount.Variable", account);
- variables.Set($"{account}.AccessKey", eksClientID);
- variables.Set($"{account}.SecretKey", eksSecretKey);
+ variables.Set($"{account}.AccessKey", awsAccessKey);
+ variables.Set($"{account}.SecretKey", awsSecretKey);
variables.Set("Octopus.Action.Kubernetes.CertificateAuthority", certificateAuthority);
variables.Set($"{certificateAuthority}.CertificatePem", eksClusterCaCertificate);
}
private void SetVariablesForRawYamlCommand(string globPaths)
{
- variables.Set("Octopus.Action.KubernetesContainers.Namespace", "nginx");
+ variables.Set("Octopus.Action.KubernetesContainers.Namespace", "nginx-2");
variables.Set(KnownVariables.Package.JsonConfigurationVariablesTargets, "**/*.{yml,yaml}");
variables.Set(KubernetesSpecialVariables.CustomResourceYamlFileName, globPaths);
}
diff --git a/source/Calamari.Tests/KubernetesFixtures/KubernetesContextScriptWrapperLiveFixtureGke.cs b/source/Calamari.Tests/KubernetesFixtures/KubernetesContextScriptWrapperLiveFixtureGke.cs
index 240cdda1f..990ec36a6 100644
--- a/source/Calamari.Tests/KubernetesFixtures/KubernetesContextScriptWrapperLiveFixtureGke.cs
+++ b/source/Calamari.Tests/KubernetesFixtures/KubernetesContextScriptWrapperLiveFixtureGke.cs
@@ -53,7 +53,8 @@ protected override async Task> GetEnvironmentVars(Can
return new Dictionary
{
{ "GOOGLE_CLOUD_KEYFILE_JSON", await ExternalVariables.Get(ExternalVariable.GoogleCloudJsonKeyfile, cancellationToken) },
- { "USE_GKE_GCLOUD_AUTH_PLUGIN", "True" }
+ { "USE_GKE_GCLOUD_AUTH_PLUGIN", "True" },
+ { "TF_VAR_static_resource_prefix", StaticTestResourcePrefix }
};
}
diff --git a/source/Calamari.Tests/KubernetesFixtures/ManifestReporterTests.cs b/source/Calamari.Tests/KubernetesFixtures/ManifestReporterTests.cs
new file mode 100644
index 000000000..b5b7d674d
--- /dev/null
+++ b/source/Calamari.Tests/KubernetesFixtures/ManifestReporterTests.cs
@@ -0,0 +1,116 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using Calamari.Common.Plumbing.FileSystem;
+using Calamari.Common.Plumbing.ServiceMessages;
+using Calamari.Common.Plumbing.Variables;
+using Calamari.Kubernetes;
+using Calamari.Testing.Helpers;
+using FluentAssertions;
+using NUnit.Framework;
+
+namespace Calamari.Tests.KubernetesFixtures
+{
+ [TestFixture]
+ public class ManifestReporterTests
+ {
+ [Test]
+ public void GivenValidYaml_ShouldPostSingleServiceMessage()
+ {
+ var memoryLog = new InMemoryLog();
+ var variables = new CalamariVariables();
+
+ var yaml = @"foo: bar";
+ var expectedJson = "{\"foo\": \"bar\"}";
+ using (CreateFile(yaml, out var filePath))
+ {
+ var mr = new ManifestReporter(variables, CalamariPhysicalFileSystem.GetPhysicalFileSystem(), memoryLog);
+
+ mr.ReportManifestApplied(filePath);
+
+ var expected = ServiceMessage.Create(SpecialVariables.ServiceMessageNames.ManifestApplied.Name, ("ns", "default"), ("manifest", expectedJson));
+ memoryLog.ServiceMessages.Should().BeEquivalentTo(new List { expected });
+ }
+ }
+
+ [Test]
+ public void GivenInValidManifest_ShouldNotPostServiceMessage()
+ {
+ var memoryLog = new InMemoryLog();
+ var variables = new CalamariVariables();
+
+ var yaml = @"text - Bar";
+ using (CreateFile(yaml, out var filePath))
+ {
+ var mr = new ManifestReporter(variables, CalamariPhysicalFileSystem.GetPhysicalFileSystem(), memoryLog);
+
+ mr.ReportManifestApplied(filePath);
+
+ memoryLog.ServiceMessages.Should().BeEmpty();
+ }
+ }
+
+ [Test]
+ public void GivenNamespaceInManifest_ShouldReportManifestNamespace()
+ {
+ var memoryLog = new InMemoryLog();
+ var variables = new CalamariVariables();
+ var yaml = @"metadata:
+ name: game-demo
+ namespace: XXX";
+ using (CreateFile(yaml, out var filePath))
+ {
+ var variableNs = Some.String();
+ variables.Set(SpecialVariables.Namespace, variableNs);
+ var mr = new ManifestReporter(variables, CalamariPhysicalFileSystem.GetPhysicalFileSystem(), memoryLog);
+
+ mr.ReportManifestApplied(filePath);
+
+ memoryLog.ServiceMessages.First().Properties.Should().Contain(new KeyValuePair("ns", "XXX"));
+ }
+ }
+
+ [Test]
+ public void GivenNamespaceNotInManifest_ShouldReportVariableNamespace()
+ {
+ var memoryLog = new InMemoryLog();
+ var variables = new CalamariVariables();
+ var yaml = @"foo: bar";
+ using (CreateFile(yaml, out var filePath))
+ {
+ var variableNs = Some.String();
+ variables.Set(SpecialVariables.Namespace, variableNs);
+ var mr = new ManifestReporter(variables, CalamariPhysicalFileSystem.GetPhysicalFileSystem(), memoryLog);
+
+ mr.ReportManifestApplied(filePath);
+
+ memoryLog.ServiceMessages.First().Properties.Should().Contain(new KeyValuePair("ns", variableNs));
+ }
+ }
+
+ [Test]
+ public void GiveNoNamespaces_ShouldDefaultNamespace()
+ {
+ var memoryLog = new InMemoryLog();
+ var variables = new CalamariVariables();
+ var yaml = @"foo: bar";
+ using (CreateFile(yaml, out var filePath))
+ {
+ var mr = new ManifestReporter(variables, CalamariPhysicalFileSystem.GetPhysicalFileSystem(), memoryLog);
+
+ mr.ReportManifestApplied(filePath);
+
+ memoryLog.ServiceMessages.First().Properties.Should().Contain(new KeyValuePair("ns", "default"));
+ }
+ }
+
+ IDisposable CreateFile(string yaml, out string filePath)
+ {
+ var tempDir = TemporaryDirectory.Create();
+ filePath = Path.Combine(tempDir.DirectoryPath, $"{Guid.NewGuid():d}.tmp");
+ File.WriteAllText(filePath, yaml);
+ return tempDir;
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/Calamari.Tests/KubernetesFixtures/ResourceStatus/ResourceUpdateReporterTests.cs b/source/Calamari.Tests/KubernetesFixtures/ResourceStatus/ResourceUpdateReporterTests.cs
index 9c1b461bb..7337eaa16 100644
--- a/source/Calamari.Tests/KubernetesFixtures/ResourceStatus/ResourceUpdateReporterTests.cs
+++ b/source/Calamari.Tests/KubernetesFixtures/ResourceStatus/ResourceUpdateReporterTests.cs
@@ -29,7 +29,7 @@ public void ReportsCreatedResourcesCorrectly()
reporter.ReportUpdatedResources(originalStatuses, newStatuses, 1);
var serviceMessages = log.ServiceMessages
- .Where(message => message.Name == SpecialVariables.KubernetesResourceStatusServiceMessageName)
+ .Where(message => message.Name == SpecialVariables.ServiceMessageNames.ResourceStatus.Name)
.ToList();
serviceMessages.Select(message => message.Properties["name"])
@@ -72,7 +72,7 @@ public void ReportsUpdatedResourcesCorrectly()
reporter.ReportUpdatedResources(originalStatuses, newStatuses, 1);
var serviceMessages = log.ServiceMessages
- .Where(message => message.Name == SpecialVariables.KubernetesResourceStatusServiceMessageName)
+ .Where(message => message.Name == SpecialVariables.ServiceMessageNames.ResourceStatus.Name)
.ToList();
serviceMessages.Should().ContainSingle().Which.Properties
@@ -102,7 +102,7 @@ public void ReportsRemovedResourcesCorrectly()
reporter.ReportUpdatedResources(originalStatuses, newStatuses, 1);
var serviceMessages = log.ServiceMessages
- .Where(message => message.Name == SpecialVariables.KubernetesResourceStatusServiceMessageName)
+ .Where(message => message.Name == SpecialVariables.ServiceMessageNames.ResourceStatus.Name)
.ToList();
serviceMessages.Should().ContainSingle().Which.Properties
@@ -129,7 +129,7 @@ public void ClusterScopedResourcesAreIgnored()
reporter.ReportUpdatedResources(new Dictionary(), newStatuses, 1);
var serviceMessages = log.ServiceMessages
- .Where(message => message.Name == SpecialVariables.KubernetesResourceStatusServiceMessageName)
+ .Where(message => message.Name == SpecialVariables.ServiceMessageNames.ResourceStatus.Name)
.ToList();
serviceMessages.Should().ContainSingle().Which.Properties
diff --git a/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/eks.tf b/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/eks.tf
index 14e615081..fd9e78242 100644
--- a/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/eks.tf
+++ b/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/eks.tf
@@ -1,228 +1,41 @@
-resource "aws_security_group" "cluster" {
- ingress = [{
- cidr_blocks = ["0.0.0.0/0"]
- description = ""
- from_port = 0
- ipv6_cidr_blocks = ["::/0"]
- prefix_list_ids = []
- protocol = "-1"
- security_groups = []
- self = false
- to_port = 0
- }]
- egress = [{
- cidr_blocks = [
- "0.0.0.0/0",
- ]
- description = ""
- from_port = 0
- ipv6_cidr_blocks = [
- "::/0",
- ]
- prefix_list_ids = []
- protocol = "-1"
- security_groups = []
- self = false
- to_port = 0
- }]
- vpc_id = aws_vpc.default.id
-}
-
-resource "aws_iam_role" "nodes" {
- name = "${random_pet.prefix.id}-nodes"
-
- assume_role_policy = jsonencode({
- Statement = [{
- Action = "sts:AssumeRole"
- Effect = "Allow"
- Principal = {
- Service = "ec2.amazonaws.com"
- }
- }]
- Version = "2012-10-17"
- })
-}
-
-resource "aws_iam_role_policy_attachment" "example-AmazonEKSWorkerNodePolicy" {
- policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
- role = aws_iam_role.nodes.name
-}
-
-resource "aws_iam_role_policy_attachment" "example-AmazonEKS_CNI_Policy" {
- policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
- role = aws_iam_role.nodes.name
-}
-
-resource "aws_iam_role_policy_attachment" "example-AmazonEC2ContainerRegistryReadOnly" {
- policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
- role = aws_iam_role.nodes.name
-}
-
-resource "aws_eks_node_group" "default" {
- cluster_name = aws_eks_cluster.default.name
- node_group_name = "${random_pet.prefix.id}-nodes"
- node_role_arn = aws_iam_role.nodes.arn
- subnet_ids = aws_subnet.default.*.id
-
- scaling_config {
- desired_size = 1
- max_size = 1
- min_size = 1
- }
-}
-
-resource "aws_eks_cluster" "default" {
- name = "${random_pet.prefix.id}-eks"
- role_arn = aws_iam_role.cluster.arn
- version = "1.28"
-
- tags = {
- octopus-environment = "Staging"
- octopus-role = "discovery-role"
- source = "calamari-e2e-tests"
- }
-
- vpc_config {
- endpoint_private_access = true
- public_access_cidrs = ["0.0.0.0/0"]
- subnet_ids = aws_subnet.default.*.id
- security_group_ids = [aws_security_group.cluster.id]
- }
-}
-
-data "aws_iam_policy_document" "cluster" {
- statement {
- actions = [
- "sts:AssumeRole"
- ]
- principals {
- type = "Service"
- identifiers = ["eks.amazonaws.com"]
- }
- }
-}
-
-resource "aws_iam_role" "cluster" {
- path = "/test/"
- assume_role_policy = data.aws_iam_policy_document.cluster.json
- managed_policy_arns = [
- "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy",
- "arn:aws:iam::aws:policy/AmazonEKSServicePolicy",
- "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy",
- ]
-}
-
-data "aws_iam_policy_document" "userRole" {
- statement {
- actions = [
- "sts:AssumeRole"
- ]
- principals {
- type = "AWS"
- identifiers = [aws_iam_user.default.arn]
- }
- principals {
- type = "AWS"
- identifiers = [aws_iam_role.ec2.arn]
- }
- }
+data "aws_iam_role" "iam_role_with_cluster_access" {
+ name = "${var.static_resource_prefix}-iam-role-with-cluster-access"
}
-data "aws_iam_policy_document" "ec2Role" {
- statement {
- actions = [
- "sts:AssumeRole",
- ]
- principals {
- type = "Service"
- identifiers = ["ec2.amazonaws.com"]
- }
- }
+data "aws_eks_cluster" "default" {
+ name = "${var.static_resource_prefix}-eks"
}
-resource "aws_iam_instance_profile" "profile" {
- role = aws_iam_role.ec2.name
+data "aws_iam_instance_profile" "profile" {
+ name = "${var.static_resource_prefix}-instance-profile"
}
-resource "aws_iam_role" "ec2" {
- assume_role_policy = data.aws_iam_policy_document.ec2Role.json
- managed_policy_arns = [aws_iam_policy.default.arn]
+resource "aws_iam_user" "default" {
+ name = "${var.static_resource_prefix}-${random_pet.name.id}"
+ path = "/test/"
}
-resource "aws_iam_role" "user" {
- assume_role_policy = data.aws_iam_policy_document.userRole.json
- managed_policy_arns = [
- aws_iam_policy.default.arn
- ]
+resource "aws_iam_access_key" "default" {
+ user = aws_iam_user.default.name
}
data "aws_iam_policy_document" "user" {
statement {
+ effect = "Allow"
actions = [
- "sts:AssumeRole",
- "eks:ListClusters",
- "eks:ListTagsForResource",
- "eks:AccessKubernetesApi",
- "eks:DescribeCluster",
+ "sts:AssumeRole"
]
- effect = "Allow"
- resources = ["*"]
+
+ resources = [data.aws_iam_role.iam_role_with_cluster_access.arn]
}
}
-resource "aws_iam_policy" "default" {
+resource "aws_iam_policy" "user" {
path = "/test/"
policy = data.aws_iam_policy_document.user.json
}
resource "aws_iam_user_policy_attachment" "default" {
user = aws_iam_user.default.name
- policy_arn = aws_iam_policy.default.arn
-}
-
-data "aws_availability_zones" "available" {
-}
-
-resource "aws_iam_access_key" "default" {
- user = aws_iam_user.default.name
-}
-
-resource "aws_iam_user" "default" {
- name = "${random_pet.prefix.id}-test"
- path = "/test/"
-}
-
-resource "aws_subnet" "default" {
- count = 2
-
- availability_zone = data.aws_availability_zones.available.names[count.index]
- cidr_block = "10.0.${count.index}.0/24"
- vpc_id = aws_vpc.default.id
- map_public_ip_on_launch = true
-}
-
-resource "aws_vpc" "default" {
- cidr_block = "10.0.0.0/16"
- enable_dns_support = true
- enable_dns_hostnames = true
-}
-
-resource "aws_internet_gateway" "default" {
- vpc_id = aws_vpc.default.id
-}
-
-resource "aws_route_table" "default" {
- vpc_id = aws_vpc.default.id
-
- route {
- cidr_block = "0.0.0.0/0"
- gateway_id = aws_internet_gateway.default.id
- }
-}
-
-resource "aws_route_table_association" "default" {
- count = 2
-
- subnet_id = aws_subnet.default[count.index].id
- route_table_id = aws_route_table.default.id
+ policy_arn = aws_iam_policy.user.arn
}
diff --git a/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/main.tf b/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/main.tf
index 97e63d59a..53a348157 100644
--- a/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/main.tf
+++ b/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/main.tf
@@ -1,5 +1,5 @@
-variable "tests_source_dir" {
+variable "static_resource_prefix" {
type = string
}
-resource "random_pet" "prefix" {}
\ No newline at end of file
+resource "random_pet" "name" {}
\ No newline at end of file
diff --git a/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/outputs.tf b/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/outputs.tf
index eb7c0b545..9205834de 100644
--- a/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/outputs.tf
+++ b/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/outputs.tf
@@ -8,37 +8,37 @@ output "eks_secret_key" {
}
output "eks_iam_role_arn" {
- value = aws_iam_role.user.arn
+ value = data.aws_iam_role.iam_role_with_cluster_access.arn
}
output "eks_cluster_endpoint" {
description = "Endpoint for EKS control plane."
- value = aws_eks_cluster.default.endpoint
+ value = data.aws_eks_cluster.default.endpoint
}
output "eks_cluster_ca_certificate" {
- value = base64decode(aws_eks_cluster.default.certificate_authority[0].data)
+ value = base64decode(data.aws_eks_cluster.default.certificate_authority[0].data)
sensitive = true
}
output "eks_cluster_name" {
description = "EKS name."
- value = aws_eks_cluster.default.name
+ value = data.aws_eks_cluster.default.name
}
output "eks_cluster_arn" {
description = "EKS ARN"
- value = aws_eks_cluster.default.arn
+ value = data.aws_eks_cluster.default.arn
}
output "aws_vpc_id" {
- value = aws_vpc.default.id
+ value = one(data.aws_eks_cluster.default.vpc_config[*].vpc_id)
}
output "aws_subnet_id" {
- value = aws_subnet.default[0].id
+ value = tolist(data.aws_eks_cluster.default.vpc_config[0].subnet_ids)[0]
}
output "aws_iam_instance_profile_name" {
- value = aws_iam_instance_profile.profile.name
+ value = data.aws_iam_instance_profile.profile.name
}
\ No newline at end of file
diff --git a/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/providers.tf b/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/providers.tf
index 028ed3122..b25d159e9 100644
--- a/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/providers.tf
+++ b/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/providers.tf
@@ -2,7 +2,7 @@ terraform {
required_providers {
aws = {
source = "hashicorp/aws"
- version = ">= 3.45.0"
+ version = "5.64.0"
}
kubernetes = {
source = "hashicorp/kubernetes"
diff --git a/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/GKE/gke.tf b/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/GKE/gke.tf
index 44b424e2b..85f073e3c 100644
--- a/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/GKE/gke.tf
+++ b/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/GKE/gke.tf
@@ -1,58 +1,8 @@
-# Get latest version
-data "google_container_engine_versions" "main" {
- location = local.region
- project = "octopus-api-tester"
-
- # Since this is just a string match, it's recommended that you append a . after minor versions
- # to ensure that prefixes such as 1.1 don't match versions like 1.12.5-gke.10 accidentally.
- version_prefix = "1.28."
-}
-
-# VPC
-resource "google_compute_network" "vpc" {
- name = "${random_pet.prefix.id}-vpc"
- auto_create_subnetworks = "false"
- project = "octopus-api-tester"
-}
-
-# Subnet
-resource "google_compute_subnetwork" "subnet" {
- name = "${random_pet.prefix.id}-subnet"
- region = local.region
- network = google_compute_network.vpc.name
- ip_cidr_range = "10.10.0.0/24"
- project = "octopus-api-tester"
-}
-
-locals {
- # we will pick the latest k8s version
- master_version = data.google_container_engine_versions.main.valid_master_versions[0]
-}
-
-resource "google_container_cluster" "default" {
- name = "${random_pet.prefix.id}-gke"
- project = "octopus-api-tester"
- min_master_version = local.master_version
- initial_node_count = 1
- node_config {
- preemptible = true
- machine_type = "e2-medium"
- }
- master_auth {
- client_certificate_config {
- issue_client_certificate = true
- }
- }
-
- # to prevent automatic updates to cluster
- release_channel {
- channel = "UNSPECIFIED"
- }
-
- network = google_compute_network.vpc.name
- subnetwork = google_compute_subnetwork.subnet.name
+data "google_container_cluster" "default" {
+ name = "${var.static_resource_prefix}-gke"
+ project = "octopus-api-tester"
}
data "google_client_config" "default" {
- depends_on = [google_container_cluster.default]
+ depends_on = [data.google_container_cluster.default]
}
\ No newline at end of file
diff --git a/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/GKE/main.tf b/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/GKE/main.tf
index d5bff7a84..1a53e9c95 100644
--- a/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/GKE/main.tf
+++ b/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/GKE/main.tf
@@ -1 +1,3 @@
-resource "random_pet" "prefix" {}
\ No newline at end of file
+variable "static_resource_prefix" {
+ type = string
+}
\ No newline at end of file
diff --git a/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/GKE/outputs.tf b/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/GKE/outputs.tf
index 273a900e1..8afda02fc 100644
--- a/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/GKE/outputs.tf
+++ b/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/GKE/outputs.tf
@@ -1,10 +1,10 @@
output "gke_cluster_endpoint" {
description = "Endpoint for GKE control plane."
- value = google_container_cluster.default.endpoint
+ value = data.google_container_cluster.default.endpoint
}
output "gke_cluster_ca_certificate" {
- value = base64decode(google_container_cluster.default.master_auth.0.cluster_ca_certificate)
+ value = base64decode(data.google_container_cluster.default.master_auth.0.cluster_ca_certificate)
sensitive = true
}
@@ -15,15 +15,15 @@ output "gke_token" {
output "gke_cluster_name" {
description = "GKE name."
- value = google_container_cluster.default.name
+ value = data.google_container_cluster.default.name
}
output "gke_cluster_project" {
description = "GKE clusters project."
- value = google_container_cluster.default.project
+ value = data.google_container_cluster.default.project
}
output "gke_cluster_location" {
description = "GKE clusters location."
- value = google_container_cluster.default.location
+ value = data.google_container_cluster.default.location
}
\ No newline at end of file
diff --git a/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/eks.kubernetes.tf b/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/EKS/eks.kubernetes.tf
similarity index 89%
rename from source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/eks.kubernetes.tf
rename to source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/EKS/eks.kubernetes.tf
index 9e07e7a3b..f27b4b9a3 100644
--- a/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/eks.kubernetes.tf
+++ b/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/EKS/eks.kubernetes.tf
@@ -1,51 +1,50 @@
-data "aws_eks_cluster" "default" {
- name = aws_eks_cluster.default.name
-}
-
-data "aws_eks_cluster_auth" "default" {
- name = aws_eks_cluster.default.name
-}
-
-provider "kubernetes" {
- alias = "aws"
- host = data.aws_eks_cluster.default.endpoint
- cluster_ca_certificate = base64decode(data.aws_eks_cluster.default.certificate_authority[0].data)
- token = data.aws_eks_cluster_auth.default.token
-}
-
-resource "local_file" "kubeconfig" {
- sensitive_content = templatefile("${path.module}/kubeconfig.tpl", {
- cluster_name = aws_eks_cluster.default.name,
- cluster_ca = data.aws_eks_cluster.default.certificate_authority[0].data,
- endpoint = data.aws_eks_cluster.default.endpoint,
- })
- filename = "./kubeconfig-${aws_eks_cluster.default.name}"
-}
-
-resource "kubernetes_config_map" "aws_auth" {
- provider = kubernetes.aws
- metadata {
- name = "aws-auth"
- namespace = "kube-system"
- }
- data = {
- mapRoles = <<-EOT
- - rolearn: ${aws_iam_role.user.arn}
- username: system:node:{{EC2PrivateDNSName}}
- groups:
- - system:bootstrappers
- - system:nodes
- - rolearn: ${aws_iam_role.ec2.arn}
- username: system:node:{{EC2PrivateDNSName}}
- groups:
- - system:bootstrappers
- - system:nodes
-EOT
- mapUsers = <<-EOT
- - userarn: ${aws_iam_user.default.arn}
- username: ${aws_iam_user.default.name}
- groups:
- - system:masters
-EOT
- }
-}
+data "aws_eks_cluster" "default" {
+ name = aws_eks_cluster.default.name
+}
+
+data "aws_eks_cluster_auth" "default" {
+ name = aws_eks_cluster.default.name
+}
+
+provider "kubernetes" {
+ alias = "aws"
+ host = data.aws_eks_cluster.default.endpoint
+ cluster_ca_certificate = base64decode(data.aws_eks_cluster.default.certificate_authority[0].data)
+ token = data.aws_eks_cluster_auth.default.token
+}
+
+resource "local_file" "kubeconfig" {
+ sensitive_content = templatefile("${path.module}/kubeconfig.tpl", {
+ cluster_name = aws_eks_cluster.default.name,
+ cluster_ca = data.aws_eks_cluster.default.certificate_authority[0].data,
+ endpoint = data.aws_eks_cluster.default.endpoint,
+ })
+ filename = "./kubeconfig-${aws_eks_cluster.default.name}"
+}
+
+resource "kubernetes_config_map" "aws_auth" {
+ provider = kubernetes.aws
+ metadata {
+ name = "aws-auth"
+ namespace = "kube-system"
+ }
+ data = {
+ mapRoles = <<-EOT
+ - rolearn: ${aws_iam_role.user.arn}
+ username: ${aws_iam_role.user.name}
+ groups:
+ - system:masters
+ - rolearn: ${aws_iam_role.ec2.arn}
+ username: system:node:{{EC2PrivateDNSName}}
+ groups:
+ - system:bootstrappers
+ - system:nodes
+EOT
+ mapUsers = <<-EOT
+ - userarn: ${aws_iam_user.default.arn}
+ username: ${aws_iam_user.default.name}
+ groups:
+ - system:masters
+EOT
+ }
+}
\ No newline at end of file
diff --git a/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/EKS/eks.tf b/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/EKS/eks.tf
new file mode 100644
index 000000000..a8455a93c
--- /dev/null
+++ b/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/EKS/eks.tf
@@ -0,0 +1,234 @@
+resource "aws_security_group" "cluster" {
+ ingress = [{
+ cidr_blocks = ["0.0.0.0/0"]
+ description = ""
+ from_port = 0
+ ipv6_cidr_blocks = ["::/0"]
+ prefix_list_ids = []
+ protocol = "-1"
+ security_groups = []
+ self = false
+ to_port = 0
+ }]
+ egress = [{
+ cidr_blocks = [
+ "0.0.0.0/0",
+ ]
+ description = ""
+ from_port = 0
+ ipv6_cidr_blocks = [
+ "::/0",
+ ]
+ prefix_list_ids = []
+ protocol = "-1"
+ security_groups = []
+ self = false
+ to_port = 0
+ }]
+ vpc_id = aws_vpc.default.id
+}
+
+resource "aws_iam_role" "nodes" {
+ name = "${var.static_resource_prefix}-nodes"
+
+ assume_role_policy = jsonencode({
+ Statement = [{
+ Action = "sts:AssumeRole"
+ Effect = "Allow"
+ Principal = {
+ Service = "ec2.amazonaws.com"
+ }
+ }]
+ Version = "2012-10-17"
+ })
+}
+
+resource "aws_iam_role_policy_attachment" "example-AmazonEKSWorkerNodePolicy" {
+ policy_arn = "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy"
+ role = aws_iam_role.nodes.name
+}
+
+resource "aws_iam_role_policy_attachment" "example-AmazonEKS_CNI_Policy" {
+ policy_arn = "arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy"
+ role = aws_iam_role.nodes.name
+}
+
+resource "aws_iam_role_policy_attachment" "example-AmazonEC2ContainerRegistryReadOnly" {
+ policy_arn = "arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly"
+ role = aws_iam_role.nodes.name
+}
+
+resource "aws_eks_node_group" "default" {
+ cluster_name = aws_eks_cluster.default.name
+ node_group_name = "${var.static_resource_prefix}-nodes"
+ node_role_arn = aws_iam_role.nodes.arn
+ subnet_ids = aws_subnet.default.*.id
+
+ scaling_config {
+ desired_size = 1
+ max_size = 1
+ min_size = 1
+ }
+}
+
+resource "aws_eks_cluster" "default" {
+ name = "${var.static_resource_prefix}-eks"
+ role_arn = aws_iam_role.cluster.arn
+ version = "1.28"
+
+ tags = {
+ octopus-environment = "Staging"
+ octopus-role = "discovery-role"
+ source = "calamari-e2e-tests"
+ }
+
+ vpc_config {
+ endpoint_private_access = true
+ public_access_cidrs = ["0.0.0.0/0"]
+ subnet_ids = aws_subnet.default.*.id
+ security_group_ids = [aws_security_group.cluster.id]
+ }
+}
+
+data "aws_iam_policy_document" "cluster" {
+ statement {
+ actions = [
+ "sts:AssumeRole"
+ ]
+ principals {
+ type = "Service"
+ identifiers = ["eks.amazonaws.com"]
+ }
+ }
+}
+
+resource "aws_iam_role" "cluster" {
+ path = "/test/"
+ assume_role_policy = data.aws_iam_policy_document.cluster.json
+ managed_policy_arns = [
+ "arn:aws:iam::aws:policy/AmazonEKSClusterPolicy",
+ "arn:aws:iam::aws:policy/AmazonEKSServicePolicy",
+ "arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy",
+ ]
+}
+
+data "aws_iam_policy_document" "userRole" {
+ statement {
+ actions = [
+ "sts:AssumeRole"
+ ]
+ principals {
+ type = "AWS"
+ identifiers = ["017645897735"] # TODO: replace with var (account id)
+ }
+ principals {
+ type = "AWS"
+ identifiers = [aws_iam_user.default.arn]
+ }
+ principals {
+ type = "AWS"
+ identifiers = [aws_iam_role.ec2.arn]
+ }
+ }
+}
+
+data "aws_iam_policy_document" "ec2Role" {
+ statement {
+ actions = [
+ "sts:AssumeRole",
+ ]
+ principals {
+ type = "Service"
+ identifiers = ["ec2.amazonaws.com"]
+ }
+ }
+}
+
+resource "aws_iam_instance_profile" "profile" {
+ name = "${var.static_resource_prefix}-instance-profile"
+ role = aws_iam_role.ec2.name
+}
+
+resource "aws_iam_role" "ec2" {
+ assume_role_policy = data.aws_iam_policy_document.ec2Role.json
+ managed_policy_arns = [aws_iam_policy.default.arn]
+}
+
+resource "aws_iam_role" "user" {
+ name = "${var.static_resource_prefix}-iam-role-with-cluster-access"
+ assume_role_policy = data.aws_iam_policy_document.userRole.json
+ managed_policy_arns = [
+ aws_iam_policy.default.arn
+ ]
+}
+
+data "aws_iam_policy_document" "user" {
+ statement {
+ actions = [
+ "sts:AssumeRole",
+ "eks:ListClusters",
+ "eks:ListTagsForResource",
+ "eks:AccessKubernetesApi",
+ "eks:DescribeCluster",
+ ]
+ effect = "Allow"
+ resources = ["*"]
+ }
+}
+
+resource "aws_iam_policy" "default" {
+ path = "/test/"
+ policy = data.aws_iam_policy_document.user.json
+}
+
+resource "aws_iam_user_policy_attachment" "default" {
+ user = aws_iam_user.default.name
+ policy_arn = aws_iam_policy.default.arn
+}
+
+data "aws_availability_zones" "available" {
+}
+
+resource "aws_iam_access_key" "default" {
+ user = aws_iam_user.default.name
+}
+
+resource "aws_iam_user" "default" {
+ name = "${var.static_resource_prefix}-user"
+ path = "/test/"
+}
+
+resource "aws_subnet" "default" {
+ count = 2
+
+ availability_zone = data.aws_availability_zones.available.names[count.index]
+ cidr_block = "10.0.${count.index}.0/24"
+ vpc_id = aws_vpc.default.id
+ map_public_ip_on_launch = true
+}
+
+resource "aws_vpc" "default" {
+ cidr_block = "10.0.0.0/16"
+ enable_dns_support = true
+ enable_dns_hostnames = true
+}
+
+resource "aws_internet_gateway" "default" {
+ vpc_id = aws_vpc.default.id
+}
+
+resource "aws_route_table" "default" {
+ vpc_id = aws_vpc.default.id
+
+ route {
+ cidr_block = "0.0.0.0/0"
+ gateway_id = aws_internet_gateway.default.id
+ }
+}
+
+resource "aws_route_table_association" "default" {
+ count = 2
+
+ subnet_id = aws_subnet.default[count.index].id
+ route_table_id = aws_route_table.default.id
+}
diff --git a/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/kubeconfig.tpl b/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/EKS/kubeconfig.tpl
similarity index 94%
rename from source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/kubeconfig.tpl
rename to source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/EKS/kubeconfig.tpl
index 52de89ba0..e5cdc262b 100644
--- a/source/Calamari.Tests/KubernetesFixtures/Terraform/Clusters/EKS/kubeconfig.tpl
+++ b/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/EKS/kubeconfig.tpl
@@ -1,29 +1,29 @@
-apiVersion: v1
-preferences: {}
-kind: Config
-
-clusters:
-- cluster:
- server: ${endpoint}
- certificate-authority-data: ${cluster_ca}
- name: ${cluster_name}
-
-contexts:
-- context:
- cluster: ${cluster_name}
- user: ${cluster_name}
- name: ${cluster_name}
-
-current-context: ${cluster_name}
-
-users:
-- name: ${cluster_name}
- user:
- exec:
- apiVersion: client.authentication.k8s.io/v1beta1
- command: aws
- args:
- - eks
- - get-token
- - --cluster-name=${cluster_name}
+apiVersion: v1
+preferences: {}
+kind: Config
+
+clusters:
+- cluster:
+ server: ${endpoint}
+ certificate-authority-data: ${cluster_ca}
+ name: ${cluster_name}
+
+contexts:
+- context:
+ cluster: ${cluster_name}
+ user: ${cluster_name}
+ name: ${cluster_name}
+
+current-context: ${cluster_name}
+
+users:
+- name: ${cluster_name}
+ user:
+ exec:
+ apiVersion: client.authentication.k8s.io/v1beta1
+ command: aws
+ args:
+ - eks
+ - get-token
+ - --cluster-name=${cluster_name}
- --region=region
\ No newline at end of file
diff --git a/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/EKS/main.tf b/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/EKS/main.tf
new file mode 100644
index 000000000..1a53e9c95
--- /dev/null
+++ b/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/EKS/main.tf
@@ -0,0 +1,3 @@
+variable "static_resource_prefix" {
+ type = string
+}
\ No newline at end of file
diff --git a/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/EKS/outputs.tf b/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/EKS/outputs.tf
new file mode 100644
index 000000000..8321de39e
--- /dev/null
+++ b/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/EKS/outputs.tf
@@ -0,0 +1,44 @@
+output "eks_client_id" {
+ value = aws_iam_access_key.default.id
+}
+
+output "eks_secret_key" {
+ value = aws_iam_access_key.default.secret
+ sensitive = true
+}
+
+output "eks_iam_role_arn" {
+ value = aws_iam_role.user.arn
+}
+
+output "eks_cluster_endpoint" {
+ description = "Endpoint for EKS control plane."
+ value = aws_eks_cluster.default.endpoint
+}
+
+output "eks_cluster_ca_certificate" {
+ value = base64decode(aws_eks_cluster.default.certificate_authority[0].data)
+ sensitive = true
+}
+
+output "eks_cluster_name" {
+ description = "EKS name."
+ value = aws_eks_cluster.default.name
+}
+
+output "eks_cluster_arn" {
+ description = "EKS ARN"
+ value = aws_eks_cluster.default.arn
+}
+
+output "aws_vpc_id" {
+ value = aws_vpc.default.id
+}
+
+output "aws_subnet_id" {
+ value = aws_subnet.default[0].id
+}
+
+output "aws_iam_instance_profile_name" {
+ value = aws_iam_instance_profile.profile.name
+}
\ No newline at end of file
diff --git a/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/EKS/providers.tf b/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/EKS/providers.tf
new file mode 100644
index 000000000..a64967e36
--- /dev/null
+++ b/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/EKS/providers.tf
@@ -0,0 +1,16 @@
+terraform {
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = "5.64.0"
+ }
+ kubernetes = {
+ source = "hashicorp/kubernetes"
+ version = ">= 2.3.2"
+ }
+ }
+ required_version = ">= 0.15"
+}
+
+provider "aws" {
+}
diff --git a/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/GKE/gke.tf b/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/GKE/gke.tf
new file mode 100644
index 000000000..5e16cbed3
--- /dev/null
+++ b/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/GKE/gke.tf
@@ -0,0 +1,58 @@
+# Get latest version
+data "google_container_engine_versions" "main" {
+ location = local.region
+ project = "octopus-api-tester"
+
+ # Since this is just a string match, it's recommended that you append a . after minor versions
+ # to ensure that prefixes such as 1.1 don't match versions like 1.12.5-gke.10 accidentally.
+ version_prefix = "1.28."
+}
+
+# VPC
+resource "google_compute_network" "vpc" {
+ name = "${var.static_resource_prefix}-vpc"
+ auto_create_subnetworks = "false"
+ project = "octopus-api-tester"
+}
+
+# Subnet
+resource "google_compute_subnetwork" "subnet" {
+ name = "${var.static_resource_prefix}-subnet"
+ region = local.region
+ network = google_compute_network.vpc.name
+ ip_cidr_range = "10.10.0.0/24"
+ project = "octopus-api-tester"
+}
+
+locals {
+ # we will pick the latest k8s version
+ master_version = data.google_container_engine_versions.main.valid_master_versions[0]
+}
+
+resource "google_container_cluster" "default" {
+ name = "${var.static_resource_prefix}-gke"
+ project = "octopus-api-tester"
+ min_master_version = local.master_version
+ initial_node_count = 1
+ node_config {
+ preemptible = true
+ machine_type = "e2-medium"
+ }
+ master_auth {
+ client_certificate_config {
+ issue_client_certificate = true
+ }
+ }
+
+ # to prevent automatic updates to cluster
+ release_channel {
+ channel = "UNSPECIFIED"
+ }
+
+ network = google_compute_network.vpc.name
+ subnetwork = google_compute_subnetwork.subnet.name
+}
+
+data "google_client_config" "default" {
+ depends_on = [google_container_cluster.default]
+}
\ No newline at end of file
diff --git a/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/GKE/main.tf b/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/GKE/main.tf
new file mode 100644
index 000000000..1a53e9c95
--- /dev/null
+++ b/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/GKE/main.tf
@@ -0,0 +1,3 @@
+variable "static_resource_prefix" {
+ type = string
+}
\ No newline at end of file
diff --git a/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/GKE/providers.tf b/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/GKE/providers.tf
new file mode 100644
index 000000000..38ccb214d
--- /dev/null
+++ b/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/GKE/providers.tf
@@ -0,0 +1,22 @@
+terraform {
+ required_providers {
+ google = {
+ source = "hashicorp/google"
+ version = "3.71.0"
+ }
+ kubernetes = {
+ source = "hashicorp/kubernetes"
+ version = ">= 2.3.2"
+ }
+ }
+ required_version = ">= 0.15"
+}
+
+locals {
+ region = "australia-southeast1"
+}
+
+provider "google" {
+ region = local.region
+ zone = "australia-southeast1-c"
+}
\ No newline at end of file
diff --git a/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/readme.md b/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/readme.md
new file mode 100644
index 000000000..db5c76841
--- /dev/null
+++ b/source/Calamari.Tests/KubernetesFixtures/Terraform/StaticClusters/readme.md
@@ -0,0 +1,10 @@
+# Static test infrastructure
+
+Static clusters for cloud provider specific authentication tests are provisioned using Terraform Cloud.
+
+- [EKS configuration](https://app.terraform.io/app/octopus-deploy/workspaces/calamari-testing-kubernetes-static-infrastructure-eks)
+- [AKS configuration (In progress)](https://app.terraform.io/app/octopus-deploy/workspaces/calamari-testing-kubernetes-static-infrastructure-sks)
+- [GKE configuration](https://app.terraform.io/app/octopus-deploy/workspaces/calamari-testing-kubernetes-static-infrastructure-gke)
+
+Ensure all the tests that are written against these clusters do not interact with each other.
+Tests that do anything more than test authentication/cloud provider specific features should be written to target a local kind instance.
\ No newline at end of file
diff --git a/source/Calamari.Tests/Some.cs b/source/Calamari.Tests/Some.cs
new file mode 100644
index 000000000..dd5a47147
--- /dev/null
+++ b/source/Calamari.Tests/Some.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Threading;
+
+namespace Calamari.Tests
+{
+ public static class Some
+ {
+ static int next;
+
+ public static string String() => "S__" + Int();
+
+ public static int Int() => Interlocked.Increment(ref next);
+ }
+}
\ No newline at end of file
diff --git a/source/Calamari/Commands/CleanCommand.cs b/source/Calamari/Commands/CleanCommand.cs
index 06fa9816e..51ad580b2 100644
--- a/source/Calamari/Commands/CleanCommand.cs
+++ b/source/Calamari/Commands/CleanCommand.cs
@@ -39,7 +39,7 @@ public override int Execute(string[] commandLineArguments)
if (days <=0 && releases <= 0)
throw new CommandException("A value must be provided for either --days or --releases");
- var deploymentJournal = new DeploymentJournal(fileSystem, SemaphoreFactory.Get(), variables);
+ var deploymentJournal = new DeploymentJournal(fileSystem, new SystemSemaphoreManager(), variables);
var clock = new SystemClock();
var retentionPolicy = new RetentionPolicy(fileSystem, deploymentJournal, clock);
diff --git a/source/Calamari/Commands/DeployPackageCommand.cs b/source/Calamari/Commands/DeployPackageCommand.cs
index 898418060..daa0228a9 100644
--- a/source/Calamari/Commands/DeployPackageCommand.cs
+++ b/source/Calamari/Commands/DeployPackageCommand.cs
@@ -100,7 +100,7 @@ public override int Execute(string[] commandLineArguments)
featureClasses.Add(new NginxFeature(NginxServer.AutoDetect(), fileSystem));
}
- var semaphore = SemaphoreFactory.Get();
+ var semaphore = new SystemSemaphoreManager();
var journal = new DeploymentJournal(fileSystem, semaphore, variables);
var conventions = new List
diff --git a/source/Calamari/Commands/Java/DeployJavaArchiveCommand.cs b/source/Calamari/Commands/Java/DeployJavaArchiveCommand.cs
index cdd334ddc..5cd15232d 100644
--- a/source/Calamari/Commands/Java/DeployJavaArchiveCommand.cs
+++ b/source/Calamari/Commands/Java/DeployJavaArchiveCommand.cs
@@ -78,7 +78,7 @@ public override int Execute(string[] commandLineArguments)
Log.Info("Deploying: " + archiveFile);
- var semaphore = SemaphoreFactory.Get();
+ var semaphore = new SystemSemaphoreManager();
var journal = new DeploymentJournal(fileSystem, semaphore, variables);
var jarTools = new JarTool(commandLineRunner, log, fileSystem, variables);
var packageExtractor = new JarPackageExtractor(jarTools).WithExtractionLimits(log, variables);
diff --git a/source/Calamari/Commands/TransferPackageCommand.cs b/source/Calamari/Commands/TransferPackageCommand.cs
index 06a4234a4..cf9fc36cc 100644
--- a/source/Calamari/Commands/TransferPackageCommand.cs
+++ b/source/Calamari/Commands/TransferPackageCommand.cs
@@ -37,7 +37,7 @@ public override int Execute(string[] commandLineArguments)
if (packageFile == null) // required: true in the above call means it will throw rather than return null, but there's no way to tell the compiler that. And ! doesn't work in older frameworks
throw new CommandException("Package File path could not be determined");
- var journal = new DeploymentJournal(fileSystem, SemaphoreFactory.Get(), variables);
+ var journal = new DeploymentJournal(fileSystem, new SystemSemaphoreManager(), variables);
var conventions = new List
{
diff --git a/source/Calamari/Kubernetes/Commands/Executors/GatherAndApplyRawYamlExecutor.cs b/source/Calamari/Kubernetes/Commands/Executors/GatherAndApplyRawYamlExecutor.cs
index cc1384c5d..f831dcaf9 100644
--- a/source/Calamari/Kubernetes/Commands/Executors/GatherAndApplyRawYamlExecutor.cs
+++ b/source/Calamari/Kubernetes/Commands/Executors/GatherAndApplyRawYamlExecutor.cs
@@ -6,7 +6,6 @@
using Calamari.Common.Commands;
using Calamari.Common.Plumbing.FileSystem;
using Calamari.Common.Plumbing.Logging;
-using Calamari.Common.Plumbing.Variables;
using Calamari.Kubernetes.Integration;
using Calamari.Kubernetes.ResourceStatus.Resources;
using Octopus.CoreUtilities.Extensions;
@@ -21,15 +20,18 @@ class GatherAndApplyRawYamlExecutor : BaseKubernetesApplyExecutor, IRawYamlKuber
{
readonly ILog log;
readonly ICalamariFileSystem fileSystem;
+ readonly IManifestReporter manifestReporter;
readonly Kubectl kubectl;
public GatherAndApplyRawYamlExecutor(
ILog log,
ICalamariFileSystem fileSystem,
+ IManifestReporter manifestReporter,
Kubectl kubectl) : base(log)
{
this.log = log;
this.fileSystem = fileSystem;
+ this.manifestReporter = manifestReporter;
this.kubectl = kubectl;
}
@@ -37,7 +39,7 @@ protected override async Task> ApplyAndGetResour
{
var variables = deployment.Variables;
var globs = variables.GetPaths(SpecialVariables.CustomResourceYamlFileName);
-
+
if (globs.IsNullOrEmpty())
return Enumerable.Empty();
@@ -65,38 +67,31 @@ protected override async Task> ApplyAndGetResour
IEnumerable ApplyBatchAndReturnResourceIdentifiers(RunningDeployment deployment, GlobDirectory globDirectory)
{
- if (!LogFoundFiles(globDirectory))
+ var files = fileSystem.EnumerateFilesRecursively(globDirectory.Directory).ToArray();
+ if (!files.Any())
+ {
+ log.Warn($"No files found matching '{globDirectory.Glob}'");
return Array.Empty();
+ }
+
+ ReportEachManifestBeingApplied(globDirectory, files);
string[] executeArgs = {"apply", "-f", $@"""{globDirectory.Directory}""", "--recursive", "-o", "json"};
executeArgs = executeArgs.AddOptionsForServerSideApply(deployment.Variables, log);
-
var result = kubectl.ExecuteCommandAndReturnOutput(executeArgs);
return ProcessKubectlCommandOutput(deployment, result, globDirectory.Directory);
}
- ///
- /// Logs files that are found at the relevant glob locations.
- ///
- ///
- /// True if files are found, False if no files exist at this location
- bool LogFoundFiles(GlobDirectory globDirectory)
+ void ReportEachManifestBeingApplied(GlobDirectory globDirectory, string[] files)
{
var directoryWithTrailingSlash = globDirectory.Directory + Path.DirectorySeparatorChar;
- var files = fileSystem.EnumerateFilesRecursively(globDirectory.Directory).ToArray();
- if (!files.Any())
- {
- log.Warn($"No files found matching '{globDirectory.Glob}'");
- return false;
- }
-
foreach (var file in files)
{
- log.Verbose($"Matched file: {fileSystem.GetRelativePath(directoryWithTrailingSlash, file)}");
+ var fullFilePath = fileSystem.GetRelativePath(directoryWithTrailingSlash, file);
+ log.Verbose($"Matched file: {fullFilePath}");
+ manifestReporter.ReportManifestApplied(file);
}
-
- return true;
}
}
}
\ No newline at end of file
diff --git a/source/Calamari/Kubernetes/ManifestReporter.cs b/source/Calamari/Kubernetes/ManifestReporter.cs
new file mode 100644
index 000000000..b5c54ef6c
--- /dev/null
+++ b/source/Calamari/Kubernetes/ManifestReporter.cs
@@ -0,0 +1,104 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using Calamari.Common.Plumbing.FileSystem;
+using Calamari.Common.Plumbing.Logging;
+using Calamari.Common.Plumbing.ServiceMessages;
+using Calamari.Common.Plumbing.Variables;
+using YamlDotNet.Core;
+using YamlDotNet.RepresentationModel;
+using YamlDotNet.Serialization;
+using YamlDotNet.Serialization.NamingConventions;
+
+namespace Calamari.Kubernetes
+{
+ public interface IManifestReporter
+ {
+ void ReportManifestApplied(string filePath);
+ }
+
+ public class ManifestReporter : IManifestReporter
+ {
+ readonly IVariables variables;
+ readonly ICalamariFileSystem fileSystem;
+ readonly ILog log;
+
+ static readonly IDeserializer YamlDeserializer = new Deserializer();
+
+ static readonly ISerializer YamlSerializer = new SerializerBuilder()
+ .WithNamingConvention(CamelCaseNamingConvention.Instance)
+ .JsonCompatible()
+ .Build();
+
+ public ManifestReporter(IVariables variables, ICalamariFileSystem fileSystem, ILog log)
+ {
+ this.variables = variables;
+ this.fileSystem = fileSystem;
+ this.log = log;
+ }
+
+ string GetNamespace(YamlMappingNode yamlRoot)
+ {
+ var implicitNamespace = variables.Get(SpecialVariables.Namespace) ?? "default";
+
+ if (yamlRoot.Children.TryGetValue("metadata", out var metadataNode) && metadataNode is YamlMappingNode metadataMappingNode &&
+ metadataMappingNode.Children.TryGetValue("namespace", out var namespaceNode) && namespaceNode is YamlScalarNode namespaceScalarNode &&
+ !string.IsNullOrWhiteSpace(namespaceScalarNode.Value))
+ {
+ implicitNamespace = namespaceScalarNode.Value;
+ }
+
+ return implicitNamespace;
+ }
+
+ public void ReportManifestApplied(string filePath)
+ {
+ using (var yamlFile = fileSystem.OpenFile(filePath, FileAccess.ReadWrite))
+ {
+ try
+ {
+ var yamlStream = new YamlStream();
+ yamlStream.Load(new StreamReader(yamlFile));
+
+ foreach (var document in yamlStream.Documents)
+ {
+ if (!(document.RootNode is YamlMappingNode rootNode))
+ {
+ log.Warn("Could not parse manifest, resources will not be added to live object status");
+ continue;
+ }
+
+ var updatedDocument = YamlNodeToJson(rootNode);
+
+ var ns = GetNamespace(rootNode);
+ log.WriteServiceMessage(new ServiceMessage(SpecialVariables.ServiceMessageNames.ManifestApplied.Name,
+ new Dictionary
+ {
+ { SpecialVariables.ServiceMessageNames.ManifestApplied.ManifestAttribute, updatedDocument },
+ { SpecialVariables.ServiceMessageNames.ManifestApplied.NamespaceAttribute, ns }
+ }));
+ }
+ }
+ catch (SemanticErrorException)
+ {
+ log.Warn("Invalid YAML syntax found, resources will not be added to live object status");
+ }
+ }
+ }
+
+ static string YamlNodeToJson(YamlNode node)
+ {
+ var stream = new YamlStream { new YamlDocument(node) };
+ using (var writer = new StringWriter())
+ {
+ stream.Save(writer);
+
+ using (var reader = new StringReader(writer.ToString()))
+ {
+ var yamlObject = YamlDeserializer.Deserialize(reader);
+ return yamlObject is null ? string.Empty : YamlSerializer.Serialize(yamlObject).Trim();
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/source/Calamari/Kubernetes/ResourceStatus/ResourceUpdateReporter.cs b/source/Calamari/Kubernetes/ResourceStatus/ResourceUpdateReporter.cs
index f596fc9d7..3c7096074 100644
--- a/source/Calamari/Kubernetes/ResourceStatus/ResourceUpdateReporter.cs
+++ b/source/Calamari/Kubernetes/ResourceStatus/ResourceUpdateReporter.cs
@@ -99,7 +99,7 @@ private void SendServiceMessage(Resource resource, bool removed, int checkCount)
{"checkCount", checkCount.ToString()}
};
- var message = new ServiceMessage(SpecialVariables.KubernetesResourceStatusServiceMessageName, parameters);
+ var message = new ServiceMessage(SpecialVariables.ServiceMessageNames.ResourceStatus.Name, parameters);
log.WriteServiceMessage(message);
}
}
diff --git a/source/Calamari/Kubernetes/SpecialVariables.cs b/source/Calamari/Kubernetes/SpecialVariables.cs
index 1c3b6be11..aa79808c7 100644
--- a/source/Calamari/Kubernetes/SpecialVariables.cs
+++ b/source/Calamari/Kubernetes/SpecialVariables.cs
@@ -36,8 +36,6 @@ public static class SpecialVariables
public const string KubeConfig = "Octopus.KubeConfig.Path";
public const string KustomizeManifest = "Octopus.Kustomize.Manifest.Path";
- public const string KubernetesResourceStatusServiceMessageName = "k8s-status";
-
public const string ServerSideApplyEnabled = "Octopus.Action.Kubernetes.ServerSideApply.Enabled";
public const string ServerSideApplyForceConflicts = "Octopus.Action.Kubernetes.ServerSideApply.ForceConflicts";
@@ -66,5 +64,20 @@ public static string ValuesFilePath(string key)
}
}
}
+
+ public class ServiceMessageNames
+ {
+ public static class ResourceStatus
+ {
+ public const string Name = "k8s-status";
+ }
+
+ public static class ManifestApplied
+ {
+ public const string Name = "k8s-manifest-applied";
+ public const string ManifestAttribute = "manifest";
+ public const string NamespaceAttribute = "ns";
+ }
+ }
}
}
diff --git a/source/Calamari/Program.cs b/source/Calamari/Program.cs
index 837b19177..8a245b7e1 100644
--- a/source/Calamari/Program.cs
+++ b/source/Calamari/Program.cs
@@ -28,6 +28,7 @@
using Calamari.Azure.Kubernetes.Discovery;
using Calamari.Integration.FullFramework;
using Calamari.Integration.Iis;
+using Calamari.Kubernetes;
using Calamari.Kubernetes.Commands.Executors;
namespace Calamari
@@ -73,8 +74,7 @@ protected override void ConfigureContainer(ContainerBuilder builder, CommonOptio
builder.RegisterType().As().SingleInstance();
builder.RegisterType().AsSelf();
builder.RegisterType().As().SingleInstance();
-
-
+
//TODO: Once this runs on both netcore and full framework, this can be converted to a runtime conditional check
#if WINDOWS_CERTIFICATE_STORE_SUPPORT
@@ -85,7 +85,7 @@ protected override void ConfigureContainer(ContainerBuilder builder, CommonOptio
#else
builder.RegisterType().As().SingleInstance();
#endif
-
+ builder.RegisterType().As().SingleInstance();
builder.RegisterType().As();
builder.RegisterType().As();
builder.RegisterType().As();
@@ -98,7 +98,7 @@ protected override void ConfigureContainer(ContainerBuilder builder, CommonOptio
.As()
.SingleInstance();
- builder.RegisterInstance(SemaphoreFactory.Get()).As();
+ builder.RegisterInstance(new SystemSemaphoreManager()).As();
builder.RegisterModule();