Skip to content

Commit 0237a3f

Browse files
Add documentation and fix code (#60)
* Add documentation and fix code * Minor fixes * Self-review fixes * Fix * Fix the incorrect env vars configuration * Bump hello to the release version * Update integration_tests/manifests/hello-world-algorithm.yaml Co-authored-by: George Zubrienko <gzu@ecco.com> --------- Co-authored-by: George Zubrienko <gzu@ecco.com>
1 parent be02d8f commit 0237a3f

7 files changed

Lines changed: 148 additions & 47 deletions

File tree

README.md

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,113 @@
22

33
SDK for the following Sneaks & Data Platform Services:
44

5-
- Crystal, a high-performance runner for containerized machine learning algorithms based on Akka framework and Kubernetes Jobs API.
5+
- Nexus, a high-performance runner for containerized machine learning algorithms based on Akka framework and Kubernetes Jobs API.
66
- Boxer, an authorization API based on Json Web Tokens.
7+
8+
# Usage
9+
To use the SnD.ApiClient, you can follow these steps:
10+
1. Install the SnD.ApiClient package from NuGet.
11+
This package is hosted on GitHub Packages, so you need to add the GitHub Packages NuGet source to your project.
12+
You can do this by running the following command in your terminal, replacing `your-token` and `your-username` with your GitHub token and username:
13+
```bash
14+
export GITHUB_TOKEN=your-token
15+
export USERNAME=your-username
16+
dotnet nuget add source --username $USERNAME --password $GITHUB_TOKEN \
17+
--store-password-in-clear-text \
18+
--name github "https://nuget.pkg.github.com/sneaksAndData/index.json"
19+
```
20+
21+
2. Inject the Nexus Client into application dependency injection container:
22+
23+
```csharp
24+
using SnD.ApiClient.Config;
25+
using SnD.ApiClient.Extensions;
26+
27+
var services = new ServiceCollection();
28+
29+
// Add Http Client for Nexus Client
30+
services.AddHttpClient()
31+
32+
// Add ILoggerFactory for logging
33+
services.AddLogging();
34+
35+
// Assuming that we have a configuration object that contains the necessary settings for the Nexus Client
36+
// named configurationRoot
37+
services.Configure<NexusClientOptions>(configurationRoot.GetSection(nameof(NexusClientOptions)))
38+
39+
// Add retry policy for transient errors
40+
// For now, only RetryAllErrors is supported, which retries on all exceptions. In the future, we may add more specific retry policies.
41+
services.AddNexusRetryPolicy(sp => new RetryAllErrors(sp.GetRequiredService<ILogger<RetryAllErrors>>(), sp.GetRequiredService<IOptions<NexusClientOptions>>()))
42+
43+
// Add the Nexus Client to the dependency injection container
44+
services.AddNexusClient();
45+
46+
// Add the Boxer Client to the dependency injection container
47+
services.AddBoxerClient(configurationRoot.GetSection(nameof(BoxerClientOptions)));
48+
49+
// Add azure credential provider for authentication
50+
// This is required for Nexus Client to authenticate with Azure Entra ID to obtain the Access token for calling Nexus API.
51+
// This will add the authenticaiton provider with the "https://management.core.windows.net/.default" scope.
52+
// Addionaly it will alsot inject Boxer authentication provider to the dependency injection container,
53+
// which will be used by the Nexus Client to authenticate with Boxer API.
54+
services.AuthorizeWithBoxerOnAzure().AddAuthenticationProvider()
55+
```
56+
57+
## Creating and Awaiting Nexus Jobs
58+
59+
The acceptance test `test/SnD.ApiClient.Tests.Acceptance/Nexus/NexusAcceptancetests.cs` shows the full runtime flow:
60+
61+
1. Resolve `INexusClient` from DI.
62+
2. Build a `NexusAlgorithmRequest` with the algorithm payload and other parameters.
63+
3. Call `CreateRunAsync(...)` to submit the run.
64+
4. Use `AwaitRunAsync(...)` with a polling interval and cancellation timeout.
65+
5. Check final state with `IsFinished(...)` and optionally `HasSucceeded(...)` if to validate success vs failure.
66+
67+
```csharp
68+
using System;
69+
using System.Threading;
70+
using System.Threading.Tasks;
71+
using KiotaPosts.Client.Models.Models;
72+
using Microsoft.Extensions.DependencyInjection;
73+
using SnD.ApiClient.Nexus;
74+
using SnD.ApiClient.Nexus.Base;
75+
76+
// Assuning that we resolved INexusClient from the dependency injection container and have the algorithm name and payload ready.
77+
INexusClient nexusClient;
78+
79+
// Start from generated request model, then attach serialized payload.
80+
var request = NexusAlgorithmRequest.Create(
81+
this.configuration.AlgorithmPayload,
82+
customConfiguration: null,
83+
parentRequest: null,
84+
payloadValidFor: "6h",
85+
requestApiVersion: null,
86+
tag: "example"
87+
);
88+
89+
// Use a bounded timeout so await does not run forever.
90+
using var cts = new CancellationTokenSource(TimeSpan.FromMinutes(5));
91+
92+
// Create the run and get the request ID.
93+
var createResponse = await nexusClient.CreateRunAsync(request, algorithmName, dryRun: false, cancellationToken: cts.Token);
94+
95+
// Await the run completion with polling, and get the final result.
96+
var result = await nexusClient.AwaitRunAsync(
97+
createResponse.RequestId,
98+
algorithmName,
99+
pollInterval: TimeSpan.FromSeconds(2),
100+
cancellationToken: cts.Token);
101+
102+
if (!nexusClient.IsFinished(result))
103+
{
104+
throw new TimeoutException("Nexus run did not reach a terminal state in time.");
105+
}
106+
107+
var succeeded = nexusClient.HasSucceeded(result);
108+
Console.WriteLine($"Run {createResponse.RequestId} finished with status: {result.Status}");
109+
110+
if (succeeded == false)
111+
{
112+
throw new Exception($"Nexus run failed with status: {result.Status}");
113+
}
114+
```

integration_tests/manifests/hello-world-algorithm.yaml

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ spec:
1919
image: nexus-hello
2020
registry: ghcr.io/sneaksanddata
2121
serviceAccountName: nexus
22-
versionTag: 0.1.2
22+
versionTag: 0.1.3
2323
datadogIntegrationSettings:
2424
mountDatadogSocket: true
2525
errorHandlingBehaviour:
@@ -34,32 +34,38 @@ spec:
3434
valueFrom:
3535
fieldRef:
3636
fieldPath: status.hostIP
37-
- name: PROTEUS__DD_SITE
38-
value: datadoghq.eu
39-
- name: NEXUS__ALGORITHM_NAME
40-
value: nexus-hello
41-
- name: NEXUS__SCHEDULER_URL
42-
value: http://nexus.nexus.svc.cluster.local:8080
43-
- name: NEXUS__RECEIVER_URL
44-
value: http://dockerhost.nexus.svc.cluster.local:8081
45-
- name: NEXUS__STORAGE_CLIENT_CLASS
46-
value: adapta.storage.blob.s3_storage_client.S3StorageClient
47-
- name: ALGORITHM_STORAGE__BASE_PATH
48-
value: s3a://nexus/storage
49-
- name: NEXUS__ALGORITHM_OUTPUT_PATH
50-
value: s3a://nexus/result
51-
- name: NEXUS__TELEMETRY_PATH
52-
value: s3a://nexus/telemetry
53-
- name: ALGORITHM_STORAGE_TYPE
54-
value: S3
37+
- name: NEXUS__CLIENT__SCHEDULER
38+
value: http://nexus.default.svc.cluster.local:8080
39+
- name: NEXUS__CLIENT__SCHEDULER_ACCESS_TOKEN
40+
value: ""
41+
- name: NEXUS__CLIENT__RECEIVER
42+
value: http://nexus-receiver.default.svc.cluster.local:8080
5543
- name: PROTEUS__AWS_REGION
5644
value: us-east-1
5745
- name: PROTEUS__AWS_ENDPOINT
58-
value: http://dockerhost.nexus.svc.cluster.local:9000
46+
value: http://minio.default.svc.cluster.local:9000
5947
- name: PROTEUS__AWS_SECRET_ACCESS_KEY
6048
value: minioadmin
6149
- name: PROTEUS__AWS_ACCESS_KEY_ID
6250
value: minioadmin
51+
- name: NEXUS__METRICS__PROVIDER
52+
value: adapta.metrics.providers.void_provider.VoidMetricsProvider
53+
- name: NEXUS__RESULT__STORAGE_CLIENT_CLASS
54+
value: adapta.storage.blob.s3_storage_client.S3StorageClient
55+
- name: NEXUS__RESULT__OUTPUT_PATH
56+
value: s3a://nexus-sdk-tests
57+
- name: NEXUS__REMOTE_ALGORITHM__COMPRESSION_IMPORT_PATH
58+
value: ""
59+
- name: NEXUS__REMOTE_ALGORITHM__DECOMPRESSION_IMPORT_PATH
60+
value: ""
61+
- name: NEXUS__INPUTS__QUERY_ENABLED_STORE__CONNECTION_STRING
62+
value: ""
63+
- name: NEXUS__THREADING__BLOCKING_POOL_MAX_SIZE
64+
value: '10'
65+
- name: NEXUS__LOGGING__DATADOG__ENABLED
66+
value: "'0'"
67+
- name: NEXUS__LOGGING__FIXED_TEMPLATE
68+
value: ''
6369
mappedEnvironmentVariables: []
6470
maximumRetries: 1
6571
workgroupRef:

src/SnD.ApiClient/Nexus/Base/INexusClient.cs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using KiotaPosts.Client.Models.Models;
2-
using KiotaPosts.Client.Models.V1;
32
using SnD.ApiClient.Nexus.Models;
43

54
namespace SnD.ApiClient.Nexus.Base;
@@ -11,20 +10,11 @@ public interface INexusClient
1110
/// </summary>
1211
/// <param name="algorithmRequest">Algorithm parameters as JsonElement</param>
1312
/// <param name="algorithm">Algorithm name</param>
14-
/// <param name="customConfiguration">Custom configuration for algorithm run</param>
15-
/// <param name="parentRequest">Optional Parent request reference, if applicable. Specifying a parent request allows
16-
/// indirect cancellation of the submission - via cancellation of a parent.</param>
17-
/// <param name="tag">Client side assigned run tag.</param>
18-
/// <param name="payloadValidFor">Payload pre-signed URL validity period.</param>
1913
/// <param name="dryRun">Dry run, if set to True, will only buffer a submission but skip job creation.</param>
2014
/// <param name="cancellationToken">Cancellation token</param>
2115
/// <returns>Instance of object <see cref="CreateRunResponse"/> with run ID</returns>
2216
Task<CreateRunResponse> CreateRunAsync(NexusAlgorithmRequest algorithmRequest,
2317
string algorithm,
24-
NexusAlgorithmSpec? customConfiguration,
25-
AlgorithmRequestRef? parentRequest,
26-
string? tag,
27-
string? payloadValidFor,
2818
bool dryRun,
2919
CancellationToken cancellationToken);
3020

File renamed without changes.

src/SnD.ApiClient/Nexus/NexusClient.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using KiotaPosts.Client;
22
using KiotaPosts.Client.Models.Models;
3-
using KiotaPosts.Client.Models.V1;
43
using Microsoft.Extensions.Logging;
54
using Microsoft.Kiota.Abstractions;
65
using SnD.ApiClient.Nexus.Base;
@@ -14,10 +13,6 @@ public class NexusClient(IRequestAdapter adapter, ILogger<NexusClient> logger) :
1413

1514
public async Task<CreateRunResponse> CreateRunAsync(NexusAlgorithmRequest algorithmRequest,
1615
string algorithm,
17-
NexusAlgorithmSpec? customConfiguration,
18-
AlgorithmRequestRef? parentRequest,
19-
string? tag,
20-
string? payloadValidFor,
2116
bool dryRun,
2217
CancellationToken cancellationToken)
2318
{

test/SnD.ApiClient.Tests.Acceptance/Nexus/NexusAcceptancetests.cs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using System.Net.Http;
33
using System.Threading;
44
using System.Threading.Tasks;
5-
using KiotaPosts.Client.Models.Models;
65
using Microsoft.Extensions.Configuration;
76
using Microsoft.Extensions.DependencyInjection;
87
using Microsoft.Extensions.Logging;
@@ -13,6 +12,7 @@
1312
using SnD.ApiClient.Extensions;
1413
using SnD.ApiClient.Nexus;
1514
using SnD.ApiClient.Nexus.Base;
15+
using SnD.ApiClient.Nexus.Models;
1616
using SnD.ApiClient.Tests.Acceptance.Config;
1717
using Xunit;
1818

@@ -50,15 +50,19 @@ public NexusAcceptanceTests()
5050
public async Task TestCanRunAlgorithm()
5151
{
5252
Skip.If(string.IsNullOrEmpty(configuration.AlgorithmName) || configuration.AlgorithmPayload == null, "Algorithm payload and/or name is empty.");
53-
53+
54+
var request = NexusAlgorithmRequest.Create(
55+
this.configuration.AlgorithmPayload,
56+
customConfiguration: null,
57+
parentRequest: null,
58+
payloadValidFor: "6h",
59+
requestApiVersion: null,
60+
tag: "example"
61+
);
5462
var nexusClient = this.services.GetRequiredService<INexusClient>();
5563
var response = await nexusClient.CreateRunAsync(
56-
(this.configuration.AlgorithmRequest?? new AlgorithmRequest()).ToNexusAlgorithmRequest(this.configuration.AlgorithmPayload),
64+
request,
5765
configuration.AlgorithmName,
58-
null,
59-
null,
60-
null,
61-
null,
6266
false,
6367
CancellationToken.None
6468
);
@@ -72,7 +76,9 @@ public async Task TestCanRunAlgorithm()
7276
configuration.AlgorithmName,
7377
TimeSpan.FromSeconds(2),
7478
cts.Token);
79+
7580
Assert.NotNull(runResult);
7681
Assert.True(nexusClient.IsFinished(runResult), "Run did not finish in expected time.");
82+
Assert.True(nexusClient.HasSucceeded(runResult), runResult.RunErrorMessage);
7783
}
7884
}

test/SnD.ApiClient.Tests/Nexus/NexusClientTests.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,6 @@ await nexusClient.CreateRunAsync(
9090
null,
9191
null),
9292
algorithm,
93-
null,
94-
null,
95-
null,
96-
null,
9793
false,
9894
CancellationToken.None);
9995

0 commit comments

Comments
 (0)