private static SshCommand ExecuteCommand(SshClient client, string commandText) { ConsoleManager.WriteLine("SSH TX:"); ConsoleManager.WriteLine(commandText, ConsoleColor.Green); using (var command = client.CreateCommand(commandText)) { var result = command.Execute(); ConsoleManager.WriteLine("SSH RX:"); if (command.ExitStatus != 0) { ConsoleManager.ErrorWriteLine("Error " + command.ExitStatus); ConsoleManager.ErrorWriteLine(command.Error); } if (string.IsNullOrWhiteSpace(result) == false) { ConsoleManager.WriteLine(result, ConsoleColor.Yellow); } return(command); } }
/// <summary> /// Starts the monitor mode. /// </summary> /// <param name="fsMonitor">The fs monitor.</param> /// <param name="sshClient">The SSH client.</param> /// <param name="sftpClient">The SFTP client.</param> /// <param name="shellStream">The shell stream.</param> /// <param name="verbOptions">The verb options.</param> private static void StartMonitorMode(FileSystemMonitor fsMonitor, SshClient sshClient, SftpClient sftpClient, ShellStream shellStream, MonitorVerbOptions verbOptions) { fsMonitor.FileSystemEntryChanged += (s, e) => { // Detect changes to the monitor file by ignoring deletions and checking file paths. if (e.ChangeType != FileSystemEntryChangeType.FileAdded && e.ChangeType != FileSystemEntryChangeType.FileModified) { return; } // If the change was not in the monitor file, then ignore it if (e.Path.ToLowerInvariant().Equals(verbOptions.MonitorFile.ToLowerInvariant()) == false) { return; } // Create a new deployment once CreateNewDeployment(sshClient, sftpClient, shellStream, verbOptions); }; fsMonitor.Start(); ConsoleManager.WriteLine("File System Monitor is now running."); ConsoleManager.WriteLine("Writing a new monitor file will trigger a new deployment."); ConsoleManager.WriteLine("Press H for help!"); ConsoleManager.WriteLine("Ground Control to Major Tom: Have a nice trip in space!", ConsoleColor.DarkCyan); }
/// <summary> /// Prepares the given target path for deployment. If clean target is false, it does nothing. /// </summary> /// <param name="sftpClient">The SFTP client.</param> /// <param name="targetPath">The target path.</param> /// <param name="cleanTarget">if set to <c>true</c> [clean target].</param> private static void PrepareTargetPath(SftpClient sftpClient, MonitorVerbOptions verbOptions) { if (verbOptions.CleanTarget == 0) { return; } ConsoleManager.WriteLine(" Cleaning Target Path '" + verbOptions.TargetPath + "'", ConsoleColor.Green); DeleteLinuxDirectoryRecursive(sftpClient, verbOptions.TargetPath); }
/// <summary> /// Creates the given directory structure on the target machine. /// </summary> /// <param name="sftpClient">The SFTP client.</param> /// <param name="targetPath">The target path.</param> private static void CreateTargetPath(SftpClient sftpClient, MonitorVerbOptions verbOptions) { if (sftpClient.Exists(verbOptions.TargetPath) == true) { return; } ConsoleManager.WriteLine(" Target Path '" + verbOptions.TargetPath + "' does not exist. -- Will attempt to create.", ConsoleColor.Green); CreateLinuxDirectoryRecursive(sftpClient, verbOptions.TargetPath); ConsoleManager.WriteLine(" Target Path '" + verbOptions.TargetPath + "' created successfully.", ConsoleColor.Green); }
/// <summary> /// Checks that both, SFTP and SSH clients have a working connection. If they don't it attempts to reconnect. /// </summary> /// <param name="sshClient">The SSH client.</param> /// <param name="sftpClient">The SFTP client.</param> /// <param name="verbOptions">The verb options.</param> private static void EnsureMonitorConnection(SshClient sshClient, SftpClient sftpClient, MonitorVerbOptions verbOptions) { if (sshClient.IsConnected == false) { ConsoleManager.WriteLine("Connecting to host " + verbOptions.Host + ":" + verbOptions.Port + " via SSH."); sshClient.Connect(); } if (sftpClient.IsConnected == false) { ConsoleManager.WriteLine("Connecting to host " + verbOptions.Host + ":" + verbOptions.Port + " via SFTP."); sftpClient.Connect(); } }
/// <summary> /// Runs pre and post deployment commands over the SSH client /// </summary> /// <param name="sshClient">The SSH client.</param> /// <param name="commandText">The command text.</param> private static void RunShellStreamCommand(ShellStream shellStream, MonitorVerbOptions verbOptions) { var commandText = verbOptions.PostCommand; if (string.IsNullOrWhiteSpace(commandText) == true) { return; } ConsoleManager.WriteLine(" Executing shell command.", ConsoleColor.Green); shellStream.WriteLine(commandText); shellStream.Flush(); ConsoleManager.WriteLine(" TX: " + commandText, ConsoleColor.DarkYellow); }
/// <summary> /// Prints the currently supplied monitor mode options. /// </summary> /// <param name="verbOptions">The verb options.</param> /// <param name="monitorFile">The monitor file.</param> /// <param name="sourcePath">The source path.</param> /// <param name="targetPath">The target path.</param> private static void PrintMonitorOptions(MonitorVerbOptions verbOptions) { ConsoleManager.WriteLine(string.Empty); ConsoleManager.WriteLine("Monitor mode starting"); ConsoleManager.WriteLine("Monitor parameters follow: "); ConsoleManager.WriteLine(" Monitor File " + verbOptions.MonitorFile, ConsoleColor.DarkYellow); ConsoleManager.WriteLine(" Source Path " + verbOptions.SourcePath, ConsoleColor.DarkYellow); ConsoleManager.WriteLine(" Excluded Files " + verbOptions.ExcludeFileSuffixes, ConsoleColor.DarkYellow); ConsoleManager.WriteLine(" Target Address " + verbOptions.Host + ":" + verbOptions.Port.ToString(), ConsoleColor.DarkYellow); ConsoleManager.WriteLine(" Username " + verbOptions.Username, ConsoleColor.DarkYellow); ConsoleManager.WriteLine(" Target Path " + verbOptions.TargetPath, ConsoleColor.DarkYellow); ConsoleManager.WriteLine(" Clean Target " + (verbOptions.CleanTarget == 0 ? "NO" : "YES"), ConsoleColor.DarkYellow); ConsoleManager.WriteLine(" Pre Deployment " + verbOptions.PreCommand, ConsoleColor.DarkYellow); ConsoleManager.WriteLine(" Post Deployment " + verbOptions.PostCommand, ConsoleColor.DarkYellow); }
/// <summary> /// Runs the deployment command. /// </summary> /// <param name="sshClient">The SSH client.</param> /// <param name="commandText">The command text.</param> private static void RunSshClientCommand(SshClient sshClient, MonitorVerbOptions verbOptions) { var commandText = verbOptions.PreCommand; if (string.IsNullOrWhiteSpace(commandText) == true) { return; } ConsoleManager.WriteLine(" Executing SSH client command.", ConsoleColor.Green); var result = sshClient.RunCommand(commandText); ConsoleManager.WriteLine(string.Format(" SSH TX: {0}", commandText), ConsoleColor.DarkYellow); ConsoleManager.WriteLine(string.Format(" SSH RX: [{0}] ", result.ExitStatus, result.Result), ConsoleColor.DarkYellow); }
/// <summary> /// Creates a new deployment cycle. /// </summary> /// <param name="sshClient">The SSH client.</param> /// <param name="sftpClient">The SFTP client.</param> /// <param name="shellStream">The shell stream.</param> /// <param name="verbOptions">The verb options.</param> private static void CreateNewDeployment(SshClient sshClient, SftpClient sftpClient, ShellStream shellStream, MonitorVerbOptions verbOptions) { // At this point the change has been detected; Make sure we are not deploying ConsoleManager.WriteLine(string.Empty); if (IsDeploying) { ConsoleManager.WriteLine("WARNING: Deployment already in progress. Deployment will not occur.", ConsoleColor.DarkYellow); return; } // Lock Deployment IsDeploying = true; var stopwatch = new System.Diagnostics.Stopwatch(); stopwatch.Start(); try { ForwardShellStreamOutput = false; PrintDeploymentNumber(DeploymentNumber); RunSshClientCommand(sshClient, verbOptions); CreateTargetPath(sftpClient, verbOptions); PrepareTargetPath(sftpClient, verbOptions); UploadFilesToTarget(sftpClient, verbOptions); } catch (Exception ex) { PrintException(ex); } finally { // Unlock deployment IsDeploying = false; DeploymentNumber++; stopwatch.Stop(); ConsoleManager.WriteLine(" Finished deployment in " + Math.Round(stopwatch.Elapsed.TotalSeconds, 2).ToString() + " seconds.", ConsoleColor.Green); ForwardShellStreamOutput = true; RunShellStreamCommand(shellStream, verbOptions); } }
/// <summary> /// Uploads the files in the source Windows path to the target Linux path. /// </summary> /// <param name="sftpClient">The SFTP client.</param> /// <param name="targetPath">The target path.</param> /// <param name="sourcePath">The source path.</param> /// <param name="ignoreFileSuffixes">The ignore file suffixes.</param> private static void UploadFilesToTarget(SftpClient sftpClient, MonitorVerbOptions verbOptions) { var filesInSource = System.IO.Directory.GetFiles(verbOptions.SourcePath, FileSystemMonitor.AllFilesPattern, System.IO.SearchOption.AllDirectories); var filesToDeploy = new List <string>(); foreach (var file in filesInSource) { var ignore = false; foreach (var ignoreSuffix in verbOptions.ExcludeFileSuffixList) { if (file.EndsWith(ignoreSuffix)) { ignore = true; break; } } if (ignore) { continue; } filesToDeploy.Add(file); } ConsoleManager.WriteLine(" Deploying " + filesToDeploy.Count + " files.", ConsoleColor.Green); foreach (var file in filesToDeploy) { var relativePath = MakeRelativePath(file, verbOptions.SourcePath + Path.DirectorySeparatorChar); var fileTargetPath = Path.Combine(verbOptions.TargetPath, relativePath).Replace(WindowsDirectorySeparatorChar, LinuxDirectorySeparatorChar); var targetDirectory = Path.GetDirectoryName(fileTargetPath).Replace(WindowsDirectorySeparatorChar, LinuxDirectorySeparatorChar); CreateLinuxDirectoryRecursive(sftpClient, targetDirectory); using (var fileStream = System.IO.File.OpenRead(file)) { sftpClient.UploadFile(fileStream, fileTargetPath); } } }
/// <summary> /// Stops the monitor mode by closing SFTP and SSH connections, and stopping the File System Monitor. /// </summary> /// <param name="sftpClient">The SFTP client.</param> /// <param name="sshClient">The SSH client.</param> /// <param name="fsMonitor">The fs monitor.</param> private static void StopMonitorMode(SftpClient sftpClient, SshClient sshClient, FileSystemMonitor fsMonitor) { ConsoleManager.WriteLine(string.Empty); fsMonitor.Stop(); ConsoleManager.WriteLine("File System monitor was stopped."); if (sftpClient.IsConnected == true) { sftpClient.Disconnect(); } ConsoleManager.WriteLine("SFTP client disconnected."); if (sshClient.IsConnected == true) { sshClient.Disconnect(); } ConsoleManager.WriteLine("SSH client disconnected."); ConsoleManager.WriteLine("Application will exit now."); }
/// <summary> /// Starts the user interaction. /// </summary> /// <param name="fsMonitor">The fs monitor.</param> /// <param name="sshClient">The SSH client.</param> /// <param name="sftpClient">The SFTP client.</param> /// <param name="shellStream">The shell stream.</param> /// <param name="verbOptions">The verb options.</param> private static void StartUserInteraction(FileSystemMonitor fsMonitor, SshClient sshClient, SftpClient sftpClient, ShellStream shellStream, MonitorVerbOptions verbOptions) { ForwardShellStreamInput = false; while (true) { var readKey = Console.ReadKey(true); if (readKey.Key == ConsoleKey.F1) { ForwardShellStreamInput = !ForwardShellStreamInput; if (ForwardShellStreamInput) { ConsoleManager.WriteLine(" >> Entered console input forwarding.", ConsoleColor.Green); ForwardShellStreamOutput = true; } else { ConsoleManager.WriteLine(" >> Left console input forwarding.", ConsoleColor.Red); } continue; } if (ForwardShellStreamInput) { if (readKey.Key == ConsoleKey.Enter) { shellStream.WriteLine(string.Empty); } else { shellStream.WriteByte((byte)readKey.KeyChar); } shellStream.Flush(); continue; } if (readKey.Key == ConsoleKey.Q) { break; } if (readKey.Key == ConsoleKey.C) { ConsoleManager.Clear(); continue; } if (readKey.Key == ConsoleKey.N) { CreateNewDeployment(sshClient, sftpClient, shellStream, verbOptions); continue; } if (readKey.Key == ConsoleKey.E) { RunSshClientCommand(sshClient, verbOptions); continue; } if (readKey.Key == ConsoleKey.S) { RunShellStreamCommand(shellStream, verbOptions); continue; } if (readKey.Key != ConsoleKey.H) { ConsoleManager.WriteLine("Unrecognized command '" + readKey.KeyChar + "' -- Press 'H' to get a list of available commands.", ConsoleColor.Red); } if (readKey.Key == ConsoleKey.H) { var helpColor = ConsoleColor.Cyan; ConsoleManager.WriteLine("Console help", helpColor); ConsoleManager.WriteLine(" H Prints this screen", helpColor); ConsoleManager.WriteLine(" Q Quits this application", helpColor); ConsoleManager.WriteLine(" C Clears the screen", helpColor); ConsoleManager.WriteLine(" N Force a deployment cycle", helpColor); ConsoleManager.WriteLine(" E Run the Pre-deployment command", helpColor); ConsoleManager.WriteLine(" S Run the Post-deployment command", helpColor); ConsoleManager.WriteLine(" F1 Toggle shell-interactive mode", helpColor); ConsoleManager.WriteLine(string.Empty); continue; } } }
/// <summary> /// Prints the deployment number the Monitor is currently in. /// </summary> /// <param name="deploymentNumber">The deployment number.</param> private static void PrintDeploymentNumber(int deploymentNumber) { ConsoleManager.WriteLine(" Starting deployment ID " + deploymentNumber + " - " + DateTime.Now.ToLongDateString() + " " + DateTime.Now.ToLongTimeString(), ConsoleColor.Green); }
static void Main(string[] args) { #region Handle Single Instance Application bool isNewMutex; AppMutex = new Mutex(true, MutexName, out isNewMutex); if (isNewMutex == false) { AppMutex = null; Environment.ExitCode = CommandLine.Parser.DefaultExitCodeFail; return; } #endregion Console.WriteLine("SSH Deployment Tool [Version " + typeof(Unosquare.Labs.SshDeploy.Program).Assembly.GetName().Version.ToString() + "]"); Console.WriteLine("(c) 2015 Unosquare SA de CV. All Rights Reserved."); var invokedVerbName = string.Empty; CliVerbOptionsBase invokedVerbOptions = null; var options = new CliOptions(); var parseResult = CommandLine.Parser.Default.ParseArguments(args, options, (verb, verbOptions) => { invokedVerbName = verb; invokedVerbOptions = verbOptions as CliVerbOptionsBase; if (invokedVerbOptions != null) { ConsoleManager.Verbose = invokedVerbOptions.Verbose != 0; } }); if (parseResult == false) { Environment.ExitCode = CommandLine.Parser.DefaultExitCodeFail; return; } try { switch (invokedVerbName) { case CliOptions.RunVerb: { var verbOptions = invokedVerbOptions as RunVerbOptions; DeploymentManager.ExecuteRunVerb(verbOptions); break; } case CliOptions.ShellVerb: { var verbOptions = invokedVerbOptions as ShellVerbOptions; DeploymentManager.ExecuteShellVerb(verbOptions); break; } case CliOptions.MonitorVerb: { var verbOptions = invokedVerbOptions as MonitorVerbOptions; DeploymentManager.ExecuteMonitorVerb(verbOptions); break; } } } catch (Exception ex) { ConsoleManager.ErrorWriteLine("Error - " + ex.GetType().Name); ConsoleManager.ErrorWriteLine(ex.Message); ConsoleManager.ErrorWriteLine(ex.StackTrace); Environment.ExitCode = CommandLine.Parser.DefaultExitCodeFail; } if (Environment.ExitCode != 0) { ConsoleManager.ErrorWriteLine("Completed with errors. Exit Code " + Environment.ExitCode.ToString()); } else { ConsoleManager.WriteLine("Completed."); } }