Skip to content

Commit

Permalink
Adjustments for FreeBSD
Browse files Browse the repository at this point in the history
  • Loading branch information
joperator committed May 28, 2024
1 parent abbe484 commit 1f57a3b
Show file tree
Hide file tree
Showing 58 changed files with 421 additions and 73 deletions.
62 changes: 62 additions & 0 deletions src/Agent.Listener/Configuration.FreeBSD/DaemonControlManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Versioning;
using System.Text;
using Microsoft.VisualStudio.Services.Agent.Util;

namespace Microsoft.VisualStudio.Services.Agent.Listener.Configuration
{
[ServiceLocator(Default = typeof(DaemonControlManager))]
[SupportedOSPlatform("freebsd")]
public interface IFreeBSDServiceControlManager : IAgentService
{
void GenerateScripts(AgentSettings settings);
}

[SupportedOSPlatform("freebsd")]
public class DaemonControlManager : ServiceControlManager, IFreeBSDServiceControlManager
{
private const string _svcNamePattern = "vsts.agent.{0}.{1}.{2}.service";
private const string _svcDisplayPattern = "Azure Pipelines Agent ({0}.{1}.{2})";
private const string _shTemplate = "daemon.svc.sh.template";
private const string _shName = "svc.sh";

public void GenerateScripts(AgentSettings settings)
{
try
{
string serviceName;
string serviceDisplayName;
CalculateServiceName(settings, _svcNamePattern, _svcDisplayPattern, out serviceName, out serviceDisplayName);

string svcShPath = Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Root), _shName);

string svcShContent = File.ReadAllText(Path.Combine(HostContext.GetDirectory(WellKnownDirectory.Bin), _shTemplate));
var tokensToReplace = new Dictionary<string, string>
{
{ "{{SvcDescription}}", serviceDisplayName },
{ "{{SvcNameVar}}", serviceName }
};

svcShContent = tokensToReplace.Aggregate(
svcShContent,
(current, item) => current.Replace(item.Key, item.Value));

File.WriteAllText(svcShPath, svcShContent, new UTF8Encoding(false));

var unixUtil = HostContext.CreateService<IUnixUtil>();
unixUtil.ChmodAsync("755", svcShPath).GetAwaiter().GetResult();
}
catch (Exception ex)
{
Trace.Error(ex);
throw;
}
}
}
}
15 changes: 14 additions & 1 deletion src/Agent.Listener/Configuration/ConfigurationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ public async Task ConfigureAsync(CommandSettings command)
{
case PlatformUtil.OS.OSX:
case PlatformUtil.OS.Linux:
case PlatformUtil.OS.FreeBSD:
// Write the section header.
WriteSection(StringUtil.Loc("EulasSectionHeader"));

Expand Down Expand Up @@ -351,6 +352,7 @@ public async Task ConfigureAsync(CommandSettings command)
{
case PlatformUtil.OS.OSX:
case PlatformUtil.OS.Linux:
case PlatformUtil.OS.FreeBSD:
// Save the provided admin cred for compat with previous agent.
_store.SaveCredential(credProvider.CredentialData);
break;
Expand Down Expand Up @@ -457,6 +459,12 @@ public async Task ConfigureAsync(CommandSettings command)
var serviceControlManager = HostContext.GetService<ILinuxServiceControlManager>();
serviceControlManager.GenerateScripts(agentSettings);
}
else if (PlatformUtil.RunningOnFreeBSD)
{
// generate service config script for FreeBSD
var serviceControlManager = HostContext.GetService<IFreeBSDServiceControlManager>();
serviceControlManager.GenerateScripts(agentSettings);
}
else if (PlatformUtil.RunningOnMacOS)
{
// generate service config script for macOS
Expand Down Expand Up @@ -508,7 +516,12 @@ public async Task UnconfigureAsync(CommandSettings command)
else if (PlatformUtil.RunningOnLinux)
{
// unconfig systemd service first
throw new InvalidOperationException(StringUtil.Loc("UnconfigureServiceDService"));
throw new InvalidOperationException(StringUtil.Loc("UnconfigureSystemDService"));
}
else if (PlatformUtil.RunningOnFreeBSD)
{
// unconfig daemon service first
throw new InvalidOperationException(StringUtil.Loc("UnconfigureDaemonService"));
}
else if (PlatformUtil.RunningOnMacOS)
{
Expand Down
1 change: 1 addition & 0 deletions src/Agent.Listener/Configuration/NegotiateCredential.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public override VssCredentials GetVssCredentials(IHostContext context)
switch (PlatformUtil.HostOS)
{
case PlatformUtil.OS.Linux:
case PlatformUtil.OS.FreeBSD:
case PlatformUtil.OS.OSX:
credentialCache.Add(new Uri(url), "NTLM", credential);
break;
Expand Down
2 changes: 1 addition & 1 deletion src/Agent.Plugins/PipelineCache/FingerprintCreator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ namespace Agent.Plugins.PipelineCache
public static class FingerprintCreator
{
private static readonly bool isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
private static readonly bool isCaseSensitive = RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
private static readonly bool isCaseSensitive = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD);

// https://github.com/Microsoft/azure-pipelines-task-lib/blob/master/node/docs/findingfiles.md#matchoptions
private static readonly Options minimatchOptions = new Options
Expand Down
2 changes: 1 addition & 1 deletion src/Agent.Sdk/CommandPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ public VssConnection InitializeVssConnection()

VssClientHttpRequestSettings.Default.UserAgent = headerValues;

if (PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnMacOS)
if (PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD || PlatformUtil.RunningOnMacOS)
{
// The .NET Core 2.1 runtime switched its HTTP default from HTTP 1.1 to HTTP 2.
// This causes problems with some versions of the Curl handler.
Expand Down
2 changes: 1 addition & 1 deletion src/Agent.Sdk/ContainerInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ public string TranslateContainerPathForImageOS(PlatformUtil.OS runningOs, string
{
if (!string.IsNullOrEmpty(path))
{
if (runningOs == PlatformUtil.OS.Windows && ImageOS == PlatformUtil.OS.Linux)
if (runningOs == PlatformUtil.OS.Windows && ((ImageOS == PlatformUtil.OS.Linux) || (ImageOS == PlatformUtil.OS.FreeBSD)))
{
return translateWindowsDriveRegex.Replace(path, "/").Replace("\\", "/");
}
Expand Down
2 changes: 1 addition & 1 deletion src/Agent.Sdk/TaskPlugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public VssConnection InitializeVssConnection()

VssClientHttpRequestSettings.Default.UserAgent = headerValues;

if (PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnMacOS)
if (PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD || PlatformUtil.RunningOnMacOS)
{
// The .NET Core 2.1 runtime switched its HTTP default from HTTP 1.1 to HTTP 2.
// This causes problems with some versions of the Curl handler.
Expand Down
2 changes: 1 addition & 1 deletion src/Agent.Sdk/Util/IOUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public static string ExeExtension
public static StringComparison FilePathStringComparison
{
get =>
PlatformUtil.RunningOnLinux
(PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD)
? StringComparison.Ordinal
: StringComparison.OrdinalIgnoreCase;
}
Expand Down
53 changes: 32 additions & 21 deletions src/Agent.Sdk/Util/PlatformUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public static class PlatformUtil
private static OperatingSystem[] net6SupportedSystems;
private static HttpClient httpClient = new HttpClient();

private static readonly string[] linuxReleaseFilePaths = new string[2] { "/etc/os-release", "/usr/lib/os-release" };
private static readonly string[] unixReleaseFilePaths = new string[2] { "/etc/os-release", "/usr/lib/os-release" };

// System.Runtime.InteropServices.OSPlatform is a struct, so it is
// not suitable for switch statements.
Expand All @@ -37,8 +37,9 @@ public static class PlatformUtil
public enum OS
{
Linux,
FreeBSD,
OSX,
Windows,
Windows
}

public static OS HostOS
Expand All @@ -50,6 +51,10 @@ public static OS HostOS
{
return OS.Linux;
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD))
{
return OS.FreeBSD;
}
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
return OS.OSX;
Expand Down Expand Up @@ -81,6 +86,12 @@ public static bool RunningOnLinux
get => PlatformUtil.HostOS == PlatformUtil.OS.Linux;
}

[SupportedOSPlatformGuard("freebsd")]
public static bool RunningOnFreeBSD
{
get => PlatformUtil.HostOS == PlatformUtil.OS.FreeBSD;
}

public static bool RunningOnAlpine
{
get
Expand Down Expand Up @@ -151,7 +162,7 @@ public static string GetSystemId()
#pragma warning disable CA1416 // SupportedOSPlatformGuard not honored on enum members
return PlatformUtil.HostOS switch
{
PlatformUtil.OS.Linux => GetLinuxId(),
PlatformUtil.OS.Linux or PlatformUtil.OS.FreeBSD => GetUnixId(),
PlatformUtil.OS.OSX => "MacOS",
PlatformUtil.OS.Windows => GetWindowsId(),
_ => null
Expand All @@ -164,7 +175,7 @@ public static SystemVersion GetSystemVersion()
#pragma warning disable CA1416 // SupportedOSPlatformGuard not honored on enum members
return PlatformUtil.HostOS switch
{
PlatformUtil.OS.Linux => new SystemVersion(GetLinuxName(), null),
PlatformUtil.OS.Linux or PlatformUtil.OS.FreeBSD => new SystemVersion(GetUnixName(), null),
PlatformUtil.OS.OSX => new SystemVersion(GetOSxName(), null),
PlatformUtil.OS.Windows => new SystemVersion(GetWindowsName(), GetWindowsVersion()),
_ => null
Expand Down Expand Up @@ -200,36 +211,36 @@ private static void DetectRHEL6()
}
}

private static string GetLinuxReleaseFilePath()
private static string GetUnixReleaseFilePath()
{
if (RunningOnLinux)
if (RunningOnLinux || RunningOnFreeBSD)
{
string releaseFilePath = linuxReleaseFilePaths.FirstOrDefault(x => File.Exists(x), null);
string releaseFilePath = unixReleaseFilePaths.FirstOrDefault(x => File.Exists(x), null);
return releaseFilePath;
}

return null;
}

private static string GetLinuxId()
private static string GetUnixId()
{

string filePath = GetLinuxReleaseFilePath();
string filePath = GetUnixReleaseFilePath();

if (RunningOnLinux && filePath != null)
if (filePath != null)
{
Regex linuxIdRegex = new Regex("^ID\\s*=\\s*\"?(?<id>[0-9a-z._-]+)\"?");
Regex unixIdRegex = new Regex("^ID\\s*=\\s*\"?(?<id>[0-9a-z._-]+)\"?");

using (StreamReader reader = new StreamReader(filePath))
{
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
var linuxIdRegexMatch = linuxIdRegex.Match(line);
var unixIdRegexMatch = unixIdRegex.Match(line);

if (linuxIdRegexMatch.Success)
if (unixIdRegexMatch.Success)
{
return linuxIdRegexMatch.Groups["id"].Value;
return unixIdRegexMatch.Groups["id"].Value;
}
}
}
Expand All @@ -238,25 +249,25 @@ private static string GetLinuxId()
return null;
}

private static string GetLinuxName()
private static string GetUnixName()
{

string filePath = GetLinuxReleaseFilePath();
string filePath = GetUnixReleaseFilePath();

if (RunningOnLinux && filePath != null)
if (filePath != null)
{
Regex linuxVersionIdRegex = new Regex("^VERSION_ID\\s*=\\s*\"?(?<id>[0-9a-z._-]+)\"?");
Regex unixVersionIdRegex = new Regex("^VERSION_ID\\s*=\\s*\"?(?<id>[0-9a-z._-]+)\"?");

using (StreamReader reader = new StreamReader(filePath))
{
while (!reader.EndOfStream)
{
string line = reader.ReadLine();
var linuxVersionIdRegexMatch = linuxVersionIdRegex.Match(line);
var unixVersionIdRegexMatch = unixVersionIdRegex.Match(line);

if (linuxVersionIdRegexMatch.Success)
if (unixVersionIdRegexMatch.Success)
{
return linuxVersionIdRegexMatch.Groups["id"].Value;
return unixVersionIdRegexMatch.Groups["id"].Value;
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Agent.Sdk/Util/VssUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public static void InitializeVssClientSettings(ProductInfoHeaderValue additional
VssClientHttpRequestSettings.Default.UserAgent = headerValues;
VssClientHttpRequestSettings.Default.ClientCertificateManager = clientCert;

if (PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnMacOS)
if (PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD || PlatformUtil.RunningOnMacOS)
{
// The .NET Core 2.1 runtime switched its HTTP default from HTTP 1.1 to HTTP 2.
// This causes problems with some versions of the Curl handler.
Expand Down
2 changes: 1 addition & 1 deletion src/Agent.Worker/ContainerOperationProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -839,7 +839,7 @@ private async Task StartContainerAsync(IExecutionContext executionContext, Conta
}
}

if (PlatformUtil.RunningOnLinux)
if (PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD)
{
bool useNode20InUnsupportedSystem = AgentKnobs.UseNode20InUnsupportedSystem.GetValue(executionContext).AsBoolean();

Expand Down
2 changes: 1 addition & 1 deletion src/Agent.Worker/Handlers/NodeHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ public async Task RunAsync()
bool useNode20InUnsupportedSystem = AgentKnobs.UseNode20InUnsupportedSystem.GetValue(ExecutionContext).AsBoolean();
bool node20ResultsInGlibCErrorHost = false;

if (PlatformUtil.HostOS == PlatformUtil.OS.Linux && !useNode20InUnsupportedSystem)
if ((PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD) && !useNode20InUnsupportedSystem)
{
if (supportsNode20.HasValue)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Agent.Worker/Release/Artifacts/GitHubHttpClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ private T QueryItem<T>(string accessToken, string url, out string errorMessage)
request.Headers.Add("Authorization", "Token " + accessToken);
request.Headers.Add("User-Agent", "VSTS-Agent/" + BuildConstants.AgentPackage.Version);

if (PlatformUtil.RunningOnMacOS || PlatformUtil.RunningOnLinux)
if (PlatformUtil.RunningOnMacOS || PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD)
{
request.Version = HttpVersion.Version11;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ protected override async Task<HttpResponseMessage> SendAsync(
{
ArgUtil.NotNull(request, nameof(request));

if (PlatformUtil.RunningOnMacOS || PlatformUtil.RunningOnLinux)
if (PlatformUtil.RunningOnMacOS || PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD)
{
request.Version = HttpVersion.Version11;
}
Expand Down
15 changes: 12 additions & 3 deletions src/Agent.Worker/ResourceMetricsManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -267,16 +267,25 @@ private MemoryInfo GetMemoryInfo()
memoryInfo.UsedMemoryMB = (totalMemory - freeMemory) / 1024;
}

if (PlatformUtil.RunningOnLinux)
if (PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD)
{
// Some compact Linux distributions like UBI may not have "free" utility installed, or it may have a custom output
// We don't want to break currently existing pipelines with ADO warnings
// so related errors thrown here will be sent to the trace or debug logs by caller methods

try
{
processStartInfo.FileName = "free";
processStartInfo.Arguments = "-m";
if (PlatformUtil.RunningOnLinux)
{
processStartInfo.FileName = "free";
processStartInfo.Arguments = "-m";
}
else
{
processStartInfo.FileName = "vmstat";
processStartInfo.Arguments = "-h";
}

processStartInfo.RedirectStandardOutput = true;

using (var process = Process.Start(processStartInfo))
Expand Down
Loading

0 comments on commit 1f57a3b

Please sign in to comment.