Add OpenSslLegacyShim to ensure OpenSSL 1.1 libraries are accessible on Linux
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
This commit introduces the OpenSslLegacyShim class, which sets the LD_LIBRARY_PATH environment variable to include the directory containing OpenSSL 1.1 native libraries. This is necessary for Mongo2Go to function correctly on Linux platforms that do not ship these libraries by default. The shim checks if the current operating system is Linux and whether the required directory exists before modifying the environment variable.
This commit is contained in:
@@ -20,6 +20,239 @@ public sealed class EntryTraceAnalyzerTests
|
||||
_output = output;
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResolveAsync_CollapsesBundleExecWrapper()
|
||||
{
|
||||
var fs = new TestRootFileSystem();
|
||||
fs.AddBinaryFile("/usr/bin/bundle", CreateGoBinary(), executable: true);
|
||||
fs.AddBinaryFile("/usr/local/bin/puma", CreateGoBinary(), executable: true);
|
||||
fs.AddFile("/config.rb", "port 3000\n", executable: false);
|
||||
|
||||
var context = new EntryTraceContext(
|
||||
fs,
|
||||
ImmutableDictionary<string, string>.Empty,
|
||||
ImmutableArray.Create("/usr/bin", "/usr/local/bin"),
|
||||
"/",
|
||||
"app",
|
||||
"sha256:bundle-image",
|
||||
"scan-bundle",
|
||||
NullLogger.Instance);
|
||||
|
||||
var spec = EntrypointSpecification.FromExecForm(
|
||||
new[] { "bundle", "exec", "puma", "-C", "config.rb" },
|
||||
null);
|
||||
|
||||
var analyzer = CreateAnalyzer();
|
||||
var result = await analyzer.ResolveAsync(spec, context);
|
||||
|
||||
Assert.Equal(EntryTraceOutcome.Resolved, result.Outcome);
|
||||
var terminal = Assert.Single(result.Terminals);
|
||||
Assert.Equal("/usr/local/bin/puma", terminal.Path);
|
||||
|
||||
var bundleNode = Assert.Single(result.Nodes.Where(n => n.DisplayName == "bundle"));
|
||||
Assert.Equal("language-launcher", bundleNode.Metadata?["wrapper.category"]);
|
||||
Assert.Equal("bundle exec", bundleNode.Metadata?["wrapper.name"]);
|
||||
Assert.Contains(result.Edges, edge => edge.Relationship == "wrapper" && edge.FromNodeId == bundleNode.Id);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResolveAsync_CollapsesDockerPhpEntrypointWrapper()
|
||||
{
|
||||
var fs = new TestRootFileSystem();
|
||||
fs.AddFile("/usr/local/bin/docker-php-entrypoint", "#!/bin/sh\nexec \"$@\"\n", executable: true);
|
||||
fs.AddBinaryFile("/usr/local/sbin/php-fpm", CreateGoBinary(), executable: true);
|
||||
fs.AddFile("/usr/local/etc/php-fpm.conf", "include=/etc/php-fpm.d/*.conf\n", executable: false);
|
||||
|
||||
var context = new EntryTraceContext(
|
||||
fs,
|
||||
ImmutableDictionary<string, string>.Empty,
|
||||
ImmutableArray.Create("/usr/local/bin", "/usr/local/sbin"),
|
||||
"/app",
|
||||
"www-data",
|
||||
"sha256:php-image",
|
||||
"scan-php",
|
||||
NullLogger.Instance);
|
||||
|
||||
var spec = EntrypointSpecification.FromExecForm(
|
||||
new[] { "/usr/local/bin/docker-php-entrypoint", "php-fpm", "-y", "/usr/local/etc/php-fpm.conf" },
|
||||
null);
|
||||
|
||||
var analyzer = CreateAnalyzer();
|
||||
var result = await analyzer.ResolveAsync(spec, context);
|
||||
|
||||
Assert.Equal(EntryTraceOutcome.Resolved, result.Outcome);
|
||||
var terminal = Assert.Single(result.Terminals);
|
||||
Assert.Equal("/usr/local/sbin/php-fpm", terminal.Path);
|
||||
|
||||
var wrapperNode = Assert.Single(result.Nodes.Where(n => n.DisplayName.Contains("docker-php-entrypoint", StringComparison.OrdinalIgnoreCase)));
|
||||
Assert.Equal("language-launcher", wrapperNode.Metadata?["wrapper.category"]);
|
||||
Assert.Equal("docker-php-entrypoint", wrapperNode.Metadata?["wrapper.name"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResolveAsync_CollapsesNpmExecWrapper()
|
||||
{
|
||||
var fs = new TestRootFileSystem();
|
||||
fs.AddBinaryFile("/usr/bin/npm", CreateGoBinary(), executable: true);
|
||||
fs.AddBinaryFile("/usr/bin/node", CreateGoBinary(), executable: true);
|
||||
fs.AddFile("/server.js", "console.log('hello');", executable: false);
|
||||
|
||||
var context = new EntryTraceContext(
|
||||
fs,
|
||||
ImmutableDictionary<string, string>.Empty,
|
||||
ImmutableArray.Create("/usr/bin"),
|
||||
"/srv",
|
||||
"node",
|
||||
"sha256:npm-image",
|
||||
"scan-npm",
|
||||
NullLogger.Instance);
|
||||
|
||||
var spec = EntrypointSpecification.FromExecForm(
|
||||
new[] { "npm", "exec", "--yes", "node", "server.js" },
|
||||
null);
|
||||
|
||||
var analyzer = CreateAnalyzer();
|
||||
var result = await analyzer.ResolveAsync(spec, context);
|
||||
|
||||
Assert.True(
|
||||
result.Diagnostics.All(d => d.Severity != EntryTraceDiagnosticSeverity.Warning),
|
||||
string.Join(", ", result.Diagnostics.Select(d => $"{d.Severity}:{d.Reason}:{d.Message}")));
|
||||
if (result.Outcome != EntryTraceOutcome.Resolved)
|
||||
{
|
||||
throw new XunitException("Outcome: " + result.Outcome + " Diagnostics: " + string.Join(", ", result.Diagnostics.Select(d => $"{d.Severity}:{d.Reason}:{d.Message}")));
|
||||
}
|
||||
|
||||
if (result.Terminals.Length == 0)
|
||||
{
|
||||
throw new XunitException("Terminals missing; diagnostics=" + string.Join(", ", result.Diagnostics.Select(d => $"{d.Severity}:{d.Reason}:{d.Message}")) +
|
||||
"; nodes=" + string.Join(", ", result.Nodes.Select(n => $"{n.Kind}:{n.DisplayName}")));
|
||||
}
|
||||
|
||||
var terminal = Assert.Single(result.Terminals);
|
||||
Assert.Equal("/usr/bin/node", terminal.Path);
|
||||
|
||||
var npmNode = Assert.Single(result.Nodes.Where(n => n.DisplayName.Contains("npm", StringComparison.OrdinalIgnoreCase)));
|
||||
Assert.Equal("language-launcher", npmNode.Metadata?["wrapper.category"]);
|
||||
Assert.Equal("npm", npmNode.Metadata?["wrapper.name"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResolveAsync_CollapsesYarnNodeWrapper()
|
||||
{
|
||||
var fs = new TestRootFileSystem();
|
||||
fs.AddBinaryFile("/usr/local/bin/yarn", CreateGoBinary(), executable: true);
|
||||
fs.AddBinaryFile("/usr/bin/node", CreateGoBinary(), executable: true);
|
||||
fs.AddFile("/app.js", "console.log('app');", executable: false);
|
||||
|
||||
var context = new EntryTraceContext(
|
||||
fs,
|
||||
ImmutableDictionary<string, string>.Empty,
|
||||
ImmutableArray.Create("/usr/local/bin", "/usr/bin"),
|
||||
"/workspace",
|
||||
"node",
|
||||
"sha256:yarn-image",
|
||||
"scan-yarn",
|
||||
NullLogger.Instance);
|
||||
|
||||
var spec = EntrypointSpecification.FromExecForm(
|
||||
new[] { "/usr/local/bin/yarn", "node", "app.js" },
|
||||
null);
|
||||
|
||||
var analyzer = CreateAnalyzer();
|
||||
var result = await analyzer.ResolveAsync(spec, context);
|
||||
|
||||
Assert.Equal(EntryTraceOutcome.Resolved, result.Outcome);
|
||||
if (result.Terminals.Length == 0)
|
||||
{
|
||||
throw new XunitException("Terminals missing; diagnostics=" + string.Join(", ", result.Diagnostics.Select(d => $"{d.Severity}:{d.Reason}:{d.Message}")) +
|
||||
"; nodes=" + string.Join(", ", result.Nodes.Select(n => $"{n.Kind}:{n.DisplayName}")));
|
||||
}
|
||||
var terminal = Assert.Single(result.Terminals);
|
||||
Assert.Equal("/usr/bin/node", terminal.Path);
|
||||
|
||||
var yarnNode = Assert.Single(result.Nodes.Where(n => n.DisplayName.Contains("yarn", StringComparison.OrdinalIgnoreCase)));
|
||||
Assert.Equal("language-launcher", yarnNode.Metadata?["wrapper.category"]);
|
||||
Assert.Equal("yarn node", yarnNode.Metadata?["wrapper.name"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResolveAsync_CollapsesPipenvRunWrapper()
|
||||
{
|
||||
var fs = new TestRootFileSystem();
|
||||
fs.AddBinaryFile("/usr/local/bin/pipenv", CreateGoBinary(), executable: true);
|
||||
fs.AddBinaryFile("/usr/bin/python", CreateGoBinary(), executable: true);
|
||||
fs.AddFile("/app.py", "print('ok')\n", executable: false);
|
||||
|
||||
var context = new EntryTraceContext(
|
||||
fs,
|
||||
ImmutableDictionary<string, string>.Empty,
|
||||
ImmutableArray.Create("/usr/local/bin", "/usr/bin"),
|
||||
"/service",
|
||||
"python",
|
||||
"sha256:pipenv-image",
|
||||
"scan-pipenv",
|
||||
NullLogger.Instance);
|
||||
|
||||
var spec = EntrypointSpecification.FromExecForm(
|
||||
new[] { "pipenv", "run", "python", "app.py" },
|
||||
null);
|
||||
|
||||
var analyzer = CreateAnalyzer();
|
||||
var result = await analyzer.ResolveAsync(spec, context);
|
||||
|
||||
Assert.Equal(EntryTraceOutcome.Resolved, result.Outcome);
|
||||
if (result.Terminals.Length == 0)
|
||||
{
|
||||
throw new XunitException("Terminals missing; diagnostics=" + string.Join(", ", result.Diagnostics.Select(d => $"{d.Severity}:{d.Reason}:{d.Message}")) +
|
||||
"; nodes=" + string.Join(", ", result.Nodes.Select(n => $"{n.Kind}:{n.DisplayName}")));
|
||||
}
|
||||
var terminal = Assert.Single(result.Terminals);
|
||||
Assert.Equal("/usr/bin/python", terminal.Path);
|
||||
|
||||
var pipenvNode = Assert.Single(result.Nodes.Where(n => n.DisplayName.Contains("pipenv", StringComparison.OrdinalIgnoreCase)));
|
||||
Assert.Equal("language-launcher", pipenvNode.Metadata?["wrapper.category"]);
|
||||
Assert.Equal("pipenv run", pipenvNode.Metadata?["wrapper.name"]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResolveAsync_CollapsesPoetryRunWrapper()
|
||||
{
|
||||
var fs = new TestRootFileSystem();
|
||||
fs.AddBinaryFile("/usr/local/bin/poetry", CreateGoBinary(), executable: true);
|
||||
fs.AddBinaryFile("/usr/bin/python", CreateGoBinary(), executable: true);
|
||||
fs.AddFile("/manage.py", "print('manage')\n", executable: false);
|
||||
|
||||
var context = new EntryTraceContext(
|
||||
fs,
|
||||
ImmutableDictionary<string, string>.Empty,
|
||||
ImmutableArray.Create("/usr/local/bin", "/usr/bin"),
|
||||
"/srv/app",
|
||||
"python",
|
||||
"sha256:poetry-image",
|
||||
"scan-poetry",
|
||||
NullLogger.Instance);
|
||||
|
||||
var spec = EntrypointSpecification.FromExecForm(
|
||||
new[] { "poetry", "run", "python", "manage.py" },
|
||||
null);
|
||||
|
||||
var analyzer = CreateAnalyzer();
|
||||
var result = await analyzer.ResolveAsync(spec, context);
|
||||
|
||||
Assert.Equal(EntryTraceOutcome.Resolved, result.Outcome);
|
||||
if (result.Terminals.Length == 0)
|
||||
{
|
||||
throw new XunitException("Terminals missing; diagnostics=" + string.Join(", ", result.Diagnostics.Select(d => $"{d.Severity}:{d.Reason}:{d.Message}")) +
|
||||
"; nodes=" + string.Join(", ", result.Nodes.Select(n => $"{n.Kind}:{n.DisplayName}")));
|
||||
}
|
||||
var terminal = Assert.Single(result.Terminals);
|
||||
Assert.Equal("/usr/bin/python", terminal.Path);
|
||||
|
||||
var poetryNode = Assert.Single(result.Nodes.Where(n => n.DisplayName.Contains("poetry", StringComparison.OrdinalIgnoreCase)));
|
||||
Assert.Equal("language-launcher", poetryNode.Metadata?["wrapper.category"]);
|
||||
Assert.Equal("poetry run", poetryNode.Metadata?["wrapper.name"]);
|
||||
}
|
||||
|
||||
private static EntryTraceAnalyzer CreateAnalyzer()
|
||||
{
|
||||
var options = Options.Create(new EntryTraceAnalyzerOptions
|
||||
@@ -269,6 +502,7 @@ public sealed class EntryTraceAnalyzerTests
|
||||
{
|
||||
var fs = new TestRootFileSystem();
|
||||
fs.AddBinaryFile("/usr/bin/node", CreateGoBinary(), executable: true);
|
||||
fs.AddFile("/app/server.js", "console.log('server');\n", executable: false);
|
||||
|
||||
var config = new OciImageConfig
|
||||
{
|
||||
@@ -284,6 +518,11 @@ public sealed class EntryTraceAnalyzerTests
|
||||
"scan-history",
|
||||
NullLogger.Instance);
|
||||
|
||||
var candidates = imageContext.Context.Candidates;
|
||||
var historyCandidate = Assert.Single(candidates);
|
||||
Assert.Equal("history", historyCandidate.Source);
|
||||
Assert.True(historyCandidate.Command.SequenceEqual(new[] { "/usr/bin/node", "/app/server.js" }));
|
||||
|
||||
var analyzer = CreateAnalyzer();
|
||||
var result = await analyzer.ResolveAsync(imageContext.Entrypoint, imageContext.Context);
|
||||
|
||||
@@ -291,7 +530,7 @@ public sealed class EntryTraceAnalyzerTests
|
||||
Assert.Contains(result.Diagnostics, diagnostic => diagnostic.Reason == EntryTraceUnknownReason.InferredEntrypointFromHistory);
|
||||
var terminal = Assert.Single(result.Terminals);
|
||||
Assert.Equal("/usr/bin/node", terminal.Path);
|
||||
Assert.Contains("/usr/bin/node", terminal.Arguments[0]);
|
||||
Assert.Contains("/app/server.js", terminal.Arguments);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -315,6 +554,11 @@ public sealed class EntryTraceAnalyzerTests
|
||||
"scan-supervisor",
|
||||
NullLogger.Instance);
|
||||
|
||||
var candidates = imageContext.Context.Candidates;
|
||||
var supervisorCandidate = Assert.Single(candidates);
|
||||
Assert.Equal("supervisor", supervisorCandidate.Source);
|
||||
Assert.True(supervisorCandidate.Command.SequenceEqual(new[] { "gunicorn", "app:app" }));
|
||||
|
||||
var analyzer = CreateAnalyzer();
|
||||
var result = await analyzer.ResolveAsync(imageContext.Entrypoint, imageContext.Context);
|
||||
|
||||
@@ -322,7 +566,43 @@ public sealed class EntryTraceAnalyzerTests
|
||||
Assert.Contains(result.Diagnostics, diagnostic => diagnostic.Reason == EntryTraceUnknownReason.InferredEntrypointFromSupervisor);
|
||||
var terminal = Assert.Single(result.Terminals);
|
||||
Assert.Equal("/usr/bin/gunicorn", terminal.Path);
|
||||
Assert.Contains("gunicorn", terminal.Arguments[0]);
|
||||
Assert.Contains("app:app", terminal.Arguments);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResolveAsync_DiscoversEntrypointScriptCandidate()
|
||||
{
|
||||
var fs = new TestRootFileSystem();
|
||||
fs.AddFile("/usr/local/bin/image-entrypoint", """
|
||||
#!/bin/sh
|
||||
exec python /srv/service.py
|
||||
""", executable: true);
|
||||
fs.AddBinaryFile("/usr/bin/python", CreateGoBinary(), executable: true);
|
||||
fs.AddFile("/srv/service.py", "print('run')\n", executable: false);
|
||||
|
||||
var config = new OciImageConfig();
|
||||
var options = new EntryTraceAnalyzerOptions();
|
||||
var imageContext = EntryTraceImageContextFactory.Create(
|
||||
config,
|
||||
fs,
|
||||
options,
|
||||
"sha256:image-entrypoint",
|
||||
"scan-entrypoint",
|
||||
NullLogger.Instance);
|
||||
|
||||
var candidates = imageContext.Context.Candidates;
|
||||
var scriptCandidate = Assert.Single(candidates);
|
||||
Assert.Equal("entrypoint-script", scriptCandidate.Source);
|
||||
Assert.True(scriptCandidate.Command.SequenceEqual(new[] { "/usr/local/bin/image-entrypoint" }));
|
||||
|
||||
var analyzer = CreateAnalyzer();
|
||||
var result = await analyzer.ResolveAsync(imageContext.Entrypoint, imageContext.Context);
|
||||
|
||||
Assert.Equal(EntryTraceOutcome.Resolved, result.Outcome);
|
||||
Assert.Contains(result.Diagnostics, diagnostic => diagnostic.Reason == EntryTraceUnknownReason.InferredEntrypointFromEntrypointScript);
|
||||
var terminal = Assert.Single(result.Terminals);
|
||||
Assert.Equal("/usr/bin/python", terminal.Path);
|
||||
Assert.Contains("/srv/service.py", terminal.Arguments);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user