Commit 8ef50903 authored by Yuanle Song's avatar Yuanle Song
Browse files

support remote-user, node-name, ssh-key in config

file. Now all options except --versin and -i are supported in config
file
parent 687553a2
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -27,6 +27,10 @@ type WellsConfig(fn: string) =
    match getEnv (configKeyToEnvVar key) with
      | null | "" -> keyValuePairs.TryFind key
      | envValue -> Some envValue
  member this.GetStrDefault key defaultValue =
    match this.GetStr key with
    | None -> defaultValue
    | Some value -> value
  member this.GetBool key =
    Option.map (fun value -> Regex.IsMatch(value, "^(yes|true|enable|1)$", RegexOptions.IgnoreCase)) (this.GetStr key)
  member this.GetFloat key =
+30 −26
Original line number Diff line number Diff line
@@ -50,6 +50,11 @@ type CLIArguments =
            | Ssh_Key _ -> "ssh private key, used when backup to remote ssh node"
            | Version _ -> "show mbackup version and exit"

type MbackupRuntimeConfig =
    { Logger: Logger
      Config: WellsConfig
      Options: ParseResults<CLIArguments> }

let programFilesDirWin = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) |> ensureWinDir
let programFilesDir = toMingwPath programFilesDirWin
let mbackupProgramDirWin = programFilesDirWin + "mbackup\\"
@@ -74,7 +79,6 @@ let mbackupInstallDir = mbackupInstallDirWin |> toMingwPath

let userHomeWin =
    getEnvDefault "HOME" (Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)) |> ensureWinDir

let userHome = userHomeWin |> toMingwPath

let userConfigDirWin = programDataDirWin + "mbackup\\"
@@ -82,8 +86,6 @@ let userConfigDir = programDataDir + "mbackup/"
let runtimeDirWin = appDataLocalDirWin + "mbackup\\"
let runtimeDir = appDataLocalDir + "mbackup/"

let mbackupConfigFile = userConfigDirWin + "mbackup.txt"

// return true if target is a local dir. local dir can be unix style or windows style.
let isLocalTarget (target: string) = target.StartsWith "/" || Regex.IsMatch(target, "^[c-z]:", RegexOptions.IgnoreCase)

@@ -161,7 +163,7 @@ let generateMbackupList (logger: Logger) =
                mbackupList
            true
    with
    | :? System.IO.IOException as ex ->
    | :? IOException as ex ->
        logger.Error "Read/write file failed: %s %s" ex.Source ex.Message
        false
    | ex ->
@@ -170,11 +172,12 @@ let generateMbackupList (logger: Logger) =

exception PrivateKeyNotFoundException of string

let addOptionsForRemoteBackup (results: ParseResults<CLIArguments>) (logger: Logger) (rsyncCmd: string list) =
let addOptionsForRemoteBackup (rc: MbackupRuntimeConfig) (rsyncCmd: string list) =
    let options = rc.Options
    let sshExeFile = mbackupProgramDir + "rsync-w64/usr/bin/ssh.exe"
    let sshConfigFile = userHome + ".ssh/config"
    let sshKnownHostsFile = userHome + ".ssh/known_hosts"
    let sshPrivateKeyFile = results.GetResult(Ssh_Key, defaultValue = userHome + ".ssh/id_rsa") |> toMingwPath
    let sshPrivateKeyFile = options.GetResult(Ssh_Key, rc.Config.GetStrDefault "ssh-key" (userHome + ".ssh/id_rsa")) |> toMingwPath
    let sshPrivateKeyFileWin = toWinPath sshPrivateKeyFile
    if not (File.Exists(sshPrivateKeyFileWin)) then
        raise (PrivateKeyNotFoundException("ssh private key doesn't exist: " + sshPrivateKeyFileWin))
@@ -188,37 +191,39 @@ let addOptionsForRemoteBackup (results: ParseResults<CLIArguments>) (logger: Log
                [ sprintf "-e \"'%s'%s -i %s -o StrictHostKeyChecking=ask -o UserKnownHostsFile=%s\""
                          sshExeFile sshConfigFileOption sshPrivateKeyFile sshKnownHostsFile]

        let nodeName = results.GetResult(Node_Name, defaultValue = Net.Dns.GetHostName())
        let nodeName = options.GetResult(Node_Name, (rc.Config.GetStrDefault "node-name" (Net.Dns.GetHostName())))
        let remoteLogFile = sprintf "/var/log/mbackup/%s.log" nodeName
        let remoteUser = results.GetResult(Remote_User, defaultValue = Environment.UserName)
        let remoteUser = options.GetResult(Remote_User, (rc.Config.GetStrDefault "remote-user" Environment.UserName))
        let rsyncCmd = List.append rsyncCmd [ sprintf "--remote-option=--log-file=%s" remoteLogFile ]
        let rsyncCmd = List.append rsyncCmd [ sprintf "--chown=%s:%s" remoteUser remoteUser ]
        rsyncCmd


[<EntryPoint>]
let main argv =
    let logger = Logger()
    let options =
        let errorHandler = ProcessExiter(colorizer = function ErrorCode.HelpText -> None | _ -> Some ConsoleColor.Red)

        let parser = ArgumentParser.Create<CLIArguments>(programName = "mbackup.exe", errorHandler = errorHandler)
    let results = parser.Parse argv
        parser.Parse argv
    let rc = {
        MbackupRuntimeConfig.Config =
          let mbackupConfigFile = userConfigDirWin + "mbackup.txt"
          WellsConfig(mbackupConfigFile)
        Logger = logger
        Options = options
    }

    if results.Contains Version then
    if options.Contains Version then
        printfn "mbackup %s" versionStr
        Environment.Exit(ExitSuccess)

    let dryRun = results.Contains Dry_Run
    let itemizeChanges = results.Contains Itemize_Changes

    let logger = Logger()

    logger.Info "user config dir: %s" userConfigDirWin
    logger.Info "runtime dir: %s" runtimeDirWin
    logger.Debug "program dir: %s" mbackupProgramDirWin

    let rsyncCmd: string list = []
    let rsyncCmd = appendWhen dryRun rsyncCmd "--dry-run"
    let rsyncCmd = appendWhen itemizeChanges rsyncCmd "-i"
    let rsyncCmd = appendWhen (options.Contains Dry_Run) rsyncCmd "--dry-run"
    let rsyncCmd = appendWhen (options.Contains Itemize_Changes) rsyncCmd "-i"
    let rsyncCmd =
        List.append rsyncCmd
            ("-h --stats -togr --delete --delete-excluded --ignore-missing-args".Split [| ' ' |] |> Array.toList)
@@ -241,10 +246,9 @@ let main argv =
        else target

    let backupTargetMaybe =
        match results.TryGetResult Target with
        match options.TryGetResult Target with
        | None ->
            let mbackupConfig = WellsConfig(mbackupConfigFile)
            let backupTargetMaybe = mbackupConfig.GetStr("target")
            let backupTargetMaybe = rc.Config.GetStr("target")
            Option.map normalizeTarget backupTargetMaybe
        | Some backupTarget -> Some(normalizeTarget backupTarget)

@@ -256,7 +260,7 @@ let main argv =
        try
            let rsyncCmd =
                if not (isLocalTarget backupTarget) then
                  addOptionsForRemoteBackup results logger rsyncCmd
                  addOptionsForRemoteBackup rc rsyncCmd
                else
                  rsyncCmd
            let rsyncCmd = List.append rsyncCmd [ "/" ]
+2 −1
Original line number Diff line number Diff line
# example config file
# example config file, lines start with # are comments.
# target=d:\backup\
# target=myhost.example.com:/data/backup/somedir/
# remote-user=foo
+1 −1
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@
    <RuntimeIdentifiers>win10-x64</RuntimeIdentifiers>
    <RootNamespace>Mbackup</RootNamespace>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
    <Version>0.2.4.0</Version>
    <Version>0.2.6.0</Version>
  </PropertyGroup>

  <ItemGroup>
+3 −3
Original line number Diff line number Diff line
@@ -194,9 +194,6 @@ yes. I should support this.

Both local.list and local.exclude.

** 2019-11-16 support reading remote-user=xxx option from config file.
cli arg > env var > config file.
same with target option.
** 2019-11-14 learn how to create an msi installer.
- WiX Toolset
  https://wixtoolset.org/
@@ -687,6 +684,9 @@ same with target option.
    it contains hostname.
  - 
* done                                                                :entry:
** 2019-11-16 support reading remote-user=xxx option from config file.
cli arg > env var > config file.
same with target option.
** 2019-11-13 next todos; scheduled task command line.
- DONE fix TODOs in F# code
- DONE add rsync command and arguments for running in windows.