191 lines
5.0 KiB
Swift
191 lines
5.0 KiB
Swift
import ArgumentParser
|
|
import CliClient
|
|
import Dependencies
|
|
import Foundation
|
|
import Logging
|
|
import Rainbow
|
|
import ShellClient
|
|
|
|
extension CommandConfiguration {
|
|
static func playbookCommandConfiguration(
|
|
commandName: String,
|
|
abstract: String,
|
|
examples: (label: String, example: String)...
|
|
) -> Self {
|
|
guard examples.count > 0 else {
|
|
fatalError("Did not supply any examples.")
|
|
}
|
|
return Self(
|
|
commandName: commandName,
|
|
abstract: "\(abstract.blue)",
|
|
discussion: """
|
|
\("NOTE:".yellow) Most options are not required if you have a configuration file setup.
|
|
|
|
\("Examples:".yellow)
|
|
|
|
\(examples.map { "\($0.label.green.italic)\n $ hpa \($0.example)" }.joined(separator: "\n"))
|
|
|
|
\("Passing extra args to the playbook.".green.italic)
|
|
$ hpa \(examples[0].example) -- --vault-id "myId@$SCRIPTS/vault-gopass-client"
|
|
|
|
\("See Also:".yellow) \("Ansible playbook options.".italic)
|
|
|
|
$ ansible-playbook --help
|
|
|
|
\("IMPORTANT NOTE:".red) Any extra arguments to pass to the playbook invocation have to
|
|
be at the end with `--` before any arguments otherwise there will
|
|
be an "Unkown option" error. See examples above.
|
|
"""
|
|
)
|
|
}
|
|
}
|
|
|
|
extension BasicGlobalOptions {
|
|
|
|
var shellOrDefault: ShellCommand.Shell {
|
|
guard let shell else { return .zsh(useDashC: true) }
|
|
return .custom(path: shell, useDashC: true)
|
|
}
|
|
}
|
|
|
|
extension GlobalOptions {
|
|
|
|
var shellOrDefault: ShellCommand.Shell { basic.shellOrDefault }
|
|
}
|
|
|
|
func ensureString(
|
|
globals: GlobalOptions,
|
|
configuration: Configuration,
|
|
globalsKeyPath: KeyPath<GlobalOptions, String?>,
|
|
configurationKeyPath: KeyPath<Configuration, String?>
|
|
) throws -> String {
|
|
if let global = globals[keyPath: globalsKeyPath] {
|
|
return global
|
|
}
|
|
guard let configuration = configuration[keyPath: configurationKeyPath] else {
|
|
throw RunPlaybookError.playbookNotFound
|
|
}
|
|
return configuration
|
|
}
|
|
|
|
func withSetupLogger(
|
|
commandName: String,
|
|
globals: BasicGlobalOptions,
|
|
quietOnlyPlaybook: Bool = false,
|
|
dependencies setupDependencies: (inout DependencyValues) -> Void = { _ in },
|
|
operation: @escaping () async throws -> Void
|
|
) async rethrows {
|
|
try await withDependencies {
|
|
$0.logger = .init(label: "\("hpa".yellow)")
|
|
if quietOnlyPlaybook || !globals.quiet {
|
|
switch globals.verbose {
|
|
case 0:
|
|
$0.logger.logLevel = .info
|
|
case 1:
|
|
$0.logger.logLevel = .debug
|
|
case 2...:
|
|
$0.logger.logLevel = .trace
|
|
default:
|
|
$0.logger.logLevel = .info
|
|
}
|
|
}
|
|
$0.logger[metadataKey: "command"] = "\(commandName.blue)"
|
|
} operation: {
|
|
try await operation()
|
|
}
|
|
}
|
|
|
|
func withSetupLogger(
|
|
commandName: String,
|
|
globals: GlobalOptions,
|
|
dependencies setupDependencies: (inout DependencyValues) -> Void = { _ in },
|
|
operation: @escaping () async throws -> Void
|
|
) async rethrows {
|
|
try await withSetupLogger(
|
|
commandName: commandName,
|
|
globals: globals.basic,
|
|
quietOnlyPlaybook: globals.quietOnlyPlaybook,
|
|
dependencies: setupDependencies,
|
|
operation: operation
|
|
)
|
|
}
|
|
|
|
func runPlaybook(
|
|
commandName: String,
|
|
globals: GlobalOptions,
|
|
configuration: Configuration? = nil,
|
|
extraArgs: [String],
|
|
_ args: String...
|
|
) async throws {
|
|
try await runPlaybook(
|
|
commandName: commandName,
|
|
globals: globals,
|
|
configuration: configuration,
|
|
extraArgs: extraArgs,
|
|
args
|
|
)
|
|
}
|
|
|
|
private extension CliClient {
|
|
func ensuredConfiguration(_ configuration: Configuration?) throws -> Configuration {
|
|
guard let configuration else {
|
|
return try loadConfiguration()
|
|
}
|
|
return configuration
|
|
}
|
|
}
|
|
|
|
func runPlaybook(
|
|
commandName: String,
|
|
globals: GlobalOptions,
|
|
configuration: Configuration? = nil,
|
|
extraArgs: [String],
|
|
_ args: [String]
|
|
) async throws {
|
|
try await withSetupLogger(commandName: commandName, globals: globals) {
|
|
@Dependency(\.cliClient) var cliClient
|
|
@Dependency(\.logger) var logger
|
|
|
|
logger.debug("Begin run playbook: \(globals)")
|
|
|
|
let configuration = try cliClient.ensuredConfiguration(configuration)
|
|
|
|
logger.debug("Configuration: \(configuration)")
|
|
|
|
let playbookDir = try ensureString(
|
|
globals: globals,
|
|
configuration: configuration,
|
|
globalsKeyPath: \.playbookDir,
|
|
configurationKeyPath: \.playbookDir
|
|
)
|
|
let playbook = "\(playbookDir)/main.yml"
|
|
|
|
let inventory = (try? ensureString(
|
|
globals: globals,
|
|
configuration: configuration,
|
|
globalsKeyPath: \.inventoryPath,
|
|
configurationKeyPath: \.inventoryPath
|
|
)) ?? "\(playbookDir)/inventory.ini"
|
|
|
|
var playbookArgs = [
|
|
"ansible-playbook", playbook,
|
|
"--inventory", inventory
|
|
]
|
|
|
|
if let defaultArgs = configuration.defaultPlaybookArgs {
|
|
playbookArgs.append(defaultArgs)
|
|
}
|
|
|
|
try await cliClient.runCommand(
|
|
quiet: globals.quietOnlyPlaybook ? true : globals.basic.quiet,
|
|
shell: globals.shellOrDefault,
|
|
playbookArgs + args + extraArgs
|
|
)
|
|
}
|
|
}
|
|
|
|
enum RunPlaybookError: Error {
|
|
case playbookNotFound
|
|
case configurationError
|
|
}
|