Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/Agent.Sdk/Knob/AgentKnobs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -745,8 +745,9 @@ public class AgentKnobs
public static readonly Knob UseNode24ToStartContainer = new Knob(
nameof(UseNode24ToStartContainer),
"If true, try to start container job using Node24, then fallback to Node20, then Node16.",
new RuntimeKnobSource("AZP_AGENT_USE_NODE24_TO_START_CONTAINER"),
new PipelineFeatureSource("UseNode24ToStartContainer"),
new RuntimeKnobSource("AZP_AGENT_USE_NODE24_TO_START_CONTAINER"),
new EnvironmentKnobSource("AZP_AGENT_USE_NODE24_TO_START_CONTAINER"),
new BuiltInDefaultKnobSource("false"));

public static readonly Knob EnableNewMaskerAndRegexes = new Knob(
Expand Down
15 changes: 12 additions & 3 deletions src/Agent.Worker/ContainerOperationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -648,21 +648,30 @@ string useDoubleQuotes(string value)
}
else if (logLine.Contains(labelContainerStartupUsingNode20))
{
executionContext.Debug("Using Node 20 for container startup.");
string warningMsg = useNode24ToStartContainer
? "Cannot run Node 24 in container. Falling back to Node 20 for container startup."
: "Using Node 20 for container startup.";
executionContext.Warning(warningMsg);
containerStartupCompleted = true;
container.ResultNodePath = node20ContainerPath;
break;
}
else if (logLine.Contains(labelContainerStartupUsingNode16))
{
executionContext.Warning("Can not run Node 20 in container. Falling back to Node 16 for container startup.");
string warningMsg = useNode24ToStartContainer
? "Cannot run Node 24 and Node 20 in container. Falling back to Node 16 for container startup."
: "Cannot run Node 20 in container. Falling back to Node 16 for container startup.";
executionContext.Warning(warningMsg);
containerStartupCompleted = true;
container.ResultNodePath = node16ContainerPath;
break;
}
else if (logLine.Contains(labelContainerStartupFailed))
{
executionContext.Error("Can not run both Node 20 and Node 16 in container. Container startup failed.");
string errorMsg = useNode24ToStartContainer
? "Cannot run Node 24, Node 20, and Node 16 in container. Container startup failed."
: "Cannot run both Node 20 and Node 16 in container. Container startup failed.";
executionContext.Error(errorMsg);
containerStartupCompleted = true;
break;
}
Expand Down
86 changes: 76 additions & 10 deletions src/Agent.Worker/ContainerOperationProviderEnhanced.cs
Original file line number Diff line number Diff line change
Expand Up @@ -648,8 +648,10 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta
trace.Info($"Configured {container.MountVolumes.Count} volume mounts: {string.Join(", ", mountSummary)}");

bool useNode20ToStartContainer = AgentKnobs.UseNode20ToStartContainer.GetValue(executionContext).AsBoolean();
bool useNode24ToStartContainer = AgentKnobs.UseNode24ToStartContainer.GetValue(executionContext).AsBoolean();
bool useAgentNode = false;

string labelContainerStartupUsingNode24 = "container-startup-using-node-24";
string labelContainerStartupUsingNode20 = "container-startup-using-node-20";
string labelContainerStartupUsingNode16 = "container-startup-using-node-16";
string labelContainerStartupFailed = "container-startup-failed";
Expand All @@ -662,6 +664,7 @@ string containerNodePath(string nodeFolder)
string nodeContainerPath = containerNodePath(NodeHandler.NodeFolder);
string node16ContainerPath = containerNodePath(NodeHandler.Node16Folder);
string node20ContainerPath = containerNodePath(NodeHandler.Node20_1Folder);
string node24ContainerPath = containerNodePath(NodeHandler.Node24Folder);

if (container.IsJobContainer)
{
Expand Down Expand Up @@ -697,9 +700,22 @@ string useDoubleQuotes(string value)
else
{
useAgentNode = true;
trace.Info($"Using agent-provided Node.js. Node20 enabled: {useNode20ToStartContainer}");
trace.Info($"Node paths - Default: {nodeContainerPath}, Node16: {node16ContainerPath}, Node20: {node20ContainerPath}");
string sleepCommand = useNode20ToStartContainer ? $"'{node20ContainerPath}' --version && echo '{labelContainerStartupUsingNode20}' && {nodeSetInterval(node20ContainerPath)} || '{node16ContainerPath}' --version && echo '{labelContainerStartupUsingNode16}' && {nodeSetInterval(node16ContainerPath)} || echo '{labelContainerStartupFailed}'" : nodeSetInterval(nodeContainerPath);
trace.Info($"Using agent-provided Node.js. Node20 enabled: {useNode20ToStartContainer}, Node24 enabled: {useNode24ToStartContainer}");
trace.Info($"Node paths - Default: {nodeContainerPath}, Node16: {node16ContainerPath}, Node20: {node20ContainerPath}, Node24: {node24ContainerPath}");
string sleepCommand;

if (useNode24ToStartContainer)
{
sleepCommand = $"'{node24ContainerPath}' --version && echo '{labelContainerStartupUsingNode24}' && {nodeSetInterval(node24ContainerPath)} || '{node20ContainerPath}' --version && echo '{labelContainerStartupUsingNode20}' && {nodeSetInterval(node20ContainerPath)} || '{node16ContainerPath}' --version && echo '{labelContainerStartupUsingNode16}' && {nodeSetInterval(node16ContainerPath)} || echo '{labelContainerStartupFailed}'";
}
else if (useNode20ToStartContainer)
{
sleepCommand = $"'{node20ContainerPath}' --version && echo '{labelContainerStartupUsingNode20}' && {nodeSetInterval(node20ContainerPath)} || '{node16ContainerPath}' --version && echo '{labelContainerStartupUsingNode16}' && {nodeSetInterval(node16ContainerPath)} || echo '{labelContainerStartupFailed}'";
}
else
{
sleepCommand = nodeSetInterval(nodeContainerPath);
}
container.ContainerCommand = PlatformUtil.RunningOnWindows ? $"cmd.exe /c call {useDoubleQuotes(sleepCommand)}" : $"bash -c \"{sleepCommand}\"";
container.ResultNodePath = nodeContainerPath;
}
Expand Down Expand Up @@ -762,7 +778,7 @@ string useDoubleQuotes(string value)

executionContext.Warning($"Docker container {container.ContainerId} is not in running state.");
}
else if (useAgentNode && useNode20ToStartContainer)
else if (useAgentNode && (useNode20ToStartContainer || useNode24ToStartContainer))
{
bool containerStartupCompleted = false;
int containerStartupTimeoutInMilliseconds = 10000;
Expand All @@ -777,23 +793,39 @@ string useDoubleQuotes(string value)

foreach (string logLine in containerLogs)
{
if (logLine.Contains(labelContainerStartupUsingNode20))
if (logLine.Contains(labelContainerStartupUsingNode24))
{
executionContext.Debug("Using Node 20 for container startup.");
executionContext.Debug("Using Node 24 for container startup.");
containerStartupCompleted = true;
container.ResultNodePath = node24ContainerPath;
break;
}
else if (logLine.Contains(labelContainerStartupUsingNode20))
{
string warningMsg = useNode24ToStartContainer
? "Cannot run Node 24 in container. Falling back to Node 20 for container startup."
: "Using Node 20 for container startup.";
executionContext.Warning(warningMsg);
containerStartupCompleted = true;
container.ResultNodePath = node20ContainerPath;
break;
}
else if (logLine.Contains(labelContainerStartupUsingNode16))
{
executionContext.Warning("Can not run Node 20 in container. Falling back to Node 16 for container startup.");
string warningMsg = useNode24ToStartContainer
? "Cannot run Node 24 and Node 20 in container. Falling back to Node 16 for container startup."
: "Cannot run Node 20 in container. Falling back to Node 16 for container startup.";
executionContext.Warning(warningMsg);
containerStartupCompleted = true;
container.ResultNodePath = node16ContainerPath;
break;
}
else if (logLine.Contains(labelContainerStartupFailed))
{
executionContext.Error("Can not run both Node 20 and Node 16 in container. Container startup failed.");
string errorMsg = useNode24ToStartContainer
? "Cannot run Node 24, Node 20, and Node 16 in container. Container startup failed."
: "Cannot run both Node 20 and Node 16 in container. Container startup failed.";
executionContext.Error(errorMsg);
containerStartupCompleted = true;
break;
}
Expand Down Expand Up @@ -1114,8 +1146,30 @@ string useDoubleQuotes(string value)
if (PlatformUtil.RunningOnLinux)
{
bool useNode20InUnsupportedSystem = AgentKnobs.UseNode20InUnsupportedSystem.GetValue(executionContext).AsBoolean();
bool useNode24InUnsupportedSystem = AgentKnobs.UseNode24InUnsupportedSystem.GetValue(executionContext).AsBoolean();

if (!useNode24InUnsupportedSystem)
{
var node24 = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), NodeHandler.Node24Folder, "bin", $"node{IOUtil.ExeExtension}"));

string node24TestCmd = $"bash -c \"{node24} -v\"";
List<string> node24VersionOutput = await DockerExec(executionContext, container.ContainerId, node24TestCmd, noExceptionOnError: true);

container.NeedsNode20Redirect = WorkerUtilities.IsCommandResultGlibcError(executionContext, node24VersionOutput, out string node24InfoLine);

if (!useNode20InUnsupportedSystem)
if (container.NeedsNode20Redirect)
{
PublishTelemetry(
executionContext,
new Dictionary<string, string>
{
{ "ContainerNode24to20Fallback", container.NeedsNode20Redirect.ToString() }
}
);
}
}

if (!useNode20InUnsupportedSystem && (useNode24InUnsupportedSystem || container.NeedsNode20Redirect))
{
var node20 = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), NodeHandler.Node20_1Folder, "bin", $"node{IOUtil.ExeExtension}"));

Expand All @@ -1130,12 +1184,24 @@ string useDoubleQuotes(string value)
executionContext,
new Dictionary<string, string>
{
{ "ContainerNode20to16Fallback", container.NeedsNode16Redirect.ToString() }
{ "ContainerNode20to16Fallback", container.NeedsNode16Redirect.ToString() }
}
);
}
}

if (!container.NeedsNode20Redirect)
{
container.ResultNodePath = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), NodeHandler.Node24Folder, "bin", $"node{IOUtil.ExeExtension}"));
}
else if (!container.NeedsNode16Redirect)
{
container.ResultNodePath = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), NodeHandler.Node20_1Folder, "bin", $"node{IOUtil.ExeExtension}"));
}
else
{
container.ResultNodePath = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), NodeHandler.Node16Folder, "bin", $"node{IOUtil.ExeExtension}"));
}
}

if (!string.IsNullOrEmpty(containerUserName))
Expand Down
Loading