Example #1
0
    def test_gpg_update_recipients(self):
        """
        update existing base64'd secret with another recipient, confirm content is the same
        """
        tag = '?{gpg:secret/sauce_with_fingerprints}'
        secret = GPGSecret("super secret value",
                           [{
                               'fingerprint': KEY.fingerprint
                           }],
                           encode_base64=True)
        REF_CONTROLLER[tag] = secret
        ref = REF_CONTROLLER[tag]
        self.assertTrue(
            os.path.isfile(
                os.path.join(REFS_HOME, 'secret/sauce_with_fingerprints')))
        self.assertTrue(len(ref.recipients), 1)

        file_with_secret_tags = tempfile.mktemp()
        with open(file_with_secret_tags, 'w') as fp:
            fp.write(
                'I am a file with a ?{gpg:secret/sauce_with_fingerprints}')

        new_recipients = [{
            'fingerprint': KEY.fingerprint
        }, {
            'fingerprint': KEY2.fingerprint
        }]
        ref.update_recipients(new_recipients)
        ref = REF_CONTROLLER[tag]

        self.assertTrue(len(ref.recipients), 2)
        revealed = REVEALER.reveal_raw_file(file_with_secret_tags)
        self.assertEqual("I am a file with a c3VwZXIgc2VjcmV0IHZhbHVl",
                         revealed)
Example #2
0
    def test_gpg_write_reveal(self):
        "write secret, confirm secret file exists, reveal and compare content"
        tag = '?{gpg:secret/sauce}'
        REF_CONTROLLER[tag] = GPGSecret("super secret value", [{'fingerprint': KEY.fingerprint}])
        self.assertTrue(os.path.isfile(os.path.join(REFS_HOME, 'secret/sauce')))

        file_with_secret_tags = tempfile.mktemp()
        with open(file_with_secret_tags, 'w') as fp:
            fp.write('I am a file with a ?{gpg:secret/sauce}')
        revealed = REVEALER.reveal_raw_file(file_with_secret_tags)
        self.assertEqual("I am a file with a super secret value", revealed)
Example #3
0
def secret_write(args, ref_controller):
    "Write secret to ref_controller based on cli args"
    token_name = args.write
    file_name = args.file
    data = None

    if file_name is None:
        fatal_error('--file is required with --write')
    if file_name == '-':
        data = ''
        for line in sys.stdin:
            data += line
    else:
        with open(file_name) as fp:
            data = fp.read()

    if token_name.startswith("gpg:"):
        type_name, token_path = token_name.split(":")
        recipients = [dict((("name", name), )) for name in args.recipients]
        if args.target_name:
            inv = inventory_reclass(args.inventory_path)
            try:
                recipients = inv['nodes'][args.target_name]['parameters'][
                    'kapitan']['secrets']['gpg']['recipients']
            except KeyError:
                # TODO: Keeping gpg recipients backwards-compatible until we make a breaking release
                logger.warning(
                    "WARNING: parameters.kapitan.secrets.recipients is deprecated, "
                    +
                    "please move them to parameters.kapitan.secrets.gpg.recipients"
                )
                recipients = inv['nodes'][args.target_name]['parameters'][
                    'kapitan']['secrets']['recipients']
        secret_obj = GPGSecret(data, recipients, encode_base64=args.base64)
        tag = '?{{gpg:{}}}'.format(token_path)
        ref_controller[tag] = secret_obj

    elif token_name.startswith("gkms:"):
        type_name, token_path = token_name.split(":")
        key = args.key
        if args.target_name:
            inv = inventory_reclass(args.inventory_path)
            key = inv['nodes'][args.target_name]['parameters']['kapitan'][
                'secrets']['gkms']['key']
        if not key:
            raise KapitanError(
                "No KMS key specified. Use --key or specify in parameters.kapitan.secrets.gkms.key and use --target"
            )
        secret_obj = GoogleKMSSecret(data, key, encode_base64=args.base64)
        tag = '?{{gkms:{}}}'.format(token_path)
        ref_controller[tag] = secret_obj
    else:
        fatal_error("Invalid token: {}".format(token_name))
Example #4
0
    def test_gpg_write_embedded_reveal(self):
        "write and compile embedded secret, confirm secret file exists, reveal and compare content"
        tag = "?{gpg:secret/sauce}"
        REF_CONTROLLER_EMBEDDED[tag] = GPGSecret("super secret value", [{"fingerprint": KEY.fingerprint}])
        self.assertTrue(os.path.isfile(os.path.join(REFS_HOME, "secret/sauce")))
        ref_obj = REF_CONTROLLER_EMBEDDED[tag]

        file_with_secret_tags = tempfile.mktemp()
        with open(file_with_secret_tags, "w") as fp:
            fp.write("I am a file with a {}".format(ref_obj.compile()))
        revealed = REVEALER_EMBEDDED.reveal_raw_file(file_with_secret_tags)
        self.assertEqual("I am a file with a super secret value", revealed)
Example #5
0
    def test_gpg_base64_write_reveal(self):
        """
        write secret for base64 encoded content, confirm secret file exists,
        reveal and compare content
        """
        tag = '?{gpg:secret/sauce2}'
        REF_CONTROLLER[tag] = GPGSecret("super secret value", [{'fingerprint': KEY.fingerprint}],
                                        encode_base64=True)
        self.assertTrue(os.path.isfile(os.path.join(REFS_HOME, 'secret/sauce2')))

        file_with_secret_tags = tempfile.mktemp()
        with open(file_with_secret_tags, 'w') as fp:
            fp.write('I am a file with a ?{gpg:secret/sauce2}')
        revealed = REVEALER.reveal_raw_file(file_with_secret_tags)
        self.assertEqual("I am a file with a c3VwZXIgc2VjcmV0IHZhbHVl", revealed)
Example #6
0
    def test_gpg_write_ref_path_reveal(self):
        """
        write secret content, confirm secret file exists,
        reveal from ref file path and compare content
        """
        tag = "?{gpg:secret/sauce3}"
        REF_CONTROLLER_EMBEDDED[tag] = GPGSecret(
            "super secret value", [{
                "fingerprint": KEY.fingerprint
            }])
        reffile_name = os.path.join(REFS_HOME, "secret/sauce3")
        self.assertTrue(os.path.isfile(reffile_name))

        ref = REF_CONTROLLER.ref_from_ref_file(reffile_name)
        revealed = ref.reveal()
        self.assertEqual("super secret value", revealed)
Example #7
0
def ref_write(args, ref_controller):
    "Write ref to ref_controller based on cli args"
    token_name = args.write
    file_name = args.file
    data = None

    if file_name is None:
        fatal_error("--file is required with --write")
    if file_name == "-":
        data = ""
        for line in sys.stdin:
            data += line
    else:
        with open(file_name) as fp:
            data = fp.read()

    if token_name.startswith("gpg:"):
        type_name, token_path = token_name.split(":")
        recipients = [dict((("name", name), )) for name in args.recipients]
        if args.target_name:
            inv = inventory_reclass(args.inventory_path)
            kap_inv_params = inv["nodes"][
                args.target_name]["parameters"]["kapitan"]
            if "secrets" not in kap_inv_params:
                raise KapitanError(
                    "parameters.kapitan.secrets not defined in inventory of target {}"
                    .format(args.target_name))

            recipients = kap_inv_params["secrets"]["gpg"]["recipients"]
        if not recipients:
            raise KapitanError(
                "No GPG recipients specified. Use --recipients or specify them in "
                +
                "parameters.kapitan.secrets.gpg.recipients and use --target-name"
            )
        secret_obj = GPGSecret(data, recipients, encode_base64=args.base64)
        tag = "?{{gpg:{}}}".format(token_path)
        ref_controller[tag] = secret_obj

    elif token_name.startswith("gkms:"):
        type_name, token_path = token_name.split(":")
        key = args.key
        if args.target_name:
            inv = inventory_reclass(args.inventory_path)
            kap_inv_params = inv["nodes"][
                args.target_name]["parameters"]["kapitan"]
            if "secrets" not in kap_inv_params:
                raise KapitanError(
                    "parameters.kapitan.secrets not defined in inventory of target {}"
                    .format(args.target_name))

            key = kap_inv_params["secrets"]["gkms"]["key"]
        if not key:
            raise KapitanError(
                "No KMS key specified. Use --key or specify it in parameters.kapitan.secrets.gkms.key and use --target-name"
            )
        secret_obj = GoogleKMSSecret(data, key, encode_base64=args.base64)
        tag = "?{{gkms:{}}}".format(token_path)
        ref_controller[tag] = secret_obj

    elif token_name.startswith("awskms:"):
        type_name, token_path = token_name.split(":")
        key = args.key
        if args.target_name:
            inv = inventory_reclass(args.inventory_path)
            kap_inv_params = inv["nodes"][
                args.target_name]["parameters"]["kapitan"]
            if "secrets" not in kap_inv_params:
                raise KapitanError(
                    "parameters.kapitan.secrets not defined in inventory of target {}"
                    .format(args.target_name))

            key = kap_inv_params["secrets"]["awskms"]["key"]
        if not key:
            raise KapitanError(
                "No KMS key specified. Use --key or specify it in parameters.kapitan.secrets.awskms.key and use --target-name"
            )
        secret_obj = AWSKMSSecret(data, key, encode_base64=args.base64)
        tag = "?{{awskms:{}}}".format(token_path)
        ref_controller[tag] = secret_obj

    elif token_name.startswith("base64:"):
        type_name, token_path = token_name.split(":")
        _data = data.encode()
        encoding = "original"
        if args.base64:
            _data = base64.b64encode(_data).decode()
            _data = _data.encode()
            encoding = "base64"
        ref_obj = Base64Ref(_data, encoding=encoding)
        tag = "?{{base64:{}}}".format(token_path)
        ref_controller[tag] = ref_obj

    elif token_name.startswith("vaultkv:"):
        type_name, token_path = token_name.split(":")
        _data = data.encode()
        vault_params = {}
        encoding = "original"
        if args.target_name:
            inv = inventory_reclass(args.inventory_path)
            kap_inv_params = inv["nodes"][
                args.target_name]["parameters"]["kapitan"]
            if "secrets" not in kap_inv_params:
                raise KapitanError(
                    "parameters.kapitan.secrets not defined in inventory of target {}"
                    .format(args.target_name))

            vault_params = kap_inv_params["secrets"]["vaultkv"]
        if args.vault_auth:
            vault_params["auth"] = args.vault_auth
        if vault_params.get("auth") is None:
            raise KapitanError(
                "No Authentication type parameter specified. Specify it"
                " in parameters.kapitan.secrets.vaultkv.auth and use --target-name or use --vault-auth"
            )

        secret_obj = VaultSecret(_data, vault_params)
        tag = "?{{vaultkv:{}}}".format(token_path)
        ref_controller[tag] = secret_obj

    elif token_name.startswith("plain:"):
        type_name, token_path = token_name.split(":")
        _data = data.encode()
        encoding = "original"
        if args.base64:
            _data = base64.b64encode(_data).decode()
            _data = _data.encode()
            encoding = "base64"
        ref_obj = PlainRef(_data, encoding=encoding)
        tag = "?{{plain:{}}}".format(token_path)
        ref_controller[tag] = ref_obj

    else:
        fatal_error(
            "Invalid token: {name}. Try using gpg/gkms/awskms/vaultkv/base64/plain:{name}"
            .format(name=token_name))
Example #8
0
def secret_write(args, ref_controller):
    "Write secret to ref_controller based on cli args"
    token_name = args.write
    file_name = args.file
    data = None

    if file_name is None:
        fatal_error('--file is required with --write')
    if file_name == '-':
        data = ''
        for line in sys.stdin:
            data += line
    else:
        with open(file_name) as fp:
            data = fp.read()

    if token_name.startswith("gpg:"):
        type_name, token_path = token_name.split(":")
        recipients = [dict((("name", name), )) for name in args.recipients]
        if args.target_name:
            inv = inventory_reclass(args.inventory_path)
            kap_inv_params = inv['nodes'][
                args.target_name]['parameters']['kapitan']
            if 'secrets' not in kap_inv_params:
                raise KapitanError(
                    "parameters.kapitan.secrets not defined in {}".format(
                        args.target_name))

            recipients = kap_inv_params['secrets']['gpg']['recipients']
        if not recipients:
            raise KapitanError(
                "No GPG recipients specified. Use --recipients or specify them in "
                + "parameters.kapitan.secrets.gpg.recipients and use --target")
        secret_obj = GPGSecret(data, recipients, encode_base64=args.base64)
        tag = '?{{gpg:{}}}'.format(token_path)
        ref_controller[tag] = secret_obj

    elif token_name.startswith("gkms:"):
        type_name, token_path = token_name.split(":")
        key = args.key
        if args.target_name:
            inv = inventory_reclass(args.inventory_path)
            kap_inv_params = inv['nodes'][
                args.target_name]['parameters']['kapitan']
            if 'secrets' not in kap_inv_params:
                raise KapitanError(
                    "parameters.kapitan.secrets not defined in {}".format(
                        args.target_name))

            key = kap_inv_params['secrets']['gkms']['key']
        if not key:
            raise KapitanError(
                "No KMS key specified. Use --key or specify it in parameters.kapitan.secrets.gkms.key and use --target"
            )
        secret_obj = GoogleKMSSecret(data, key, encode_base64=args.base64)
        tag = '?{{gkms:{}}}'.format(token_path)
        ref_controller[tag] = secret_obj

    elif token_name.startswith("awskms:"):
        type_name, token_path = token_name.split(":")
        key = args.key
        if args.target_name:
            inv = inventory_reclass(args.inventory_path)
            kap_inv_params = inv['nodes'][
                args.target_name]['parameters']['kapitan']
            if 'secrets' not in kap_inv_params:
                raise KapitanError(
                    "parameters.kapitan.secrets not defined in {}".format(
                        args.target_name))

            key = kap_inv_params['secrets']['awskms']['key']
        if not key:
            raise KapitanError(
                "No KMS key specified. Use --key or specify it in parameters.kapitan.secrets.awskms.key and use --target"
            )
        secret_obj = AWSKMSSecret(data, key, encode_base64=args.base64)
        tag = '?{{awskms:{}}}'.format(token_path)
        ref_controller[tag] = secret_obj

    elif token_name.startswith("ref:"):
        type_name, token_path = token_name.split(":")
        _data = data.encode()
        encoding = 'original'
        if args.base64:
            _data = base64.b64encode(_data).decode()
            _data = _data.encode()
            encoding = 'base64'
        ref_obj = Ref(_data, encoding=encoding)
        tag = '?{{ref:{}}}'.format(token_path)
        ref_controller[tag] = ref_obj

    else:
        fatal_error(
            "Invalid token: {name}. Try using gpg/gkms/awskms/ref:{name}".
            format(name=token_name))
Example #9
0
def main():
    """main function for command line usage"""
    parser = argparse.ArgumentParser(prog=PROJECT_NAME,
                                     description=DESCRIPTION)
    parser.add_argument('--version', action='version', version=VERSION)
    subparser = parser.add_subparsers(help="commands")

    eval_parser = subparser.add_parser('eval', help='evaluate jsonnet file')
    eval_parser.add_argument('jsonnet_file', type=str)
    eval_parser.add_argument('--output',
                             type=str,
                             choices=('yaml', 'json', 'str'),
                             default=from_dot_kapitan('eval', 'output',
                                                      'yaml'),
                             help='set output format, default is "yaml"')
    eval_parser.add_argument('--vars',
                             type=str,
                             default=from_dot_kapitan('eval', 'vars', []),
                             nargs='*',
                             metavar='VAR',
                             help='set variables')
    eval_parser.add_argument('--search-paths',
                             '-J',
                             type=str,
                             nargs='+',
                             default=from_dot_kapitan('eval', 'search-paths',
                                                      ['.']),
                             metavar='JPATH',
                             help='set search paths, default is ["."]')

    compile_parser = subparser.add_parser('compile', help='compile targets')
    compile_parser.add_argument('--search-paths',
                                '-J',
                                type=str,
                                nargs='+',
                                default=from_dot_kapitan(
                                    'compile', 'search-paths', ['.']),
                                metavar='JPATH',
                                help='set search paths, default is ["."]')
    compile_parser.add_argument('--verbose',
                                '-v',
                                help='set verbose mode',
                                action='store_true',
                                default=from_dot_kapitan(
                                    'compile', 'verbose', False))
    compile_parser.add_argument('--prune',
                                help='prune jsonnet output',
                                action='store_true',
                                default=from_dot_kapitan(
                                    'compile', 'prune', False))
    compile_parser.add_argument('--quiet',
                                help='set quiet mode, only critical output',
                                action='store_true',
                                default=from_dot_kapitan(
                                    'compile', 'quiet', False))
    compile_parser.add_argument('--output-path',
                                type=str,
                                default=from_dot_kapitan(
                                    'compile', 'output-path', '.'),
                                metavar='PATH',
                                help='set output path, default is "."')
    compile_parser.add_argument('--targets',
                                '-t',
                                help='targets to compile, default is all',
                                type=str,
                                nargs='+',
                                default=from_dot_kapitan(
                                    'compile', 'targets', []),
                                metavar='TARGET')
    compile_parser.add_argument(
        '--parallelism',
        '-p',
        type=int,
        default=from_dot_kapitan('compile', 'parallelism', 4),
        metavar='INT',
        help='Number of concurrent compile processes, default is 4')
    compile_parser.add_argument(
        '--indent',
        '-i',
        type=int,
        default=from_dot_kapitan('compile', 'indent', 2),
        metavar='INT',
        help='Indentation spaces for YAML/JSON, default is 2')
    compile_parser.add_argument(
        '--secrets-path',
        help='set secrets path, default is "./secrets"',
        default=from_dot_kapitan('compile', 'secrets-path', './secrets'))
    compile_parser.add_argument(
        '--reveal',
        help='reveal secrets (warning: this will write sensitive data)',
        action='store_true',
        default=from_dot_kapitan('compile', 'reveal', False))
    compile_parser.add_argument(
        '--inventory-path',
        default=from_dot_kapitan('compile', 'inventory-path', './inventory'),
        help='set inventory path, default is "./inventory"')
    compile_parser.add_argument(
        '--cache',
        '-c',
        help='enable compilation caching to .kapitan_cache, default is False',
        action='store_true',
        default=from_dot_kapitan('compile', 'cache', False))
    compile_parser.add_argument(
        '--cache-paths',
        type=str,
        nargs='+',
        default=from_dot_kapitan('compile', 'cache-paths', []),
        metavar='PATH',
        help='cache additional paths to .kapitan_cache, default is []')
    compile_parser.add_argument('--ignore-version-check',
                                help='ignore the version from .kapitan',
                                action='store_true',
                                default=from_dot_kapitan(
                                    'compile', 'ignore-version-check', False))

    inventory_parser = subparser.add_parser('inventory', help='show inventory')
    inventory_parser.add_argument(
        '--target-name',
        '-t',
        default=from_dot_kapitan('inventory', 'target-name', ''),
        help='set target name, default is all targets')
    inventory_parser.add_argument(
        '--inventory-path',
        default=from_dot_kapitan('inventory', 'inventory-path', './inventory'),
        help='set inventory path, default is "./inventory"')
    inventory_parser.add_argument('--flat',
                                  '-F',
                                  help='flatten nested inventory variables',
                                  action='store_true',
                                  default=from_dot_kapitan(
                                      'inventory', 'flat', False))
    inventory_parser.add_argument(
        '--pattern',
        '-p',
        default=from_dot_kapitan('inventory', 'pattern', ''),
        help=
        'filter pattern (e.g. parameters.mysql.storage_class, or storage_class,'
        + ' or storage_*), default is ""')
    inventory_parser.add_argument('--verbose',
                                  '-v',
                                  help='set verbose mode',
                                  action='store_true',
                                  default=from_dot_kapitan(
                                      'inventory', 'verbose', False))

    searchvar_parser = subparser.add_parser(
        'searchvar', help='show all inventory files where var is declared')
    searchvar_parser.add_argument(
        'searchvar',
        type=str,
        metavar='VARNAME',
        help=
        'e.g. parameters.mysql.storage_class, or storage_class, or storage_*')
    searchvar_parser.add_argument(
        '--inventory-path',
        default=from_dot_kapitan('searchvar', 'inventory-path', './inventory'),
        help='set inventory path, default is "./inventory"')
    searchvar_parser.add_argument('--verbose',
                                  '-v',
                                  help='set verbose mode',
                                  action='store_true',
                                  default=from_dot_kapitan(
                                      'searchvar', 'verbose', False))
    searchvar_parser.add_argument('--pretty-print',
                                  '-p',
                                  help='Pretty print content of var',
                                  action='store_true',
                                  default=from_dot_kapitan(
                                      'searchvar', 'pretty-print', False))

    secrets_parser = subparser.add_parser('secrets', help='manage secrets')
    secrets_parser.add_argument(
        '--write',
        '-w',
        help='write secret token',
        metavar='TOKENNAME',
    )
    secrets_parser.add_argument(
        '--update',
        help='update recipients for secret token',
        metavar='TOKENNAME',
    )
    secrets_parser.add_argument('--update-targets',
                                action='store_true',
                                default=from_dot_kapitan(
                                    'secrets', 'update-targets', False),
                                help='update target secrets')
    secrets_parser.add_argument('--validate-targets',
                                action='store_true',
                                default=from_dot_kapitan(
                                    'secrets', 'validate-targets', False),
                                help='validate target secrets')
    secrets_parser.add_argument('--base64',
                                '-b64',
                                help='base64 encode file content',
                                action='store_true',
                                default=from_dot_kapitan(
                                    'secrets', 'base64', False))
    secrets_parser.add_argument('--reveal',
                                '-r',
                                help='reveal secrets',
                                action='store_true',
                                default=from_dot_kapitan(
                                    'secrets', 'reveal', False))
    secrets_parser.add_argument(
        '--file',
        '-f',
        help='read file or directory, set "-" for stdin',
        metavar='FILENAME')
    secrets_parser.add_argument('--target-name',
                                '-t',
                                help='grab recipients from target name')
    secrets_parser.add_argument(
        '--inventory-path',
        default=from_dot_kapitan('secrets', 'inventory-path', './inventory'),
        help='set inventory path, default is "./inventory"')
    secrets_parser.add_argument('--recipients',
                                '-R',
                                help='set recipients',
                                type=str,
                                nargs='+',
                                default=from_dot_kapitan(
                                    'secrets', 'recipients', []),
                                metavar='RECIPIENT')
    secrets_parser.add_argument(
        '--secrets-path',
        help='set secrets path, default is "./secrets"',
        default=from_dot_kapitan('secrets', 'secrets-path', './secrets'))
    secrets_parser.add_argument('--backend',
                                help='set secrets backend, default is "gpg"',
                                type=str,
                                choices=('gpg', ),
                                default=from_dot_kapitan(
                                    'secrets', 'backend', 'gpg'))
    secrets_parser.add_argument(
        '--verbose',
        '-v',
        help='set verbose mode (warning: this will show sensitive data)',
        action='store_true',
        default=from_dot_kapitan('secrets', 'verbose', False))

    args = parser.parse_args()

    logger.debug('Running with args: %s', args)

    try:
        cmd = sys.argv[1]
    except IndexError:
        parser.print_help()
        sys.exit(1)

    if cmd == 'eval':
        file_path = args.jsonnet_file
        search_paths = [os.path.abspath(path) for path in args.search_paths]
        ext_vars = {}
        if args.vars:
            ext_vars = dict(var.split('=') for var in args.vars)
        json_output = None

        def _search_imports(cwd, imp):
            return search_imports(cwd, imp, search_paths)

        json_output = jsonnet_file(
            file_path,
            import_callback=_search_imports,
            native_callbacks=resource_callbacks(search_paths),
            ext_vars=ext_vars)
        if args.output == 'yaml':
            json_obj = json.loads(json_output)
            yaml.safe_dump(json_obj, sys.stdout, default_flow_style=False)
        elif json_output:
            print(json_output)

    elif cmd == 'compile':
        if args.verbose:
            logging.basicConfig(
                level=logging.DEBUG,
                format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
        elif args.quiet:
            logging.basicConfig(level=logging.CRITICAL, format="%(message)s")
        else:
            logging.basicConfig(level=logging.INFO, format="%(message)s")

        search_paths = [os.path.abspath(path) for path in args.search_paths]

        if not args.ignore_version_check:
            check_version()

        ref_controller = RefController(args.secrets_path)

        compile_targets(args.inventory_path,
                        search_paths,
                        args.output_path,
                        args.parallelism,
                        args.targets,
                        ref_controller,
                        prune=(args.prune),
                        indent=args.indent,
                        reveal=args.reveal,
                        cache=args.cache,
                        cache_paths=args.cache_paths)

    elif cmd == 'inventory':
        if args.verbose:
            logging.basicConfig(
                level=logging.DEBUG,
                format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
        else:
            logging.basicConfig(level=logging.INFO, format="%(message)s")

        if args.pattern and args.target_name == '':
            parser.error("--pattern requires --target_name")
        try:
            inv = inventory_reclass(args.inventory_path)
            if args.target_name != '':
                inv = inv['nodes'][args.target_name]
                if args.pattern != '':
                    pattern = args.pattern.split(".")
                    inv = deep_get(inv, pattern)
            if args.flat:
                inv = flatten_dict(inv)
                yaml.dump(inv, sys.stdout, width=10000)
            else:
                yaml.dump(inv,
                          sys.stdout,
                          Dumper=PrettyDumper,
                          default_flow_style=False)
        except Exception as e:
            if not isinstance(e, KapitanError):
                logger.error("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~")
                traceback.print_exc()
            sys.exit(1)

    elif cmd == 'searchvar':
        if args.verbose:
            logging.basicConfig(
                level=logging.DEBUG,
                format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
        else:
            logging.basicConfig(level=logging.INFO, format="%(message)s")

        searchvar(args.searchvar, args.inventory_path, args.pretty_print)

    elif cmd == 'secrets':
        if args.verbose:
            logging.basicConfig(
                level=logging.DEBUG,
                format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
        else:
            logging.basicConfig(level=logging.INFO, format="%(message)s")

        ref_controller = RefController(args.secrets_path)

        if args.write is not None:
            if args.file is None:
                parser.error('--file is required with --write')
            data = None
            recipients = [dict((("name", name), )) for name in args.recipients]
            if args.target_name:
                inv = inventory_reclass(args.inventory_path)
                # TODO move into kapitan:secrets:gpg:recipients key
                recipients = inv['nodes'][args.target_name]['parameters'][
                    'kapitan']['secrets']['recipients']
            if args.file == '-':
                data = ''
                for line in sys.stdin:
                    data += line
            else:
                with open(args.file) as fp:
                    data = fp.read()
            # TODO deprecate backend and move to passing ref tags in command line
            if args.backend == "gpg":
                secret_obj = GPGSecret(data, recipients, args.base64)
                ref_controller.backends['gpg'][args.write] = secret_obj
        elif args.reveal:
            revealer = Revealer(ref_controller)
            if args.file is None:
                parser.error('--file is required with --reveal')
            if args.file == '-':
                # TODO deal with RefHashMismatchError or KeyError exceptions
                out = revealer.reveal_raw_file(None)
                sys.stdout.write(out)
            elif args.file:
                for rev_obj in revealer.reveal_path(args.file):
                    sys.stdout.write(rev_obj.content)
        elif args.update:
            # update recipients for secret tag
            # args.recipients is a list, convert to recipients dict
            recipients = [
                dict([
                    ("name", name),
                ]) for name in args.recipients
            ]
            if args.target_name:
                inv = inventory_reclass(args.inventory_path)
                # TODO move into kapitan:secrets:gpg:recipients key
                recipients = inv['nodes'][args.target_name]['parameters'][
                    'kapitan']['secrets']['recipients']
            if args.backend == "gpg":
                secret_obj = ref_controller.backends['gpg'][args.update]
                secret_obj.update_recipients(recipients)
                ref_controller.backends['gpg'][args.update] = secret_obj
        elif args.update_targets or args.validate_targets:
            # update recipients for all secrets in secrets_path
            # use --secrets-path to set scanning path
            inv = inventory_reclass(args.inventory_path)
            targets = set(inv['nodes'].keys())
            secrets_path = os.path.abspath(args.secrets_path)
            target_token_paths = search_target_token_paths(
                secrets_path, targets)
            ret_code = 0
            # override gpg backend for new secrets_path
            ref_controller.register_backend(GPGBackend(secrets_path))
            for target_name, token_paths in target_token_paths.items():
                try:
                    recipients = inv['nodes'][target_name]['parameters'][
                        'kapitan']['secrets']['recipients']
                    for token_path in token_paths:
                        secret_obj = ref_controller.backends['gpg'][token_path]
                        target_fingerprints = set(
                            lookup_fingerprints(recipients))
                        secret_fingerprints = set(
                            lookup_fingerprints(secret_obj.recipients))
                        if target_fingerprints != secret_fingerprints:
                            if args.validate_targets:
                                logger.info("%s recipient mismatch",
                                            token_path)
                                ret_code = 1
                            else:
                                new_recipients = [
                                    dict([
                                        ("fingerprint", f),
                                    ]) for f in target_fingerprints
                                ]
                                secret_obj.update_recipients(new_recipients)
                                ref_controller.backends['gpg'][
                                    token_path] = secret_obj
                except KeyError:
                    logger.debug(
                        "secret_gpg_update_target: target: %s has no inventory recipients, skipping",
                        target_name)
            sys.exit(ret_code)