cloud-init-dev team mailing list archive
-
cloud-init-dev team
-
Mailing list archive
-
Message #05512
Re: [Merge] ~chad.smith/cloud-init:feature/cli-cloudinit-query into cloud-init:master
minor comments inline.
only thought is to talk about names of things and expected values ('cloudname') and such.
we need to hhave that somewhere, and i didn't read close enough to see it.
Diff comments:
> diff --git a/cloudinit/cmd/main.py b/cloudinit/cmd/main.py
> index 0eee583..5a43702 100644
> --- a/cloudinit/cmd/main.py
> +++ b/cloudinit/cmd/main.py
> @@ -842,6 +846,12 @@ def main(sysv_args=None):
> clean_parser(parser_clean)
> parser_clean.set_defaults(
> action=('clean', handle_clean_args))
> + elif sysv_args[0] == 'query':
> + from cloudinit.cmd.query import (
why the import here?
> + get_parser as query_parser, handle_args as handle_query_args)
> + query_parser(parser_query)
> + parser_query.set_defaults(
> + action=('render', handle_query_args))
> elif sysv_args[0] == 'status':
> from cloudinit.cmd.status import (
> get_parser as status_parser, handle_status_args)
> diff --git a/cloudinit/cmd/query.py b/cloudinit/cmd/query.py
> new file mode 100644
> index 0000000..194dd73
> --- /dev/null
> +++ b/cloudinit/cmd/query.py
> @@ -0,0 +1,158 @@
> +# This file is part of cloud-init. See LICENSE file for license information.
> +
> +"""Query standardized instance metadata from the command line."""
> +
> +import argparse
> +import os
> +import six
> +import sys
> +
> +from cloudinit.handlers.jinja_template import (
> + convert_jinja_instance_data, render_jinja_payload)
> +from cloudinit.cmd.devel import addLogHandlerCLI, read_cfg_paths
> +from cloudinit import log
> +from cloudinit.sources import (
> + INSTANCE_JSON_FILE, INSTANCE_JSON_SENSITIVE_FILE, REDACT_SENSITIVE_VALUE)
> +from cloudinit import util
> +
> +NAME = 'query'
> +LOG = log.getLogger(NAME)
> +
> +
> +def get_parser(parser=None):
> + """Build or extend an arg parser for query utility.
> +
> + @param parser: Optional existing ArgumentParser instance representing the
> + query subcommand which will be extended to support the args of
> + this utility.
> +
> + @returns: ArgumentParser with proper argument configuration.
> + """
> + if not parser:
> + parser = argparse.ArgumentParser(
> + prog=NAME, description='Query cloud-init instance data')
> + parser.add_argument(
> + '-d', '--debug', action='store_true', default=False,
> + help='Add verbose messages during template render')
> + parser.add_argument(
> + '-i', '--instance-data', type=str,
> + help=('Path to instance-data.json file. Default is /run/cloud-init/%s'
> + % INSTANCE_JSON_FILE))
> + parser.add_argument(
> + '-l', '--list-keys', action='store_true', default=False,
> + help=('List query keys available at the provided instance-data'
> + ' <varname>.'))
> + parser.add_argument(
> + '-u', '--user-data', type=str,
> + help=('Path to user-data file. Default is'
> + ' /var/lib/cloud/instance/user-data.txt'))
> + parser.add_argument(
> + '-v', '--vendor-data', type=str,
> + help=('Path to vendor-data file. Default is'
> + ' /var/lib/cloud/instance/vendor-data.txt'))
> + parser.add_argument(
> + 'varname', type=str, nargs='?',
> + help=('A dot-delimited instance data variable to query from'
> + ' instance-data query. For example: v2.local_hostname'))
> + parser.add_argument(
> + '-a', '--all', action='store_true', default=False, dest='dump_all',
> + help='Dump all available instance-data')
> + parser.add_argument(
> + '-f', '--format', type=str, dest='format',
> + help=('Optionally specify a custom output format string. Any'
> + ' instance-data variable can be specified between double-curly'
> + ' braces. For example -f "{{ v2.cloud_name }}"'))
> + return parser
> +
> +
> +def handle_args(name, args):
> + """Handle calls to 'cloud-init query' as a subcommand."""
> + paths = None
> + addLogHandlerCLI(LOG, log.DEBUG if args.debug else log.WARNING)
> + if not any([args.list_keys, args.varname, args.format, args.dump_all]):
> + LOG.error(
> + 'Expected one of the options: --all, --format,'
> + ' --list-keys or varname')
> + get_parser().print_help()
> + return 1
> +
> + if not args.instance_data:
> + paths = read_cfg_paths()
> + if os.getuid() == 0:
> + default_json_fn = INSTANCE_JSON_SENSITIVE_FILE
this could just as well be done in the 'get_parser' above.
then it would show up in help.
> + else:
> + default_json_fn = INSTANCE_JSON_FILE # World readable
> + instance_data_fn = os.path.join(paths.run_dir, default_json_fn)
> + else:
> + instance_data_fn = args.instance_data
> + if not args.user_data:
> + if not paths:
> + paths = read_cfg_paths()
> + user_data_fn = paths.get_ipath('userdata')
> + else:
> + user_data_fn = args.user_data
> + if not args.vendor_data:
> + if not paths:
> + paths = read_cfg_paths()
> + vendor_data_fn = paths.get_ipath('vendordata')
> + else:
> + vendor_data_fn = args.vendor_data
> +
> + try:
> + with open(instance_data_fn) as stream:
> + instance_json = stream.read()
> + except IOError:
> + LOG.error('Missing instance-data.json file: %s', instance_data_fn)
> + return 1
> +
> + instance_data = util.load_json(instance_json)
> + if os.getuid() != 0:
> + instance_data['userdata'] = (
> + '<%s> file:%s' % (REDACT_SENSITIVE_VALUE, user_data_fn))
> + instance_data['vendordata'] = (
> + '<%s> file:%s' % (REDACT_SENSITIVE_VALUE, vendor_data_fn))
> + else:
> + instance_data['userdata'] = util.load_file(user_data_fn)
> + instance_data['vendordata'] = util.load_file(vendor_data_fn)
> + if args.format:
> + payload = '## template: jinja\n{fmt}'.format(fmt=args.format)
> + rendered_payload = render_jinja_payload(
> + payload=payload, payload_fn='query commandline',
> + instance_data=instance_data,
> + debug=True if args.debug else False)
> + if rendered_payload:
> + print(rendered_payload)
> + return 0
> + return 1
> +
> + response = convert_jinja_instance_data(instance_data)
> + if args.varname:
> + try:
> + for var in args.varname.split('.'):
> + response = response[var]
> + except KeyError:
> + LOG.error('Undefined instance-data key %s', args.varname)
> + return 1
> + if args.list_keys:
> + if not isinstance(response, dict):
> + LOG.error("--list-keys provided but '%s' is not a dict", var)
> + return 1
> + response = '\n'.join(sorted(response.keys()))
> + elif args.list_keys:
> + response = '\n'.join(sorted(response.keys()))
> + if not isinstance(response, six.string_types):
> + response = util.json_dumps(response)
> + print(response)
> + return 0
> +
> +
> +def main():
> + """Tool to query specific instance-data values."""
> + parser = get_parser()
> + sys.exit(handle_args(NAME, parser.parse_args()))
> +
> +
> +if __name__ == '__main__':
> + main()
> +
> +# vi: ts=4 expandtab
--
https://code.launchpad.net/~chad.smith/cloud-init/+git/cloud-init/+merge/354891
Your team cloud-init commiters is requested to review the proposed merge of ~chad.smith/cloud-init:feature/cli-cloudinit-query into cloud-init:master.