Skip to content

Commit abe6a1a

Browse files
authored
Bug fix. Extensions in component collections are not propagated to child components correctly causing some expected information to get overwritten. (#503)
* Bug fix. Extensions in component collections are not propagated to child components correctly causing some expected information to get overwritten. * Bug fix. Extensions in component collections are not propagated to child components correctly causing some expected information to get overwritten.
1 parent 095d347 commit abe6a1a

4 files changed

Lines changed: 158 additions & 9 deletions

File tree

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.0.11
1+
2.0.12

src/VirtualClient/VirtualClient.Contracts.UnitTests/ComponentFactoryTests.cs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
namespace VirtualClient.Contracts
55
{
66
using System;
7-
using System.Collections;
87
using System.Collections.Generic;
98
using System.IO;
109
using System.Linq;
@@ -369,6 +368,35 @@ public void ComponentFactoryAddsExpectedComponentLevelExtensionsToSubComponents(
369368
}
370369
}
371370

371+
[Test]
372+
[TestCase("TEST-PROFILE-EXTENSIONS-1-PARALLEL.json")]
373+
public void ComponentFactoryHandlesComponentLevelExtensionAnomalies_1(string profileName)
374+
{
375+
// Scenario:
376+
// A partner team is trying to pass in extensions objects to components within side
377+
// of a parallel execution block. The information in the extensions objects for the first component
378+
// in the parallel execution block seems to be overriding the ones that come afterwards.
379+
ExecutionProfile profile = File.ReadAllText(Path.Combine(MockFixture.TestAssemblyDirectory, "Resources", profileName))
380+
.FromJson<ExecutionProfile>();
381+
382+
ExecutionProfileElement parallelLoop = profile.Actions.First();
383+
IEnumerable<ExecutionProfileElement> parallelLoopComponents = parallelLoop.Components;
384+
385+
Assert.IsTrue(parallelLoopComponents?.Count() == 2);
386+
387+
VirtualClientComponentCollection parallelExecution = ComponentFactory.CreateComponent(parallelLoop, this.mockFixture.Dependencies) as VirtualClientComponentCollection;
388+
389+
for (int i = 0; i < parallelLoopComponents.Count(); i++)
390+
{
391+
ExecutionProfileElement profileElement = parallelLoopComponents.ElementAt(i);
392+
VirtualClientComponent runtimeComponent = parallelExecution.ElementAt(i);
393+
394+
Assert.IsTrue(profileElement.Extensions.TryGetValue("ActionCustomParameters", out JToken expectedExtensions));
395+
Assert.IsTrue(runtimeComponent.Extensions.TryGetValue("ActionCustomParameters", out JToken actualExtensions));
396+
Assert.AreEqual(expectedExtensions.ToJson().RemoveWhitespace(), actualExtensions.ToJson().RemoveWhitespace());
397+
}
398+
}
399+
372400
[Test]
373401
public void ComponentFactoryAddsExpectedComponentLevelExtensionsToSubComponents_Deep_Nesting()
374402
{
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
{
2+
"Description": "Extensions Within Parallel Execution Block Example",
3+
"Actions": [
4+
{
5+
"Type": "ParallelExecution",
6+
"Components": [
7+
{
8+
"Type": "TestExecutor",
9+
"Parameters": {
10+
"CheckDescription": "Execute Check 1",
11+
"Scenario": "ExecuteWorkload1",
12+
"ActionId": "a0b8c882-f5fd-4573-8d1e-140f7e7af601",
13+
"HostType": "Standalone",
14+
"CheckTool": "check_tool.ps1",
15+
"CheckToolType": "pwsh7",
16+
"CheckToolArguments": "-Check 1 -StringToPrint 'some string to print -+/<>!@#$%^&*()'",
17+
"CheckCase": "GetVmHostname",
18+
"MetricsName": "VmHostname",
19+
"MetricsUnit": "NA",
20+
"CheckToolVersion": "1.0.4",
21+
"PackageName": "checktool",
22+
"AcceptActionContext": true,
23+
"Tags": "Powershell"
24+
},
25+
"ActionCustomParameters": {
26+
"CustomPropertyX": "PropertyXValue1",
27+
"CustomPropertyY": "PropertyYValue1",
28+
"CustomPropertyZ": "PropertyZValue1",
29+
"ExpectedMetrics": {
30+
"PassFailMode": "AllPass / OnePass1",
31+
"Expectations": [
32+
{
33+
"MetricsName": "VMHostNameCheck1",
34+
"MetricsUnit": "CheckResult",
35+
"MetricsValue": 0,
36+
"MetricsUpperBound": null,
37+
"MetricsLowerBound": null
38+
},
39+
{
40+
"MetricsName": "HostHealth1",
41+
"MetricsUnit": "Int",
42+
"MetricsValue": 0,
43+
"MetricsUpperBound": null,
44+
"MetricsLowerBound": null
45+
}
46+
]
47+
},
48+
"TrackingId": "d3e2a596-5214-4084-8718-ee568ce24a14",
49+
"ScenarioRunId": "713e6d8f-37e3-4f94-b5eb-a1ed0201f754",
50+
"ScenarioReportingId": "af37b321-69fa-142c-691b-0332f614c822",
51+
"GroupId": "8bddaff1-5c3c-446c-bda6-2432af59aa12",
52+
"VMResourceName": "TestVM-1",
53+
"Region": "westus",
54+
"ZoneId": "",
55+
"ResourceGroup": "ResourceGroup01",
56+
"VMId": "bc52fda0-b06a-4426-a70c-411c6ebbf006",
57+
"AadTenantId": "72f988bf-86f1-41af-91ab-2d7cd011db47",
58+
"RoleId": "cb67cddd-2456-4eab-a723-6c8108ce3db8",
59+
"ManifestVersion": "1.0"
60+
}
61+
},
62+
{
63+
"Type": "TestExecutor",
64+
"Parameters": {
65+
"CheckDescription": "Execute Check 2",
66+
"Scenario": "ExecuteWorkload1",
67+
"ActionId": "a0b8c882-f5fd-4573-8d1e-140f7e7af601",
68+
"HostType": "Standalone",
69+
"CheckTool": "check_tool.ps1",
70+
"CheckToolType": "pwsh7",
71+
"CheckToolArguments": "-Check 2 -StringToPrint 'some string to print -+/<>!@#$%^&*()'",
72+
"CheckCase": "GetVmHostname",
73+
"MetricsName": "VmHostname",
74+
"MetricsUnit": "NA",
75+
"CheckToolVersion": "1.0.4",
76+
"PackageName": "checktool",
77+
"AcceptActionContext": true,
78+
"Tags": "Powershell"
79+
},
80+
"ActionCustomParameters": {
81+
"CustomPropertyX": "PropertyXValue2",
82+
"CustomPropertyY": "PropertyYValue2",
83+
"CustomPropertyZ": "PropertyZValue2",
84+
"ExpectedMetrics": {
85+
"PassFailMode": "AllPass / OnePass2",
86+
"Expectations": [
87+
{
88+
"MetricsName": "VMHostNameCheck2",
89+
"MetricsUnit": "CheckResult2",
90+
"MetricsValue": 0,
91+
"MetricsUpperBound": null,
92+
"MetricsLowerBound": null
93+
},
94+
{
95+
"MetricsName": "HostHealth3",
96+
"MetricsUnit": "Int",
97+
"MetricsValue": 0,
98+
"MetricsUpperBound": null,
99+
"MetricsLowerBound": null
100+
}
101+
]
102+
},
103+
"TrackingId": "e3e2a596-5214-4084-8718-ee568ce24a14",
104+
"ScenarioRunId": "113e6d8f-37e3-4f94-b5eb-a1ed0201f754",
105+
"ScenarioReportingId": "bf37b321-69fa-142c-691b-0332f614c822",
106+
"GroupId": "9bddaff1-5c3c-446c-bda6-2432af59aa12",
107+
"VMResourceName": "TestVM-2",
108+
"Region": "westus2",
109+
"ZoneId": "3",
110+
"ResourceGroup": "ResourceGroup02",
111+
"VMId": "ab52fda0-b06a-4426-a70c-411c6ebbf006",
112+
"AadTenantId": "72f988bf-86f1-41af-91ab-2d7cd011db47",
113+
"RoleId": "db67cddd-2456-4eab-a723-6c8108ce3db8",
114+
"ManifestVersion": "2.0"
115+
}
116+
}
117+
]
118+
}
119+
]
120+
}

src/VirtualClient/VirtualClient.Contracts/ComponentFactory.cs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,11 @@ private static VirtualClientComponent CreateComponent(
114114
metadata.AddRange(componentDescription.Metadata, withReplace: false);
115115
}
116116

117-
// Extensions are merged at each level down the hierarchy. Extensions at higher levels
118-
// takes priority overriding extensions at lower levels (i.e. withReplace: false).
119-
// This allows extensions to be set at higher levels that is then in turn applied to
120-
// components throughout the hierarchy.
117+
// Extensions are merged at each level down the hierarchy. Parent component
118+
// extensions are merged with child/subcomponent extensions.
121119
if (componentDescription.Extensions?.Any() == true)
122120
{
123-
extensions.AddRange(componentDescription.Extensions, withReplace: false);
121+
component.Extensions.AddRange(componentDescription.Extensions, withReplace: false);
124122
}
125123

126124
if (failFast != null)
@@ -138,6 +136,10 @@ private static VirtualClientComponent CreateComponent(
138136
component.Metadata.AddRange(metadata, withReplace: true);
139137
}
140138

139+
// Extensions at lower levels takes priority overriding extensions at higher levels.
140+
// This allows extensions to be set at higher levels that is then in turn applied to
141+
// components throughout the hierarchy while also supporting overriding individual parts
142+
// of the extensions as needed in the child subcomponents.
141143
if (extensions?.Any() == true)
142144
{
143145
component.Extensions.AddRange(extensions, withReplace: true);
@@ -160,13 +162,12 @@ private static VirtualClientComponent CreateComponent(
160162
subcomponentType,
161163
dependencies,
162164
new Dictionary<string, IConvertible>(metadata, StringComparer.OrdinalIgnoreCase),
163-
extensions,
165+
component.Extensions, // Extensions for the parent component to include.
164166
randomizationSeed,
165167
failFast,
166168
logToFile);
167169

168170
childComponent.ComponentType = componentDescription.ComponentType;
169-
170171
componentCollection.Add(childComponent);
171172
}
172173

0 commit comments

Comments
 (0)