Esempio n. 1
0
        /// <inheritdoc/>
        public override void Run(CommandLine commandLine)
        {
            if (commandLine.HasHelpOption)
            {
                Console.WriteLine(usage);
                Program.Exit(0);
            }

            if (commandLine.Arguments.Length < 2)
            {
                Console.WriteLine(usage);
                Program.Exit(1);
            }

            if (Environment.GetEnvironmentVariable("NEON_RUN_ENV") != null)
            {
                Console.Error.WriteLine("*** ERROR: [neon run ...] cannot be executed recursively.");
                Program.Exit(1);
            }

            var commandSplit     = Program.CommandLine.Split();
            var leftCommandLine  = commandSplit.Left.Shift(1);
            var rightCommandLine = commandSplit.Right;

            if (rightCommandLine == null || rightCommandLine.Arguments.Length == 0)
            {
                Console.Error.WriteLine("*** ERROR: Expecting a [--] argument followed by a shell command.");
                Program.Exit(1);
            }

            var orgDirectory = Directory.GetCurrentDirectory();
            var runFolder    = Path.Combine(HiveHelper.GetRunFolder(), Guid.NewGuid().ToString("D"));
            var runEnvPath   = Path.Combine(runFolder, "__runenv.txt");
            var exitCode     = 1;

            try
            {
                // Create the temporary run folder and make it the current directory.

                Directory.CreateDirectory(runFolder);

                // We need to load variables from any files specified on the command line,
                // decrypting them as required.

                var allVars = new Dictionary <string, string>(StringComparer.InvariantCultureIgnoreCase);

                if (leftCommandLine.Arguments.Length > 0)
                {
                    bool   askVaultPass     = leftCommandLine.HasOption("--ask-vault-pass");
                    string tempPasswordPath = null;
                    string passwordName     = null;

                    try
                    {
                        if (askVaultPass)
                        {
                            // Note that [--ask-vault-pass] takes presidence over [--vault-password-file].

                            var password = NeonHelper.ReadConsolePassword("Vault password: "******"D");

                            tempPasswordPath = Path.Combine(passwordsFolder, $"{guid}.tmp");
                            passwordName     = Path.GetFileName(tempPasswordPath);

                            File.WriteAllText(tempPasswordPath, password);
                        }
                        else
                        {
                            passwordName = leftCommandLine.GetOption("--vault-password-file");
                        }

                        if (!string.IsNullOrEmpty(passwordName))
                        {
                            AnsibleCommand.VerifyPassword(passwordName);
                        }

                        // Decrypt the variables files, add the variables to the environment
                        // and also to the [allVars] dictionary which we'll use below to
                        // create the run variables file.

                        foreach (var varFile in leftCommandLine.Arguments)
                        {
                            var varContents = File.ReadAllText(varFile);

                            if (varContents.StartsWith("$ANSIBLE_VAULT;"))
                            {
                                // The variable file is encrypted so we're going recursively invoke
                                // the following command to decrypt it:
                                //
                                //      neon ansible vault view -- --vault-password=NAME VARS-PATH
                                //
                                // This uses the password to decrypt the variables to STDOUT.

                                if (string.IsNullOrEmpty(passwordName))
                                {
                                    Console.Error.WriteLine($"*** ERROR: [{varFile}] is encrypted.  Use [--ask-vault-pass] or [--vault-password-file] to specify the password.");
                                    Program.Exit(1);
                                }

                                var result = Program.ExecuteRecurseCaptureStreams(
                                    new object[]
                                {
                                    "ansible",
                                    "vault",
                                    "--",
                                    "view",
                                    $"--vault-password-file={passwordName}",
                                    varFile
                                });

                                if (result.ExitCode != 0)
                                {
                                    Console.Error.Write(result.AllText);
                                    Program.Exit(result.ExitCode);
                                }

                                varContents = NeonHelper.StripAnsibleWarnings(result.OutputText);
                            }

                            // [varContents] now holds the decrypted variables formatted as YAML.
                            // We're going to parse this and set the appropriate environment
                            // variables.
                            //
                            // Note that we're going to ignore variables with multi-line values.

                            var yaml = new YamlStream();
                            var vars = new List <KeyValuePair <string, string> >();

                            try
                            {
                                yaml.Load(varContents);
                            }
                            catch (Exception e)
                            {
                                throw new HiveException($"Unable to parse YAML from decrypted [{varFile}]: {NeonHelper.ExceptionError(e)}", e);
                            }

                            if (yaml.Documents.FirstOrDefault() != null)
                            {
                                ParseYamlVariables(vars, (YamlMappingNode)yaml.Documents.First().RootNode);
                            }

                            foreach (var variable in vars)
                            {
                                if (variable.Value != null && variable.Value.Contains('\n'))
                                {
                                    continue;   // Ignore variables with multi-line values.
                                }

                                allVars[variable.Key] = variable.Value;
                                Environment.SetEnvironmentVariable(variable.Key, variable.Value);
                            }
                        }
                    }
                    finally
                    {
                        if (tempPasswordPath != null && File.Exists(tempPasswordPath))
                        {
                            File.Delete(tempPasswordPath);  // Don't need this any more.
                        }
                    }
                }

                // We need to generate the NEON_RUN_ENV file defining the environment variables
                // loaded by the command.  This file format is compatible with the Docker
                // [run] command's [--env-file=PATH] option and will be used by nested calls to
                // [neon] to pass these variables through to the tool container as required.

                Environment.SetEnvironmentVariable("NEON_RUN_ENV", runEnvPath);

                using (var runEnvWriter = new StreamWriter(runEnvPath, false, Encoding.UTF8))
                {
                    foreach (var item in allVars)
                    {
                        runEnvWriter.WriteLine($"{item.Key}={item.Value}");
                    }
                }

                // Execute the command in the appropriate shell for the current workstation.

                var sbCommand = new StringBuilder();

                foreach (var arg in rightCommandLine.Items)
                {
                    if (sbCommand.Length > 0)
                    {
                        sbCommand.Append(' ');
                    }

                    if (arg.Contains(' '))
                    {
                        sbCommand.Append("\"" + arg + "\"");
                    }
                    else
                    {
                        sbCommand.Append(arg);
                    }
                }

                exitCode = NeonHelper.ExecuteShell(sbCommand.ToString());
            }
            finally
            {
                // Restore the current directory.

                Directory.SetCurrentDirectory(orgDirectory);

                // Cleanup

                if (Directory.Exists(runFolder))
                {
                    Directory.Delete(runFolder, true);
                }
            }

            Program.Exit(exitCode);
        }
Esempio n. 2
0
        /// <inheritdoc/>
        public override void Run(CommandLine commandLine)
        {
            if (HiveHelper.InToolContainer)
            {
                Console.Error.WriteLine("*** ERROR: [file] commands cannot be run inside a Docker container.");
                Program.Exit(1);
            }

            if (commandLine.Arguments.Length == 0 || commandLine.HasHelpOption)
            {
                Help();
                Program.Exit(0);
            }

            // Parse the arguments.

            var command      = commandLine.Arguments.ElementAtOrDefault(0);
            var source       = string.Empty;
            var target       = string.Empty;
            var passwordName = string.Empty;

            switch (command)
            {
            // These commands accept two parameters.

            case "create":
            case "edit":
            case "view":

                target       = commandLine.Arguments.ElementAtOrDefault(1);
                passwordName = commandLine.Arguments.ElementAtOrDefault(2);

                if (string.IsNullOrEmpty(target))
                {
                    Console.Error.WriteLine("*** ERROR: PATH argument is missing.");
                    Program.Exit(1);
                }

                if (string.IsNullOrEmpty(passwordName))
                {
                    Console.Error.WriteLine("*** ERROR: PASSWORD-NAME argument is missing.");
                    Program.Exit(1);
                }
                break;

            // These commands accept three parameters.

            case "encrypt":
            case "decrypt":

                source       = commandLine.Arguments.ElementAtOrDefault(1);
                target       = commandLine.Arguments.ElementAtOrDefault(2);
                passwordName = commandLine.Arguments.ElementAtOrDefault(3);

                if (string.IsNullOrEmpty(source))
                {
                    Console.Error.WriteLine("*** ERROR: SOURCE argument is missing.");
                    Program.Exit(1);
                }

                if (string.IsNullOrEmpty(target))
                {
                    Console.Error.WriteLine("*** ERROR: TARGET argument is missing.");
                    Program.Exit(1);
                }

                if (string.IsNullOrEmpty(passwordName))
                {
                    Console.Error.WriteLine("*** ERROR: PASSWORD-NAME argument is missing.");
                    Program.Exit(1);
                }
                break;

            default:

                Console.Error.WriteLine($"*** ERROR: Unexpected [{command}] command.");
                Program.Exit(1);
                break;
            }

            var editor = commandLine.GetOption("--editor", "nano");

            switch (editor.ToLowerInvariant())
            {
            case "nano":

                Environment.SetEnvironmentVariable("EDITOR", "/bin/nano");
                break;

            case "vim":

                Environment.SetEnvironmentVariable("EDITOR", "/usr/bin/vim");
                break;

            case "vi":

                Environment.SetEnvironmentVariable("EDITOR", "/usr/bin/vi");
                break;

            default:

                Console.Error.WriteLine($"*** ERROR: [--editor={editor}] does not specify a known editor.  Specify one of: NANO, VIM, or VI.");
                Program.Exit(1);
                break;
            }

            // Ensure that the password file actually exists.

            Covenant.Assert(!string.IsNullOrEmpty(passwordName));

            if (!File.Exists(Path.Combine(HiveHelper.GetAnsiblePasswordsFolder(), passwordName)))
            {
                Console.Error.WriteLine($"*** ERROR: Password file for [{passwordName}] does not exist.");
                Program.Exit(1);
            }

            // $note(jeff.lill):
            //
            // I tried to call [Program.ExecuteRecurse()] here to recurse into
            // the [neon vault -- COMMAND --vault-password-file=NAME PATH] commands
            // but it didn't work for [edit].  It looks like the command did run but
            // then gets stuck.  I could have sworn that I had this working at one
            // point but I can't get it working again.  I think the standard
            // I/O streams being redirect might be confusing Docker and Ansible,
            // since Ansible needs to access the Docker TTY.
            //
            // The [view] command was also a bit wonky.  For example, two blank
            // lines in the encrypted file would be returned as only a single
            // blank line.
            //
            // The (not so bad) workaround is to simply recurse into
            // [Program.Main()].  It's a little sloppy but should be OK
            // (and will be faster to boot).  I'm going to do this for
            // all of the commands.

            switch (command)
            {
            case "create":

                Program.Main(
                    new string[]
                {
                    "ansible",
                    "vault",
                    "--",
                    "create",
                    $"--vault-password-file={passwordName}",
                    target
                });

                break;

            case "decrypt":

                File.Copy(source, target, overwrite: true);

                Program.Main(
                    new string[]
                {
                    "ansible",
                    "vault",
                    "--",
                    "decrypt",
                    $"--vault-password-file={passwordName}",
                    target
                });

                break;

            case "edit":

                Program.Main(
                    new string[]
                {
                    "ansible",
                    "vault",
                    $"--editor={editor}",
                    "--",
                    "edit",
                    $"--vault-password-file={passwordName}",
                    target
                });

                break;

            case "encrypt":

                File.Copy(source, target, overwrite: true);

                Program.Main(
                    new string[]
                {
                    "ansible",
                    "vault",
                    "--",
                    "encrypt",
                    $"--vault-password-file={passwordName}",
                    target
                });

                break;

            case "view":

                Program.Main(
                    new string[]
                {
                    "ansible",
                    "vault",
                    "--",
                    "view",
                    $"--vault-password-file={passwordName}",
                    target
                });

                break;

            default:

                Console.Error.WriteLine($"*** ERROR: Unexpected [{command}] command.");
                Program.Exit(1);
                break;
            }
        }
Esempio n. 3
0
        /// <inheritdoc/>
        public override void Run(CommandLine commandLine)
        {
            if (commandLine.Arguments.Length == 0 || commandLine.HasHelpOption)
            {
                Help();
                Program.Exit(0);
            }

            string folder = commandLine.Arguments.First();
            string path;

            switch (folder.ToLowerInvariant())
            {
            case "ansible-roles":

                path = HiveHelper.GetAnsibleRolesFolder();
                break;

            case "ansible-vault":

                path = HiveHelper.GetAnsiblePasswordsFolder();
                break;

            case "logins":

                path = HiveHelper.GetLoginFolder();
                break;

            case "setup":

                path = HiveHelper.GetVmTemplatesFolder();
                break;

            case "temp":

                path = Program.HiveTempFolder;
                break;

            case "vpn":

                path = HiveHelper.GetVpnFolder();
                break;

            default:

                Console.Error.WriteLine($"*** ERROR: Unexpected folder [{commandLine.Arguments.First()}].");
                Program.Exit(1);
                return;
            }

            if (commandLine.HasOption("--open"))
            {
                if (NeonHelper.IsWindows)
                {
                    Process.Start(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "explorer.exe"), $"\"{path}\"");
                }
                else if (NeonHelper.IsOSX)
                {
                    throw new NotImplementedException("$todo(jeff.lill): Implement this for OSX.");
                }
                else
                {
                    throw new NotSupportedException("[--open] option is not supported on this platform.");
                }
            }
            else
            {
                Console.Write(path);
            }
        }