Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add license controller + process termination support #239

Open
wants to merge 76 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
da816f5
fix cdfretries
abdullah-cognite Feb 20, 2025
9cb0a7d
remove extra try catch
abdullah-cognite Feb 20, 2025
6b04b94
update
abdullah-cognite Feb 20, 2025
7830f3a
update
abdullah-cognite Feb 20, 2025
da7ea8d
add license controller
abdullah-cognite Feb 20, 2025
a0c5ae3
update version
abdullah-cognite Feb 20, 2025
651687f
add process utils
abdullah-cognite Feb 20, 2025
33f972e
update
abdullah-cognite Feb 21, 2025
b6716b6
update
abdullah-cognite Feb 21, 2025
479375d
update
abdullah-cognite Feb 21, 2025
90c773c
fix test
abdullah-cognite Feb 21, 2025
dcf495e
update
abdullah-cognite Feb 24, 2025
24767cb
update
abdullah-cognite Feb 24, 2025
697a34a
update
abdullah-cognite Feb 24, 2025
e795a06
update license tracker
abdullah-cognite Feb 27, 2025
fe1be5d
address comment
abdullah-cognite Feb 27, 2025
b0f2313
update process utils test
abdullah-cognite Feb 27, 2025
b202f7b
update
abdullah-cognite Feb 27, 2025
e20269f
address some comments
abdullah-cognite Feb 27, 2025
c1e29a0
address comments
abdullah-cognite Feb 28, 2025
fb6069e
address comments
abdullah-cognite Feb 28, 2025
38810d9
update gh action
abdullah-cognite Feb 28, 2025
3558d1f
update process utils
abdullah-cognite Feb 28, 2025
26c37db
update
abdullah-cognite Feb 28, 2025
c588467
update
abdullah-cognite Feb 28, 2025
5bb4a8d
update
abdullah-cognite Feb 28, 2025
b0f194b
UPDATE
abdullah-cognite Feb 28, 2025
4e4ae7e
fix modellibrarytest
abdullah-cognite Feb 28, 2025
46931da
update github action
abdullah-cognite Feb 28, 2025
4e72896
update
abdullah-cognite Feb 28, 2025
421b6ab
update controller test
abdullah-cognite Feb 28, 2025
5dfad5d
update
abdullah-cognite Feb 28, 2025
92f79cd
update license test
abdullah-cognite Feb 28, 2025
76908ec
update signature
abdullah-cognite Feb 28, 2025
ea8ae41
update
abdullah-cognite Feb 28, 2025
261e5ac
update yml
abdullah-cognite Feb 28, 2025
8a8bd65
remove process kill logic
abdullah-cognite Feb 28, 2025
392726d
cleanup on cancellation token.cancel
abdullah-cognite Feb 28, 2025
d6acf36
update
abdullah-cognite Mar 3, 2025
8449426
update gh action
abdullah-cognite Mar 3, 2025
dd2beb8
update action
abdullah-cognite Mar 3, 2025
3d9aa0b
update
abdullah-cognite Mar 3, 2025
4ae261a
use faketimeprovider
abdullah-cognite Mar 3, 2025
3d992c8
use faketimeprovider from microsoft
abdullah-cognite Mar 3, 2025
6203ce5
update action
abdullah-cognite Mar 3, 2025
68cc9ff
update license controller
abdullah-cognite Mar 3, 2025
2ce3cc1
address comments
abdullah-cognite Mar 3, 2025
cba6a49
slight formatting change
abdullah-cognite Mar 3, 2025
1db226e
update changelog + GH action
abdullah-cognite Mar 3, 2025
3d7c36d
fix bug in license controller + add test
abdullah-cognite Mar 4, 2025
4a5f0e7
update
abdullah-cognite Mar 4, 2025
cd64bad
add controller name
abdullah-cognite Mar 4, 2025
5a50777
forced logging for the GH action
abdullah-cognite Mar 4, 2025
a4b9e4d
explicit logging
abdullah-cognite Mar 5, 2025
4595950
update
abdullah-cognite Mar 5, 2025
59c6de3
add acquisition time
abdullah-cognite Mar 5, 2025
475314f
small updates
abdullah-cognite Mar 5, 2025
5045f00
add log verification to processutils test
abdullah-cognite Mar 5, 2025
67c4cea
update
abdullah-cognite Mar 5, 2025
b3a9efd
fix process utils for windows test
abdullah-cognite Mar 5, 2025
db3bdb6
update
abdullah-cognite Mar 5, 2025
bd8966d
add more logging to ProcessUtils
abdullah-cognite Mar 5, 2025
6eec933
update
abdullah-cognite Mar 5, 2025
cbd5caa
update
abdullah-cognite Mar 6, 2025
129ee33
update test for processutils
abdullah-cognite Mar 6, 2025
f74048b
[debugging] ProcessUtilsTest
abdullah-cognite Mar 6, 2025
bf5f685
[debugging] ProcessUtilsTest
abdullah-cognite Mar 6, 2025
206bb4d
Merge branch 'main' into fix-cdf-retries
abdullah-cognite Mar 6, 2025
a5a52f7
update version
abdullah-cognite Mar 6, 2025
ffd92f7
update
abdullah-cognite Mar 6, 2025
bb10c98
remove default retry configuration
abdullah-cognite Mar 6, 2025
50ff20d
remove finally block from process utils test
abdullah-cognite Mar 6, 2025
571e797
update processutils test
abdullah-cognite Mar 6, 2025
cfbcc5e
remove variable
abdullah-cognite Mar 6, 2025
23e1e8f
Merge branch 'main' into fix-cdf-retries
abdullah-cognite Mar 10, 2025
d3b0d6a
address risk review + add canceltoken to release license
abdullah-cognite Mar 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

All notable changes to this project will be documented in this file.

## Release v1.0.0-beta-014 (2025-02-20)

### Features

* Fix bug where invalid config file lead to connector running in a loop displaying connection error
* Introduce support for license tracking and holding.
* Introduce support for Process termination.

## Release v1.0.0-beta-013 (2025-02-17)

### Features
Expand Down
177 changes: 177 additions & 0 deletions Cognite.Simulator.Tests/UtilsTests/LicenseControllerTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
using Xunit;
using System;
using System.Threading;
using Microsoft.Extensions.Logging;
using Moq;
using Cognite.Simulator.Utils;

namespace Cognite.Simulator.Tests.UtilsTests{

public enum TestLicenseState
{
Released,
Held
}

public class LicenseTrackerTests
{
private readonly Mock<ILogger> _loggerMock = new Mock<ILogger>();



[Fact]
public void LicenseTracker_ShouldReleaseLicense_WhenNotInUse()
{
// Arrange
Mock<Func<object>> _releaseLicenseFuncMock = new Mock<Func<object>>();
Mock<Func<object>> _acquireLicenseFuncMock = new Mock<Func<object>>();
var licenseLockTime = TimeSpan.FromMilliseconds(100);
var licenseState = TestLicenseState.Released;
var tracker = new LicenseController(
licenseLockTime: licenseLockTime,
releaseLicenseFunc: () =>
{
_releaseLicenseFuncMock.Object();
licenseState = TestLicenseState.Released;
return null;
},
acquireLicenseFunc: () => {
_acquireLicenseFuncMock.Object();
licenseState = TestLicenseState.Held;
return null;
},
_loggerMock.Object
);

// default state of the license should be released
Assert.Equal(TestLicenseState.Released, licenseState);
tracker.AcquireLicense();
_acquireLicenseFuncMock.Verify(f => f(), Times.Once);
Assert.Equal(TestLicenseState.Held, licenseState);
Assert.True(tracker.LicenseHeld);
using (tracker.BeginUsage()) {
// Use and immediately release
}

// Add buffer time to account for timer inconsistencies
Thread.Sleep(250);

_releaseLicenseFuncMock.Verify(f => f(), Times.Once);
Assert.Equal(TestLicenseState.Released, licenseState);
Assert.False(tracker.LicenseHeld);
tracker.Dispose();
}

[Fact]
public void LicenseTracker_ShouldNotRelease_WhileInUse()
{
// Arrange
var licenseLockTime = TimeSpan.FromMilliseconds(100);
var licenseState = TestLicenseState.Released;
var tracker = new LicenseController(
licenseLockTime: licenseLockTime,
releaseLicenseFunc: () =>
{
licenseState = TestLicenseState.Released;
return null;
},
acquireLicenseFunc: () => {
licenseState = TestLicenseState.Held;
return null;
},
_loggerMock.Object
);

// Act
tracker.AcquireLicense();
Assert.Equal(TestLicenseState.Held, licenseState);
Assert.True(tracker.LicenseHeld);
using (var usage = tracker.BeginUsage())
{
Thread.Sleep(200); // Wait longer than lock time
Assert.True(tracker.LicenseHeld); // Should not release while in use
}

tracker.Dispose();
}

[Fact]
public void LicenseTracker_ShouldResetTimer_OnNewUsage()
{
// Arrange
var licenseLockTime = TimeSpan.FromMilliseconds(100);
var licenseState = TestLicenseState.Released;
var tracker = new LicenseController(
licenseLockTime: licenseLockTime,
releaseLicenseFunc: () =>
{
licenseState = TestLicenseState.Released;
return null;
},
acquireLicenseFunc: () => {
licenseState = TestLicenseState.Held;
return null;
},
_loggerMock.Object
);

// Act
tracker.AcquireLicense();
Assert.Equal(TestLicenseState.Held, licenseState);
using (tracker.BeginUsage()) { } // First usage
Thread.Sleep(50);

using (tracker.BeginUsage()) { } // Second usage should reset timer
Thread.Sleep(50);

// Assert
Assert.True(tracker.LicenseHeld); // Should not be released yet due to timer reset

Thread.Sleep(100); // Wait for full lock time
Assert.True(licenseState == TestLicenseState.Released);
Assert.False(tracker.LicenseHeld);

tracker.Dispose();
}

[Fact]
public void LicenseTracker_ShouldPreventRelease_DuringContinuousUsage()
{
// Arrange
var licenseLockTime = TimeSpan.FromMilliseconds(100);
var licenseState = TestLicenseState.Released;
var tracker = new LicenseController(
licenseLockTime: licenseLockTime,
releaseLicenseFunc: () =>
{
licenseState = TestLicenseState.Released;
return null;
},
acquireLicenseFunc: () => {
licenseState = TestLicenseState.Held;
return null;
},
_loggerMock.Object
);

// Act
tracker.AcquireLicense();
using (var usage1 = tracker.BeginUsage())
{
Thread.Sleep(50);
using (var usage2 = tracker.BeginUsage())
{
Thread.Sleep(100);
// Should not release during overlapping usage
Assert.True(tracker.LicenseHeld);
}
}

Thread.Sleep(200); // Wait longer than lock time after all usage ends

// Assert
Assert.True(licenseState == TestLicenseState.Released);
tracker.Dispose();
}
}
}
128 changes: 128 additions & 0 deletions Cognite.Simulator.Tests/UtilsTests/ProcessUtilsTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
using Microsoft.Extensions.Logging;
using Moq;
using System;
using System.Diagnostics;
using System.Management;
using System.Runtime.InteropServices;
using System.Security.Principal;
using Xunit;
using Cognite.Simulator.Utils;

namespace Cognite.Simulator.Tests.UtilsTests
{
public class ProcessUtilsTests
{
[Fact]
public void GetProcessOwnerWmi_ThrowsOnNonWindows()
{
// We can't easily mock this static method, so we'll only run this test on non-Windows platforms
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Assert.Throws<PlatformNotSupportedException>(() => ProcessUtils.GetProcessOwnerWmi(1));
}
}

[Fact]
public void GetCurrentUsername_ThrowsOnNonWindows()
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
Assert.Throws<PlatformNotSupportedException>(() => ProcessUtils.GetCurrentUsername());
}
}

[Fact]
public void KillProcess_KillsOwnedProcessesOnly()
{
// Only run on Windows for simplicity
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return;
}

// Arrange
var mockLogger = new Mock<ILogger>();
var processName = "notepad";

// Create test process to ensure at least one exists
Process testProcess = null;
try
{
testProcess = Process.Start("notepad.exe");

// Act
ProcessUtils.KillProcess(processName, mockLogger.Object);

// Assert
// Verify log messages were called with correct parameters
TestUtilities.VerifyLog(mockLogger, LogLevel.Debug, "Searching for process : {processName}", Times.Once(), isContainsCheck: true);

Check failure on line 58 in Cognite.Simulator.Tests/UtilsTests/ProcessUtilsTest.cs

View workflow job for this annotation

GitHub Actions / build

The type arguments for method 'TestUtilities.VerifyLog<TLogger>(Mock<ILogger<TLogger>>, LogLevel, string, Times, bool)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

Check failure on line 58 in Cognite.Simulator.Tests/UtilsTests/ProcessUtilsTest.cs

View workflow job for this annotation

GitHub Actions / build

The type arguments for method 'TestUtilities.VerifyLog<TLogger>(Mock<ILogger<TLogger>>, LogLevel, string, Times, bool)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

// Verify that process.Kill was called if this process belongs to current user
string currentUser = ProcessUtils.GetCurrentUsername().ToLower();
TestUtilities.VerifyLog(mockLogger, LogLevel.Information, "Killing process with PID", Times.Once(), isContainsCheck: true);

Check failure on line 62 in Cognite.Simulator.Tests/UtilsTests/ProcessUtilsTest.cs

View workflow job for this annotation

GitHub Actions / build

The type arguments for method 'TestUtilities.VerifyLog<TLogger>(Mock<ILogger<TLogger>>, LogLevel, string, Times, bool)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

Check failure on line 62 in Cognite.Simulator.Tests/UtilsTests/ProcessUtilsTest.cs

View workflow job for this annotation

GitHub Actions / build

The type arguments for method 'TestUtilities.VerifyLog<TLogger>(Mock<ILogger<TLogger>>, LogLevel, string, Times, bool)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

// Verify that our testProcess was killed (it should belong to current user)
Assert.Throws<InvalidOperationException>(() => testProcess.Refresh());
}
finally
{
// Cleanup - make sure process is closed
testProcess?.Close();
}
}

[Fact]
public void KillProcess_HandlesExceptions()
{
// Arrange
var mockLogger = new Mock<ILogger>();

// Act - pass a non-existent process name
ProcessUtils.KillProcess("ThisProcessDoesNotExist_12345", mockLogger.Object);

// Assert - verify error wasn't logged since no exception should occur
// when process not found (empty enumeration handled gracefully)
TestUtilities.VerifyLog(mockLogger, LogLevel.Information, "Failed to kill process", Times.Once(), isContainsCheck: true);

Check failure on line 85 in Cognite.Simulator.Tests/UtilsTests/ProcessUtilsTest.cs

View workflow job for this annotation

GitHub Actions / build

The type arguments for method 'TestUtilities.VerifyLog<TLogger>(Mock<ILogger<TLogger>>, LogLevel, string, Times, bool)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

Check failure on line 85 in Cognite.Simulator.Tests/UtilsTests/ProcessUtilsTest.cs

View workflow job for this annotation

GitHub Actions / build

The type arguments for method 'TestUtilities.VerifyLog<TLogger>(Mock<ILogger<TLogger>>, LogLevel, string, Times, bool)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
}

[Fact]
public void GetProcessOwnerWmi_ReturnsOwnerString()
{
// Skip test on non-Windows platforms
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return;
}

// Arrange - get current process ID
int processId = Process.GetCurrentProcess().Id;

// Act
string owner = ProcessUtils.GetProcessOwnerWmi(processId);

// Assert
Assert.NotEqual("No Owner Found", owner);
Assert.NotEqual("Access Denied or Process Exited", owner);
Assert.Contains("\\", owner); // Owner format should be "domain\user"
}

[Fact]
public void GetProcessOwnerWmi_ReturnsNoOwnerFound_WhenProcessDoesNotExist()
{
// Skip test on non-Windows platforms
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return;
}

// Arrange - unlikely that process ID 999999 exists
int nonExistentProcessId = 999999;

// Act
string owner = ProcessUtils.GetProcessOwnerWmi(nonExistentProcessId);

// Assert
Assert.Equal("No Owner Found", owner);
}
}
}
1 change: 1 addition & 0 deletions Cognite.Simulator.Utils/Cognite.Simulator.Utils.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<PackageReference Include="Cognite.ExtractorUtils" Version="1.29.0" />
<!-- <ProjectReference Include="..\..\dotnet-extractor-utils\ExtractorUtils\ExtractorUtils.csproj" /> -->
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Management" Version="9.0.2" />
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 2 additions & 0 deletions Cognite.Simulator.Utils/Configuration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Cognite.Extractor.StateStorage;
using Cognite.Extractor.Utils;
using Cognite.Simulator.Utils.Automation;
using Cognite.Extractor.Common;

namespace Cognite.Simulator.Utils
{
Expand Down Expand Up @@ -75,6 +76,7 @@
}

if (Cognite.CdfRetries == null) {
Cognite.CdfRetries = new RetryConfig();
Cognite.CdfRetries.MaxRetries = 11;
Cognite.CdfRetries.MaxDelay = 60 * 1000; //ms
}
Expand Down Expand Up @@ -266,9 +268,9 @@
public bool Enabled { get; set; }
}

public class DefaultConnectorConfig : ConnectorConfig

Check warning on line 271 in Cognite.Simulator.Utils/Configuration.cs

View workflow job for this annotation

GitHub Actions / build

Missing XML comment for publicly visible type or member 'DefaultConnectorConfig'
{
public ModelLibraryConfig ModelLibrary { get; set; }

Check warning on line 273 in Cognite.Simulator.Utils/Configuration.cs

View workflow job for this annotation

GitHub Actions / build

Missing XML comment for publicly visible type or member 'DefaultConnectorConfig.ModelLibrary'
public RoutineLibraryConfig RoutineLibrary { get; set; }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ public static async Task RunStandalone()
catch (Exception e)
{
logger.LogError(e, "Unhandled exception: {message}", e.Message);
throw;
}
}
}
Expand Down
Loading
Loading