From 1cdc1ea09f62eaa074092755f417491d4f071d95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20St=C3=B8vring?= Date: Wed, 10 May 2023 13:29:10 +0200 Subject: [PATCH] Introduces VirtualMachineFleet and VirtualMachineFleetLive --- Packages/VirtualMachine/Package.swift | 5 +- .../VirtualMachineFleet.swift | 84 +------------------ .../VirtualMachineFleetLive.swift | 84 +++++++++++++++++++ Tartelet/Sources/CompositionRoot.swift | 3 +- project.yml | 2 + 5 files changed, 96 insertions(+), 82 deletions(-) create mode 100644 Packages/VirtualMachine/Sources/VirtualMachineFleetLive/VirtualMachineFleetLive.swift diff --git a/Packages/VirtualMachine/Package.swift b/Packages/VirtualMachine/Package.swift index d584f67..0701248 100644 --- a/Packages/VirtualMachine/Package.swift +++ b/Packages/VirtualMachine/Package.swift @@ -11,6 +11,7 @@ let package = Package( .library(name: "VirtualMachineEditorService", targets: ["VirtualMachineEditorService"]), .library(name: "VirtualMachineFactory", targets: ["VirtualMachineFactory"]), .library(name: "VirtualMachineFleet", targets: ["VirtualMachineFleet"]), + .library(name: "VirtualMachineFleetLive", targets: ["VirtualMachineFleetLive"]), .library(name: "VirtualMachineResourcesCopier", targets: ["VirtualMachineResourcesCopier"]), .library(name: "VirtualMachineResourcesService", targets: ["VirtualMachineResourcesService"]), .library(name: "VirtualMachineResourcesServiceEditor", targets: ["VirtualMachineResourcesServiceEditor"]), @@ -33,9 +34,11 @@ let package = Package( .target(name: "VirtualMachineFactory", dependencies: [ "VirtualMachine" ]), - .target(name: "VirtualMachineFleet", dependencies: [ + .target(name: "VirtualMachineFleet"), + .target(name: "VirtualMachineFleetLive", dependencies: [ "VirtualMachine", "VirtualMachineFactory", + "VirtualMachineFleet", .product(name: "LogConsumer", package: "Logging") ]), .target(name: "VirtualMachineResourcesCopier", dependencies: [ diff --git a/Packages/VirtualMachine/Sources/VirtualMachineFleet/VirtualMachineFleet.swift b/Packages/VirtualMachine/Sources/VirtualMachineFleet/VirtualMachineFleet.swift index 46d3fca..840a451 100644 --- a/Packages/VirtualMachine/Sources/VirtualMachineFleet/VirtualMachineFleet.swift +++ b/Packages/VirtualMachine/Sources/VirtualMachineFleet/VirtualMachineFleet.swift @@ -1,83 +1,7 @@ import Combine -import LogConsumer -import VirtualMachine -import VirtualMachineFactory -public final class VirtualMachineFleet { - public let isStarted: AnyPublisher - - private let logger: LogConsumer - private let virtualMachineFactory: VirtualMachineFactory - private var activeTasks: [Task<(), Error>] = [] - private let _isStarted = CurrentValueSubject(false) - - public init(logger: LogConsumer, virtualMachineFactory: VirtualMachineFactory) { - self.logger = logger - self.virtualMachineFactory = virtualMachineFactory - self.isStarted = _isStarted.eraseToAnyPublisher() - } - - public func start(numberOfMachines: Int) throws { - guard !_isStarted.value else { - return - } - _isStarted.value = true - let preferredName = try virtualMachineFactory.preferredVirtualMachineName - for index in 0 ..< numberOfMachines { - let name = preferredName + "-\(index + 1)" - startSequentiallyRunningVirtualMachines(named: name) - } - } - - public func stop() { - guard _isStarted.value else { - return - } - _isStarted.value = false - for task in activeTasks { - task.cancel() - } - activeTasks = [] - } -} - -private extension VirtualMachineFleet { - private func startSequentiallyRunningVirtualMachines(named name: String) { - let task = Task { - while !Task.isCancelled { - try await runVirtualMachine(named: name) - } - } - activeTasks.append(task) - } - - private func runVirtualMachine(named name: String) async throws { - let virtualMachine: VirtualMachine - do { - virtualMachine = try await virtualMachineFactory.makeVirtualMachine(named: name) - } catch { - logger.error("Could not create virtual machine named \"%@\": %@", name, error.localizedDescription) - throw error - } - try await withTaskCancellationHandler { - logger.info("Start virtual machine named \"%@\"", name) - do { - try await virtualMachine.start() - logger.info("Did stop virtual machine named \"%@\"", name) - } catch { - logger.info("Could not start virtual machine named \"%@\": %@", name, error.localizedDescription) - throw error - } - } onCancel: { - Task.detached(priority: .high) { - self.logger.info("Stop virtual machine named \"%@\"", name) - do { - try await virtualMachine.stop() - } catch { - self.logger.info("Could not stop virtual machine named \"%@\": %@", name, error.localizedDescription) - throw error - } - } - } - } +public protocol VirtualMachineFleet { + var isStarted: AnyPublisher { get } + func start(numberOfMachines: Int) throws + func stop() } diff --git a/Packages/VirtualMachine/Sources/VirtualMachineFleetLive/VirtualMachineFleetLive.swift b/Packages/VirtualMachine/Sources/VirtualMachineFleetLive/VirtualMachineFleetLive.swift new file mode 100644 index 0000000..56b26df --- /dev/null +++ b/Packages/VirtualMachine/Sources/VirtualMachineFleetLive/VirtualMachineFleetLive.swift @@ -0,0 +1,84 @@ +import Combine +import LogConsumer +import VirtualMachine +import VirtualMachineFactory +import VirtualMachineFleet + +public final class VirtualMachineFleetLive: VirtualMachineFleet { + public let isStarted: AnyPublisher + + private let logger: LogConsumer + private let virtualMachineFactory: VirtualMachineFactory + private var activeTasks: [Task<(), Error>] = [] + private let _isStarted = CurrentValueSubject(false) + + public init(logger: LogConsumer, virtualMachineFactory: VirtualMachineFactory) { + self.logger = logger + self.virtualMachineFactory = virtualMachineFactory + self.isStarted = _isStarted.eraseToAnyPublisher() + } + + public func start(numberOfMachines: Int) throws { + guard !_isStarted.value else { + return + } + _isStarted.value = true + let preferredName = try virtualMachineFactory.preferredVirtualMachineName + for index in 0 ..< numberOfMachines { + let name = preferredName + "-\(index + 1)" + startSequentiallyRunningVirtualMachines(named: name) + } + } + + public func stop() { + guard _isStarted.value else { + return + } + _isStarted.value = false + for task in activeTasks { + task.cancel() + } + activeTasks = [] + } +} + +private extension VirtualMachineFleetLive { + private func startSequentiallyRunningVirtualMachines(named name: String) { + let task = Task { + while !Task.isCancelled { + try await runVirtualMachine(named: name) + } + } + activeTasks.append(task) + } + + private func runVirtualMachine(named name: String) async throws { + let virtualMachine: VirtualMachine + do { + virtualMachine = try await virtualMachineFactory.makeVirtualMachine(named: name) + } catch { + logger.error("Could not create virtual machine named \"%@\": %@", name, error.localizedDescription) + throw error + } + try await withTaskCancellationHandler { + logger.info("Start virtual machine named \"%@\"", name) + do { + try await virtualMachine.start() + logger.info("Did stop virtual machine named \"%@\"", name) + } catch { + logger.info("Could not start virtual machine named \"%@\": %@", name, error.localizedDescription) + throw error + } + } onCancel: { + Task.detached(priority: .high) { + self.logger.info("Stop virtual machine named \"%@\"", name) + do { + try await virtualMachine.stop() + } catch { + self.logger.info("Could not stop virtual machine named \"%@\": %@", name, error.localizedDescription) + throw error + } + } + } + } +} diff --git a/Tartelet/Sources/CompositionRoot.swift b/Tartelet/Sources/CompositionRoot.swift index 2258bb5..cda536a 100644 --- a/Tartelet/Sources/CompositionRoot.swift +++ b/Tartelet/Sources/CompositionRoot.swift @@ -26,6 +26,7 @@ import TartVirtualMachineSourceNameRepository import VirtualMachineEditorService import VirtualMachineFactory import VirtualMachineFleet +import VirtualMachineFleetLive import VirtualMachineResourcesCopier import VirtualMachineResourcesService import VirtualMachineResourcesServiceEditor @@ -34,7 +35,7 @@ import VirtualMachineSourceNameRepository enum CompositionRoot { static let dock = Dock(showAppInDock: showAppInDockPublisher.rawValue) - static let fleet = VirtualMachineFleet( + static let fleet: VirtualMachineFleet = VirtualMachineFleetLive( logger: logger(withCategory: .virtualMachine), virtualMachineFactory: fleetVirtualMachineFactory ) diff --git a/project.yml b/project.yml index d425151..1f99a15 100644 --- a/project.yml +++ b/project.yml @@ -76,6 +76,8 @@ targets: product: VirtualMachineFactory - package: VirtualMachine product: VirtualMachineFleet + - package: VirtualMachine + product: VirtualMachineFleetLive - package: VirtualMachine product: VirtualMachineEditorService - package: VirtualMachine