Skip to content

Commit

Permalink
Add Process Provisioner and fix retry logic
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcocanc committed Nov 30, 2022
1 parent 3a7517c commit 5ddadb3
Show file tree
Hide file tree
Showing 14 changed files with 125 additions and 54 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,4 @@ fastlane/test_output
iOSInjectionProject/

# Mac System Files
.DS_Store
*.DS_Store
36 changes: 24 additions & 12 deletions Cilicon.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@

/* Begin PBXBuildFile section */
A9492E5E2922376B005616CE /* ImageCopier.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9492E5D2922376B005616CE /* ImageCopier.swift */; };
A9517C492937ACB500785136 /* ProcessProvisioner.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9517C482937ACB500785136 /* ProcessProvisioner.swift */; };
A9517C4B2937AFB900785136 /* ProcessProvisionerConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9517C4A2937AFB900785136 /* ProcessProvisionerConfig.swift */; };
A951BCD9292B966A00FFDACC /* VMConfigHelper+RunConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A951BCD8292B966A00FFDACC /* VMConfigHelper+RunConfig.swift */; };
A9728DD52918F79000342A77 /* CiliconApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9728DD42918F79000342A77 /* CiliconApp.swift */; };
A9728DD72918F79000342A77 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9728DD62918F79000342A77 /* ContentView.swift */; };
A9728DD92918F79100342A77 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A9728DD82918F79100342A77 /* Assets.xcassets */; };
A9728DE42918F7C500342A77 /* VirtualMachineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9728DE32918F7C500342A77 /* VirtualMachineView.swift */; };
A9728DE62918F7CB00342A77 /* VMBundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9728DE52918F7CB00342A77 /* VMBundle.swift */; };
A9728DE82918F7D000342A77 /* VMManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9728DE72918F7D000342A77 /* VMManager.swift */; };
A9728DEA2918F7D500342A77 /* Task+Retrying.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9728DE92918F7D500342A77 /* Task+Retrying.swift */; };
A9728DFC2918F9A000342A77 /* VMConfigurationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9728DF62918F9A000342A77 /* VMConfigurationHelper.swift */; };
A9728DFD2918F9A000342A77 /* GithubConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9728DF72918F9A000342A77 /* GithubConfig.swift */; };
A9728DFD2918F9A000342A77 /* GithubProvisionerConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9728DF72918F9A000342A77 /* GithubProvisionerConfig.swift */; };
A9728DFE2918F9A000342A77 /* HardwareConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9728DF82918F9A000342A77 /* HardwareConfig.swift */; };
A9728DFF2918F9A000342A77 /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9728DF92918F9A000342A77 /* Config.swift */; };
A9728E002918F9A000342A77 /* ProvisionerConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9728DFA2918F9A000342A77 /* ProvisionerConfig.swift */; };
Expand Down Expand Up @@ -46,6 +47,8 @@

/* Begin PBXFileReference section */
A9492E5D2922376B005616CE /* ImageCopier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageCopier.swift; sourceTree = "<group>"; };
A9517C482937ACB500785136 /* ProcessProvisioner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessProvisioner.swift; sourceTree = "<group>"; };
A9517C4A2937AFB900785136 /* ProcessProvisionerConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessProvisionerConfig.swift; sourceTree = "<group>"; };
A951BCD6292B93D000FFDACC /* Virtualization.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Virtualization.framework; path = System/Library/Frameworks/Virtualization.framework; sourceTree = SDKROOT; };
A951BCD8292B966A00FFDACC /* VMConfigHelper+RunConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VMConfigHelper+RunConfig.swift"; sourceTree = "<group>"; };
A951BCDB292BB9B400FFDACC /* Installer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Installer.swift; sourceTree = "<group>"; };
Expand All @@ -57,9 +60,8 @@
A9728DE32918F7C500342A77 /* VirtualMachineView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VirtualMachineView.swift; sourceTree = "<group>"; };
A9728DE52918F7CB00342A77 /* VMBundle.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VMBundle.swift; sourceTree = "<group>"; };
A9728DE72918F7D000342A77 /* VMManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VMManager.swift; sourceTree = "<group>"; };
A9728DE92918F7D500342A77 /* Task+Retrying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Task+Retrying.swift"; sourceTree = "<group>"; };
A9728DF62918F9A000342A77 /* VMConfigurationHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VMConfigurationHelper.swift; sourceTree = "<group>"; };
A9728DF72918F9A000342A77 /* GithubConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GithubConfig.swift; sourceTree = "<group>"; };
A9728DF72918F9A000342A77 /* GithubProvisionerConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GithubProvisionerConfig.swift; sourceTree = "<group>"; };
A9728DF82918F9A000342A77 /* HardwareConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HardwareConfig.swift; sourceTree = "<group>"; };
A9728DF92918F9A000342A77 /* Config.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = "<group>"; };
A9728DFA2918F9A000342A77 /* ProvisionerConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProvisionerConfig.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -103,6 +105,14 @@
/* End PBXFrameworksBuildPhase section */

/* Begin PBXGroup section */
A9517C472937ACA800785136 /* Process */ = {
isa = PBXGroup;
children = (
A9517C482937ACB500785136 /* ProcessProvisioner.swift */,
);
path = Process;
sourceTree = "<group>";
};
A951BCCD292B922D00FFDACC /* Common */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -152,7 +162,6 @@
A9728E022918F9A800342A77 /* Provisioner */,
A9728E0C2918F9CF00342A77 /* AppleEvents */,
A9728DF52918F98500342A77 /* Config */,
A9728DE92918F7D500342A77 /* Task+Retrying.swift */,
A951BCD8292B966A00FFDACC /* VMConfigHelper+RunConfig.swift */,
A9728DD82918F79100342A77 /* Assets.xcassets */,
A9728DDD2918F79100342A77 /* Cilicon.entitlements */,
Expand All @@ -166,7 +175,8 @@
A9728DF92918F9A000342A77 /* Config.swift */,
A9728DFB2918F9A000342A77 /* ConfigManager.swift */,
A9FDAB2229375E8100B8CA1F /* DirectoryMountConfig.swift */,
A9728DF72918F9A000342A77 /* GithubConfig.swift */,
A9728DF72918F9A000342A77 /* GithubProvisionerConfig.swift */,
A9517C4A2937AFB900785136 /* ProcessProvisionerConfig.swift */,
A9728DF82918F9A000342A77 /* HardwareConfig.swift */,
A9728DFA2918F9A000342A77 /* ProvisionerConfig.swift */,
);
Expand All @@ -177,6 +187,7 @@
isa = PBXGroup;
children = (
A9728E042918F9BF00342A77 /* Provisioner.swift */,
A9517C472937ACA800785136 /* Process */,
A9728E032918F9B000342A77 /* Github Actions */,
);
path = Provisioner;
Expand Down Expand Up @@ -326,14 +337,15 @@
A9728DD72918F79000342A77 /* ContentView.swift in Sources */,
A9728DD52918F79000342A77 /* CiliconApp.swift in Sources */,
A9728E0B2918F9C600342A77 /* GHAppAuthHelper.swift in Sources */,
A9728DEA2918F7D500342A77 /* Task+Retrying.swift in Sources */,
A9728DE62918F7CB00342A77 /* VMBundle.swift in Sources */,
A9517C4B2937AFB900785136 /* ProcessProvisionerConfig.swift in Sources */,
A9728E152918FA4700342A77 /* AppleEvents.swift in Sources */,
A9492E5E2922376B005616CE /* ImageCopier.swift in Sources */,
A9728DFC2918F9A000342A77 /* VMConfigurationHelper.swift in Sources */,
A9728DE42918F7C500342A77 /* VirtualMachineView.swift in Sources */,
A9728E0A2918F9C600342A77 /* GithubActionsProvisioner.swift in Sources */,
A9728DFD2918F9A000342A77 /* GithubConfig.swift in Sources */,
A9728DFD2918F9A000342A77 /* GithubProvisionerConfig.swift in Sources */,
A9517C492937ACB500785136 /* ProcessProvisioner.swift in Sources */,
A9728DFF2918F9A000342A77 /* Config.swift in Sources */,
A9728DE82918F7D000342A77 /* VMManager.swift in Sources */,
A9728E052918F9BF00342A77 /* Provisioner.swift in Sources */,
Expand Down Expand Up @@ -499,7 +511,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.0.1;
PRODUCT_BUNDLE_IDENTIFIER = com.traderepublic.cilicon;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down Expand Up @@ -530,7 +542,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.0.1;
PRODUCT_BUNDLE_IDENTIFIER = com.traderepublic.cilicon;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down Expand Up @@ -562,7 +574,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.0.1;
PRODUCT_BUNDLE_IDENTIFIER = "com.traderepublic.cilicon-installer";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down Expand Up @@ -593,7 +605,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 1.0;
MARKETING_VERSION = 1.0.1;
PRODUCT_BUNDLE_IDENTIFIER = "com.traderepublic.cilicon-installer";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down
Binary file removed Cilicon/.DS_Store
Binary file not shown.
4 changes: 4 additions & 0 deletions Cilicon/Config/Config.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ struct Config: Decodable {
/// Start and End of the copying phase are signaled with system sounds.
/// Must be the full path including `/Volumes/`.
let autoTransferImageVolume: String?
/// Delay in seconds before retrying to provision the image a failed cycle
let retryDelay: Int

enum CodingKeys: CodingKey {
case provisioner
Expand All @@ -30,6 +32,7 @@ struct Config: Decodable {
case runnerName
case editorMode
case autoTransferImageVolume
case retryDelay
}

init(from decoder: Decoder) throws {
Expand All @@ -42,5 +45,6 @@ struct Config: Decodable {
self.runnerName = try container.decodeIfPresent(String.self, forKey: .runnerName)
self.editorMode = try container.decodeIfPresent(Bool.self, forKey: .editorMode) ?? false
self.autoTransferImageVolume = try container.decodeIfPresent(String.self, forKey: .autoTransferImageVolume)
self.retryDelay = try container.decodeIfPresent(Int.self, forKey: .retryDelay) ?? 5
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

struct GithubConfig: Decodable {
struct GithubProvisionerConfig: Decodable {
/// The Github API URL. Will be `https://api.github.com/` in most cases
let apiURL: URL?
/// The App Id of the installed application with Organization "Self-hosted runners" Read & Write access.
Expand Down
19 changes: 19 additions & 0 deletions Cilicon/Config/ProcessProvisionerConfig.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import Foundation

struct ProcessProvisionerConfig: Decodable {
/// The executable to be run
let executablePath: String
/// The arguments to be passed to the executable. These will be appended to the bundle path and action arguments.
let arguments: [String]

enum CodingKeys: CodingKey {
case executablePath
case arguments
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.executablePath = try container.decode(String.self, forKey: .executablePath)
self.arguments = try container.decodeIfPresent([String].self, forKey: .arguments) ?? []
}
}
9 changes: 7 additions & 2 deletions Cilicon/Config/ProvisionerConfig.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import Foundation

enum ProvisionerConfig: Decodable {
case github(GithubConfig)
case github(GithubProvisionerConfig)
case process(ProcessProvisionerConfig)
case none

enum CodingKeys: CodingKey {
Expand All @@ -14,15 +15,19 @@ enum ProvisionerConfig: Decodable {
let type = try container.decode(ProvisionerType.self, forKey: .type)
switch type {
case .github:
let config = try container.decode(GithubConfig.self, forKey: .config)
let config = try container.decode(GithubProvisionerConfig.self, forKey: .config)
self = .github(config)
case .process:
let config = try container.decode(ProcessProvisionerConfig.self, forKey: .config)
self = .process(config)
case .none:
self = .none
}
}

enum ProvisionerType: String, Decodable {
case github
case process
case none
}
}
12 changes: 7 additions & 5 deletions Cilicon/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ struct ContentView: View {
switch vmManager.vmState {
case .running(let vm):
VirtualMachineView(virtualMachine: vm).onAppear {
Task.retrying(maxRetryCount: 5, retryDelay: 5) {
Task.detached {
try await vmManager.start(vm: vm)
}
}
Expand All @@ -39,10 +39,12 @@ struct ContentView: View {
}
}
.navigationTitle(title)
.onAppear {
Task.retrying(maxRetryCount: 5, retryDelay: 5) {
await vmManager.onAppear()
}
.onAppear(perform: onAppear)
}

func onAppear() {
Task.detached {
try await vmManager.setupAndRunVM()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import Foundation

class GithubActionsProvisioner: Provisioner {
let config: Config
let ghConfig: GithubConfig
let ghConfig: GithubProvisionerConfig
let service: GithubService
let fileManager: FileManager

init(config: Config, ghConfig: GithubConfig, fileManager: FileManager = .default) {
init(config: Config, ghConfig: GithubProvisionerConfig, fileManager: FileManager = .default) {
self.config = config
self.ghConfig = ghConfig
self.service = GithubService(config: ghConfig)
Expand Down
4 changes: 2 additions & 2 deletions Cilicon/Provisioner/Github Actions/GithubService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ class GithubService {
return jsonDecoder
}()

let config: GithubConfig
let config: GithubProvisionerConfig
let baseURL: URL

init(config: GithubConfig) {
init(config: GithubProvisionerConfig) {
self.config = config
self.baseURL = config.apiURL ?? URL(string: "https://api.github.com/")!
let config = URLSessionConfiguration.default
Expand Down
50 changes: 50 additions & 0 deletions Cilicon/Provisioner/Process/ProcessProvisioner.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// TaskProvisioner.swift
// Cilicon
//
// Created by Marco Cancellieri on 30.11.22.
//

import Foundation
/// The Process Provisioner will call an executable of your choice. With the bundle path as well as the action ("provision" or "deprovision") as arguments.
class ProcessProvisioner: Provisioner {
let path: String
let arguments: [String]

init(path: String, arguments: [String]) {
self.path = path
self.arguments = arguments
}

func provision(bundle: VMBundle) async throws {
try runProcess(bundle: bundle, action: "provision")
}

func deprovision(bundle: VMBundle) async throws {
try runProcess(bundle: bundle, action: "deprovision")
}

func runProcess(bundle: VMBundle, action: String) throws {
let executableURL = URL(filePath: (path as NSString).standardizingPath)
let args = [bundle.url.relativePath, action] + arguments
let proc = try Process.run(executableURL, arguments: args)
proc.waitUntilExit()
let status = proc.terminationStatus
guard status == 0 else {
throw ProcessProvisionerError.nonZeroStatus(status: status,
executable: executableURL,
arguments: args)
}
}
}

enum ProcessProvisionerError: LocalizedError {
case nonZeroStatus(status: Int32, executable: URL, arguments: [String])

var errorDescription: String? {
switch self {
case .nonZeroStatus(let status, let executable, let arguments):
return "Expected 0 Termination status, got \(status) instead when running \(executable.relativePath) with arguments: \(arguments.joined(separator: " "))"
}
}
}
27 changes: 0 additions & 27 deletions Cilicon/Task+Retrying.swift

This file was deleted.

6 changes: 5 additions & 1 deletion Cilicon/VMManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class VMManager: NSObject, ObservableObject {
switch config.provisioner {
case .github(let githubConfig):
self.provisioner = GithubActionsProvisioner(config: config, ghConfig: githubConfig)
case .process(let processConfig):
self.provisioner = ProcessProvisioner(path: processConfig.executablePath, arguments: processConfig.arguments)
case .none:
self.provisioner = nil
}
Expand All @@ -31,13 +33,15 @@ class VMManager: NSObject, ObservableObject {
}

@MainActor
func onAppear() async {
func setupAndRunVM() async throws {
do {
vmState = .initializing
try await setupAndRunVirtualMachine()
}
catch {
vmState = .failed(error.localizedDescription)
try await Task.sleep(for: .seconds(config.retryDelay))
try await setupAndRunVM()
}
}

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ Cilicon creates a clone of your Virtual Machine bundle for each run. [APFS clone

Depending on the provisioner you choose, Cilicon places files required by your Guest OS in your bundle's `Resources` folder.

The [Github Actions provisioner](/Cilicon/Provisioner/Github%20Actions/GithubActionsProvisioner.swift) provisions the image with the runner download URL, a registration token, the runner name and runner labels.
The [Github Actions Provisioner](/Cilicon/Provisioner/Github%20Actions/GithubActionsProvisioner.swift) provisions the image with the runner download URL, a registration token, the runner name and runner labels.

The Process Provisioner runs an executable of your choice when provisioning and deprovisioning a bundle. It passes the bundle path, the action as well as any extra arguments of your choice to the executable.

You may also opt out of using a provisioner by setting the provisioner type to `none`. This may work fine with services like Buildkite which use non-expiring registration tokens.

Expand Down

0 comments on commit 5ddadb3

Please sign in to comment.