Skip to content

Commit e64e8b3

Browse files
authored
Merge pull request #760 from Project-MONAI/release/2023.04.24.2
Release/0.1.9
2 parents 5a14064 + 01fbb01 commit e64e8b3

File tree

30 files changed

+1314
-305
lines changed

30 files changed

+1314
-305
lines changed

guidelines/mwm-developer-setup.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,17 @@ Note. if you already have docker container for Minio Rabbit etc running Stop the
3232
- `kubectl apply -n argo -f https://raw.githubusercontent.com/argoproj/argo-workflows/master/manifests/quick-start-postgres.yaml`
3333
- `kubectl config set-context --current --namespace=argo`
3434

35+
To disable argo authentication run
36+
37+
kubectl patch deployment \
38+
argo-server \
39+
--namespace argo \
40+
--type='json' \
41+
-p='[{"op": "replace", "path": "/spec/template/spec/containers/0/args", "value": [
42+
"server",
43+
"--auth-mode=server"
44+
]}]'
45+
3546
Note. below Im using bash as its my preferred option, But if you to are using bash and your on windows (wsl2) you MUST make sure you windows .kube/config is also pointing to the same K8's cluster, this is because the code running in vs will look in there for the context to write k8's secrets too!
3647

3748
now in a bash window (can be cmd or powershell)

src/TaskManager/API/Migrations/M001_TaskDispatchEventInfo_addVerion.cs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
//
2-
// Copyright 2023 Guy’s and St Thomas’ NHS Foundation Trust
3-
//
4-
// Licensed under the Apache License, Version 2.0 (the "License");
5-
// you may not use this file except in compliance with the License.
6-
// You may obtain a copy of the License at
7-
//
8-
// http://www.apache.org/licenses/LICENSE-2.0
9-
//
10-
// Unless required by applicable law or agreed to in writing, software
11-
// distributed under the License is distributed on an "AS IS" BASIS,
12-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
// See the License for the specific language governing permissions and
14-
// limitations under the License.
15-
1+
/*
2+
* Copyright 2022 MONAI Consortium
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
1616
using Monai.Deploy.WorkflowManager.TaskManager.API.Models;
1717
using Mongo.Migration.Migrations.Document;
1818
using MongoDB.Bson;

src/TaskManager/API/Migrations/M001_TaskExecutionStats_addVersion.cs

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,19 @@
1-
//
2-
// Copyright 2023 Guy’s and St Thomas’ NHS Foundation Trust
3-
//
4-
// Licensed under the Apache License, Version 2.0 (the "License");
5-
// you may not use this file except in compliance with the License.
6-
// You may obtain a copy of the License at
7-
//
8-
// http://www.apache.org/licenses/LICENSE-2.0
9-
//
10-
// Unless required by applicable law or agreed to in writing, software
11-
// distributed under the License is distributed on an "AS IS" BASIS,
12-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
// See the License for the specific language governing permissions and
14-
// limitations under the License.
15-
// using Monai.Deploy.WorkflowManager.TaskManager.API.Models;
1+
/*
2+
* Copyright 2022 MONAI Consortium
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
1617
using Monai.Deploy.WorkflowManager.TaskManager.API.Models;
1718
using Mongo.Migration.Migrations.Document;
1819
using MongoDB.Bson;

src/TaskManager/Plug-ins/AideClinicalReview/AideClinicalReviewPlugin.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,13 @@ private void Initialize()
8888
_workflowName = Event.TaskPluginArguments[Keys.WorkflowName];
8989
}
9090

91-
if (Event.TaskPluginArguments.ContainsKey(Keys.Notifications))
91+
if (Event.TaskPluginArguments.ContainsKey(Keys.Notifications) && Boolean.TryParse(Event.TaskPluginArguments[Keys.Notifications], out var result))
9292
{
93-
_notifications = Boolean.TryParse(Event.TaskPluginArguments[Keys.Notifications], out bool result);
93+
_notifications = result;
94+
}
95+
else
96+
{
97+
_notifications = true;
9498
}
9599

96100
if (Event.TaskPluginArguments.ContainsKey(Keys.ReviewedExecutionId))

src/TaskManager/Plug-ins/Argo/ArgoClient.cs

Lines changed: 107 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -22,37 +22,9 @@
2222

2323
namespace Monai.Deploy.WorkflowManager.TaskManager.Argo
2424
{
25-
public interface IArgoClient
25+
public class ArgoClient : BaseArgoClient, IArgoClient
2626
{
27-
Task<Workflow> Argo_CreateWorkflowAsync(string argoNamespace, WorkflowCreateRequest body, CancellationToken cancellationToken);
28-
29-
Task<Workflow> Argo_GetWorkflowAsync(string argoNamespace, string name, string getOptions_resourceVersion, string fields, CancellationToken cancellationToken);
30-
31-
Task<WorkflowTemplate> Argo_GetWorkflowTemplateAsync(string argoNamespace, string name, string getOptions_resourceVersion);
32-
33-
Task<Workflow> Argo_StopWorkflowAsync(string argoNamespace, string name, WorkflowStopRequest body);
34-
35-
Task<Workflow> Argo_TerminateWorkflowAsync(string argoNamespace, string name, WorkflowTerminateRequest body);
36-
37-
Task<Version?> Argo_GetVersionAsync();
38-
39-
Task<string?> Argo_Get_WorkflowLogsAsync(string argoNamespace, string name, string podName, string logOptions_container);
40-
41-
Task<WorkflowTemplate> Argo_CreateWorkflowTemplateAsync(string argoNamespace, WorkflowTemplateCreateRequest body, CancellationToken cancellationToken);
42-
}
43-
44-
public class ArgoClient : IArgoClient
45-
{
46-
private readonly HttpClient _httpClient;
47-
48-
public string BaseUrl { get; set; } = "http://localhost:2746";
49-
50-
private string FormattedBaseUrl { get { return BaseUrl != null ? BaseUrl.TrimEnd('/') : ""; } }
51-
52-
public ArgoClient(HttpClient httpClient)
53-
{
54-
_httpClient = httpClient;
55-
}
27+
public ArgoClient(HttpClient httpClient) : base(httpClient) { }
5628

5729
public async Task<Workflow> Argo_CreateWorkflowAsync(string argoNamespace, WorkflowCreateRequest body, CancellationToken cancellationToken)
5830
{
@@ -164,27 +136,113 @@ public async Task<WorkflowTemplate> Argo_GetWorkflowTemplateAsync(string argoNam
164136
urlBuilder.Length--;
165137
return await GetRequest<string>(urlBuilder, true).ConfigureAwait(false);
166138
}
167-
private async Task<T> SendRequest<T>(StringContent stringContent, StringBuilder urlBuilder, string Method, CancellationToken cancellationToken)
139+
140+
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
141+
/// <returns>A successful response.</returns>
142+
/// <exception cref="ApiException">A server side error occurred.</exception>
143+
public virtual async Task<WorkflowTemplate> Argo_CreateWorkflowTemplateAsync(string argoNamespace, WorkflowTemplateCreateRequest body, CancellationToken cancellationToken)
144+
{
145+
Guard.Against.NullOrWhiteSpace(argoNamespace);
146+
Guard.Against.Null(body);
147+
148+
var urlBuilder = new StringBuilder();
149+
urlBuilder.Append(CultureInfo.InvariantCulture, $"{FormattedBaseUrl}/api/v1/workflow-templates/{argoNamespace}");
150+
151+
var method = "POST";
152+
var content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body));
153+
return await SendRequest<WorkflowTemplate>(content, urlBuilder, method, cancellationToken).ConfigureAwait(false);
154+
}
155+
156+
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
157+
/// <returns>A successful response.</returns>
158+
/// <exception cref="ApiException">A server side error occurred.</exception>
159+
public virtual async Task<bool> Argo_DeleteWorkflowTemplateAsync(string argoNamespace, string templateName, CancellationToken cancellationToken)
168160
{
169-
using (var request = new HttpRequestMessage())
161+
Guard.Against.NullOrWhiteSpace(argoNamespace);
162+
163+
var urlBuilder = new StringBuilder();
164+
urlBuilder.Append(CultureInfo.InvariantCulture, $"{FormattedBaseUrl}/api/v1/workflow-templates/{argoNamespace}/{templateName}");
165+
166+
var method = "DELETE";
167+
var response = await HttpClient.SendAsync(new HttpRequestMessage(new HttpMethod(method), urlBuilder.ToString()), cancellationToken).ConfigureAwait(false);
168+
return (int)response.StatusCode == 200;
169+
}
170+
171+
public static string ConvertToString(object value, CultureInfo cultureInfo)
172+
{
173+
if (value == null)
170174
{
171-
stringContent.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json");
172-
request.Content = stringContent;
173-
request.Method = new HttpMethod(Method);
174-
request.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json"));
175-
request.RequestUri = new Uri(urlBuilder.ToString(), UriKind.RelativeOrAbsolute);
175+
return "";
176+
}
176177

177-
HttpResponseMessage? response = null;
178-
try
178+
if (value is Enum)
179+
{
180+
var name = Enum.GetName(value.GetType(), value);
181+
if (name != null)
179182
{
180-
response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
183+
var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name);
184+
if (field != null)
185+
{
186+
if (System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute)) is System.Runtime.Serialization.EnumMemberAttribute attribute)
187+
{
188+
return attribute.Value ?? name;
189+
}
190+
}
191+
192+
var converted = Convert.ToString(Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType()), cultureInfo));
193+
return converted ?? string.Empty;
181194
}
182-
catch (Exception ex)
195+
}
196+
else if (value is bool boolean)
197+
{
198+
return Convert.ToString(boolean, cultureInfo).ToLowerInvariant();
199+
}
200+
else if (value is byte[] v)
201+
{
202+
return Convert.ToBase64String(v);
203+
}
204+
else if (value.GetType().IsArray)
205+
{
206+
var array = Enumerable.OfType<object>((Array)value);
207+
return string.Join(",", Enumerable.Select(array, o => ConvertToString(o, cultureInfo)));
208+
}
209+
210+
var result = Convert.ToString(value, cultureInfo);
211+
return result ?? "";
212+
}
213+
}
214+
215+
/// <summary>
216+
/// <see cref="BaseArgoClient"/> generic functions relating to argo requests
217+
/// </summary>
218+
public class BaseArgoClient
219+
{
220+
public string BaseUrl { get; set; } = "http://localhost:2746";
221+
222+
protected string FormattedBaseUrl { get { return BaseUrl != null ? BaseUrl.TrimEnd('/') : ""; } }
223+
224+
protected readonly HttpClient HttpClient;
225+
226+
public BaseArgoClient(HttpClient httpClient)
227+
{
228+
HttpClient = httpClient;
229+
}
230+
231+
protected async Task<T> SendRequest<T>(StringContent stringContent, StringBuilder urlBuilder, string method, CancellationToken cancellationToken)
232+
{
233+
using (var request = new HttpRequestMessage())
234+
{
235+
if (stringContent is not null)
183236
{
184-
var mess = ex.Message;
185-
throw;
237+
stringContent.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json");
238+
request.Content = stringContent;
186239
}
240+
request.Method = new HttpMethod(method);
241+
request.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json"));
242+
request.RequestUri = new Uri(urlBuilder.ToString(), UriKind.RelativeOrAbsolute);
187243

244+
HttpResponseMessage? response = null;
245+
response = await HttpClient.SendAsync(request, HttpCompletionOption.ResponseContentRead, cancellationToken).ConfigureAwait(false);
188246

189247
try
190248
{
@@ -222,7 +280,7 @@ private async Task<T> SendRequest<T>(StringContent stringContent, StringBuilder
222280
}
223281
}
224282

225-
private async Task<T?> GetRequest<T>(StringBuilder urlBuilder, bool isLogs = false)
283+
protected async Task<T?> GetRequest<T>(StringBuilder urlBuilder, bool isLogs = false)
226284
{
227285

228286
using (var request = new HttpRequestMessage())
@@ -231,7 +289,7 @@ private async Task<T> SendRequest<T>(StringContent stringContent, StringBuilder
231289
request.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json"));
232290
request.RequestUri = new Uri(urlBuilder.ToString(), UriKind.RelativeOrAbsolute);
233291

234-
var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
292+
var response = await HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead).ConfigureAwait(false);
235293
try
236294
{
237295
var headers_ = Enumerable.ToDictionary(response.Headers, h_ => h_.Key, h_ => h_.Value);
@@ -271,10 +329,9 @@ private async Task<T> SendRequest<T>(StringContent stringContent, StringBuilder
271329
}
272330
}
273331

274-
275332
protected virtual async Task<ObjectResponseResult<T?>> ReadObjectResponseAsync<T>(HttpResponseMessage response, IReadOnlyDictionary<string, IEnumerable<string>> headers, bool isLogs = false)
276333
{
277-
if (response == null || response.Content == null)
334+
if (response == null || response.Content == null || response.Content.GetType().Name == "EmptyContent")
278335
{
279336
return new ObjectResponseResult<T?>(default, string.Empty);
280337
}
@@ -306,7 +363,7 @@ private async Task<T> SendRequest<T>(StringContent stringContent, StringBuilder
306363
}
307364
}
308365

309-
protected static string DecodeLogs(string logInput)
366+
public static string DecodeLogs(string logInput)
310367
{
311368
var rows = logInput.Split(new String[] { "\n" }, StringSplitOptions.None);
312369
var jsonBody = $"[{string.Join(",", rows)}]";
@@ -348,23 +405,7 @@ protected virtual async Task<ObjectResponseResult<string>> ReadLogResponseAsync(
348405
}
349406
}
350407

351-
/// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param>
352-
/// <returns>A successful response.</returns>
353-
/// <exception cref="ApiException">A server side error occurred.</exception>
354-
public virtual async Task<WorkflowTemplate> Argo_CreateWorkflowTemplateAsync(string argoNamespace, WorkflowTemplateCreateRequest body, CancellationToken cancellationToken)
355-
{
356-
Guard.Against.NullOrWhiteSpace(argoNamespace);
357-
Guard.Against.Null(body);
358-
359-
var urlBuilder = new StringBuilder();
360-
urlBuilder.Append(CultureInfo.InvariantCulture, $"{FormattedBaseUrl}/api/v1/workflow-templates/{argoNamespace}");
361-
362-
var Method = "POST";
363-
var content = new StringContent(Newtonsoft.Json.JsonConvert.SerializeObject(body));
364-
return await SendRequest<WorkflowTemplate>(content, urlBuilder, Method, cancellationToken).ConfigureAwait(false);
365-
}
366-
367-
protected struct ObjectResponseResult<T>
408+
protected readonly struct ObjectResponseResult<T>
368409
{
369410
public ObjectResponseResult(T responseObject, string responseText)
370411
{
@@ -377,61 +418,19 @@ public ObjectResponseResult(T responseObject, string responseText)
377418
public string Text { get; }
378419
}
379420

380-
private string ConvertToString(object value, CultureInfo cultureInfo)
381-
{
382-
if (value == null)
383-
{
384-
return "";
385-
}
386-
387-
if (value is Enum)
388-
{
389-
var name = Enum.GetName(value.GetType(), value);
390-
if (name != null)
391-
{
392-
var field = System.Reflection.IntrospectionExtensions.GetTypeInfo(value.GetType()).GetDeclaredField(name);
393-
if (field != null)
394-
{
395-
if (System.Reflection.CustomAttributeExtensions.GetCustomAttribute(field, typeof(System.Runtime.Serialization.EnumMemberAttribute)) is System.Runtime.Serialization.EnumMemberAttribute attribute)
396-
{
397-
return attribute.Value ?? name;
398-
}
399-
}
400-
401-
var converted = Convert.ToString(Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType()), cultureInfo));
402-
return converted ?? string.Empty;
403-
}
404-
}
405-
else if (value is bool boolean)
406-
{
407-
return Convert.ToString(boolean, cultureInfo).ToLowerInvariant();
408-
}
409-
else if (value is byte[] v)
410-
{
411-
return Convert.ToBase64String(v);
412-
}
413-
else if (value.GetType().IsArray)
414-
{
415-
var array = Enumerable.OfType<object>((Array)value);
416-
return string.Join(",", Enumerable.Select(array, o => ConvertToString(o, cultureInfo)));
417-
}
418-
419-
var result = Convert.ToString(value, cultureInfo);
420-
return result ?? "";
421-
}
422421
class ArgoLogEntry
423422
{
424423
public string Content { get; set; } = "";
425424

426425
public string PodName { get; set; } = "";
427426
}
427+
428428
class ArgoLogEntryResult
429429
{
430430
public ArgoLogEntry Result { get; set; } = new ArgoLogEntry();
431431
}
432432
}
433433

434-
435434
public class Version
436435
{
437436
[Newtonsoft.Json.JsonProperty("buildDate", Required = Newtonsoft.Json.Required.Always)]

0 commit comments

Comments
 (0)