Skip to content

Commit 7f95c94

Browse files
Adding knob to enable agent to use Node24 when task has handler data (#5396)
* Adding knob to enable agent to use Node24 when task has handler data * Changing the default node runner to node20_1 * Minor changes * Updating L0 test * Minor fix * Minor fix * Making changes in the L0 test cases * Adding a comment
1 parent 6cb268e commit 7f95c94

File tree

3 files changed

+127
-13
lines changed

3 files changed

+127
-13
lines changed

src/Agent.Sdk/Knob/AgentKnobs.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,14 @@ public class AgentKnobs
216216
new EnvironmentKnobSource("AGENT_USE_NODE24_IN_UNSUPPORTED_SYSTEM"),
217217
new BuiltInDefaultKnobSource("false"));
218218

219+
public static readonly Knob UseNode24withHandlerData = new Knob(
220+
nameof(UseNode24withHandlerData),
221+
"Forces the agent to use Node 24 handler if the task has handler data for it",
222+
new PipelineFeatureSource("UseNode24withHandlerData"),
223+
new RuntimeKnobSource("AGENT_USE_NODE24_WITH_HANDLER_DATA"),
224+
new EnvironmentKnobSource("AGENT_USE_NODE24_WITH_HANDLER_DATA"),
225+
new BuiltInDefaultKnobSource("false"));
226+
219227
public static readonly Knob FetchByCommitForFullClone = new Knob(
220228
nameof(FetchByCommitForFullClone),
221229
"If true, allow fetch by commit when doing a full clone (depth=0).",

src/Agent.Worker/Handlers/NodeHandler.cs

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,15 +55,15 @@ public string[] GetFilteredPossibleNodeFolders(string nodeFolderName, string[] p
5555
public sealed class NodeHandler : Handler, INodeHandler
5656
{
5757
private readonly INodeHandlerHelper nodeHandlerHelper;
58-
private const string node10Folder = "node10";
58+
private const string Node10Folder = "node10";
5959
internal const string NodeFolder = "node";
6060
internal static readonly string Node16Folder = "node16";
6161
internal static readonly string Node20_1Folder = "node20_1";
6262
internal static readonly string Node24Folder = "node24";
6363
private static readonly string nodeLTS = Node16Folder;
6464
private const string useNodeKnobLtsKey = "LTS";
6565
private const string useNodeKnobUpgradeKey = "UPGRADE";
66-
private string[] possibleNodeFolders = { NodeFolder, node10Folder, Node16Folder, Node20_1Folder, Node24Folder };
66+
private string[] possibleNodeFolders = { NodeFolder, Node10Folder, Node16Folder, Node20_1Folder, Node24Folder };
6767
private static Regex _vstsTaskLibVersionNeedsFix = new Regex("^[0-2]\\.[0-9]+", RegexOptions.Compiled | RegexOptions.IgnoreCase);
6868
private static string[] _extensionsNode6 ={
6969
"if (process.versions.node && process.versions.node.match(/^5\\./)) {",
@@ -368,14 +368,17 @@ public string GetNodeLocation(bool node20ResultsInGlibCError, bool node24Results
368368
bool useNode20_1 = AgentKnobs.UseNode20_1.GetValue(ExecutionContext).AsBoolean();
369369
bool UseNode20InUnsupportedSystem = AgentKnobs.UseNode20InUnsupportedSystem.GetValue(ExecutionContext).AsBoolean();
370370
bool useNode24 = AgentKnobs.UseNode24.GetValue(ExecutionContext).AsBoolean();
371+
bool UseNode24withHandlerData = AgentKnobs.UseNode24withHandlerData.GetValue(ExecutionContext).AsBoolean();
372+
bool taskHasNode6Data = Data is NodeHandlerData;
371373
bool taskHasNode10Data = Data is Node10HandlerData;
372374
bool taskHasNode16Data = Data is Node16HandlerData;
373375
bool taskHasNode20_1Data = Data is Node20_1HandlerData;
374376
bool taskHasNode24Data = Data is Node24HandlerData;
375377
string useNodeKnob = AgentKnobs.UseNode.GetValue(ExecutionContext).AsString();
376378

377-
string nodeFolder = NodeHandler.NodeFolder;
378-
if (taskHasNode24Data && useNode24)
379+
//using Node20_1 as default node version
380+
string nodeFolder = NodeHandler.Node20_1Folder;
381+
if (taskHasNode24Data && UseNode24withHandlerData)
379382
{
380383
Trace.Info($"Task.json has node24 handler data: {taskHasNode24Data}");
381384
nodeFolder = GetNodeFolderWithFallback(NodeHandler.Node24Folder, node20ResultsInGlibCError, node24ResultsInGlibCError, inContainer);
@@ -393,27 +396,37 @@ public string GetNodeLocation(bool node20ResultsInGlibCError, bool node24Results
393396
else if (taskHasNode10Data)
394397
{
395398
Trace.Info($"Task.json has node10 handler data: {taskHasNode10Data}");
396-
nodeFolder = NodeHandler.node10Folder;
399+
nodeFolder = NodeHandler.Node10Folder;
400+
}
401+
else if (taskHasNode6Data)
402+
{
403+
Trace.Info($"Task.json has node6 handler data: {taskHasNode6Data}");
404+
nodeFolder = NodeHandler.NodeFolder;
397405
}
398406
else if (PlatformUtil.RunningOnAlpine)
399407
{
400408
Trace.Info($"Detected Alpine, using node10 instead of node (6)");
401-
nodeFolder = NodeHandler.node10Folder;
409+
nodeFolder = NodeHandler.Node10Folder;
402410
}
403411

404-
if (useNode20_1)
412+
if (useNode24)
413+
{
414+
Trace.Info($"Found UseNode24 knob, using node24 for node tasks: {useNode24}");
415+
nodeFolder = GetNodeFolderWithFallback(NodeHandler.Node24Folder, node20ResultsInGlibCError, node24ResultsInGlibCError, inContainer);
416+
}
417+
else if (useNode20_1)
405418
{
406419
Trace.Info($"Found UseNode20_1 knob, using node20_1 for node tasks {useNode20_1} node20ResultsInGlibCError = {node20ResultsInGlibCError}");
407420
nodeFolder = GetNodeFolderWithFallback(NodeHandler.Node20_1Folder, node20ResultsInGlibCError, node24ResultsInGlibCError, inContainer);
408421
}
409-
410422
else if (useNode10)
411423
{
412424
Trace.Info($"Found UseNode10 knob, use node10 for node tasks: {useNode10}");
413-
nodeFolder = NodeHandler.node10Folder;
425+
nodeFolder = NodeHandler.Node10Folder;
414426
}
415-
if (nodeFolder == NodeHandler.NodeFolder &&
416-
AgentKnobs.AgentDeprecatedNodeWarnings.GetValue(ExecutionContext).AsBoolean() == true)
427+
// Warn for deprecated node versions
428+
if ((nodeFolder == NodeHandler.NodeFolder || nodeFolder == NodeHandler.Node10Folder || nodeFolder == NodeHandler.Node16Folder) &&
429+
AgentKnobs.AgentDeprecatedNodeWarnings.GetValue(ExecutionContext).AsBoolean())
417430
{
418431
ExecutionContext.Warning(StringUtil.Loc("DeprecatedRunner", Task.Name.ToString()));
419432
}

src/Test/L0/NodeHandlerL0.cs

Lines changed: 95 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public void UseNodeForNodeHandlerEnvVarNotSet()
6161
}
6262

6363
[Theory]
64+
[InlineData("node")]
6465
[InlineData("node10")]
6566
[InlineData("node16")]
6667
[InlineData("node20_1")]
@@ -74,7 +75,7 @@ public void UseNewNodeForNewNodeHandler(string nodeVersion)
7475
// For node24, set the required knob
7576
if (nodeVersion == "node24")
7677
{
77-
Environment.SetEnvironmentVariable("AGENT_USE_NODE24", "true");
78+
Environment.SetEnvironmentVariable("AGENT_USE_NODE24_WITH_HANDLER_DATA", "true");
7879
}
7980

8081
try
@@ -91,6 +92,7 @@ public void UseNewNodeForNewNodeHandler(string nodeVersion)
9192
nodeHandler.ExecutionContext = CreateTestExecutionContext(thc);
9293
nodeHandler.Data = nodeVersion switch
9394
{
95+
"node" => new NodeHandlerData(),
9496
"node10" => new Node10HandlerData(),
9597
"node16" => new Node16HandlerData(),
9698
"node20_1" => new Node20_1HandlerData(),
@@ -110,11 +112,101 @@ public void UseNewNodeForNewNodeHandler(string nodeVersion)
110112
{
111113
if (nodeVersion == "node24")
112114
{
113-
Environment.SetEnvironmentVariable("AGENT_USE_NODE24", null);
115+
Environment.SetEnvironmentVariable("AGENT_USE_NODE24_WITH_HANDLER_DATA", null);
114116
}
115117
}
116118
}
117119

120+
//test the AGENT_USE_NODE24_WITH_HANDLER_DATA knob
121+
[Theory]
122+
[InlineData("node")]
123+
[InlineData("node10")]
124+
[InlineData("node16")]
125+
[InlineData("node20_1")]
126+
[InlineData("node24")]
127+
[Trait("Level", "L0")]
128+
[Trait("Category", "Common")]
129+
public void ForceUseNode24Knob(string nodeVersion)
130+
{
131+
ResetNodeKnobs();
132+
133+
Environment.SetEnvironmentVariable("AGENT_USE_NODE24", "true");
134+
135+
try
136+
{
137+
// Use a unique test name per data row to avoid sharing the same trace file across parallel runs
138+
using (TestHostContext thc = CreateTestHostContext($"{nameof(ForceUseNode24Knob)}_{nodeVersion}"))
139+
{
140+
thc.SetSingleton(new WorkerCommandManager() as IWorkerCommandManager);
141+
thc.SetSingleton(new ExtensionManager() as IExtensionManager);
142+
143+
NodeHandler nodeHandler = new NodeHandler(nodeHandlerHalper.Object);
144+
145+
nodeHandler.Initialize(thc);
146+
nodeHandler.ExecutionContext = CreateTestExecutionContext(thc);
147+
nodeHandler.Data = nodeVersion switch
148+
{
149+
"node" => new NodeHandlerData(),
150+
"node10" => new Node10HandlerData(),
151+
"node16" => new Node16HandlerData(),
152+
"node20_1" => new Node20_1HandlerData(),
153+
"node24" => new Node24HandlerData(),
154+
_ => throw new Exception("Invalid node version"),
155+
};
156+
157+
string actualLocation = nodeHandler.GetNodeLocation(node20ResultsInGlibCError: false, node24ResultsInGlibCError: false, inContainer: false);
158+
string expectedLocation = Path.Combine(thc.GetDirectory(WellKnownDirectory.Externals),
159+
"node24",
160+
"bin",
161+
$"node{IOUtil.ExeExtension}");
162+
Assert.Equal(expectedLocation, actualLocation);
163+
}
164+
}
165+
finally
166+
{
167+
Environment.SetEnvironmentVariable("AGENT_USE_NODE24", null);
168+
}
169+
}
170+
171+
//tests that Node24 is NOT used when handler data exists but knob is false
172+
[Fact]
173+
[Trait("Level", "L0")]
174+
[Trait("Category", "Common")]
175+
public void DoNotUseNode24WhenHandlerDataKnobIsFalse()
176+
{
177+
ResetNodeKnobs();
178+
179+
Environment.SetEnvironmentVariable("AGENT_USE_NODE24_WITH_HANDLER_DATA", "false");
180+
181+
try
182+
{
183+
using (TestHostContext thc = CreateTestHostContext())
184+
{
185+
thc.SetSingleton(new WorkerCommandManager() as IWorkerCommandManager);
186+
thc.SetSingleton(new ExtensionManager() as IExtensionManager);
187+
188+
NodeHandler nodeHandler = new NodeHandler(nodeHandlerHalper.Object);
189+
190+
nodeHandler.Initialize(thc);
191+
nodeHandler.ExecutionContext = CreateTestExecutionContext(thc);
192+
// Task has Node24HandlerData but knob is false
193+
nodeHandler.Data = new Node24HandlerData();
194+
195+
string actualLocation = nodeHandler.GetNodeLocation(node20ResultsInGlibCError: false, node24ResultsInGlibCError: false, inContainer: false);
196+
// Should fall back to Node20_1 (the default)
197+
string expectedLocation = Path.Combine(thc.GetDirectory(WellKnownDirectory.Externals),
198+
"node20_1",
199+
"bin",
200+
$"node{IOUtil.ExeExtension}");
201+
Assert.Equal(expectedLocation, actualLocation);
202+
}
203+
}
204+
finally
205+
{
206+
Environment.SetEnvironmentVariable("AGENT_USE_NODE24_WITH_HANDLER_DATA", null);
207+
}
208+
}
209+
118210
[Fact]
119211
[Trait("Level", "L0")]
120212
[Trait("Category", "Common")]
@@ -491,6 +583,7 @@ private void ResetNodeKnobs()
491583
Environment.SetEnvironmentVariable("AGENT_USE_NODE20_IN_UNSUPPORTED_SYSTEM", null);
492584
Environment.SetEnvironmentVariable("AGENT_USE_NODE24", null);
493585
Environment.SetEnvironmentVariable("AGENT_USE_NODE24_IN_UNSUPPORTED_SYSTEM", null);
586+
Environment.SetEnvironmentVariable("AGENT_USE_NODE24_WITH_HANDLER_DATA", null);
494587
}
495588
}
496589
}

0 commit comments

Comments
 (0)