diff --git a/lib/ansible/cli/__init__.py b/lib/ansible/cli/__init__.py index 0106b80cf6..3b9be15760 100644 --- a/lib/ansible/cli/__init__.py +++ b/lib/ansible/cli/__init__.py @@ -262,6 +262,8 @@ class CLI(object): parser.add_option('--new-vault-password-file', dest='new_vault_password_file', help="new vault password file for rekey", action="callback", callback=CLI.expand_tilde, type=str) + parser.add_option('--output', default=None, dest='output_file', + help='output file name for encrypt or decrypt; use - for stdout') if subset_opts: diff --git a/lib/ansible/cli/vault.py b/lib/ansible/cli/vault.py index bae7377750..246a29e1e4 100644 --- a/lib/ansible/cli/vault.py +++ b/lib/ansible/cli/vault.py @@ -63,7 +63,19 @@ class VaultCLI(CLI): self.options, self.args = self.parser.parse_args() self.display.verbosity = self.options.verbosity - if len(self.args) == 0: + if self.options.output_file: + if self.action not in ['encrypt','decrypt']: + raise AnsibleOptionsError("The --output option can be used only with ansible-vault encrypt/decrypt") + + # This restriction should remain in place until it's possible to + # load multiple YAML records from a single file, or it's too easy + # to create an encrypted file that can't be read back in. But in + # the meanwhile, "cat a b c|ansible-vault encrypt --output x" is + # a workaround. + if len(self.args) > 1: + raise AnsibleOptionsError("At most one input file may be used with the --output option") + + elif len(self.args) == 0: raise AnsibleOptionsError("Vault requires at least one filename as a parameter") def run(self): @@ -87,6 +99,20 @@ class VaultCLI(CLI): self.execute() + def execute_encrypt(self): + + for f in self.args or ['-']: + self.editor.encrypt_file(f, output_file=self.options.output_file) + + self.display.display("Encryption successful", stderr=True) + + def execute_decrypt(self): + + for f in self.args or ['-']: + self.editor.decrypt_file(f, output_file=self.options.output_file) + + self.display.display("Decryption successful", stderr=True) + def execute_create(self): if len(self.args) > 1: @@ -94,13 +120,6 @@ class VaultCLI(CLI): self.editor.create_file(self.args[0]) - def execute_decrypt(self): - - for f in self.args: - self.editor.decrypt_file(f) - - self.display.display("Decryption successful", stderr=True) - def execute_edit(self): for f in self.args: self.editor.edit_file(f) @@ -110,13 +129,6 @@ class VaultCLI(CLI): for f in self.args: self.editor.view_file(f) - def execute_encrypt(self): - - for f in self.args: - self.editor.encrypt_file(f) - - self.display.display("Encryption successful", stderr=True) - def execute_rekey(self): for f in self.args: if not (os.path.isfile(f)): diff --git a/lib/ansible/parsing/vault/__init__.py b/lib/ansible/parsing/vault/__init__.py index e8636cad11..e7c60611e8 100644 --- a/lib/ansible/parsing/vault/__init__.py +++ b/lib/ansible/parsing/vault/__init__.py @@ -20,6 +20,7 @@ __metaclass__ = type import os import shlex import shutil +import sys import tempfile from io import BytesIO from subprocess import call @@ -258,21 +259,21 @@ class VaultEditor: # and restore umask os.umask(old_umask) - def encrypt_file(self, filename): + def encrypt_file(self, filename, output_file=None): check_prereqs() plaintext = self.read_data(filename) ciphertext = self.vault.encrypt(plaintext) - self.write_data(ciphertext, filename) + self.write_data(ciphertext, output_file or filename) - def decrypt_file(self, filename): + def decrypt_file(self, filename, output_file=None): check_prereqs() ciphertext = self.read_data(filename) plaintext = self.vault.decrypt(ciphertext) - self.write_data(plaintext, filename) + self.write_data(plaintext, output_file or filename) def create_file(self, filename): """ create a new encrypted file """ @@ -327,7 +328,10 @@ class VaultEditor: def read_data(self, filename): try: - f = open(filename, "rb") + if filename == '-': + f = sys.stdin + else: + f = open(filename, "rb") data = f.read() f.close() except Exception as e: @@ -336,9 +340,12 @@ class VaultEditor: return data def write_data(self, data, filename): - if os.path.isfile(filename): - os.remove(filename) - f = open(filename, "wb") + if filename == '-': + f = sys.stdout + else: + if os.path.isfile(filename): + os.remove(filename) + f = open(filename, "wb") f.write(to_bytes(data, errors='strict')) f.close()