/
githubmirror
/
salt
Обзор
Документация
Войти
/
githubmirror
/
salt
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
tests/pytests/integration/modules/test_pillar.py
703 строки
21 KB
Tyler Levy Conde
fix tests
12 май 2026, 05:16
12 май 2026, 05:16
7e17676
Код
Авторство
О чём код?
import logging import pathlib import time import types import attr import pytest log = logging.getLogger(__name__) pytestmark = [ pytest.mark.slow_test, pytest.mark.windows_whitelisted, ] @pytest.fixture(scope="module") def pillar_tree(salt_master, salt_minion, salt_call_cli): top_file = """ base: '{}': - basic """.format( salt_minion.id ) basic_pillar_file = """ monty: python os: {{ grains['os'] }} {% if grains['os'] == 'Fedora' %} class: redhat {% else %} class: other {% endif %} knights: - Lancelot - Galahad - Bedevere - Robin 12345: code: - luggage """ top_tempfile = salt_master.pillar_tree.base.temp_file("top.sls", top_file) basic_tempfile = salt_master.pillar_tree.base.temp_file( "basic.sls", basic_pillar_file ) try: with top_tempfile, basic_tempfile: ret = salt_call_cli.run("saltutil.refresh_pillar", wait=True) assert ret.returncode == 0 assert ret.data is True yield finally: # Refresh pillar again to cleaup the temp pillar ret = salt_call_cli.run("saltutil.refresh_pillar", wait=True) assert ret.returncode == 0 assert ret.data is True @pytest.mark.slow_test def test_data(salt_call_cli, pillar_tree): """ pillar.data """ ret = salt_call_cli.run("grains.items") assert ret.returncode == 0 assert ret.data grains = ret.data ret = salt_call_cli.run("pillar.data", unmask=True) assert ret.returncode == 0 assert ret.data pillar = ret.data assert pillar["os"] == grains["os"] assert pillar["monty"] == "python" if grains["os"] == "Fedora": assert pillar["class"] == "redhat" else: assert pillar["class"] == "other" @pytest.mark.slow_test def test_issue_5449_report_actual_file_roots_in_pillar( salt_call_cli, pillar_tree, base_env_state_tree_root_dir ): """ pillar['master']['file_roots'] is overwritten by the master in order to use the fileclient interface to read the pillar files. We should restore the actual file_roots when we send the pillar back to the minion. """ ret = salt_call_cli.run("pillar.data", unmask=True) assert ret.returncode == 0 assert ret.data file_roots = ret.data["master"]["file_roots"]["base"] assert pathlib.Path(base_env_state_tree_root_dir).resolve() in [ pathlib.Path(p).resolve() for p in file_roots ] @pytest.mark.slow_test def test_ext_cmd_yaml(salt_call_cli, pillar_tree): """ pillar.data for ext_pillar cmd.yaml """ ret = salt_call_cli.run("pillar.data", unmask=True) assert ret.returncode == 0 assert ret.data pillar = ret.data assert pillar["ext_spam"] == "eggs" @pytest.mark.slow_test def test_issue_5951_actual_file_roots_in_opts( salt_call_cli, pillar_tree, base_env_state_tree_root_dir ): ret = salt_call_cli.run("pillar.data", unmask=True) assert ret.returncode == 0 assert ret.data pillar_data = ret.data file_roots = pillar_data["ext_pillar_opts"]["file_roots"]["base"] assert pathlib.Path(base_env_state_tree_root_dir).resolve() in [ pathlib.Path(p).resolve() for p in file_roots ] @pytest.mark.slow_test def test_pillar_items(salt_call_cli, pillar_tree): """ Test to ensure we get expected output from pillar.items """ ret = salt_call_cli.run("pillar.items", unmask=True) assert ret.returncode == 0 assert ret.data pillar_items = ret.data assert "monty" in pillar_items assert pillar_items["monty"] == "python" assert "knights" in pillar_items assert pillar_items["knights"] == ["Lancelot", "Galahad", "Bedevere", "Robin"] @pytest.mark.slow_test def test_pillar_command_line(salt_call_cli, pillar_tree): """ Test to ensure when using pillar override on command line works """ # test when pillar is overwriting previous pillar ret = salt_call_cli.run("pillar.items", pillar={"monty": "overwrite"}, unmask=True) assert ret.returncode == 0 assert ret.data pillar_items = ret.data assert "monty" in pillar_items assert pillar_items["monty"] == "overwrite" # test when using additional pillar ret = salt_call_cli.run("pillar.items", pillar={"new": "additional"}, unmask=True) assert ret.returncode == 0 assert ret.data pillar_items = ret.data assert "new" in pillar_items assert pillar_items["new"] == "additional" def test_pillar_get_integer_key(salt_call_cli, pillar_tree): """ Test to ensure we get expected output from pillar.items """ ret = salt_call_cli.run("pillar.items", unmask=True) assert ret.returncode == 0 assert ret.data pillar_items = ret.data assert "12345" in pillar_items assert pillar_items["12345"] == {"code": ["luggage"]} ret = salt_call_cli.run("pillar.get", key="12345", unmask=True) assert ret.returncode == 0 assert ret.data pillar_get = ret.data assert "code" in pillar_get assert "luggage" in pillar_get["code"] @attr.s class PillarRefresh: master = attr.ib(repr=False) minion_id = attr.ib(repr=False) pillar_key = attr.ib() top_file = attr.ib(init=False, default=None) minion_1_pillar = attr.ib(init=False, default=None) def refresh_pillar(self, timeout=60): ret = self.master.salt_cli().run( "saltutil.refresh_pillar", wait=True, minion_tgt=self.minion_id, _timeout=timeout, ) assert ret.returncode == 0 assert ret.data is True def __enter__(self): top_file_contents = """ base: {}: - minion-1-pillar """.format( self.minion_id ) self.top_file = self.master.pillar_tree.base.temp_file( "top.sls", top_file_contents ) self.minion_1_pillar = self.master.pillar_tree.base.temp_file( "minion-1-pillar.sls", f"{self.pillar_key}: true" ) self.top_file.__enter__() self.minion_1_pillar.__enter__() return self def __exit__(self, *args): if self.top_file: self.top_file.__exit__(*args) if self.minion_1_pillar: self.minion_1_pillar.__exit__(*args) self.minion_1_pillar = None self.refresh_pillar() @pytest.fixture def key_pillar(salt_master, salt_minion): def _key_pillar(key): return PillarRefresh( master=salt_master, minion_id=salt_minion.id, pillar_key=key ) return _key_pillar @pytest.mark.slow_test def test_pillar_refresh_pillar_raw(salt_cli, salt_minion, key_pillar): """ Validate the minion's pillar.raw call behavior for new pillars """ key = "issue-54941-raw" # We do not expect to see the pillar because it does not exist yet ret = salt_cli.run("pillar.raw", key, minion_tgt=salt_minion.id) assert ret.returncode == 0 val = ret.data assert val == {} with key_pillar(key) as key_pillar_instance: # The pillar exists now but raw reads it from in-memory pillars ret = salt_cli.run("pillar.raw", key, minion_tgt=salt_minion.id) assert ret.returncode == 0 val = ret.data assert val == {} # Calling refresh_pillar to update in-memory pillars key_pillar_instance.refresh_pillar() # The pillar can now be read from in-memory pillars ret = salt_cli.run("pillar.raw", key, minion_tgt=salt_minion.id) assert ret.returncode == 0 val = ret.data assert val is True, repr(val) @pytest.mark.slow_test def test_pillar_refresh_pillar_get(salt_cli, salt_minion, key_pillar): """ Validate the minion's pillar.get call behavior for new pillars """ key = "issue-54941-get" # We do not expect to see the pillar because it does not exist yet ret = salt_cli.run("pillar.get", key, minion_tgt=salt_minion.id) assert ret.returncode == 0 val = ret.data assert val == "" with key_pillar(key) as key_pillar_instance: # The pillar exists now but get reads it from in-memory pillars, no # refresh happens ret = salt_cli.run("pillar.get", key, minion_tgt=salt_minion.id) assert ret.returncode == 0 val = ret.data assert val == "" # Calling refresh_pillar to update in-memory pillars key_pillar_instance.refresh_pillar() # The pillar can now be read from in-memory pillars ret = salt_cli.run("pillar.get", key, minion_tgt=salt_minion.id) assert ret.returncode == 0 val = ret.data assert val is True, repr(val) @pytest.mark.slow_test def test_pillar_refresh_pillar_item(salt_cli, salt_minion, key_pillar): """ Validate the minion's pillar.item call behavior for new pillars """ key = "issue-54941-item" # We do not expect to see the pillar because it does not exist yet ret = salt_cli.run("pillar.item", key, minion_tgt=salt_minion.id) assert ret.returncode == 0 val = ret.data assert key in val assert val[key] == "" with key_pillar(key) as key_pillar_instance: # The pillar exists now but get reads it from in-memory pillars, no # refresh happens ret = salt_cli.run("pillar.item", key, minion_tgt=salt_minion.id) assert ret.returncode == 0 val = ret.data assert key in val assert val[key] == "" # Calling refresh_pillar to update in-memory pillars key_pillar_instance.refresh_pillar() # The pillar can now be read from in-memory pillars ret = salt_cli.run("pillar.item", key, minion_tgt=salt_minion.id) assert ret.returncode == 0 val = ret.data assert key in val assert val[key] is True @pytest.mark.slow_test def test_pillar_refresh_pillar_items(salt_cli, salt_minion, key_pillar): """ Validate the minion's pillar.item call behavior for new pillars """ key = "issue-54941-items" # We do not expect to see the pillar because it does not exist yet ret = salt_cli.run("pillar.items", minion_tgt=salt_minion.id) assert ret.returncode == 0 val = ret.data assert key not in val with key_pillar(key) as key_pillar_instance: # A pillar.items call sees the pillar right away because a # refresh_pillar event is fired. # Calling refresh_pillar to update in-memory pillars key_pillar_instance.refresh_pillar() ret = salt_cli.run("pillar.items", minion_tgt=salt_minion.id) assert ret.returncode == 0 val = ret.data assert key in val assert val[key] is True @pytest.mark.slow_test def test_pillar_refresh_pillar_ping(salt_cli, salt_minion, key_pillar): """ Validate the minion's test.ping does not update pillars See: https://github.com/saltstack/salt/issues/54941 """ key = "issue-54941-ping" # We do not expect to see the pillar because it does not exist yet ret = salt_cli.run("pillar.item", key, minion_tgt=salt_minion.id) assert ret.returncode == 0 val = ret.data assert key in val assert val[key] == "" with key_pillar(key) as key_pillar_instance: ret = salt_cli.run("test.ping", minion_tgt=salt_minion.id) assert ret.returncode == 0 val = ret.data assert val is True # The pillar exists now but get reads it from in-memory pillars, no # refresh happens ret = salt_cli.run("pillar.item", key, minion_tgt=salt_minion.id) assert ret.returncode == 0 val = ret.data assert key in val assert val[key] == "" # Calling refresh_pillar to update in-memory pillars key_pillar_instance.refresh_pillar() # The pillar can now be read from in-memory pillars ret = salt_cli.run("pillar.item", key, minion_tgt=salt_minion.id) assert ret.returncode == 0 val = ret.data assert key in val assert val[key] is True @pytest.mark.slow_test @pytest.mark.timeout_unless_on_windows(120) def test_pillar_refresh_pillar_scheduler(salt_master, salt_cli, salt_minion): """ Ensure schedule jobs in pillar are only updated when values change. """ top_sls = """ base: '{}': - test_schedule """.format( salt_minion.id ) test_schedule_sls = """ schedule: first_test_ping: function: test.ping run_on_start: True seconds: 3600 """ test_schedule_sls2 = """ schedule: first_test_ping: function: test.ping run_on_start: True seconds: 7200 """ with salt_master.pillar_tree.base.temp_file("top.sls", top_sls): with salt_master.pillar_tree.base.temp_file( "test_schedule.sls", test_schedule_sls ): # Calling refresh_pillar to update in-memory pillars salt_cli.run( "saltutil.refresh_pillar", wait=True, minion_tgt=salt_minion.id ) # Give the schedule a chance to run the job time.sleep(5) # Get the status of the job ret = salt_cli.run( "schedule.job_status", name="first_test_ping", minion_tgt=salt_minion.id ) assert "_next_fire_time" in ret.data _next_fire_time = ret.data["_next_fire_time"] # Refresh pillar salt_cli.run( "saltutil.refresh_pillar", wait=True, minion_tgt=salt_minion.id ) # Ensure next_fire_time is the same, job was not replaced ret = salt_cli.run( "schedule.job_status", name="first_test_ping", minion_tgt=salt_minion.id ) assert ret.data["_next_fire_time"] == _next_fire_time # Ensure job was replaced when seconds changes with salt_master.pillar_tree.base.temp_file( "test_schedule.sls", test_schedule_sls2 ): # Calling refresh_pillar to update in-memory pillars salt_cli.run( "saltutil.refresh_pillar", wait=True, minion_tgt=salt_minion.id ) # Give the schedule a chance to run the job time.sleep(5) ret = salt_cli.run( "schedule.job_status", name="first_test_ping", minion_tgt=salt_minion.id ) assert "_next_fire_time" in ret.data _next_fire_time = ret.data["_next_fire_time"] salt_cli.run( "saltutil.refresh_pillar", wait=True, minion_tgt=salt_minion.id ) ret = salt_cli.run( "schedule.job_status", name="first_test_ping", minion_tgt=salt_minion.id ) assert ret.data["_next_fire_time"] == _next_fire_time # Refresh pillar once we're done salt_cli.run("saltutil.refresh_pillar", wait=True, minion_tgt=salt_minion.id) @pytest.mark.slow_test @pytest.mark.parametrize( "jinja_file,app,caching,db", [ ("test_jinja_sls_as_documented", True, True, False), ("test_jinja_sls_with_minion_id", False, False, True), ("test_jinja_sls_with_minion_id_regex_compound", False, False, True), ], ) def test_pillar_match_filter_by_minion_id( salt_master, salt_cli, salt_minion, jinja_file, app, caching, db ): """ Ensure "match.filter_by" used during Pillar rendering uses the Minion ID and returns the expected values """ top_sls = """ base: {}: - test_jinja """.format( salt_minion.id ) sls_files = { "test_jinja_sls_as_documented": """ # Filter the data for the current minion into a variable: {{% set roles = salt['match.filter_by']({{ 'web*': ['app', 'caching'], '{}*': ['db'], }}, default='web*') %}} # Make the filtered data available to Pillar: roles: {{{{ roles | yaml() }}}} """.format( salt_minion.id ), "test_jinja_sls_with_minion_id": """ # Filter the data for the current minion into a variable: {{% set roles = salt['match.filter_by']({{ 'web*': ['app', 'caching'], '{}*': ['db'], }}, minion_id=grains['id'], default='web*') %}} # Make the filtered data available to Pillar: roles: {{{{ roles | yaml() }}}} """.format( salt_minion.id ), "test_jinja_sls_with_minion_id_regex_compound": """ # Filter the data for the current minion into a variable: {{% set roles = salt['match.filter_by']({{ 'web*': ['app', 'caching'], 'E@{}': ['db'], }}, minion_id=grains['id'], default='web*') %}} # Make the filtered data available to Pillar: roles: {{{{ roles | yaml() }}}} """.format( salt_minion.id ), } # test the brokenness of the currently documented example # it lacks the "minion_id" argument for "match.filter_by" and thereby # assigns the wrong roles with salt_master.pillar_tree.base.temp_file("top.sls", top_sls): with salt_master.pillar_tree.base.temp_file( "test_jinja.sls", sls_files[jinja_file] ): # Calling refresh_pillar to update in-memory pillars salt_cli.run( "saltutil.refresh_pillar", wait=True, minion_tgt=salt_minion.id ) # Get the Minion's Pillar ret = salt_cli.run( "pillar.get", "roles", unmask=True, minion_tgt=salt_minion.id ) if app: assert "app" in ret.data else: assert "app" not in ret.data if caching: assert "caching" in ret.data else: assert "caching" not in ret.data if db: assert "db" in ret.data else: assert "db" not in ret.data # Refresh pillar once we're done salt_cli.run("saltutil.refresh_pillar", wait=True, minion_tgt=salt_minion.id) @pytest.mark.slow_test def test_pillar_ext_59975(salt_call_cli): """ pillar.ext returns result. Issue #59975 """ ret = salt_call_cli.run("pillar.ext", '{"libvert": _}') assert "ext_pillar_opts" in ret.data @pytest.fixture def event_listerner_timeout(grains): if grains["os"] == "Windows": if grains["osrelease"].startswith("2019"): return types.SimpleNamespace(catch=120, miss=30) return types.SimpleNamespace(catch=90, miss=10) return types.SimpleNamespace(catch=60, miss=10) @pytest.mark.slow_test def test_pillar_refresh_pillar_beacons( base_env_pillar_tree_root_dir, salt_cli, salt_minion, salt_master, event_listener, event_listerner_timeout, ): """ Ensure beacons jobs in pillar are started after a pillar refresh and then not running when pillar is cleared. """ top_sls = """ base: '{}': - test_beacons """.format( salt_minion.id ) test_beacons_sls_empty = "" test_beacons_sls = """ beacons: status: - loadavg: - 1-min """ assert salt_minion.is_running() top_tempfile = pytest.helpers.temp_file( "top.sls", top_sls, base_env_pillar_tree_root_dir ) beacon_tempfile = pytest.helpers.temp_file( "test_beacons.sls", test_beacons_sls_empty, base_env_pillar_tree_root_dir ) with top_tempfile, beacon_tempfile: # Calling refresh_pillar to update in-memory pillars salt_cli.run("saltutil.refresh_pillar", wait=True, minion_tgt=salt_minion.id) # Ensure beacons start when pillar is refreshed with salt_master.pillar_tree.base.temp_file( "test_beacons.sls", test_beacons_sls ): # Calling refresh_pillar to update in-memory pillars salt_cli.run( "saltutil.refresh_pillar", wait=True, minion_tgt=salt_minion.id ) # Give the beacons a chance to start time.sleep(5) event_tag = "salt/beacon/*/status/*" start_time = time.time() event_pattern = (salt_master.id, event_tag) matched_events = event_listener.wait_for_events( [event_pattern], after_time=start_time, timeout=event_listerner_timeout.catch, ) assert matched_events.found_all_events # Ensure beacons sttop when pillar is refreshed with salt_master.pillar_tree.base.temp_file( "test_beacons.sls", test_beacons_sls_empty ): # Calling refresh_pillar to update in-memory pillars salt_cli.run( "saltutil.refresh_pillar", wait=True, minion_tgt=salt_minion.id ) # Give the beacons a chance to stop time.sleep(5) event_tag = "salt/beacon/*/status/*" start_time = time.time() event_pattern = (salt_master.id, event_tag) matched_events = event_listener.wait_for_events( [event_pattern], after_time=start_time, timeout=event_listerner_timeout.miss, ) assert not matched_events.found_all_events