Skip to content

Commit c5e418f

Browse files
author
Aditya Abhishek
committed
scriptExecChanges
1 parent afe8699 commit c5e418f

4 files changed

Lines changed: 163 additions & 22 deletions

File tree

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
[
2+
{
3+
"metricName": "metric1",
4+
"metricValue": 0,
5+
"metricUnit": "unit1",
6+
"metricMetadata": {
7+
"metadata1": "m1",
8+
"metadata2": "m2"
9+
}
10+
},
11+
{
12+
"metricName": "metric2",
13+
"metricValue": -1,
14+
"metricUnit": "unit2",
15+
"metricMetadata": {
16+
"metadata1": "m3",
17+
"metadata2": "m4"
18+
}
19+
},
20+
{
21+
"metricName": "metric3",
22+
"metricValue": 1.2,
23+
"metricUnit": "unit3",
24+
"metricMetadata": {
25+
"metadata1": "m5",
26+
"metadata2": "m6"
27+
}
28+
},
29+
{
30+
"metricName": "metric4",
31+
"metricValue": 1.0,
32+
"metricMetadata": {
33+
"metadata1": "m7",
34+
"metadata2": "m8"
35+
}
36+
},
37+
{
38+
"metricName": "metric5",
39+
"metricValue": "1.24",
40+
"metricUnit": "unit5"
41+
},
42+
{
43+
"metricName": "metric6",
44+
"metricValue": "-5.8"
45+
}
46+
]

src/VirtualClient/VirtualClient.Actions.UnitTests/ScriptExecutor/JsonMetricsParserTests.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class JsonMetricsParserTests
2020
private JsonMetricsParser testParser;
2121

2222
[Test]
23-
public void JsonMetricsParserVerifyMetricsForPassResults()
23+
public void JsonMetricsParserVerifyMetricsForPassResults_Format1()
2424
{
2525
string resultsPath = MockFixture.GetDirectory(typeof(JsonMetricsParserTests), "Examples", "ScriptExecutor", "validJsonExample.json");
2626
string rawText = File.ReadAllText(resultsPath);
@@ -33,6 +33,23 @@ public void JsonMetricsParserVerifyMetricsForPassResults()
3333
MetricAssert.Exists(metrics, "metric3", 1279854282929.09);
3434
}
3535

36+
[Test]
37+
public void JsonMetricsParserVerifyMetricsForPassResults_Format2()
38+
{
39+
string resultsPath = MockFixture.GetDirectory(typeof(JsonMetricsParserTests), "Examples", "ScriptExecutor", "validJsonExample_array.json");
40+
string rawText = File.ReadAllText(resultsPath);
41+
this.testParser = new JsonMetricsParser(rawText, new InMemoryLogger(), EventContext.None);
42+
IList<Metric> metrics = this.testParser.Parse();
43+
44+
Assert.AreEqual(6, metrics.Count);
45+
MetricAssert.Exists(metrics, "metric1", 0);
46+
MetricAssert.Exists(metrics, "metric2", -1);
47+
MetricAssert.Exists(metrics, "metric3", 1.2);
48+
MetricAssert.Exists(metrics, "metric4", 1);
49+
MetricAssert.Exists(metrics, "metric5", 1.24);
50+
MetricAssert.Exists(metrics, "metric6", -5.8);
51+
}
52+
3653
[Test]
3754
public void JsonMetricsParserThrowsIfTheJsonResultsHaveInvalidMetrics()
3855
{

src/VirtualClient/VirtualClient.Actions/ScriptExecutor/JsonMetricsParser.cs

Lines changed: 73 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ namespace VirtualClient.Actions
55
{
66
using System;
77
using System.Collections.Generic;
8-
using System.ComponentModel;
98
using System.Linq;
10-
using System.Text.RegularExpressions;
119
using global::VirtualClient;
1210
using global::VirtualClient.Actions;
1311
using global::VirtualClient.Contracts;
@@ -41,25 +39,87 @@ public JsonMetricsParser(string results, ILogger logger, EventContext eventConte
4139
/// Parses key metrics from the JSON based output log.
4240
/// </summary>
4341
public override IList<Metric> Parse()
44-
{
42+
{
4543
List<Metric> metrics = new List<Metric>();
4644

4745
try
4846
{
49-
JObject keyValuePairs = JObject.Parse(this.RawText);
50-
foreach (var keyValuePair in keyValuePairs.Properties())
47+
JToken token = JToken.Parse(this.RawText);
48+
49+
if (token.Type == JTokenType.Object)
5150
{
52-
if (double.TryParse(keyValuePair.Value.ToString(), out var value))
51+
// Format 1: { "metric1": 123, "metric2": 2.5, ... }
52+
JObject keyValuePairs = (JObject)token;
53+
foreach (var keyValuePair in keyValuePairs.Properties())
5354
{
54-
metrics.Add(new Metric(keyValuePair.Name, value, MetricRelativity.Undefined));
55+
if (double.TryParse(keyValuePair.Value.ToString(), out var value))
56+
{
57+
metrics.Add(new Metric(keyValuePair.Name, value, MetricRelativity.Undefined));
58+
}
59+
else
60+
{
61+
throw new WorkloadResultsException(
62+
$"Invalid JSON metrics content formatting. The metric value for '{keyValuePair.Name}' is not a valid numeric data type. Provided metric value is '{keyValuePair.Value}'",
63+
ErrorReason.InvalidResults);
64+
}
5565
}
56-
else
66+
}
67+
else if (token.Type == JTokenType.Array)
68+
{
69+
/* Format 2: {
70+
"metrics": [
71+
{
72+
"metricName": "metric1",
73+
"metricValue": "value1",
74+
"metricUnit": "unit1",
75+
"metricMetadata": {
76+
"metadata1": "m1",
77+
"metadata2": "m2"
78+
}
79+
}
80+
]
81+
}*/
82+
foreach (var metricObj in token.Children<JObject>())
5783
{
58-
throw new WorkloadResultsException(
59-
$"Invalid JSON metrics content formatting. The metric value for '{keyValuePair.Name}' is not a valid numeric data type.",
60-
ErrorReason.InvalidResults);
84+
string metricName = metricObj.Value<string>("metricName");
85+
JToken metricValueToken = metricObj["metricValue"];
86+
if (string.IsNullOrWhiteSpace(metricName))
87+
{
88+
throw new WorkloadResultsException(
89+
$"Invalid JSON metrics content formatting. 'metricName' is a required property.",
90+
ErrorReason.InvalidResults);
91+
}
92+
93+
if (metricValueToken == null || !double.TryParse(metricValueToken.ToString(), out double metricValue))
94+
{
95+
throw new WorkloadResultsException(
96+
$"Invalid JSON metrics content formatting. 'metricValue' for '{metricName}' is not a valid numeric data type. Provided metric value is '{metricValueToken}'",
97+
ErrorReason.InvalidResults);
98+
}
99+
100+
string metricUnit = metricObj.Value<string>("metricUnit");
101+
IDictionary<string, IConvertible> metricMetadata = null;
102+
if (metricObj.TryGetValue("metricMetadata", out JToken metadataToken) && metadataToken.Type == JTokenType.Object)
103+
{
104+
metricMetadata = metadataToken
105+
.Children<JProperty>()
106+
.ToDictionary(
107+
prop => prop.Name,
108+
prop => (IConvertible)Convert.ChangeType(prop.Value.ToString(), typeof(string)));
109+
}
110+
111+
metrics.Add(
112+
metricUnit != null
113+
? new Metric(metricName, metricValue, metricUnit, MetricRelativity.Undefined, tags: null, description: null, metadata: metricMetadata)
114+
: new Metric(metricName, metricValue, MetricRelativity.Undefined, tags: null, description: null, metadata: metricMetadata));
61115
}
62116
}
117+
else
118+
{
119+
throw new WorkloadResultsException(
120+
"Invalid JSON metrics content formatting. The root element must be either an object or an array.",
121+
ErrorReason.InvalidResults);
122+
}
63123
}
64124
catch (WorkloadResultsException)
65125
{
@@ -68,8 +128,8 @@ public override IList<Metric> Parse()
68128
catch (Exception exc)
69129
{
70130
throw new WorkloadResultsException(
71-
$"Invalid JSON metrics content formatting. The metrics content must be in a valid JSON key/value pair JSON-format " +
72-
$"(e.g. {{ \"metric1\": 1234, \"metric2\": 987.65, \"metric3\": 32.0023481 }} )",
131+
$"Invalid JSON metrics content formatting. The metrics content must be in a valid JSON key/value pair format " +
132+
$"(e.g. {{ \"metric1\": 1234, \"metric2\": 987.65, \"metric3\": 32.0023481 }} ) or an array of metric objects.",
73133
exc,
74134
ErrorReason.InvalidResults);
75135
}

src/VirtualClient/VirtualClient.Actions/ScriptExecutor/ScriptExecutor.cs

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ protected async Task CaptureLogsAsync(CancellationToken cancellationToken)
261261
{
262262
foreach (string logFilePath in this.fileSystem.Directory.GetFiles(fullLogPath, "*", SearchOption.AllDirectories))
263263
{
264-
this.RequestUploadAndMoveToLogsDirectory(logFilePath, destinitionLogsDir, cancellationToken);
264+
this.RequestUploadAndMoveToLogsDirectory(logFilePath, destinitionLogsDir, cancellationToken, sourceRoot: fullLogPath);
265265
}
266266
}
267267

@@ -301,9 +301,13 @@ protected Task RequestUpload(string logPath)
301301
}
302302

303303
/// <summary>
304-
/// Move the log files to central logs directory and Upload to Content Store
304+
/// Move the log files to central logs directory (retaining source directory structure) and Upload to Content Store.
305305
/// </summary>
306-
protected async Task RequestUploadAndMoveToLogsDirectory(string sourcePath, string destinitionDirectory, CancellationToken cancellationToken)
306+
private async Task RequestUploadAndMoveToLogsDirectory(
307+
string sourcePath,
308+
string destinitionDirectory,
309+
CancellationToken cancellationToken,
310+
string sourceRoot = null)
307311
{
308312
if (string.Equals(sourcePath, this.ExecutablePath))
309313
{
@@ -317,13 +321,27 @@ protected async Task RequestUploadAndMoveToLogsDirectory(string sourcePath, stri
317321

318322
await (this.FileOperationsRetryPolicy ?? Policy.NoOpAsync()).ExecuteAsync(() =>
319323
{
320-
// e.g.
321-
// /logs/anytool/executecustomscript1/2023-06-27T21-13-12-51001Z-CustomScript.sh
322-
// /logs/anytool/executecustomscript1/2023-06-27T21-15-36-12018Z-CustomScript.sh
323324
string fileName = Path.GetFileName(sourcePath);
324-
string destinitionPath = this.Combine(destinitionDirectory, BlobDescriptor.SanitizeBlobPath($"{DateTime.UtcNow.ToString("o").Replace('.', '-')}-{fileName}"));
325-
this.fileSystem.File.Move(sourcePath, destinitionPath, true);
325+
string destPath;
326326

327+
if (!string.IsNullOrEmpty(sourceRoot))
328+
{
329+
// Compute relative path from sourceRoot to sourcePath
330+
string relativePath = this.fileSystem.Path.GetRelativePath(sourceRoot, sourcePath);
331+
string destDir = this.fileSystem.Path.Combine(destinitionDirectory, this.fileSystem.Path.GetDirectoryName(relativePath));
332+
if (!this.fileSystem.Directory.Exists(destDir))
333+
{
334+
this.fileSystem.Directory.CreateDirectory(destDir);
335+
}
336+
337+
destPath = this.fileSystem.Path.Combine(destDir, BlobDescriptor.SanitizeBlobPath($"{DateTime.UtcNow:O}".Replace('.', '-') + "-" + fileName));
338+
}
339+
else
340+
{
341+
destPath = this.Combine(destinitionDirectory, BlobDescriptor.SanitizeBlobPath($"{DateTime.UtcNow:O}".Replace('.', '-') + "-" + fileName));
342+
}
343+
344+
this.fileSystem.File.Move(sourcePath, destPath, true);
327345
return Task.CompletedTask;
328346
});
329347
}

0 commit comments

Comments
 (0)