diff --git a/salt/_modules/metalk8s.py b/salt/_modules/metalk8s.py index d2546c5939..1419ba04d7 100644 --- a/salt/_modules/metalk8s.py +++ b/salt/_modules/metalk8s.py @@ -10,10 +10,13 @@ import six import socket import tempfile +import textwrap import time from salt.pillar import get_pillar from salt.exceptions import CommandExecutionError +import salt.loader +import salt.template import salt.utils.args import salt.utils.files from salt.utils.hashutils import get_hash @@ -557,3 +560,58 @@ def _clean_tmp(sfn): if source_filename: _clean_tmp(source_filename) return ret + + +def get_from_map(value, saltenv=None): + """Get a value from map.jinja so that we have an up to date value + computed from defaults.yaml and pillar. + + Basically the same as a `jinja.map_load` but with support for saltenv and + also hardcoded path to MetalK8s map.jinja. + + Also add logic to retrieve the saltenv using version in the pillar. + + Arguments: + + value (str): Name of the value to retrieve + + CLI Example: + + .. code-block:: bash + + # Retrieve `metalk8s` merge between defaults.yaml and pillar using + # current node version as saltenv + salt '*' metalk8s.get_from_map metalk8s + + # Retrieve `metalk8s` from a specific saltenv + salt '*' metalk8s.get_from_map meltak8s saltenv=my-salt-env + """ + path = "metalk8s/map.jinja" + if not saltenv: + current_version = __pillar__.get('metalk8s', {}).get('nodes', {}).get( + __grains__['id'], {}).get('version') + if not current_version: + log.warning( + 'Unable to retrieve current running version, fallback on "base"' + ) + saltenv = 'base' + else: + saltenv = "metalk8s-{}".format(current_version) + + tmplstr = textwrap.dedent( + """\ + {{% from "{path}" import {value} with context %}} + {{{{ {value} | tojson }}}} + """.format( + path=path, value=value + ) + ) + return salt.template.compile_template( + ":string:", + salt.loader.render(__opts__, __salt__), + __opts__["renderer"], + __opts__["renderer_blacklist"], + __opts__["renderer_whitelist"], + input_data=tmplstr, + saltenv=saltenv + ) diff --git a/salt/tests/unit/modules/test_metalk8s.py b/salt/tests/unit/modules/test_metalk8s.py index 4296e8c400..35a6014a42 100644 --- a/salt/tests/unit/modules/test_metalk8s.py +++ b/salt/tests/unit/modules/test_metalk8s.py @@ -5,6 +5,8 @@ from parameterized import param, parameterized from salt.exceptions import CommandExecutionError +import salt.renderers.jinja +import salt.renderers.yaml import salt.utils.files import yaml @@ -41,7 +43,13 @@ class Metalk8sTestCase(TestCase, mixins.LoaderModuleMockMixin): loader_module_globals = { "__grains__": { "id": "my_node_1" - } + }, + "__opts__": { + "renderer": "jinja|yaml", + "renderer_blacklist": [], + "renderer_whitelist": [] + }, + "__salt__": {} } def test_virtual(self): @@ -469,3 +477,44 @@ def _get_diff(*_a, **_k): self.assertEqual(remove_mock.call_count, 0) else: self.assertEqual(remove_mock.call_count, 1) + + @parameterized.expand([ + param(), + param(saltenv='my-salt-env', expected_saltenv='my-salt-env'), + param(node_version='1.2.3', expected_saltenv='metalk8s-1.2.3'), + param(node_version=None, expected_saltenv='base') + ]) + def test_get_from_map(self, saltenv=None, expected_saltenv="base", + node_version=None): + """ + Tests the return of `get_from_map` function + """ + # NOTE: The goal is not to test the `compile_template` function from + # salt so just ignore the return of the function but check that we + # give the right arguments + expected_args = { + 'input_data': '{% from "metalk8s/map.jinja" import my-key with context %}\n{{ my-key | tojson }}\n', + 'saltenv': expected_saltenv + } + pillar_content = {} + if node_version: + pillar_content = { + 'metalk8s': { + 'nodes': { + 'my_node_1': { + 'version': node_version + } + } + } + } + + compile_template_mock = MagicMock() + with patch.dict(metalk8s.__pillar__, pillar_content), \ + patch('salt.loader.render', MagicMock()), \ + patch("salt.template.compile_template", compile_template_mock): + metalk8s.get_from_map('my-key', saltenv=saltenv) + compile_template_mock.assert_called_once() + self.assertDictContainsSubset( + expected_args, + compile_template_mock.call_args[1] + )