def test_render_jinja_payload_logs_jinja_vars_on_debug(self): """When debug is True, log jinja varables available.""" payload = ( "## template: jinja\n#!/bin/sh\necho hi from {{ v1.hostname }}") instance_data = {"v1": {"hostname": "foo"}, "instance-id": "iid"} expected_log = dedent("""\ DEBUG: Converted jinja variables { "hostname": "foo", "instance-id": "iid", "instance_id": "iid", "v1": { "hostname": "foo" } } """) self.assertEqual( render_jinja_payload( payload=payload, payload_fn="myfile", instance_data=instance_data, debug=True, ), "#!/bin/sh\necho hi from foo", ) self.assertEqual(expected_log, self.logs.getvalue())
def handle_args(name, args): """Handle calls to 'cloud-init query' as a subcommand.""" 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 try: instance_data = _read_instance_data( args.instance_data, args.user_data, args.vendor_data ) except (IOError, OSError): return 1 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 # If not rendering a structured format above, query output will be either: # - JSON dump of all instance-data/jinja variables # - JSON dump of a value at an dict path into the instance-data dict. # - a list of keys for a specific dict path into the instance-data dict. response = convert_jinja_instance_data(instance_data) if args.varname: jinja_vars_with_aliases = convert_jinja_instance_data( instance_data, include_key_aliases=True ) try: response = _find_instance_data_leaf_by_varname_path( jinja_vars_without_aliases=response, jinja_vars_with_aliases=jinja_vars_with_aliases, varname=args.varname, list_keys=args.list_keys, ) except (KeyError, ValueError) as e: LOG.error(e) return 1 if args.list_keys: if not isinstance(response, dict): LOG.error( "--list-keys provided but '%s' is not a dict", args.varname ) return 1 response = "\n".join(sorted(response.keys())) if not isinstance(response, str): response = util.json_dumps(response) print(response) return 0
def test_render_jinja_payload_replaces_missing_variables_and_warns(self): """Warn on missing jinja variables and replace the absent variable.""" payload = ('## template: jinja\n#!/bin/sh\necho hi from {{ NOTHERE }}') instance_data = {'v1': {'hostname': 'foo'}, 'instance-id': 'iid'} self.assertEqual( render_jinja_payload(payload=payload, payload_fn='myfile', instance_data=instance_data), '#!/bin/sh\necho hi from CI_MISSING_JINJA_VAR/NOTHERE') expected_log = ( 'WARNING: Could not render jinja template variables in file' " 'myfile': 'NOTHERE'") self.assertIn(expected_log, self.logs.getvalue())
def test_render_jinja_payload_replaces_missing_variables_and_warns(self): """Warn on missing jinja variables and replace the absent variable.""" payload = ( '## template: jinja\n#!/bin/sh\necho hi from {{ NOTHERE }}') instance_data = {'v1': {'hostname': 'foo'}, 'instance-id': 'iid'} self.assertEqual( render_jinja_payload( payload=payload, payload_fn='myfile', instance_data=instance_data), '#!/bin/sh\necho hi from CI_MISSING_JINJA_VAR/NOTHERE') expected_log = ( 'WARNING: Could not render jinja template variables in file' " 'myfile': 'NOTHERE'") self.assertIn(expected_log, self.logs.getvalue())
def test_render_jinja_payload_replaces_missing_variables_and_warns(self): """Warn on missing jinja variables and replace the absent variable.""" payload = "## template: jinja\n#!/bin/sh\necho hi from {{ NOTHERE }}" instance_data = {"v1": {"hostname": "foo"}, "instance-id": "iid"} self.assertEqual( render_jinja_payload( payload=payload, payload_fn="myfile", instance_data=instance_data, ), "#!/bin/sh\necho hi from CI_MISSING_JINJA_VAR/NOTHERE", ) expected_log = ( "WARNING: Could not render jinja template variables in file" " 'myfile': 'NOTHERE'") self.assertIn(expected_log, self.logs.getvalue())
def test_render_jinja_payload_logs_jinja_vars_on_debug(self): """When debug is True, log jinja varables available.""" payload = ( '## template: jinja\n#!/bin/sh\necho hi from {{ v1.hostname }}') instance_data = {'v1': {'hostname': 'foo'}, 'instance-id': 'iid'} expected_log = dedent("""\ DEBUG: Converted jinja variables { "hostname": "foo", "instance_id": "iid", "v1": { "hostname": "foo" } } """) self.assertEqual( render_jinja_payload( payload=payload, payload_fn='myfile', instance_data=instance_data, debug=True), '#!/bin/sh\necho hi from foo') self.assertEqual(expected_log, self.logs.getvalue())
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 uid = os.getuid() if not all([args.instance_data, args.user_data, args.vendor_data]): paths = read_cfg_paths() if args.instance_data: instance_data_fn = args.instance_data else: redacted_data_fn = os.path.join(paths.run_dir, INSTANCE_JSON_FILE) if uid == 0: sensitive_data_fn = os.path.join(paths.run_dir, INSTANCE_JSON_SENSITIVE_FILE) if os.path.exists(sensitive_data_fn): instance_data_fn = sensitive_data_fn else: LOG.warning( 'Missing root-readable %s. Using redacted %s instead.', sensitive_data_fn, redacted_data_fn) instance_data_fn = redacted_data_fn else: instance_data_fn = redacted_data_fn if args.user_data: user_data_fn = args.user_data else: user_data_fn = os.path.join(paths.instance_link, 'user-data.txt') if args.vendor_data: vendor_data_fn = args.vendor_data else: vendor_data_fn = os.path.join(paths.instance_link, 'vendor-data.txt') try: instance_json = util.load_file(instance_data_fn) except (IOError, OSError) as e: if e.errno == EACCES: LOG.error("No read permission on '%s'. Try sudo", instance_data_fn) else: LOG.error('Missing instance-data file: %s', instance_data_fn) return 1 instance_data = util.load_json(instance_json) if uid != 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'] = load_userdata(user_data_fn) instance_data['vendordata'] = load_userdata(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, str): response = util.json_dumps(response) print(response) return 0
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 uid = os.getuid() if not all([args.instance_data, args.user_data, args.vendor_data]): paths = read_cfg_paths() if not args.instance_data: if uid == 0: default_json_fn = INSTANCE_JSON_SENSITIVE_FILE 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: user_data_fn = os.path.join(paths.instance_link, 'user-data.txt') else: user_data_fn = args.user_data if not args.vendor_data: vendor_data_fn = os.path.join(paths.instance_link, 'vendor-data.txt') else: vendor_data_fn = args.vendor_data try: instance_json = util.load_file(instance_data_fn) except IOError: LOG.error('Missing instance-data.json file: %s', instance_data_fn) return 1 instance_data = util.load_json(instance_json) if uid != 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