mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-06-02 14:29:10 -07:00
Save the command line arguments into a global context
* Once cli args are parsed, they're constant. So, save the parsed args into the global context for everyone else to use them from now on. * Port cli scripts to use the CLIARGS in the context * Refactor call to parse cli args into the run() method * Fix unittests for changes to the internals of CLI arg parsing * Port callback plugins to use context.CLIARGS * Got rid of the private self._options attribute * Use context.CLIARGS in the individual callback plugins instead. * Also output positional arguments in default and unixy plugins * Code has been simplified since we're now dealing with a dict rather than Optparse.Value
This commit is contained in:
parent
c18da65089
commit
afdbb0d9d5
36 changed files with 1033 additions and 868 deletions
|
@ -1,20 +1,6 @@
|
|||
# (c) 2014, James Tanner <tanner.jc@gmail.com>
|
||||
#
|
||||
# Ansible is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# Ansible is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# ansible-vault is a script that encrypts/decrypts YAML files. See
|
||||
# https://docs.ansible.com/playbooks_vault.html for more details.
|
||||
# Copyright: (c) 2018, Ansible Project
|
||||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import (absolute_import, division, print_function)
|
||||
__metaclass__ = type
|
||||
|
@ -22,8 +8,9 @@ __metaclass__ = type
|
|||
import os
|
||||
import sys
|
||||
|
||||
from ansible.cli import CLI
|
||||
from ansible import constants as C
|
||||
from ansible import context
|
||||
from ansible.cli import CLI
|
||||
from ansible.errors import AnsibleOptionsError
|
||||
from ansible.module_utils._text import to_text, to_bytes
|
||||
from ansible.parsing.dataloader import DataLoader
|
||||
|
@ -75,7 +62,7 @@ class VaultCLI(CLI):
|
|||
if self.action in self.can_output:
|
||||
self.parser.add_option('--output', default=None, dest='output_file',
|
||||
help='output file name for encrypt or decrypt; use - for stdout',
|
||||
action="callback", callback=CLI.unfrack_path, type='string')
|
||||
action="callback", callback=self.unfrack_path, type='string')
|
||||
|
||||
# options specific to self.actions
|
||||
if self.action == "create":
|
||||
|
@ -109,9 +96,9 @@ class VaultCLI(CLI):
|
|||
action='store', type='string',
|
||||
help='the vault id used to encrypt (required if more than vault-id is provided)')
|
||||
|
||||
def parse(self):
|
||||
def init_parser(self):
|
||||
|
||||
self.parser = CLI.base_parser(
|
||||
self.parser = super(VaultCLI, self).init_parser(
|
||||
vault_opts=True,
|
||||
vault_rekey_opts=True,
|
||||
usage="usage: %%prog [%s] [options] [vaultfile.yml]" % "|".join(sorted(self.VALID_ACTIONS)),
|
||||
|
@ -121,18 +108,21 @@ class VaultCLI(CLI):
|
|||
|
||||
self.set_action()
|
||||
|
||||
super(VaultCLI, self).parse()
|
||||
self.validate_conflicts(vault_opts=True, vault_rekey_opts=True)
|
||||
return self.parser
|
||||
|
||||
display.verbosity = self.options.verbosity
|
||||
def post_process_args(self, options, args):
|
||||
options, args = super(VaultCLI, self).post_process_args(options, args)
|
||||
self.validate_conflicts(options, vault_opts=True, vault_rekey_opts=True)
|
||||
|
||||
if self.options.vault_ids:
|
||||
for vault_id in self.options.vault_ids:
|
||||
display.verbosity = options.verbosity
|
||||
|
||||
if options.vault_ids:
|
||||
for vault_id in options.vault_ids:
|
||||
if u';' in vault_id:
|
||||
raise AnsibleOptionsError("'%s' is not a valid vault id. The character ';' is not allowed in vault ids" % vault_id)
|
||||
|
||||
if self.action not in self.can_output:
|
||||
if len(self.args) == 0:
|
||||
if not args:
|
||||
raise AnsibleOptionsError("Vault requires at least one filename as a parameter")
|
||||
else:
|
||||
# This restriction should remain in place until it's possible to
|
||||
|
@ -140,17 +130,19 @@ class VaultCLI(CLI):
|
|||
# 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 self.options.output_file and len(self.args) > 1:
|
||||
if options.output_file and len(args) > 1:
|
||||
raise AnsibleOptionsError("At most one input file may be used with the --output option")
|
||||
|
||||
if self.action == 'encrypt_string':
|
||||
if '-' in self.args or len(self.args) == 0 or self.options.encrypt_string_stdin_name:
|
||||
if '-' in args or not args or options.encrypt_string_stdin_name:
|
||||
self.encrypt_string_read_stdin = True
|
||||
|
||||
# TODO: prompting from stdin and reading from stdin seem mutually exclusive, but verify that.
|
||||
if self.options.encrypt_string_prompt and self.encrypt_string_read_stdin:
|
||||
if options.encrypt_string_prompt and self.encrypt_string_read_stdin:
|
||||
raise AnsibleOptionsError('The --prompt option is not supported if also reading input from stdin')
|
||||
|
||||
return options, args
|
||||
|
||||
def run(self):
|
||||
super(VaultCLI, self).run()
|
||||
loader = DataLoader()
|
||||
|
@ -158,7 +150,7 @@ class VaultCLI(CLI):
|
|||
# set default restrictive umask
|
||||
old_umask = os.umask(0o077)
|
||||
|
||||
vault_ids = self.options.vault_ids
|
||||
vault_ids = list(context.CLIARGS['vault_ids'])
|
||||
|
||||
# there are 3 types of actions, those that just 'read' (decrypt, view) and only
|
||||
# need to ask for a password once, and those that 'write' (create, encrypt) that
|
||||
|
@ -171,26 +163,25 @@ class VaultCLI(CLI):
|
|||
# TODO: instead of prompting for these before, we could let VaultEditor
|
||||
# call a callback when it needs it.
|
||||
if self.action in ['decrypt', 'view', 'rekey', 'edit']:
|
||||
vault_secrets = self.setup_vault_secrets(loader,
|
||||
vault_ids=vault_ids,
|
||||
vault_password_files=self.options.vault_password_files,
|
||||
ask_vault_pass=self.options.ask_vault_pass)
|
||||
vault_secrets = self.setup_vault_secrets(loader, vault_ids=vault_ids,
|
||||
vault_password_files=list(context.CLIARGS['vault_password_files']),
|
||||
ask_vault_pass=context.CLIARGS['ask_vault_pass'])
|
||||
if not vault_secrets:
|
||||
raise AnsibleOptionsError("A vault password is required to use Ansible's Vault")
|
||||
|
||||
if self.action in ['encrypt', 'encrypt_string', 'create']:
|
||||
|
||||
encrypt_vault_id = None
|
||||
# no --encrypt-vault-id self.options.encrypt_vault_id for 'edit'
|
||||
# no --encrypt-vault-id context.CLIARGS['encrypt_vault_id'] for 'edit'
|
||||
if self.action not in ['edit']:
|
||||
encrypt_vault_id = self.options.encrypt_vault_id or C.DEFAULT_VAULT_ENCRYPT_IDENTITY
|
||||
encrypt_vault_id = context.CLIARGS['encrypt_vault_id'] or C.DEFAULT_VAULT_ENCRYPT_IDENTITY
|
||||
|
||||
vault_secrets = None
|
||||
vault_secrets = \
|
||||
self.setup_vault_secrets(loader,
|
||||
vault_ids=vault_ids,
|
||||
vault_password_files=self.options.vault_password_files,
|
||||
ask_vault_pass=self.options.ask_vault_pass,
|
||||
vault_password_files=context.CLIARGS['vault_password_files'],
|
||||
ask_vault_pass=context.CLIARGS['ask_vault_pass'],
|
||||
create_new_password=True)
|
||||
|
||||
if len(vault_secrets) > 1 and not encrypt_vault_id:
|
||||
|
@ -209,7 +200,7 @@ class VaultCLI(CLI):
|
|||
self.encrypt_secret = encrypt_secret[1]
|
||||
|
||||
if self.action in ['rekey']:
|
||||
encrypt_vault_id = self.options.encrypt_vault_id or C.DEFAULT_VAULT_ENCRYPT_IDENTITY
|
||||
encrypt_vault_id = context.CLIARGS['encrypt_vault_id'] or C.DEFAULT_VAULT_ENCRYPT_IDENTITY
|
||||
# print('encrypt_vault_id: %s' % encrypt_vault_id)
|
||||
# print('default_encrypt_vault_id: %s' % default_encrypt_vault_id)
|
||||
|
||||
|
@ -218,18 +209,18 @@ class VaultCLI(CLI):
|
|||
new_vault_ids = []
|
||||
if encrypt_vault_id:
|
||||
new_vault_ids = default_vault_ids
|
||||
if self.options.new_vault_id:
|
||||
new_vault_ids.append(self.options.new_vault_id)
|
||||
if context.CLIARGS['new_vault_id']:
|
||||
new_vault_ids.append(context.CLIARGS['new_vault_id'])
|
||||
|
||||
new_vault_password_files = []
|
||||
if self.options.new_vault_password_file:
|
||||
new_vault_password_files.append(self.options.new_vault_password_file)
|
||||
if context.CLIARGS['new_vault_password_file']:
|
||||
new_vault_password_files.append(context.CLIARGS['new_vault_password_file'])
|
||||
|
||||
new_vault_secrets = \
|
||||
self.setup_vault_secrets(loader,
|
||||
vault_ids=new_vault_ids,
|
||||
vault_password_files=new_vault_password_files,
|
||||
ask_vault_pass=self.options.ask_vault_pass,
|
||||
ask_vault_pass=context.CLIARGS['ask_vault_pass'],
|
||||
create_new_password=True)
|
||||
|
||||
if not new_vault_secrets:
|
||||
|
@ -257,14 +248,14 @@ class VaultCLI(CLI):
|
|||
def execute_encrypt(self):
|
||||
''' encrypt the supplied file using the provided vault secret '''
|
||||
|
||||
if len(self.args) == 0 and sys.stdin.isatty():
|
||||
if not context.CLIARGS['args'] and sys.stdin.isatty():
|
||||
display.display("Reading plaintext input from stdin", stderr=True)
|
||||
|
||||
for f in self.args or ['-']:
|
||||
for f in context.CLIARGS['args'] or ['-']:
|
||||
# Fixme: use the correct vau
|
||||
self.editor.encrypt_file(f, self.encrypt_secret,
|
||||
vault_id=self.encrypt_vault_id,
|
||||
output_file=self.options.output_file)
|
||||
output_file=context.CLIARGS['output_file'])
|
||||
|
||||
if sys.stdout.isatty():
|
||||
display.display("Encryption successful", stderr=True)
|
||||
|
@ -296,10 +287,10 @@ class VaultCLI(CLI):
|
|||
|
||||
# remove the non-option '-' arg (used to indicate 'read from stdin') from the candidate args so
|
||||
# we don't add it to the plaintext list
|
||||
args = [x for x in self.args if x != '-']
|
||||
args = [x for x in context.CLIARGS['args'] if x != '-']
|
||||
|
||||
# We can prompt and read input, or read from stdin, but not both.
|
||||
if self.options.encrypt_string_prompt:
|
||||
if context.CLIARGS['encrypt_string_prompt']:
|
||||
msg = "String to encrypt: "
|
||||
|
||||
name = None
|
||||
|
@ -332,20 +323,21 @@ class VaultCLI(CLI):
|
|||
b_plaintext = to_bytes(stdin_text)
|
||||
|
||||
# defaults to None
|
||||
name = self.options.encrypt_string_stdin_name
|
||||
name = context.CLIARGS['encrypt_string_stdin_name']
|
||||
b_plaintext_list.append((b_plaintext, self.FROM_STDIN, name))
|
||||
|
||||
# use any leftover args as strings to encrypt
|
||||
# Try to match args up to --name options
|
||||
if hasattr(self.options, 'encrypt_string_names') and self.options.encrypt_string_names:
|
||||
name_and_text_list = list(zip(self.options.encrypt_string_names, args))
|
||||
if context.CLIARGS.get('encrypt_string_names', False):
|
||||
name_and_text_list = list(zip(context.CLIARGS['encrypt_string_names'], args))
|
||||
|
||||
# Some but not enough --name's to name each var
|
||||
if len(args) > len(name_and_text_list):
|
||||
# Trying to avoid ever showing the plaintext in the output, so this warning is vague to avoid that.
|
||||
display.display('The number of --name options do not match the number of args.',
|
||||
stderr=True)
|
||||
display.display('The last named variable will be "%s". The rest will not have names.' % self.options.encrypt_string_names[-1],
|
||||
display.display('The last named variable will be "%s". The rest will not have'
|
||||
' names.' % context.CLIARGS['encrypt_string_names'][-1],
|
||||
stderr=True)
|
||||
|
||||
# Add the rest of the args without specifying a name
|
||||
|
@ -419,11 +411,11 @@ class VaultCLI(CLI):
|
|||
def execute_decrypt(self):
|
||||
''' decrypt the supplied file using the provided vault secret '''
|
||||
|
||||
if len(self.args) == 0 and sys.stdin.isatty():
|
||||
if not context.CLIARGS['args'] and sys.stdin.isatty():
|
||||
display.display("Reading ciphertext input from stdin", stderr=True)
|
||||
|
||||
for f in self.args or ['-']:
|
||||
self.editor.decrypt_file(f, output_file=self.options.output_file)
|
||||
for f in context.CLIARGS['args'] or ['-']:
|
||||
self.editor.decrypt_file(f, output_file=context.CLIARGS['output_file'])
|
||||
|
||||
if sys.stdout.isatty():
|
||||
display.display("Decryption successful", stderr=True)
|
||||
|
@ -431,21 +423,21 @@ class VaultCLI(CLI):
|
|||
def execute_create(self):
|
||||
''' create and open a file in an editor that will be encrypted with the provided vault secret when closed'''
|
||||
|
||||
if len(self.args) > 1:
|
||||
if len(context.CLIARGS['args']) > 1:
|
||||
raise AnsibleOptionsError("ansible-vault create can take only one filename argument")
|
||||
|
||||
self.editor.create_file(self.args[0], self.encrypt_secret,
|
||||
self.editor.create_file(context.CLIARGS['args'][0], self.encrypt_secret,
|
||||
vault_id=self.encrypt_vault_id)
|
||||
|
||||
def execute_edit(self):
|
||||
''' open and decrypt an existing vaulted file in an editor, that will be encrypted again when closed'''
|
||||
for f in self.args:
|
||||
for f in context.CLIARGS['args']:
|
||||
self.editor.edit_file(f)
|
||||
|
||||
def execute_view(self):
|
||||
''' open, decrypt and view an existing vaulted file using a pager using the supplied vault secret '''
|
||||
|
||||
for f in self.args:
|
||||
for f in context.CLIARGS['args']:
|
||||
# Note: vault should return byte strings because it could encrypt
|
||||
# and decrypt binary files. We are responsible for changing it to
|
||||
# unicode here because we are displaying it and therefore can make
|
||||
|
@ -456,7 +448,7 @@ class VaultCLI(CLI):
|
|||
|
||||
def execute_rekey(self):
|
||||
''' re-encrypt a vaulted file with a new secret, the previous secret is required '''
|
||||
for f in self.args:
|
||||
for f in context.CLIARGS['args']:
|
||||
# FIXME: plumb in vault_id, use the default new_vault_secret for now
|
||||
self.editor.rekey_file(f, self.new_encrypt_secret,
|
||||
self.new_encrypt_vault_id)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue