diff --git a/Package.swift b/Package.swift index e45dd3cb..ceea8db7 100644 --- a/Package.swift +++ b/Package.swift @@ -2262,10 +2262,12 @@ let package = Package( name: "WorkerCapabilities", dependencies: [ .product(name: "AtomicModels", package: "CommandLineToolkit"), + .product(name: "DateProvider", package: "CommandLineToolkit"), "EmceeLogging", .product(name: "FileSystem", package: "CommandLineToolkit"), .product(name: "PathLib", package: "CommandLineToolkit"), .product(name: "PlistLib", package: "CommandLineToolkit"), + .product(name: "ProcessController", package: "CommandLineToolkit"), "QueueModels", .product(name: "Types", package: "CommandLineToolkit"), "WorkerCapabilitiesModels", diff --git a/Sources/EmceeLib/Commands/DistWorkCommand.swift b/Sources/EmceeLib/Commands/DistWorkCommand.swift index e1ed0693..2439c473 100644 --- a/Sources/EmceeLib/Commands/DistWorkCommand.swift +++ b/Sources/EmceeLib/Commands/DistWorkCommand.swift @@ -123,6 +123,7 @@ public final class DistWorkCommand: Command { OperatingSystemCapabilitiesProvider( operatingSystemVersionProvider: ProcessInfo.processInfo ), + WorkerArchitectureCapabilitiesProvider(logger: logger) ] ), for: WorkerCapabilitiesProvider.self diff --git a/Sources/WorkerCapabilities/ConcreteCapabilities/WorkerArchitectureCapabilitiesProvider.swift b/Sources/WorkerCapabilities/ConcreteCapabilities/WorkerArchitectureCapabilitiesProvider.swift new file mode 100644 index 00000000..a1dbf92b --- /dev/null +++ b/Sources/WorkerCapabilities/ConcreteCapabilities/WorkerArchitectureCapabilitiesProvider.swift @@ -0,0 +1,90 @@ +// +// File.swift +// +// +// Created by a.smolianin on 15.07.2022. +// + +import Foundation +import WorkerCapabilitiesModels +import ProcessController +import FileSystem +import DateProvider +import EmceeLogging + +public final class WorkerArchitectureCapabilitiesProvider: WorkerCapabilitiesProvider { + private let logger: ContextualLogger + private let commandExecutor: CommandExecutor + + public init(logger: ContextualLogger, commandExecutor: CommandExecutor) { + self.logger = logger + self.commandExecutor = commandExecutor + } + + convenience public init(logger: ContextualLogger) { + self.init(logger: logger, commandExecutor: DefaultComandExecutor()) + } + + public func workerCapabilities() -> Set { + return Set([WorkerCapability(name: WorkerCapabilityName("emcee.arch"), value: "\(getArch())")]) + } + + private func getArch() -> Arch { + var result = "\(Arch.unknown)" + do { + result = try commandExecutor.executeCommandWithOutput(env: [String: String](), arguments: ["/usr/sbin/sysctl", "-n", "machdep.cpu.brand_string"]) + logger.info("Command \"sysctl -n machdep.cpu.brand_string\" = \(result)") + } catch { + logger.error("Command \"sysctl -n machdep.cpu.brand_string\" could not be completed. Error: \(error.localizedDescription)") + return .unknown + } + switch result { + case let arch where arch.contains(Arch.m1.rawValue): + return .m1 + case let arch where arch.contains(Arch.x86.rawValue): + return .x86 + default: + return .unknown + } + } + + enum Arch: String { + case m1 = "Apple M1" + case x86 = "Intel" + case unknown //for m2 .etc + } +} + +public protocol CommandExecutor { + func executeCommandWithOutput(env: Dictionary, arguments: [String]) throws -> String +} + +extension CommandExecutor { + public func executeCommandWithOutput(env: Dictionary, arguments: [String]) throws -> String { + let dateProvider = SystemDateProvider() + let filePropertiesProvider = FilePropertiesProviderImpl() + let controller = try DefaultProcessController( + dateProvider: dateProvider, + filePropertiesProvider: filePropertiesProvider, + + subprocess: Subprocess( + arguments: arguments, + environment: Environment(env) + ) + ) + + var stdoutData = Data() + controller.onStdout { _, data, _ in stdoutData.append(contentsOf: data) } + + try controller.startAndListenUntilProcessDies() + guard let stdOut = String(data: stdoutData, encoding: .utf8) else { + return "" + } + return stdOut + } +} + +class DefaultComandExecutor: CommandExecutor { } + + + diff --git a/Tests/WorkerCapabilitiesTests/WorkerArchitectureCapabilitiesProviderTests.swift b/Tests/WorkerCapabilitiesTests/WorkerArchitectureCapabilitiesProviderTests.swift new file mode 100644 index 00000000..c5025c41 --- /dev/null +++ b/Tests/WorkerCapabilitiesTests/WorkerArchitectureCapabilitiesProviderTests.swift @@ -0,0 +1,47 @@ +// +// File.swift +// +// +// Created by a.smolianin on 25.08.2022. +// + +import Foundation +import XCTest +import WorkerCapabilities +import WorkerCapabilitiesModels + + +class WorkerArchitectureCapabilitiesProviderTests: XCTestCase { + func test___x86_architecture() throws { + class IntelXeon: CommandExecutor { + func executeCommandWithOutput(env: Dictionary, arguments: [String]) throws -> String { + return "Intel(R) Xeon(R) W-3245 CPU @ 3.20GHz" + } + } + + let archProvider = WorkerArchitectureCapabilitiesProvider(logger: .noOp, commandExecutor: IntelXeon()) + XCTAssertEqual(Set([WorkerCapability(name: WorkerCapabilityName("emcee.arch"), value: "x86")]), archProvider.workerCapabilities()) + } + + func test___m1_architecture() throws { + class M1: CommandExecutor { + func executeCommandWithOutput(env: Dictionary, arguments: [String]) throws -> String { + return "Apple M1" + } + } + + let archProvider = WorkerArchitectureCapabilitiesProvider(logger: .noOp, commandExecutor: M1()) + XCTAssertEqual(Set([WorkerCapability(name: WorkerCapabilityName("emcee.arch"), value: "m1")]), archProvider.workerCapabilities()) + } + + func test___unknown_architecture() throws { + class Unknown: CommandExecutor { + func executeCommandWithOutput(env: Dictionary, arguments: [String]) throws -> String { + return "Something unknown" + } + } + + let archProvider = WorkerArchitectureCapabilitiesProvider(logger: .noOp, commandExecutor: Unknown()) + XCTAssertEqual(Set([WorkerCapability(name: WorkerCapabilityName("emcee.arch"), value: "unknown")]), archProvider.workerCapabilities()) + } +}