@@ -4,32 +4,26 @@ import SotoS3
44import prlctl
55import libhostmgr
66
7- struct SyncVMImagesCommand : ParsableCommand {
7+ struct SyncVMImagesCommand : ParsableCommand , FollowsCommandPolicies {
88
99 static let configuration = CommandConfiguration (
1010 commandName: " vm_images " ,
1111 abstract: " Sync this machine's VM images with those avaiable remotely "
1212 )
1313
14- func run( ) throws {
15- try SyncVMImagesTask ( ) . run ( )
16- }
17- }
14+ @OptionGroup
15+ var options : SharedSyncOptions
1816
19- struct SyncVMImagesTask {
17+ static let commandIdentifier : String = " sync-vm-images "
2018
21- let storageDirectory = Configuration . shared. vmStorageDirectory
19+ /// A set of command policies that control the circumstances under which this command can be run
20+ static let commandPolicies : [ CommandPolicy ] = [
21+ . serialExecution,
22+ . scheduled( every: 3600 )
23+ ]
2224
23- func run( force: Bool = false , state: State = State . get ( ) ) throws {
24- guard !state. isRunning else {
25- print ( " Already syncing VMs, so we won't try to run again " )
26- return
27- }
28-
29- guard state. shouldRun && force else {
30- print ( " This job is not scheduled to run until \( state. nextRunTime) " )
31- return
32- }
25+ func run( ) throws {
26+ try to ( evaluateCommandPolicies ( ) , unless: options. force)
3327
3428 /// The manifest defines which images should be distributed to VM hosts
3529 let manifest = try VMRemoteImageManager ( ) . getManifest ( )
@@ -53,31 +47,27 @@ struct SyncVMImagesTask {
5347 try VMLocalImageManager ( ) . delete ( images: imagesToDelete)
5448
5549 try imagesToDownload. forEach {
56- try download ( image: $0, state : state )
50+ try download ( image: $0)
5751 }
5852
59- try State . set ( state : State ( lastRunAt : Date ( ) ) )
53+ try recordLastRun ( )
6054 }
6155
62- private func download( image: VMRemoteImageManager . RemoteImage , state: State = State . get ( ) ) throws {
56+ private func download( image: VMRemoteImageManager . RemoteImage ) throws {
57+ let storageDirectory = Configuration . shared. vmStorageDirectory
6358 let destination = storageDirectory. appendingPathComponent ( image. fileName)
6459
6560 logger. info ( " Downloading the VM – this will take a few minutes " )
6661 logger. trace ( " Downloading \( image. basename) to \( destination) " )
6762
63+ let limiter = Limiter ( policy: . throttle, operationsPerSecond: 1 )
64+
6865 try VMRemoteImageManager ( ) . download ( image: image, to: destination) { _, downloaded, total in
69- /// Only update the heartbeat every 5 seconds to avoid thrashing the disk
70- guard abs ( state. heartBeat. timeIntervalSinceNow) > 5 else {
71- return
66+ limiter. perform {
67+ try ? recordHeartbeat ( )
68+ let percent = String ( format: " %.2f " , Double ( downloaded) / Double( total) * 100 )
69+ logger. trace ( " \( percent) % complete " )
7270 }
73-
74- try ? State . set ( state: State (
75- lastRunAt: state. lastRunAt,
76- heartBeat: Date ( )
77- ) )
78-
79- let percent = String ( format: " %.2f " , Double ( downloaded) / Double( total) * 100 )
80- logger. trace ( " \( percent) % complete " )
8171 }
8272
8373 logger. info ( " Download Complete " )
@@ -99,34 +89,4 @@ struct SyncVMImagesTask {
9989 logger. info ( " \t UUID: \t \( vmToImport. uuid) " )
10090
10191 }
102-
103- struct State : Codable {
104- private static let key = " sync-vm-images-state "
105- var lastRunAt : Date = Date . distantPast
106-
107- var shouldRun : Bool {
108- let runInterval = TimeInterval ( Configuration . shared. authorizedKeysSyncInterval)
109- return self . lastRunAt < Date ( ) . addingTimeInterval ( runInterval * - 1 )
110- }
111-
112- var nextRunTime : Date {
113- let runInterval = TimeInterval ( Configuration . shared. authorizedKeysSyncInterval)
114- return self . lastRunAt. addingTimeInterval ( runInterval)
115- }
116-
117- var heartBeat : Date = Date . distantPast
118-
119- // if we haven't hear from a job in 60 seconds, assume it's failed and we should try again
120- var isRunning : Bool {
121- abs ( heartBeat. timeIntervalSinceNow) < 60
122- }
123-
124- static func get( ) -> State {
125- ( try ? StateManager . load ( key: key) ) ?? State ( )
126- }
127-
128- static func set( state: State ) throws {
129- try StateManager . store ( key: key, value: state)
130- }
131- }
13292}
0 commit comments