Skip to content
Open
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
1 change: 1 addition & 0 deletions src/Agent.Sdk/ContainerInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ public ContainerInfo(Pipelines.ContainerResource container, Boolean isJobContain
public bool IsJobContainer { get; set; }
public bool MapDockerSocket { get; set; }
public bool NeedsNode16Redirect { get; set; }
public bool NeedsNode20Redirect { get; set; }
public PlatformUtil.OS ImageOS
{
get
Expand Down
25 changes: 24 additions & 1 deletion src/Agent.Sdk/Knob/AgentKnobs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,22 @@ public class AgentKnobs
new EnvironmentKnobSource("AGENT_USE_NODE20_IN_UNSUPPORTED_SYSTEM"),
new BuiltInDefaultKnobSource("false"));

public static readonly Knob UseNode24 = new Knob(
nameof(UseNode24),
"Forces the agent to use Node 24 handler for all Node-based tasks",
new PipelineFeatureSource("UseNode24"),
new RuntimeKnobSource("AGENT_USE_NODE24"),
new EnvironmentKnobSource("AGENT_USE_NODE24"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should have pipeline feature source as well

new BuiltInDefaultKnobSource("false"));

public static readonly Knob UseNode24InUnsupportedSystem = new Knob(
nameof(UseNode24InUnsupportedSystem),
"Forces the agent to use Node 24 handler for all Node-based tasks, even if it's in an unsupported system",
new PipelineFeatureSource("UseNode24InUnsupportedSystem"),
new RuntimeKnobSource("AGENT_USE_NODE24_IN_UNSUPPORTED_SYSTEM"),
new EnvironmentKnobSource("AGENT_USE_NODE24_IN_UNSUPPORTED_SYSTEM"),
new BuiltInDefaultKnobSource("false"));

public static readonly Knob FetchByCommitForFullClone = new Knob(
nameof(FetchByCommitForFullClone),
"If true, allow fetch by commit when doing a full clone (depth=0).",
Expand Down Expand Up @@ -707,8 +723,15 @@ public class AgentKnobs
public static readonly Knob UseNode20ToStartContainer = new Knob(
nameof(UseNode20ToStartContainer),
"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.",
new RuntimeKnobSource("AZP_AGENT_USE_NODE20_TO_START_CONTAINER"),
new PipelineFeatureSource("UseNode20ToStartContainer"),
new RuntimeKnobSource("AZP_AGENT_USE_NODE20_TO_START_CONTAINER"),
new BuiltInDefaultKnobSource("false"));

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 BuiltInDefaultKnobSource("false"));

public static readonly Knob EnableNewMaskerAndRegexes = new Knob(
Expand Down
66 changes: 62 additions & 4 deletions src/Agent.Worker/ContainerOperationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ private async Task PullContainerAsync(IExecutionContext executionContext, Contai
}
}

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

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 @@ -540,6 +543,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 @@ -573,7 +577,20 @@ string useDoubleQuotes(string value)
else
{
useAgentNode = true;
string sleepCommand = useNode20ToStartContainer ? $"'{node20ContainerPath}' --version && echo '{labelContainerStartupUsingNode20}' && {nodeSetInterval(node20ContainerPath)} || '{node16ContainerPath}' --version && echo '{labelContainerStartupUsingNode16}' && {nodeSetInterval(node16ContainerPath)} || echo '{labelContainerStartupFailed}'" : nodeSetInterval(nodeContainerPath);
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 @@ -609,7 +626,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 @@ -622,7 +639,14 @@ string useDoubleQuotes(string value)

foreach (string logLine in containerLogs)
{
if (logLine.Contains(labelContainerStartupUsingNode20))
if (logLine.Contains(labelContainerStartupUsingNode24))
{
executionContext.Debug("Using Node 24 for container startup.");
containerStartupCompleted = true;
container.ResultNodePath = node24ContainerPath;
break;
}
else if (logLine.Contains(labelContainerStartupUsingNode20))
{
executionContext.Debug("Using Node 20 for container startup.");
containerStartupCompleted = true;
Expand Down Expand Up @@ -931,8 +955,29 @@ string useDoubleQuotes(string value)
if (PlatformUtil.RunningOnLinux)
{
bool useNode20InUnsupportedSystem = AgentKnobs.UseNode20InUnsupportedSystem.GetValue(executionContext).AsBoolean();
bool useNode24InUnsupportedSystem = AgentKnobs.UseNode24InUnsupportedSystem.GetValue(executionContext).AsBoolean();

if (!useNode20InUnsupportedSystem)
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 (container.NeedsNode20Redirect)
{
PublishTelemetry(
executionContext,
new Dictionary<string, string>
{
{ "ContainerNode24to20Fallback", container.NeedsNode20Redirect.ToString() }
}
);
}
}
else if (!useNode20InUnsupportedSystem)
{
var node20 = container.TranslateToContainerPath(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Externals), NodeHandler.Node20_1Folder, "bin", $"node{IOUtil.ExeExtension}"));

Expand All @@ -953,6 +998,19 @@ string useDoubleQuotes(string value)
}
}

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
14 changes: 14 additions & 0 deletions src/Agent.Worker/ExecutionContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public interface IExecutionContext : IAgentService, IKnobValueContext
/// <returns></returns>
void CancelForceTaskCompletion();
void EmitHostNode20FallbackTelemetry(bool node20ResultsInGlibCErrorHost);
void EmitHostNode24FallbackTelemetry(bool node24ResultsInGlibCErrorHost);
void PublishTaskRunnerTelemetry(Dictionary<string, string> taskRunnerData);
}

Expand Down Expand Up @@ -132,6 +133,7 @@ public sealed class ExecutionContext : AgentService, IExecutionContext, ICorrela
private FileStream _buildLogsData;
private StreamWriter _buildLogsWriter;
private bool emittedHostNode20FallbackTelemetry = false;
private bool emittedHostNode24FallbackTelemetry = false;

// only job level ExecutionContext will track throttling delay.
private long _totalThrottlingDelayInMilliseconds = 0;
Expand Down Expand Up @@ -973,6 +975,18 @@ public void EmitHostNode20FallbackTelemetry(bool node20ResultsInGlibCErrorHost)
emittedHostNode20FallbackTelemetry = true;
}
}
public void EmitHostNode24FallbackTelemetry(bool node24ResultsInGlibCErrorHost)
{
if (!emittedHostNode24FallbackTelemetry)
{
PublishTelemetry(new Dictionary<string, string>
{
{ "HostNode24to20Fallback", node24ResultsInGlibCErrorHost.ToString() }
});

emittedHostNode24FallbackTelemetry = true;
}
}

// This overload is to handle specific types some other way.
private void PublishTelemetry<T>(
Expand Down
Loading
Loading