Exemplo n.º 1
0
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
Exemplo n.º 2
0
    def test_convert_instance_data_promotes_versioned_keys_to_top_level(self):
        """Any versioned keys are promoted as top-level keys

        This provides any cloud-init standardized keys up at a top-level to
        allow ease of reference for users. Intsead of v1.availability_zone,
        the name availability_zone can be used in templates.
        """
        data = {
            'ds': {
                'dskey1': 1,
                'dskey2': 2
            },
            'v1': {
                'v1key1': 'v1.1'
            },
            'v2': {
                'v2key1': 'v2.1'
            }
        }
        expected_data = copy.deepcopy(data)
        expected_data.update({'v1key1': 'v1.1', 'v2key1': 'v2.1'})

        converted_data = convert_jinja_instance_data(data=data)
        self.assertCountEqual(['ds', 'v1', 'v2', 'v1key1', 'v2key1'],
                              converted_data.keys())
        self.assertEqual(expected_data, converted_data)
Exemplo n.º 3
0
    def test_convert_instance_data_promotes_versioned_keys_to_top_level(self):
        """Any versioned keys are promoted as top-level keys

        This provides any cloud-init standardized keys up at a top-level to
        allow ease of reference for users. Intsead of v1.availability_zone,
        the name availability_zone can be used in templates.
        """
        data = {
            "ds": {
                "dskey1": 1,
                "dskey2": 2
            },
            "v1": {
                "v1key1": "v1.1"
            },
            "v2": {
                "v2key1": "v2.1"
            },
        }
        expected_data = copy.deepcopy(data)
        expected_data.update({"v1key1": "v1.1", "v2key1": "v2.1"})

        converted_data = convert_jinja_instance_data(data=data)
        assert sorted(["ds", "v1", "v2", "v1key1",
                       "v2key1"]) == sorted(converted_data.keys())
        assert expected_data == converted_data
Exemplo n.º 4
0
    def test_convert_instance_data_decodes_decode_paths(self):
        """Any decode_paths provided are decoded by convert_instance_data."""
        data = {"key1": {"subkey1": "aGkgbW9t"}, "key2": "aGkgZGFk"}
        expected_data = copy.deepcopy(data)
        expected_data["key1"]["subkey1"] = "hi mom"

        converted_data = convert_jinja_instance_data(
            data=data, decode_paths=("key1/subkey1", ))
        assert expected_data == converted_data
Exemplo n.º 5
0
    def test_convert_instance_data_decodes_decode_paths(self):
        """Any decode_paths provided are decoded by convert_instance_data."""
        data = {'key1': {'subkey1': 'aGkgbW9t'}, 'key2': 'aGkgZGFk'}
        expected_data = copy.deepcopy(data)
        expected_data['key1']['subkey1'] = 'hi mom'

        converted_data = convert_jinja_instance_data(
            data=data, decode_paths=('key1/subkey1', ))
        self.assertEqual(expected_data, converted_data)
Exemplo n.º 6
0
 def test_convert_instance_data_hyphens_to_underscores(self):
     """Replace hyphenated keys with underscores in instance-data."""
     data = {'hyphenated-key': 'hyphenated-val',
             'underscore_delim_key': 'underscore_delimited_val'}
     expected_data = {'hyphenated_key': 'hyphenated-val',
                      'underscore_delim_key': 'underscore_delimited_val'}
     self.assertEqual(
         expected_data,
         convert_jinja_instance_data(data=data))
 def test_convert_instance_data_hyphens_to_underscores(self):
     """Replace hyphenated keys with underscores in instance-data."""
     data = {'hyphenated-key': 'hyphenated-val',
             'underscore_delim_key': 'underscore_delimited_val'}
     expected_data = {'hyphenated_key': 'hyphenated-val',
                      'underscore_delim_key': 'underscore_delimited_val'}
     self.assertEqual(
         expected_data,
         convert_jinja_instance_data(data=data))
    def test_convert_instance_data_decodes_decode_paths(self):
        """Any decode_paths provided are decoded by convert_instance_data."""
        data = {'key1': {'subkey1': 'aGkgbW9t'}, 'key2': 'aGkgZGFk'}
        expected_data = copy.deepcopy(data)
        expected_data['key1']['subkey1'] = 'hi mom'

        converted_data = convert_jinja_instance_data(
            data=data, decode_paths=('key1/subkey1',))
        self.assertEqual(
            expected_data,
            converted_data)
Exemplo n.º 9
0
    def test_convert_instance_data_most_recent_version_of_promoted_keys(self):
        """The most-recent versioned key value is promoted to top-level."""
        data = {'v1': {'key1': 'old v1 key1', 'key2': 'old v1 key2'},
                'v2': {'key1': 'newer v2 key1', 'key3': 'newer v2 key3'},
                'v3': {'key1': 'newest v3 key1'}}
        expected_data = copy.deepcopy(data)
        expected_data.update(
            {'key1': 'newest v3 key1', 'key2': 'old v1 key2',
             'key3': 'newer v2 key3'})

        converted_data = convert_jinja_instance_data(data=data)
        self.assertEqual(
            expected_data,
            converted_data)
Exemplo n.º 10
0
    def test_convert_instance_data_most_recent_version_of_promoted_keys(self):
        """The most-recent versioned key value is promoted to top-level."""
        data = {'v1': {'key1': 'old v1 key1', 'key2': 'old v1 key2'},
                'v2': {'key1': 'newer v2 key1', 'key3': 'newer v2 key3'},
                'v3': {'key1': 'newest v3 key1'}}
        expected_data = copy.deepcopy(data)
        expected_data.update(
            {'key1': 'newest v3 key1', 'key2': 'old v1 key2',
             'key3': 'newer v2 key3'})

        converted_data = convert_jinja_instance_data(data=data)
        self.assertEqual(
            expected_data,
            converted_data)
Exemplo n.º 11
0
    def test_convert_instance_data_promotes_versioned_keys_to_top_level(self):
        """Any versioned keys are promoted as top-level keys

        This provides any cloud-init standardized keys up at a top-level to
        allow ease of reference for users. Intsead of v1.availability_zone,
        the name availability_zone can be used in templates.
        """
        data = {'ds': {'dskey1': 1, 'dskey2': 2},
                'v1': {'v1key1': 'v1.1'},
                'v2': {'v2key1': 'v2.1'}}
        expected_data = copy.deepcopy(data)
        expected_data.update({'v1key1': 'v1.1', 'v2key1': 'v2.1'})

        converted_data = convert_jinja_instance_data(data=data)
        self.assertItemsEqual(
            ['ds', 'v1', 'v2', 'v1key1', 'v2key1'], converted_data.keys())
        self.assertEqual(
            expected_data,
            converted_data)
Exemplo n.º 12
0
    def test_convert_instance_data_most_recent_version_of_promoted_keys(self):
        """The most-recent versioned key value is promoted to top-level."""
        data = {
            "v1": {
                "key1": "old v1 key1",
                "key2": "old v1 key2"
            },
            "v2": {
                "key1": "newer v2 key1",
                "key3": "newer v2 key3"
            },
            "v3": {
                "key1": "newest v3 key1"
            },
        }
        expected_data = copy.deepcopy(data)
        expected_data.update({
            "key1": "newest v3 key1",
            "key2": "old v1 key2",
            "key3": "newer v2 key3",
        })

        converted_data = convert_jinja_instance_data(data=data)
        assert expected_data == converted_data
Exemplo n.º 13
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 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
Exemplo n.º 14
0
 def test_convert_instance_data_operators_to_underscores(
         self, include_key_aliases, data, expected):
     """Replace Jinja operators keys with underscores in instance-data."""
     assert expected == convert_jinja_instance_data(
         data=data, include_key_aliases=include_key_aliases)
Exemplo n.º 15
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