Skip to content

Commit 9a20563

Browse files
Users/surajitshil 03/node 24 upgrade clean (#5361)
* Fixing codebase to support upgarde to Node24 * Adding telemetry for Node24 * Fixing the container logic to support Node24 * Updating the externals.sh for node24 * Suppressing maintainability error message * Adding parameter to GetNodeLocation in the test file * Code cleaning * Updating test files * Minor fixes * Adding conditional download of node24 for win-x86 systems * New PR with the fixes * Fixing spaces * Fixing spaces --------- Co-authored-by: sanjuyadav24 <[email protected]>
1 parent 6ec5a2b commit 9a20563

File tree

9 files changed

+263
-82
lines changed

9 files changed

+263
-82
lines changed

src/Agent.Sdk/ContainerInfo.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ public ContainerInfo(Pipelines.ContainerResource container, Boolean isJobContain
9494
public bool IsJobContainer { get; set; }
9595
public bool MapDockerSocket { get; set; }
9696
public bool NeedsNode16Redirect { get; set; }
97+
public bool NeedsNode20Redirect { get; set; }
9798
public PlatformUtil.OS ImageOS
9899
{
99100
get

src/Agent.Sdk/Knob/AgentKnobs.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,22 @@ public class AgentKnobs
200200
new EnvironmentKnobSource("AGENT_USE_NODE20_IN_UNSUPPORTED_SYSTEM"),
201201
new BuiltInDefaultKnobSource("false"));
202202

203+
public static readonly Knob UseNode24 = new Knob(
204+
nameof(UseNode24),
205+
"Forces the agent to use Node 24 handler for all Node-based tasks",
206+
new PipelineFeatureSource("UseNode24"),
207+
new RuntimeKnobSource("AGENT_USE_NODE24"),
208+
new EnvironmentKnobSource("AGENT_USE_NODE24"),
209+
new BuiltInDefaultKnobSource("false"));
210+
211+
public static readonly Knob UseNode24InUnsupportedSystem = new Knob(
212+
nameof(UseNode24InUnsupportedSystem),
213+
"Forces the agent to use Node 24 handler for all Node-based tasks, even if it's in an unsupported system",
214+
new PipelineFeatureSource("UseNode24InUnsupportedSystem"),
215+
new RuntimeKnobSource("AGENT_USE_NODE24_IN_UNSUPPORTED_SYSTEM"),
216+
new EnvironmentKnobSource("AGENT_USE_NODE24_IN_UNSUPPORTED_SYSTEM"),
217+
new BuiltInDefaultKnobSource("false"));
218+
203219
public static readonly Knob FetchByCommitForFullClone = new Knob(
204220
nameof(FetchByCommitForFullClone),
205221
"If true, allow fetch by commit when doing a full clone (depth=0).",
@@ -707,8 +723,15 @@ public class AgentKnobs
707723
public static readonly Knob UseNode20ToStartContainer = new Knob(
708724
nameof(UseNode20ToStartContainer),
709725
"If true, the agent will use Node 20 to start docker container when executing container job and the container platform is the same as the host platform.",
710-
new RuntimeKnobSource("AZP_AGENT_USE_NODE20_TO_START_CONTAINER"),
711726
new PipelineFeatureSource("UseNode20ToStartContainer"),
727+
new RuntimeKnobSource("AZP_AGENT_USE_NODE20_TO_START_CONTAINER"),
728+
new BuiltInDefaultKnobSource("false"));
729+
730+
public static readonly Knob UseNode24ToStartContainer = new Knob(
731+
nameof(UseNode24ToStartContainer),
732+
"If true, try to start container job using Node24, then fallback to Node20, then Node16.",
733+
new RuntimeKnobSource("AZP_AGENT_USE_NODE24_TO_START_CONTAINER"),
734+
new PipelineFeatureSource("UseNode24ToStartContainer"),
712735
new BuiltInDefaultKnobSource("false"));
713736

714737
public static readonly Knob EnableNewMaskerAndRegexes = new Knob(

src/Agent.Worker/ContainerOperationProvider.cs

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,7 @@ private async Task PullContainerAsync(IExecutionContext executionContext, Contai
460460
}
461461
}
462462

463+
[System.Diagnostics.CodeAnalysis.SuppressMessage("Maintainability", "CA1505:Avoid unmaintainable code", Justification = "Complex container startup logic with multiple fallback paths")]
463464
private async Task StartContainerAsync(IExecutionContext executionContext, ContainerInfo container)
464465
{
465466
Trace.Entering();
@@ -526,8 +527,10 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta
526527
}
527528

528529
bool useNode20ToStartContainer = AgentKnobs.UseNode20ToStartContainer.GetValue(executionContext).AsBoolean();
530+
bool useNode24ToStartContainer = AgentKnobs.UseNode24ToStartContainer.GetValue(executionContext).AsBoolean();
529531
bool useAgentNode = false;
530532

533+
string labelContainerStartupUsingNode24 = "container-startup-using-node-24";
531534
string labelContainerStartupUsingNode20 = "container-startup-using-node-20";
532535
string labelContainerStartupUsingNode16 = "container-startup-using-node-16";
533536
string labelContainerStartupFailed = "container-startup-failed";
@@ -540,6 +543,7 @@ string containerNodePath(string nodeFolder)
540543
string nodeContainerPath = containerNodePath(NodeHandler.NodeFolder);
541544
string node16ContainerPath = containerNodePath(NodeHandler.Node16Folder);
542545
string node20ContainerPath = containerNodePath(NodeHandler.Node20_1Folder);
546+
string node24ContainerPath = containerNodePath(NodeHandler.Node24Folder);
543547

544548
if (container.IsJobContainer)
545549
{
@@ -573,7 +577,20 @@ string useDoubleQuotes(string value)
573577
else
574578
{
575579
useAgentNode = true;
576-
string sleepCommand = useNode20ToStartContainer ? $"'{node20ContainerPath}' --version && echo '{labelContainerStartupUsingNode20}' && {nodeSetInterval(node20ContainerPath)} || '{node16ContainerPath}' --version && echo '{labelContainerStartupUsingNode16}' && {nodeSetInterval(node16ContainerPath)} || echo '{labelContainerStartupFailed}'" : nodeSetInterval(nodeContainerPath);
580+
string sleepCommand;
581+
582+
if (useNode24ToStartContainer)
583+
{
584+
sleepCommand = $"'{node24ContainerPath}' --version && echo '{labelContainerStartupUsingNode24}' && {nodeSetInterval(node24ContainerPath)} || '{node20ContainerPath}' --version && echo '{labelContainerStartupUsingNode20}' && {nodeSetInterval(node20ContainerPath)} || '{node16ContainerPath}' --version && echo '{labelContainerStartupUsingNode16}' && {nodeSetInterval(node16ContainerPath)} || echo '{labelContainerStartupFailed}'";
585+
}
586+
else if (useNode20ToStartContainer)
587+
{
588+
sleepCommand = $"'{node20ContainerPath}' --version && echo '{labelContainerStartupUsingNode20}' && {nodeSetInterval(node20ContainerPath)} || '{node16ContainerPath}' --version && echo '{labelContainerStartupUsingNode16}' && {nodeSetInterval(node16ContainerPath)} || echo '{labelContainerStartupFailed}'";
589+
}
590+
else
591+
{
592+
sleepCommand = nodeSetInterval(nodeContainerPath);
593+
}
577594
container.ContainerCommand = PlatformUtil.RunningOnWindows ? $"cmd.exe /c call {useDoubleQuotes(sleepCommand)}" : $"bash -c \"{sleepCommand}\"";
578595
container.ResultNodePath = nodeContainerPath;
579596
}
@@ -609,7 +626,7 @@ string useDoubleQuotes(string value)
609626

610627
executionContext.Warning($"Docker container {container.ContainerId} is not in running state.");
611628
}
612-
else if (useAgentNode && useNode20ToStartContainer)
629+
else if (useAgentNode && (useNode20ToStartContainer || useNode24ToStartContainer))
613630
{
614631
bool containerStartupCompleted = false;
615632
int containerStartupTimeoutInMilliseconds = 10000;
@@ -622,7 +639,14 @@ string useDoubleQuotes(string value)
622639

623640
foreach (string logLine in containerLogs)
624641
{
625-
if (logLine.Contains(labelContainerStartupUsingNode20))
642+
if (logLine.Contains(labelContainerStartupUsingNode24))
643+
{
644+
executionContext.Debug("Using Node 24 for container startup.");
645+
containerStartupCompleted = true;
646+
container.ResultNodePath = node24ContainerPath;
647+
break;
648+
}
649+
else if (logLine.Contains(labelContainerStartupUsingNode20))
626650
{
627651
executionContext.Debug("Using Node 20 for container startup.");
628652
containerStartupCompleted = true;
@@ -931,8 +955,29 @@ string useDoubleQuotes(string value)
931955
if (PlatformUtil.RunningOnLinux)
932956
{
933957
bool useNode20InUnsupportedSystem = AgentKnobs.UseNode20InUnsupportedSystem.GetValue(executionContext).AsBoolean();
958+
bool useNode24InUnsupportedSystem = AgentKnobs.UseNode24InUnsupportedSystem.GetValue(executionContext).AsBoolean();
934959

935-
if (!useNode20InUnsupportedSystem)
960+
if (!useNode24InUnsupportedSystem)
961+
{
962+
var node24 = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), NodeHandler.Node24Folder, "bin", $"node{IOUtil.ExeExtension}"));
963+
964+
string node24TestCmd = $"bash -c \"{node24} -v\"";
965+
List<string> node24VersionOutput = await DockerExec(executionContext, container.ContainerId, node24TestCmd, noExceptionOnError: true);
966+
967+
container.NeedsNode20Redirect = WorkerUtilities.IsCommandResultGlibcError(executionContext, node24VersionOutput, out string node24InfoLine);
968+
969+
if (container.NeedsNode20Redirect)
970+
{
971+
PublishTelemetry(
972+
executionContext,
973+
new Dictionary<string, string>
974+
{
975+
{ "ContainerNode24to20Fallback", container.NeedsNode20Redirect.ToString() }
976+
}
977+
);
978+
}
979+
}
980+
else if (!useNode20InUnsupportedSystem)
936981
{
937982
var node20 = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), NodeHandler.Node20_1Folder, "bin", $"node{IOUtil.ExeExtension}"));
938983

@@ -953,6 +998,19 @@ string useDoubleQuotes(string value)
953998
}
954999
}
9551000

1001+
if (!container.NeedsNode20Redirect)
1002+
{
1003+
container.ResultNodePath = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), NodeHandler.Node24Folder, "bin", $"node{IOUtil.ExeExtension}"));
1004+
}
1005+
else if (!container.NeedsNode16Redirect)
1006+
{
1007+
container.ResultNodePath = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), NodeHandler.Node20_1Folder, "bin", $"node{IOUtil.ExeExtension}"));
1008+
}
1009+
else
1010+
{
1011+
container.ResultNodePath = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), NodeHandler.Node16Folder, "bin", $"node{IOUtil.ExeExtension}"));
1012+
}
1013+
9561014
}
9571015

9581016
if (!string.IsNullOrEmpty(containerUserName))

src/Agent.Worker/ExecutionContext.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ public interface IExecutionContext : IAgentService, IKnobValueContext
9696
/// <returns></returns>
9797
void CancelForceTaskCompletion();
9898
void EmitHostNode20FallbackTelemetry(bool node20ResultsInGlibCErrorHost);
99+
void EmitHostNode24FallbackTelemetry(bool node24ResultsInGlibCErrorHost);
99100
void PublishTaskRunnerTelemetry(Dictionary<string, string> taskRunnerData);
100101
}
101102

@@ -132,6 +133,7 @@ public sealed class ExecutionContext : AgentService, IExecutionContext, ICorrela
132133
private FileStream _buildLogsData;
133134
private StreamWriter _buildLogsWriter;
134135
private bool emittedHostNode20FallbackTelemetry = false;
136+
private bool emittedHostNode24FallbackTelemetry = false;
135137

136138
// only job level ExecutionContext will track throttling delay.
137139
private long _totalThrottlingDelayInMilliseconds = 0;
@@ -973,6 +975,18 @@ public void EmitHostNode20FallbackTelemetry(bool node20ResultsInGlibCErrorHost)
973975
emittedHostNode20FallbackTelemetry = true;
974976
}
975977
}
978+
public void EmitHostNode24FallbackTelemetry(bool node24ResultsInGlibCErrorHost)
979+
{
980+
if (!emittedHostNode24FallbackTelemetry)
981+
{
982+
PublishTelemetry(new Dictionary<string, string>
983+
{
984+
{ "HostNode24to20Fallback", node24ResultsInGlibCErrorHost.ToString() }
985+
});
986+
987+
emittedHostNode24FallbackTelemetry = true;
988+
}
989+
}
976990

977991
// This overload is to handle specific types some other way.
978992
private void PublishTelemetry<T>(

0 commit comments

Comments
 (0)