Skip to content

Commit

Permalink
Adjustments for FreeBSD
Browse files Browse the repository at this point in the history
  • Loading branch information
joperator committed Nov 7, 2023
1 parent 6ee2a6b commit a14b6ec
Show file tree
Hide file tree
Showing 50 changed files with 381 additions and 55 deletions.
59 changes: 59 additions & 0 deletions src/Agent.Listener/Configuration.FreeBSD/DaemonControlManager.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

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

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

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 @@ -328,6 +329,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 @@ -434,6 +436,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 @@ -485,7 +493,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 @@ -90,7 +90,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 @@ -29,7 +29,7 @@ public static string ExeExtension
public static StringComparison FilePathStringComparison
{
get =>
PlatformUtil.RunningOnLinux
(PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD)
? StringComparison.Ordinal
: StringComparison.OrdinalIgnoreCase;
}
Expand Down
40 changes: 25 additions & 15 deletions src/Agent.Sdk/Util/PlatformUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ public static class PlatformUtil
public enum OS
{
Linux,
FreeBSD,
OSX,
Windows,
Windows
}

public static OS HostOS
Expand All @@ -46,6 +47,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 @@ -87,6 +92,11 @@ public static bool RunningOnAlpine
}
}

public static bool RunningOnFreeBSD
{
get => PlatformUtil.HostOS == PlatformUtil.OS.FreeBSD;
}

public static bool RunningOnRHEL6
{
get
Expand Down Expand Up @@ -121,7 +131,7 @@ public static string GetSystemId()
{
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 @@ -132,7 +142,7 @@ public static SystemVersion GetSystemVersion()
{
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 @@ -195,22 +205,22 @@ private static void DetectRHEL7()
}
}

private static string GetLinuxId()
private static string GetUnixId()
{
if (RunningOnLinux && File.Exists("/etc/os-release"))
if ((RunningOnLinux || RunningOnFreeBSD) && File.Exists("/etc/os-release"))
{
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("/etc/os-release"))
{
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 @@ -219,22 +229,22 @@ private static string GetLinuxId()
return null;
}

private static string GetLinuxName()
private static string GetUnixName()
{
if (RunningOnLinux && File.Exists("/etc/os-release"))
if ((RunningOnLinux || RunningOnFreeBSD) && File.Exists("/etc/os-release"))
{
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("/etc/os-release"))
{
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 @@ -40,7 +40,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/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
11 changes: 7 additions & 4 deletions src/Common.props
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,18 @@
<DebugConstant>DEBUG</DebugConstant>
</PropertyGroup>

<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Windows)))' == 'true'">
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('Windows'))">
<OSPlatform>OS_WINDOWS</OSPlatform>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' == 'true'">
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('OSX'))">
<OSPlatform>OS_OSX</OSPlatform>
</PropertyGroup>
<PropertyGroup Condition="'$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))' == 'true'">
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('Linux'))">
<OSPlatform>OS_LINUX</OSPlatform>
</PropertyGroup>
<PropertyGroup Condition="$([MSBuild]::IsOSPlatform('FreeBSD'))">
<OSPlatform>OS_FREEBSD</OSPlatform>
</PropertyGroup>

<PropertyGroup Condition="'$(OSPlatform)' == 'OS_WINDOWS' AND '$(PROCESSOR_ARCHITECTURE)' == 'AMD64'">
<OSArchitecture>X64</OSArchitecture>
Expand All @@ -41,7 +44,7 @@
<OSArchitecture>X64</OSArchitecture>
</PropertyGroup>

<PropertyGroup Condition="'$(OSPlatform)' == 'OS_LINUX' AND '$(PackageRuntime)' == 'linux-x64'">
<PropertyGroup Condition="('$(OSPlatform)' == 'OS_LINUX' AND '$(PackageRuntime)' == 'linux-x64') OR ('$(OSPlatform)' == 'OS_FREEBSD' AND '$(PackageRuntime)' == 'freebsd-x64')">
<OSArchitecture>X64</OSArchitecture>
</PropertyGroup>
<PropertyGroup Condition="'$(OSPlatform)' == 'OS_LINUX' AND '$(PackageRuntime)' == 'linux-musl-x64'">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ private List<IExtension> LoadExtensions<T>() where T : class, IExtension
case "Microsoft.VisualStudio.Services.Agent.Capabilities.ICapabilitiesProvider":
Add<T>(extensions, "Microsoft.VisualStudio.Services.Agent.Capabilities.AgentCapabilitiesProvider, Microsoft.VisualStudio.Services.Agent");
Add<T>(extensions, "Microsoft.VisualStudio.Services.Agent.Capabilities.EnvironmentCapabilitiesProvider, Microsoft.VisualStudio.Services.Agent");
if (PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnMacOS)
if (PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD || PlatformUtil.RunningOnMacOS)
{
Add<T>(extensions, "Microsoft.VisualStudio.Services.Agent.Capabilities.NixCapabilitiesProvider, Microsoft.VisualStudio.Services.Agent");
}
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.VisualStudio.Services.Agent/HostContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -435,7 +435,7 @@ public T CreateService<T>() where T : class, IAgentService
{
platformTarget = arg.TypedValue.Value as Type;
}
else if (PlatformUtil.RunningOnLinux
else if ((PlatformUtil.RunningOnLinux || PlatformUtil.RunningOnFreeBSD)
&& string.Equals(arg.MemberName, nameof(ServiceLocatorAttribute.PreferredOnLinux), StringComparison.Ordinal))
{
platformTarget = arg.TypedValue.Value as Type;
Expand Down
2 changes: 2 additions & 0 deletions src/Microsoft.VisualStudio.Services.Agent/Util/VarUtil.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ public static string OS
{
case PlatformUtil.OS.Linux:
return "Linux";
case PlatformUtil.OS.FreeBSD:
return "FreeBSD";
case PlatformUtil.OS.OSX:
return "Darwin";
case PlatformUtil.OS.Windows:
Expand Down
21 changes: 21 additions & 0 deletions src/Misc/externals.sh
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,18 @@ function acquireExternalTool() {
fi
}

function linkExternalTool() {
local source_file=$1 # E.g. /usr/local/bin/node
local target_dir="$LAYOUT_DIR/externals/$2" # E.g. $LAYOUT_DIR/externals/node/bin
local target_name=$3 # E.g. node
if [ -d "$target_dir" ]; then
rm -rf "$target_dir" || checkRC 'rm'
fi

mkdir -p "$target_dir" || checkRC 'mkdir'
ln -s "$source_file" "$target_dir/$target_name" || checkRC 'ln'
}

if [[ "$PACKAGERUNTIME" == "win-x"* ]]; then
# Download external tools for Windows.

Expand Down Expand Up @@ -195,6 +207,15 @@ else
ARCH="darwin-arm64"
acquireExternalTool "${NODE_URL}/v${NODE16_VERSION}/node-v${NODE16_VERSION}-${ARCH}.tar.gz" node16 fix_nested_dir
acquireExternalTool "${NODE_URL}/v${NODE20_VERSION}/node-v${NODE20_VERSION}-${ARCH}.tar.gz" node20 fix_nested_dir
elif [[ "$PACKAGERUNTIME" == "freebsd-x64" ]]; then
# Create a link to node only for FreeBSD.

if [[ "$INCLUDE_NODE6" == "true" ]]; then
linkExternalTool /usr/local/bin/node node/bin node
fi
linkExternalTool /usr/local/bin/node node10/bin node
linkExternalTool /usr/local/bin/node node16/bin node
linkExternalTool /usr/local/bin/node node20/bin node
else
case $PACKAGERUNTIME in
"linux-musl-x64") ARCH="linux-x64-musl";;
Expand Down
2 changes: 1 addition & 1 deletion src/Misc/layoutbin/AgentService.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
var childProcess = require("child_process");
var path = require("path")

var supported = ['linux', 'darwin']
var supported = ['linux', 'freebsd', 'darwin']

if (supported.indexOf(process.platform) == -1) {
console.log('Unsupported platform: ' + process.platform);
Expand Down
Loading

0 comments on commit a14b6ec

Please sign in to comment.