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
1 change: 0 additions & 1 deletion .github/workflows/common.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ jobs:

- name: Test the container project
run: |
launchctl setenv HTTP_PROXY $HTTP_PROXY
APP_ROOT=$(mktemp -d -p "${RUNNER_TEMP}")
trap 'rm -rf "${APP_ROOT}"; echo Removing data directory ${APP_ROOT}' EXIT
echo "Created data directory ${APP_ROOT}"
Expand Down
6 changes: 3 additions & 3 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import PackageDescription
let releaseVersion = ProcessInfo.processInfo.environment["RELEASE_VERSION"] ?? "0.0.0"
let gitCommit = ProcessInfo.processInfo.environment["GIT_COMMIT"] ?? "unspecified"
let builderShimVersion = "0.6.1"
let scVersion = "0.9.1"
let scVersion = "0.10.0"

let package = Package(
name: "container",
Expand Down
20 changes: 8 additions & 12 deletions Sources/ContainerClient/FileDownloader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,24 +49,20 @@ public struct FileDownloader {
}
})

let client = FileDownloader.createClient()
let client = FileDownloader.createClient(url: url)
_ = try await client.execute(request: request, delegate: delegate).get()
try await client.shutdown()
}

private static func createClient() -> HTTPClient {
private static func createClient(url: URL) -> HTTPClient {
var httpConfiguration = HTTPClient.Configuration()
let proxyConfig: HTTPClient.Configuration.Proxy? = {
let proxyEnv = ProcessInfo.processInfo.environment["HTTP_PROXY"]
guard let proxyEnv else {
return nil
if let host = url.host {
let proxyURL = ProxyUtils.proxyFromEnvironment(scheme: url.scheme, host: host)
if let proxyURL, let proxyHost = proxyURL.host {
httpConfiguration.proxy = HTTPClient.Configuration.Proxy.server(host: proxyHost, port: proxyURL.port ?? 8080)
}
guard let url = URL(string: proxyEnv), let host = url.host(), let port = url.port else {
return nil
}
return .server(host: host, port: port)
}()
httpConfiguration.proxy = proxyConfig
}

return HTTPClient(eventLoopGroupProvider: .singleton, configuration: httpConfiguration)
}
}
5 changes: 2 additions & 3 deletions Sources/ContainerCommands/System/SystemStart.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,8 @@ extension Application {
args.append("start")
let apiServerDataUrl = appRoot.appending(path: "apiserver")
try! FileManager.default.createDirectory(at: apiServerDataUrl, withIntermediateDirectories: true)
var env = ProcessInfo.processInfo.environment.filter { key, _ in
key.hasPrefix("CONTAINER_")
}

var env = PluginLoader.filterEnvironment()
env[ApplicationRoot.environmentName] = appRoot.path(percentEncoded: false)
env[InstallRoot.environmentName] = installRoot.path(percentEncoded: false)

Expand Down
20 changes: 17 additions & 3 deletions Sources/ContainerPlugin/PluginLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,12 @@ extension PluginLoader {
}

extension PluginLoader {
public static let proxyKeys = Set([
"http_proxy", "HTTP_PROXY",
"https_proxy", "HTTPS_PROXY",
"no_proxy", "NO_PROXY",
])

public func registerWithLaunchd(
plugin: Plugin,
pluginStateRoot: URL? = nil,
Expand All @@ -212,9 +218,8 @@ extension PluginLoader {
log?.info("Registering plugin", metadata: ["id": "\(id)"])
let rootURL = pluginStateRoot ?? self.pluginResourceRoot.appending(path: plugin.name)
try FileManager.default.createDirectory(at: rootURL, withIntermediateDirectories: true)
var env = ProcessInfo.processInfo.environment.filter { key, _ in
key.hasPrefix("CONTAINER_")
}

var env = Self.filterEnvironment()
env[ApplicationRoot.environmentName] = appRoot.path(percentEncoded: false)
env[InstallRoot.environmentName] = installRoot.path(percentEncoded: false)

Expand Down Expand Up @@ -244,4 +249,13 @@ extension PluginLoader {
log?.info("Deregistering plugin", metadata: ["id": "\(plugin.getLaunchdLabel())"])
try ServiceManager.deregister(fullServiceLabel: label)
}

public static func filterEnvironment(
env: [String: String] = ProcessInfo.processInfo.environment,
additionalAllowKeys: Set<String> = Self.proxyKeys
) -> [String: String] {
env.filter { key, _ in
key.hasPrefix("CONTAINER_") || additionalAllowKeys.contains(key)
}
}
}
88 changes: 88 additions & 0 deletions Tests/ContainerPluginTests/PluginLoaderTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,94 @@ struct PluginLoaderTest {
#expect(loader.findPlugin(name: "throw") == nil)
}

@Test
func testFilterEnvironmentWithContainerPrefix() async throws {
let env = [
"CONTAINER_FOO": "bar",
"CONTAINER_BAZ": "qux",
"OTHER_VAR": "value",
]
let filtered = PluginLoader.filterEnvironment(env: env, additionalAllowKeys: [])

#expect(filtered == ["CONTAINER_FOO": "bar", "CONTAINER_BAZ": "qux"])
}

@Test
func testFilterEnvironmentWithProxyKeys() async throws {
let env = [
"http_proxy": "http://proxy:8080",
"HTTP_PROXY": "http://proxy:8080",
"https_proxy": "https://proxy:8443",
"HTTPS_PROXY": "https://proxy:8443",
"no_proxy": "localhost,127.0.0.1",
"NO_PROXY": "localhost,127.0.0.1",
"OTHER_VAR": "value",
]
let filtered = PluginLoader.filterEnvironment(env: env)

#expect(
filtered == [
"http_proxy": "http://proxy:8080",
"HTTP_PROXY": "http://proxy:8080",
"https_proxy": "https://proxy:8443",
"HTTPS_PROXY": "https://proxy:8443",
"no_proxy": "localhost,127.0.0.1",
"NO_PROXY": "localhost,127.0.0.1",
])
}

@Test
func testFilterEnvironmentWithBothContainerAndProxy() async throws {
let env = [
"CONTAINER_FOO": "bar",
"http_proxy": "http://proxy:8080",
"OTHER_VAR": "value",
"ANOTHER_VAR": "value2",
]
let filtered = PluginLoader.filterEnvironment(env: env)

#expect(
filtered == [
"CONTAINER_FOO": "bar",
"http_proxy": "http://proxy:8080",
])
}

@Test
func testFilterEnvironmentWithCustomAllowKeys() async throws {
let env = [
"CONTAINER_FOO": "bar",
"CUSTOM_KEY": "custom_value",
"OTHER_VAR": "value",
]
let filtered = PluginLoader.filterEnvironment(env: env, additionalAllowKeys: ["CUSTOM_KEY"])

#expect(
filtered == [
"CONTAINER_FOO": "bar",
"CUSTOM_KEY": "custom_value",
])
}

@Test
func testFilterEnvironmentEmpty() async throws {
let filtered = PluginLoader.filterEnvironment(env: [:])

#expect(filtered.isEmpty)
}

@Test
func testFilterEnvironmentNoMatches() async throws {
let env = [
"PATH": "/usr/bin",
"HOME": "/Users/test",
"USER": "testuser",
]
let filtered = PluginLoader.filterEnvironment(env: env, additionalAllowKeys: [])

#expect(filtered.isEmpty)
}

private func setupMock(tempURL: URL) throws -> MockPluginFactory {
let cliConfig = PluginConfig(abstract: "cli", author: "CLI", servicesConfig: nil)
let cliPlugin: Plugin = Plugin(binaryURL: URL(filePath: "/bin/cli"), config: cliConfig)
Expand Down