Skip to content

Commit 314c1eb

Browse files
authored
Merge pull request #51 from Automattic/update/vm-commands
Update Commands to use libhostmgr
2 parents 5097ff4 + 745b684 commit 314c1eb

File tree

14 files changed

+164
-271
lines changed

14 files changed

+164
-271
lines changed

Package.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ let package = Package(
2727
name: "hostmgr",
2828
dependencies: [
2929
.product(name: "ArgumentParser", package: "swift-argument-parser"),
30-
.product(name: "prlctl", package: "prlctl"),
3130
.product(name: "Logging", package: "swift-log"),
3231
.product(name: "kcpassword", package: "kcpassword-swift"),
3332
.target(name: "libhostmgr"),

Sources/hostmgr/VMCommand.swift

100644100755
Lines changed: 4 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,89 +1,18 @@
11
import Foundation
22
import ArgumentParser
3-
import prlctl
43

54
struct VMCommand: AsyncParsableCommand {
65

76
static let configuration = CommandConfiguration(
87
commandName: "vm",
98
abstract: "Allows working with VMs",
109
subcommands: [
10+
VMCleanCommand.self,
11+
VMExistsCommand.self,
12+
VMFetchCommand.self,
1113
VMListCommand.self,
1214
VMStartCommand.self,
13-
VMStopCommand.self,
14-
VMDetailsCommand.self,
15-
VMCleanCommand.self
15+
VMStopCommand.self
1616
]
1717
)
1818
}
19-
20-
// Allow passing VM objects directly
21-
extension VM: ExpressibleByArgument {
22-
23-
public init?(argument: String) {
24-
guard let foundVM = try? Parallels().lookupVM(named: argument) else {
25-
return nil
26-
}
27-
28-
self = foundVM
29-
}
30-
31-
public static var allValueStrings: [String] {
32-
guard let vms = try? Parallels().lookupAllVMs() else {
33-
return []
34-
}
35-
36-
let names = vms.map { $0.name }
37-
let uuids = vms.map { $0.uuid }
38-
39-
return names + uuids
40-
}
41-
}
42-
43-
extension StoppedVM: ExpressibleByArgument {
44-
public init?(argument: String) {
45-
guard let foundVM = try? Parallels()
46-
.lookupStoppedVMs()
47-
.first(where: { $0.name == argument || $0.uuid == argument })
48-
else {
49-
return nil
50-
}
51-
52-
self = foundVM
53-
}
54-
55-
public static var allValueStrings: [String] {
56-
guard let vms = try? Parallels().lookupStoppedVMs() else {
57-
return []
58-
}
59-
60-
let names = vms.map { $0.name }
61-
let uuids = vms.map { $0.uuid }
62-
63-
return names + uuids
64-
}
65-
}
66-
67-
extension RunningVM: ExpressibleByArgument {
68-
public init?(argument: String) {
69-
guard let foundVM = try? Parallels()
70-
.lookupRunningVMs()
71-
.first(where: { $0.name == argument || $0.uuid == argument })
72-
else {
73-
return nil
74-
}
75-
76-
self = foundVM
77-
}
78-
79-
public static var allValueStrings: [String] {
80-
guard let vms = try? Parallels().lookupRunningVMs() else {
81-
return []
82-
}
83-
84-
let names = vms.map { $0.name }
85-
let uuids = vms.map { $0.uuid }
86-
87-
return names + uuids
88-
}
89-
}

Sources/hostmgr/commands/benchmark/DiskBenchmark.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import Foundation
22
import ArgumentParser
3-
import prlctl
43
import Logging
54

65
private let startDate = Date()
Lines changed: 8 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import Foundation
22
import ArgumentParser
3-
import prlctl
43
import Logging
54
import libhostmgr
65

@@ -13,30 +12,19 @@ struct NetworkBenchmark: AsyncParsableCommand {
1312
abstract: "Test Network Speed"
1413
)
1514

16-
private static let limiter = Limiter(policy: .throttle, operationsPerSecond: 1)
17-
1815
func run() async throws {
1916
let remoteImages = try await RemoteVMRepository().listImages(sortedBy: .size)
2017

21-
guard let file = remoteImages.last else {
22-
throw CleanExit.message("Unable to find a remote image to use as a network benchmark")
18+
guard !remoteImages.isEmpty else {
19+
Console.error("Unable to find a remote image to use as a network benchmark")
20+
throw ExitCode(rawValue: -1)
2321
}
2422

25-
let manager = S3Manager(
26-
bucket: Configuration.shared.vmImagesBucket,
27-
region: Configuration.shared.vmImagesRegion
28-
)
29-
30-
let progressBar = Console.startFileDownload(file.imageObject)
23+
Console.heading("Starting Benchmark")
3124

32-
try await manager.download(
33-
object: file.imageObject,
34-
to: FileManager.default.temporaryFilePath(),
35-
progressCallback: progressBar.update
36-
)
37-
}
38-
39-
private func imageSizeSort(_ lhs: RemoteVMImage, _ rhs: RemoteVMImage) -> Bool {
40-
lhs.imageObject.size < rhs.imageObject.size
25+
for remoteImage in remoteImages {
26+
let path = try await libhostmgr.downloadRemoteImage(remoteImage)
27+
try FileManager.default.removeItem(at: path)
28+
}
4129
}
4230
}

Sources/hostmgr/commands/generate/GenerateBuildkiteJobScript.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import Foundation
22
import ArgumentParser
3-
import prlctl
43
import libhostmgr
54

65
struct GenerateBuildkiteJobScript: ParsableCommand {

Sources/hostmgr/commands/sync/SyncAuthorizedKeysCommand.swift

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,15 @@ import libhostmgr
44

55
struct SyncAuthorizedKeysCommand: AsyncParsableCommand, FollowsCommandPolicies {
66

7+
enum Constants {
8+
static let s3Key = "authorized_keys"
9+
}
10+
711
static let configuration = CommandConfiguration(
812
commandName: Configuration.SchedulableSyncCommand.authorizedKeys.rawValue,
913
abstract: "Set this machine's authorized_keys file"
1014
)
1115

12-
@Option(
13-
name: .shortAndLong,
14-
help: "The S3 bucket containing the `authorized_keys` file"
15-
)
16-
var bucket: String = Configuration.shared.authorizedKeysBucket
17-
18-
@Option(
19-
name: .shortAndLong,
20-
help: "The S3 region for the bucket"
21-
)
22-
var region: String = Configuration.shared.authorizedKeysRegion
23-
24-
@Option(
25-
name: .shortAndLong,
26-
help: "The S3 path to the authorized_keys file"
27-
)
28-
var key: String = "authorized_keys"
29-
30-
@Option(
31-
name: .shortAndLong,
32-
help: "The path to your authorized_keys file on disk (defaults to ~/.ssh/authorized_keys)"
33-
)
34-
var destination: String = Paths.authorizedKeysFilePath.path
35-
3616
@OptionGroup
3717
var options: SharedSyncOptions
3818

@@ -44,25 +24,36 @@ struct SyncAuthorizedKeysCommand: AsyncParsableCommand, FollowsCommandPolicies {
4424
]
4525

4626
func run() async throws {
27+
let destination = Paths.authorizedKeysFilePath
28+
4729
try to(evaluateCommandPolicies(), unless: options.force)
48-
logger.debug("Job schedule allows for running")
4930

50-
logger.info("Downloading file from s3://\(bucket)/\(key) in \(region) to \(destination)")
31+
Console.heading("Syncing Authorized Keys")
5132

52-
let s3Manager = S3Manager(bucket: self.bucket, region: self.region)
33+
let s3Manager = S3Manager(
34+
bucket: Configuration.shared.authorizedKeysBucket,
35+
region: Configuration.shared.authorizedKeysRegion
36+
)
5337

54-
guard let object = try await s3Manager.lookupObject(atPath: key) else {
55-
logger.error("Unable to locate authorized_keys file – exiting")
38+
guard let object = try await s3Manager.lookupObject(atPath: Constants.s3Key) else {
39+
Console.error("Unable to locate authorized_keys file – exiting")
5640
throw ExitCode(rawValue: 1)
5741
}
5842

59-
let url = URL(fileURLWithPath: self.destination)
60-
try await s3Manager.download(object: object, to: url, progressCallback: nil)
43+
let progressBar = Console.startFileDownload(object)
44+
45+
try await s3Manager.download(
46+
object: object,
47+
to: destination,
48+
progressCallback: progressBar.update
49+
)
6150

6251
/// Fix the permissions on the file, if needed
52+
Console.info("Setting file permissions on \(destination)")
6353
try FileManager.default.setAttributes([
6454
.posixPermissions: 0o600
65-
], ofItemAtPath: self.destination)
55+
], ofItemAt: destination)
56+
Console.success("Authorized Key Sync Complete")
6657

6758
try recordLastRun()
6859
}
Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
11
import Foundation
22
import ArgumentParser
3-
import prlctl
3+
import libhostmgr
44

5-
struct VMCleanCommand: ParsableCommand {
5+
struct VMCleanCommand: AsyncParsableCommand {
66

77
static let configuration = CommandConfiguration(
88
commandName: "clean",
9-
abstract: "Cleans up a VM prior to it being reused"
9+
abstract: "Clean up the VM environment prior to running another job"
1010
)
1111

12-
@Option(
13-
name: .shortAndLong,
14-
help: "The VM to clean"
15-
)
16-
var virtualMachine: StoppedVM
12+
func run() async throws {
13+
try libhostmgr.resetVMStorage()
1714

18-
func run() throws {
19-
try virtualMachine.clean()
15+
// Clean up no-longer-needed local images
16+
let deleteList = try await libhostmgr.listLocalImagesToDelete()
17+
try libhostmgr.deleteLocalImages(list: deleteList)
2018
}
2119
}

Sources/hostmgr/commands/vm/VMDetails.swift

Lines changed: 0 additions & 27 deletions
This file was deleted.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import Foundation
2+
import ArgumentParser
3+
import libhostmgr
4+
5+
struct VMExistsCommand: ParsableCommand {
6+
static let configuration = CommandConfiguration(
7+
commandName: "exists",
8+
abstract: "Exits with code 0 if the named VM exists. Otherwise exits with code 1"
9+
)
10+
11+
@Argument(help: "The exact name of the VM")
12+
var name: String
13+
14+
func run() throws {
15+
guard let localVM = try LocalVMRepository().lookupVM(withName: self.name) else {
16+
Console.crash(message: "There is no local VM named \(self.name)", reason: .fileNotFound)
17+
}
18+
19+
Console.success("VM \(localVM.basename) exists")
20+
}
21+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import ArgumentParser
2+
import libhostmgr
3+
4+
struct VMFetchCommand: AsyncParsableCommand {
5+
6+
static let configuration = CommandConfiguration(
7+
commandName: "fetch",
8+
abstract: "Download a given image if it's not already present"
9+
)
10+
11+
@Argument(
12+
help: "The name of the image you would like to download"
13+
)
14+
var name: String
15+
16+
func run() async throws {
17+
18+
if let localVM = try LocalVMRepository().lookupVM(withName: name) {
19+
if localVM.state == .packaged {
20+
try await libhostmgr.unpackVM(name: localVM.basename)
21+
return
22+
} else {
23+
Console.exit(
24+
message: "VM is present locally",
25+
style: .success
26+
)
27+
}
28+
29+
}
30+
31+
try await libhostmgr.fetchRemoteImage(name: self.name)
32+
}
33+
}

0 commit comments

Comments
 (0)