diff --git a/_lastrevision b/_lastrevision index 804a09e..e94004b 100644 --- a/_lastrevision +++ b/_lastrevision @@ -1 +1 @@ -e1eca529a18f7fde5ceec0e3d9524028c7affd12 \ No newline at end of file +d7c50289494a6b41234aee2204209083e30fbd8d \ No newline at end of file diff --git a/make-minion-reconnecting-on-changing-master-ip-bsc-1.patch b/make-minion-reconnecting-on-changing-master-ip-bsc-1.patch new file mode 100644 index 0000000..ac9dba7 --- /dev/null +++ b/make-minion-reconnecting-on-changing-master-ip-bsc-1.patch @@ -0,0 +1,770 @@ +From eb6c67e6f535cdfbf685a54c6352018673e37a12 Mon Sep 17 00:00:00 2001 +From: Victor Zhestkov +Date: Tue, 26 Nov 2024 11:59:08 +0300 +Subject: [PATCH] Make minion reconnecting on changing master IP + (bsc#1228182) + +* Minions check dns when re-connecting to a master + +Check for a chainging dns record anytime a minion gets disconnected from +it's master. See github issue #63654 #61482. + +* Regression tests for dns defined masters + +Adding tests to validate we check for changing dns anytime we're +disconnected from the currently connected master + +* Update docs for master dns changes + +Update docs to use master_alive_interval to detect master ip changes via +DNS. + +* Remove comment which is not true anymore + +* Make minion reconnecting on changing master IP + +with zeromq transport + +* Don't create schedule for alive if no master_alive_interval + +* Skip the tests if running with non-root user + +* Skip if unable to set additional IP address + +* Set master_tries to -1 for minions + +* Fix the tests + +--------- + +Co-authored-by: Daniel A. Wozniak +--- + conf/minion | 5 +- + doc/ref/configuration/minion.rst | 4 +- + salt/channel/client.py | 2 - + salt/config/__init__.py | 4 +- + salt/minion.py | 190 ++++++++---------- + salt/transport/zeromq.py | 17 +- + tests/pytests/scenarios/dns/__init__.py | 0 + tests/pytests/scenarios/dns/conftest.py | 99 +++++++++ + .../scenarios/dns/multimaster/conftest.py | 124 ++++++++++++ + .../scenarios/dns/multimaster/test_dns.py | 54 +++++ + tests/pytests/scenarios/dns/test_dns.py | 37 ++++ + .../multimaster/test_failover_master.py | 4 - + tests/pytests/unit/test_minion.py | 2 + + 13 files changed, 422 insertions(+), 120 deletions(-) + create mode 100644 tests/pytests/scenarios/dns/__init__.py + create mode 100644 tests/pytests/scenarios/dns/conftest.py + create mode 100644 tests/pytests/scenarios/dns/multimaster/conftest.py + create mode 100644 tests/pytests/scenarios/dns/multimaster/test_dns.py + create mode 100644 tests/pytests/scenarios/dns/test_dns.py + +diff --git a/conf/minion b/conf/minion +index eeef626fa8..f89e18451f 100644 +--- a/conf/minion ++++ b/conf/minion +@@ -271,9 +271,8 @@ + #ping_interval: 0 + + # To auto recover minions if master changes IP address (DDNS) +-# auth_tries: 10 +-# auth_safemode: True +-# ping_interval: 2 ++# master_alive_interval: 10 ++# master_tries: -1 + # + # Minions won't know master is missing until a ping fails. After the ping fail, + # the minion will attempt authentication and likely fails out and cause a restart. +diff --git a/doc/ref/configuration/minion.rst b/doc/ref/configuration/minion.rst +index 57af5ce4a3..a1b0f2e86e 100644 +--- a/doc/ref/configuration/minion.rst ++++ b/doc/ref/configuration/minion.rst +@@ -291,7 +291,9 @@ Default: ``0`` + + Configures how often, in seconds, the minion will verify that the current + master is alive and responding. The minion will try to establish a connection +-to the next master in the list if it finds the existing one is dead. ++to the next master in the list if it finds the existing one is dead. This ++setting can also be used to detect master DNS record changes when a minion has ++been disconnected. + + .. code-block:: yaml + +diff --git a/salt/channel/client.py b/salt/channel/client.py +index 76d7a8e5b9..34aafb2c9e 100644 +--- a/salt/channel/client.py ++++ b/salt/channel/client.py +@@ -385,8 +385,6 @@ class AsyncPubChannel: + # else take the relayed publish_port master reports + else: + publish_port = self.auth.creds["publish_port"] +- # TODO: The zeromq transport does not use connect_callback and +- # disconnect_callback. + yield self.transport.connect( + publish_port, self.connect_callback, self.disconnect_callback + ) +diff --git a/salt/config/__init__.py b/salt/config/__init__.py +index b3cd5d85ae..d4865807e6 100644 +--- a/salt/config/__init__.py ++++ b/salt/config/__init__.py +@@ -75,7 +75,7 @@ elif salt.utils.platform.is_darwin(): + else: + _DFLT_IPC_MODE = "ipc" + _DFLT_FQDNS_GRAINS = False +- _MASTER_TRIES = 1 ++ _MASTER_TRIES = -1 + _MASTER_USER = salt.utils.user.get_user() + + +@@ -1272,7 +1272,7 @@ DEFAULT_MINION_OPTS = immutabletypes.freeze( + "username": None, + "password": None, + "zmq_filtering": False, +- "zmq_monitor": False, ++ "zmq_monitor": True, + "cache_sreqs": True, + "cmd_safe": True, + "sudo_user": "", +diff --git a/salt/minion.py b/salt/minion.py +index e21a017cfd..834f0848c6 100644 +--- a/salt/minion.py ++++ b/salt/minion.py +@@ -2737,10 +2737,64 @@ class Minion(MinionBase): + # we are not connected anymore + self.connected = False + log.info("Connection to master %s lost", self.opts["master"]) ++ if self.opts["transport"] != "tcp": ++ self.schedule.delete_job(name=master_event(type="alive")) ++ ++ log.info("Trying to tune in to next master from master-list") ++ ++ if hasattr(self, "pub_channel"): ++ self.pub_channel.on_recv(None) ++ if hasattr(self.pub_channel, "auth"): ++ self.pub_channel.auth.invalidate() ++ if hasattr(self.pub_channel, "close"): ++ self.pub_channel.close() ++ if hasattr(self, "req_channel") and self.req_channel: ++ self.req_channel.close() ++ self.req_channel = None ++ ++ # if eval_master finds a new master for us, self.connected ++ # will be True again on successful master authentication ++ try: ++ master, self.pub_channel = yield self.eval_master( ++ opts=self.opts, ++ failed=True, ++ failback=tag.startswith(master_event(type="failback")), ++ ) ++ except SaltClientError: ++ pass ++ ++ if self.connected: ++ self.opts["master"] = master ++ ++ # re-init the subsystems to work with the new master ++ log.info( ++ "Re-initialising subsystems for new master %s", ++ self.opts["master"], ++ ) ++ ++ self.req_channel = salt.channel.client.AsyncReqChannel.factory( ++ self.opts, io_loop=self.io_loop ++ ) + +- if self.opts["master_type"] != "failover": +- # modify the scheduled job to fire on reconnect +- if self.opts["transport"] != "tcp": ++ # put the current schedule into the new loaders ++ self.opts["schedule"] = self.schedule.option("schedule") ++ ( ++ self.functions, ++ self.returners, ++ self.function_errors, ++ self.executors, ++ ) = self._load_modules() ++ # make the schedule to use the new 'functions' loader ++ self.schedule.functions = self.functions ++ self.pub_channel.on_recv(self._handle_payload) ++ self._fire_master_minion_start() ++ log.info("Minion is ready to receive requests!") ++ ++ # update scheduled job to run with the new master addr ++ if ( ++ self.opts["transport"] != "tcp" ++ and self.opts["master_alive_interval"] > 0 ++ ): + schedule = { + "function": "status.master", + "seconds": self.opts["master_alive_interval"], +@@ -2749,116 +2803,35 @@ class Minion(MinionBase): + "return_job": False, + "kwargs": { + "master": self.opts["master"], +- "connected": False, ++ "connected": True, + }, + } + self.schedule.modify_job( + name=master_event(type="alive", master=self.opts["master"]), + schedule=schedule, + ) +- else: +- # delete the scheduled job to don't interfere with the failover process +- if self.opts["transport"] != "tcp": +- self.schedule.delete_job(name=master_event(type="alive")) +- +- log.info("Trying to tune in to next master from master-list") +- +- if hasattr(self, "pub_channel"): +- self.pub_channel.on_recv(None) +- if hasattr(self.pub_channel, "auth"): +- self.pub_channel.auth.invalidate() +- if hasattr(self.pub_channel, "close"): +- self.pub_channel.close() +- del self.pub_channel +- +- # if eval_master finds a new master for us, self.connected +- # will be True again on successful master authentication +- try: +- master, self.pub_channel = yield self.eval_master( +- opts=self.opts, +- failed=True, +- failback=tag.startswith(master_event(type="failback")), +- ) +- except SaltClientError: +- pass +- +- if self.connected: +- self.opts["master"] = master +- +- # re-init the subsystems to work with the new master +- log.info( +- "Re-initialising subsystems for new master %s", +- self.opts["master"], +- ) +- +- self.req_channel = ( +- salt.transport.client.AsyncReqChannel.factory( +- self.opts, io_loop=self.io_loop +- ) +- ) +- +- # put the current schedule into the new loaders +- self.opts["schedule"] = self.schedule.option("schedule") +- ( +- self.functions, +- self.returners, +- self.function_errors, +- self.executors, +- ) = self._load_modules() +- # make the schedule to use the new 'functions' loader +- self.schedule.functions = self.functions +- self.pub_channel.on_recv(self._handle_payload) +- self._fire_master_minion_start() +- log.info("Minion is ready to receive requests!") +- +- # update scheduled job to run with the new master addr +- if self.opts["transport"] != "tcp": +- schedule = { +- "function": "status.master", +- "seconds": self.opts["master_alive_interval"], +- "jid_include": True, +- "maxrunning": 1, +- "return_job": False, +- "kwargs": { +- "master": self.opts["master"], +- "connected": True, +- }, +- } +- self.schedule.modify_job( +- name=master_event( +- type="alive", master=self.opts["master"] +- ), +- schedule=schedule, +- ) + +- if ( +- self.opts["master_failback"] +- and "master_list" in self.opts +- ): +- if self.opts["master"] != self.opts["master_list"][0]: +- schedule = { +- "function": "status.ping_master", +- "seconds": self.opts[ +- "master_failback_interval" +- ], +- "jid_include": True, +- "maxrunning": 1, +- "return_job": False, +- "kwargs": { +- "master": self.opts["master_list"][0] +- }, +- } +- self.schedule.modify_job( +- name=master_event(type="failback"), +- schedule=schedule, +- ) +- else: +- self.schedule.delete_job( +- name=master_event(type="failback"), persist=True +- ) +- else: +- self.restart = True +- self.io_loop.stop() ++ if self.opts["master_failback"] and "master_list" in self.opts: ++ if self.opts["master"] != self.opts["master_list"][0]: ++ schedule = { ++ "function": "status.ping_master", ++ "seconds": self.opts["master_failback_interval"], ++ "jid_include": True, ++ "maxrunning": 1, ++ "return_job": False, ++ "kwargs": {"master": self.opts["master_list"][0]}, ++ } ++ self.schedule.modify_job( ++ name=master_event(type="failback"), ++ schedule=schedule, ++ ) ++ else: ++ self.schedule.delete_job( ++ name=master_event(type="failback"), persist=True ++ ) ++ else: ++ self.restart = True ++ self.io_loop.stop() + + elif tag.startswith(master_event(type="connected")): + # handle this event only once. otherwise it will pollute the log +@@ -2870,7 +2843,10 @@ class Minion(MinionBase): + self.connected = True + # modify the __master_alive job to only fire, + # if the connection is lost again +- if self.opts["transport"] != "tcp": ++ if ( ++ self.opts["transport"] != "tcp" ++ and self.opts["master_alive_interval"] > 0 ++ ): + schedule = { + "function": "status.master", + "seconds": self.opts["master_alive_interval"], +diff --git a/salt/transport/zeromq.py b/salt/transport/zeromq.py +index 7cc6b9987f..89f705190e 100644 +--- a/salt/transport/zeromq.py ++++ b/salt/transport/zeromq.py +@@ -1,6 +1,7 @@ + """ + Zeromq transport classes + """ ++ + import errno + import hashlib + import logging +@@ -211,6 +212,12 @@ class PublishClient(salt.transport.base.PublishClient): + self.master_pub, + ) + log.debug("%r connecting to %s", self, self.master_pub) ++ if ( ++ hasattr(self, "_monitor") ++ and self._monitor is not None ++ and disconnect_callback is not None ++ ): ++ self._monitor.disconnect_callback = disconnect_callback + self._socket.connect(self.master_pub) + connect_callback(True) + +@@ -680,13 +687,21 @@ class ZeroMQSocketMonitor: + log.debug("ZeroMQ event: %s", evt) + if evt["event"] == zmq.EVENT_MONITOR_STOPPED: + self.stop() ++ elif evt["event"] == zmq.EVENT_DISCONNECTED: ++ if ( ++ hasattr(self, "disconnect_callback") ++ and self.disconnect_callback is not None ++ ): ++ self.disconnect_callback() + + def stop(self): + if self._socket is None: + return + self._socket.disable_monitor() + self._socket = None +- self._monitor_socket = None ++ if self._monitor_socket is not None: ++ self._monitor_socket.close() ++ self._monitor_socket = None + if self._monitor_stream is not None: + self._monitor_stream.close() + self._monitor_stream = None +diff --git a/tests/pytests/scenarios/dns/__init__.py b/tests/pytests/scenarios/dns/__init__.py +new file mode 100644 +index 0000000000..e69de29bb2 +diff --git a/tests/pytests/scenarios/dns/conftest.py b/tests/pytests/scenarios/dns/conftest.py +new file mode 100644 +index 0000000000..5a8850719f +--- /dev/null ++++ b/tests/pytests/scenarios/dns/conftest.py +@@ -0,0 +1,99 @@ ++import logging ++import pathlib ++import subprocess ++ ++import pytest ++ ++log = logging.getLogger(__name__) ++ ++ ++@pytest.fixture(scope="package") ++def master_alive_interval(): ++ return 5 ++ ++ ++class HostsFile: ++ """ ++ Simple helper class for tests that need to modify /etc/hosts. ++ """ ++ ++ def __init__(self, path, orig_text): ++ self._path = path ++ self._orig_text = orig_text ++ ++ @property ++ def orig_text(self): ++ return self._orig_text ++ ++ def __getattr__(self, key): ++ if key in ["_path", "_orig_text", "orig_text"]: ++ return self.__getattribute__(key) ++ return getattr(self._path, key) ++ ++ ++@pytest.fixture ++def etc_hosts(): ++ hosts = pathlib.Path("/etc/hosts") ++ orig_text = hosts.read_text(encoding="utf-8") ++ hosts = HostsFile(hosts, orig_text) ++ try: ++ yield hosts ++ finally: ++ hosts.write_text(orig_text) ++ ++ ++@pytest.fixture(scope="package") ++def master(request, salt_factories): ++ ++ try: ++ subprocess.check_output(["ip", "addr", "add", "172.16.0.1/32", "dev", "lo"]) ++ ip_addr_set = True ++ except subprocess.CalledProcessError: ++ ip_addr_set = False ++ ++ config_defaults = { ++ "open_mode": True, ++ "transport": request.config.getoption("--transport"), ++ } ++ config_overrides = { ++ "interface": "0.0.0.0", ++ } ++ factory = salt_factories.salt_master_daemon( ++ "master", ++ defaults=config_defaults, ++ overrides=config_overrides, ++ extra_cli_arguments_after_first_start_failure=["--log-level=info"], ++ ) ++ factory.ip_addr_set = ip_addr_set ++ with factory.started(start_timeout=180): ++ yield factory ++ ++ try: ++ subprocess.check_output(["ip", "addr", "del", "172.16.0.1/32", "dev", "lo"]) ++ except subprocess.CalledProcessError: ++ pass ++ ++ ++@pytest.fixture(scope="package") ++def salt_cli(master): ++ return master.salt_cli(timeout=180) ++ ++ ++@pytest.fixture(scope="package") ++def minion(master, master_alive_interval): ++ config_defaults = { ++ "transport": master.config["transport"], ++ } ++ port = master.config["ret_port"] ++ config_overrides = { ++ "master": f"master.local:{port}", ++ "publish_port": master.config["publish_port"], ++ "master_alive_interval": master_alive_interval, ++ } ++ factory = master.salt_minion_daemon( ++ "minion", ++ defaults=config_defaults, ++ overrides=config_overrides, ++ extra_cli_arguments_after_first_start_failure=["--log-level=info"], ++ ) ++ return factory +diff --git a/tests/pytests/scenarios/dns/multimaster/conftest.py b/tests/pytests/scenarios/dns/multimaster/conftest.py +new file mode 100644 +index 0000000000..3333f812ce +--- /dev/null ++++ b/tests/pytests/scenarios/dns/multimaster/conftest.py +@@ -0,0 +1,124 @@ ++import logging ++import os ++import shutil ++import subprocess ++ ++import pytest ++ ++log = logging.getLogger(__name__) ++ ++ ++@pytest.fixture(scope="package") ++def salt_mm_master_1(request, salt_factories): ++ ++ try: ++ subprocess.check_output(["ip", "addr", "add", "172.16.0.1/32", "dev", "lo"]) ++ ip_addr_set = True ++ except subprocess.CalledProcessError: ++ ip_addr_set = False ++ ++ config_defaults = { ++ "open_mode": True, ++ "transport": request.config.getoption("--transport"), ++ } ++ config_overrides = { ++ "interface": "0.0.0.0", ++ "master_sign_pubkey": True, ++ } ++ factory = salt_factories.salt_master_daemon( ++ "mm-master-1", ++ defaults=config_defaults, ++ overrides=config_overrides, ++ extra_cli_arguments_after_first_start_failure=["--log-level=info"], ++ ) ++ factory.ip_addr_set = ip_addr_set ++ try: ++ with factory.started(start_timeout=180): ++ yield factory ++ finally: ++ ++ try: ++ subprocess.check_output(["ip", "addr", "del", "172.16.0.1/32", "dev", "lo"]) ++ except subprocess.CalledProcessError: ++ pass ++ ++ ++@pytest.fixture(scope="package") ++def mm_master_1_salt_cli(salt_mm_master_1): ++ return salt_mm_master_1.salt_cli(timeout=180) ++ ++ ++@pytest.fixture(scope="package") ++def salt_mm_master_2(salt_factories, salt_mm_master_1): ++ # if salt.utils.platform.is_darwin() or salt.utils.platform.is_freebsd(): ++ # subprocess.check_output(["ifconfig", "lo0", "alias", "127.0.0.2", "up"]) ++ ++ config_defaults = { ++ "open_mode": True, ++ "transport": salt_mm_master_1.config["transport"], ++ } ++ config_overrides = { ++ "interface": "0.0.0.0", ++ "master_sign_pubkey": True, ++ } ++ ++ # Use the same ports for both masters, they are binding to different interfaces ++ for key in ( ++ "ret_port", ++ "publish_port", ++ ): ++ config_overrides[key] = salt_mm_master_1.config[key] + 1 ++ factory = salt_factories.salt_master_daemon( ++ "mm-master-2", ++ defaults=config_defaults, ++ overrides=config_overrides, ++ extra_cli_arguments_after_first_start_failure=["--log-level=info"], ++ ) ++ ++ # Both masters will share the same signing key pair ++ for keyfile in ("master_sign.pem", "master_sign.pub"): ++ shutil.copyfile( ++ os.path.join(salt_mm_master_1.config["pki_dir"], keyfile), ++ os.path.join(factory.config["pki_dir"], keyfile), ++ ) ++ with factory.started(start_timeout=180): ++ yield factory ++ ++ ++@pytest.fixture(scope="package") ++def mm_master_2_salt_cli(salt_mm_master_2): ++ return salt_mm_master_2.salt_cli(timeout=180) ++ ++ ++@pytest.fixture(scope="package") ++def salt_mm_minion_1(salt_mm_master_1, salt_mm_master_2, master_alive_interval): ++ config_defaults = { ++ "transport": salt_mm_master_1.config["transport"], ++ } ++ ++ mm_master_1_port = salt_mm_master_1.config["ret_port"] ++ mm_master_2_port = salt_mm_master_2.config["ret_port"] ++ config_overrides = { ++ "master": [ ++ f"master1.local:{mm_master_1_port}", ++ f"master2.local:{mm_master_2_port}", ++ ], ++ "publish_port": salt_mm_master_1.config["publish_port"], ++ "master_alive_interval": master_alive_interval, ++ "master_tries": -1, ++ "verify_master_pubkey_sign": True, ++ "retry_dns": True, ++ } ++ factory = salt_mm_master_1.salt_minion_daemon( ++ "mm-minion-1", ++ defaults=config_defaults, ++ overrides=config_overrides, ++ extra_cli_arguments_after_first_start_failure=["--log-level=info"], ++ ) ++ # Need to grab the public signing key from the master, either will do ++ shutil.copyfile( ++ os.path.join(salt_mm_master_1.config["pki_dir"], "master_sign.pub"), ++ os.path.join(factory.config["pki_dir"], "master_sign.pub"), ++ ) ++ # with factory.started(start_timeout=180): ++ yield factory +diff --git a/tests/pytests/scenarios/dns/multimaster/test_dns.py b/tests/pytests/scenarios/dns/multimaster/test_dns.py +new file mode 100644 +index 0000000000..fafb30c12e +--- /dev/null ++++ b/tests/pytests/scenarios/dns/multimaster/test_dns.py +@@ -0,0 +1,54 @@ ++import logging ++import subprocess ++import time ++ ++import pytest ++ ++log = logging.getLogger(__name__) ++ ++ ++@pytest.mark.skip_unless_on_linux ++@pytest.mark.skip_if_not_root ++def test_multimaster_dns( ++ salt_mm_master_1, ++ salt_mm_minion_1, ++ mm_master_1_salt_cli, ++ etc_hosts, ++ caplog, ++ master_alive_interval, ++): ++ """ ++ Verify a minion configured with multimaster hot/hot will pick up a master's ++ dns change if it's been disconnected. ++ """ ++ ++ if not salt_mm_master_1.ip_addr_set: ++ pytest.skip("Unable to set additional IP address for master1") ++ ++ etc_hosts.write_text( ++ f"{etc_hosts.orig_text}\n172.16.0.1 master1.local master2.local" ++ ) ++ ++ log.info("Added hosts record for master1.local and master2.local") ++ ++ with salt_mm_minion_1.started(start_timeout=180): ++ with caplog.at_level(logging.INFO): ++ ret = mm_master_1_salt_cli.run("test.ping", minion_tgt="mm-minion-1") ++ assert ret.returncode == 0 ++ etc_hosts.write_text( ++ f"{etc_hosts.orig_text}\n127.0.0.1 master1.local master2.local" ++ ) ++ log.info("Changed hosts record for master1.local and master2.local") ++ subprocess.check_output(["ip", "addr", "del", "172.16.0.1/32", "dev", "lo"]) ++ log.info("Removed secondary master IP address.") ++ # Wait for the minion's master_alive_interval, adding a second for ++ # reliablity. ++ time.sleep(master_alive_interval + 1) ++ assert ( ++ "Master ip address changed from 172.16.0.1 to 127.0.0.1" in caplog.text ++ ) ++ ret = mm_master_1_salt_cli.run("test.ping", minion_tgt="mm-minion-1") ++ assert ret.returncode == 0 ++ assert ( ++ "Master ip address changed from 172.16.0.1 to 127.0.0.1" in caplog.text ++ ) +diff --git a/tests/pytests/scenarios/dns/test_dns.py b/tests/pytests/scenarios/dns/test_dns.py +new file mode 100644 +index 0000000000..cd33f0e7f0 +--- /dev/null ++++ b/tests/pytests/scenarios/dns/test_dns.py +@@ -0,0 +1,37 @@ ++import logging ++import subprocess ++import time ++ ++import pytest ++ ++log = logging.getLogger(__name__) ++ ++ ++@pytest.mark.skip_unless_on_linux ++@pytest.mark.skip_if_not_root ++def test_dns_change(master, minion, salt_cli, etc_hosts, caplog, master_alive_interval): ++ """ ++ Verify a minion will pick up a master's dns change if it's been disconnected. ++ """ ++ ++ if not master.ip_addr_set: ++ pytest.skip("Unable to set additional IP address for master") ++ ++ etc_hosts.write_text(f"{etc_hosts.orig_text}\n172.16.0.1 master.local") ++ ++ with minion.started(start_timeout=180): ++ with caplog.at_level(logging.INFO): ++ ret = salt_cli.run("test.ping", minion_tgt="minion") ++ assert ret.returncode == 0 ++ etc_hosts.write_text(f"{etc_hosts.orig_text}\n127.0.0.1 master.local") ++ log.info("Changed hosts record for master1.local and master2.local") ++ subprocess.check_output(["ip", "addr", "del", "172.16.0.1/32", "dev", "lo"]) ++ log.info("Removed secondary master IP address.") ++ # Wait for the minion's master_alive_interval, adding a second for ++ # reliablity. ++ time.sleep(master_alive_interval + 1) ++ assert ( ++ "Master ip address changed from 172.16.0.1 to 127.0.0.1" in caplog.text ++ ) ++ ret = salt_cli.run("test.ping", minion_tgt="minion") ++ assert ret.returncode == 0 +diff --git a/tests/pytests/scenarios/failover/multimaster/test_failover_master.py b/tests/pytests/scenarios/failover/multimaster/test_failover_master.py +index 9f6251a4d6..ebb2899ff0 100644 +--- a/tests/pytests/scenarios/failover/multimaster/test_failover_master.py ++++ b/tests/pytests/scenarios/failover/multimaster/test_failover_master.py +@@ -162,10 +162,6 @@ def test_minions_alive_with_no_master( + """ + Make sure the minions stay alive after all masters have stopped. + """ +- if grains["os_family"] == "Debian" and grains["osmajorrelease"] == 9: +- pytest.skip( +- "Skipping on Debian 9 until flaky issues resolved. See issue #61749" +- ) + start_time = time.time() + with salt_mm_failover_master_1.stopped(): + with salt_mm_failover_master_2.stopped(): +diff --git a/tests/pytests/unit/test_minion.py b/tests/pytests/unit/test_minion.py +index a9e91742a2..017c28d163 100644 +--- a/tests/pytests/unit/test_minion.py ++++ b/tests/pytests/unit/test_minion.py +@@ -884,6 +884,8 @@ async def test_master_type_failover(minion_opts): + assert opts["master"] == "master2" + return MockPubChannel() + ++ minion_opts["master_tries"] = 1 ++ + with patch("salt.minion.resolve_dns", mock_resolve_dns), patch( + "salt.channel.client.AsyncPubChannel.factory", mock_channel_factory + ), patch("salt.loader.grains", MagicMock(return_value=[])): +-- +2.47.0 + diff --git a/revert-setting-selinux-context-for-minion-service-bs.patch b/revert-setting-selinux-context-for-minion-service-bs.patch new file mode 100644 index 0000000..7ba3190 --- /dev/null +++ b/revert-setting-selinux-context-for-minion-service-bs.patch @@ -0,0 +1,65 @@ +From 7217fccdce5df73fda1a8378dc7120642ffe8181 Mon Sep 17 00:00:00 2001 +From: Victor Zhestkov +Date: Mon, 16 Dec 2024 15:31:26 +0300 +Subject: [PATCH] Revert setting SELinux context for minion service + (bsc#1233667) + +This reverts commit d933c8f0795fdada84a01a2cc754586fa720993d. +--- + pkg/common/salt-minion.service | 1 - + pkg/old/deb/salt-minion.service | 1 - + pkg/old/suse/salt-minion.service | 1 - + pkg/old/suse/salt-minion.service.rhel7 | 1 - + 4 files changed, 4 deletions(-) + +diff --git a/pkg/common/salt-minion.service b/pkg/common/salt-minion.service +index 696d0263c3..69aff18c58 100644 +--- a/pkg/common/salt-minion.service ++++ b/pkg/common/salt-minion.service +@@ -9,7 +9,6 @@ Type=notify + NotifyAccess=all + LimitNOFILE=8192 + ExecStart=/usr/bin/salt-minion +-SELinuxContext=system_u:system_r:unconfined_t:s0 + + [Install] + WantedBy=multi-user.target +diff --git a/pkg/old/deb/salt-minion.service b/pkg/old/deb/salt-minion.service +index b0ad82c133..7e6cf14654 100644 +--- a/pkg/old/deb/salt-minion.service ++++ b/pkg/old/deb/salt-minion.service +@@ -8,7 +8,6 @@ KillMode=process + NotifyAccess=all + LimitNOFILE=8192 + ExecStart=/usr/bin/salt-minion +-SELinuxContext=system_u:system_r:unconfined_t:s0 + + [Install] + WantedBy=multi-user.target +diff --git a/pkg/old/suse/salt-minion.service b/pkg/old/suse/salt-minion.service +index b99ef06352..12f28314cb 100644 +--- a/pkg/old/suse/salt-minion.service ++++ b/pkg/old/suse/salt-minion.service +@@ -10,7 +10,6 @@ ExecStart=/usr/bin/salt-minion + KillMode=process + Restart=on-failure + RestartSec=15 +-SELinuxContext=system_u:system_r:unconfined_t:s0 + + [Install] + WantedBy=multi-user.target +diff --git a/pkg/old/suse/salt-minion.service.rhel7 b/pkg/old/suse/salt-minion.service.rhel7 +index 92cc66d32f..6917267714 100644 +--- a/pkg/old/suse/salt-minion.service.rhel7 ++++ b/pkg/old/suse/salt-minion.service.rhel7 +@@ -9,7 +9,6 @@ ExecStart=/usr/bin/salt-minion + KillMode=process + Restart=on-failure + RestartSec=15 +-SELinuxContext=system_u:system_r:unconfined_t:s0 + + [Install] + WantedBy=multi-user.target +-- +2.47.1 + diff --git a/salt.changes b/salt.changes index 35b9df8..c59cdfb 100644 --- a/salt.changes +++ b/salt.changes @@ -1,3 +1,29 @@ +------------------------------------------------------------------- +Thu Jan 9 12:57:12 UTC 2025 - Pablo Suárez Hernández + +- Revert setting SELinux context for minion service (bsc#1233667) +- Remove System V init support + * Make systemd the only supported init system by removing System V init + and insserv references + * Ensure package builds with no init system dependencies if built + without systemd (for example for use in containers) + * Apply some spec-cleaner suggestions (update copyright year, sort + requirements, adjust spacing) + +- Added: + * revert-setting-selinux-context-for-minion-service-bs.patch + +------------------------------------------------------------------- +Tue Nov 26 14:53:34 UTC 2024 - Victor Zhestkov + +- Fix the condition of alternatives for Tumbleweed and Leap 16 +- Use update-alternatives for salt-call and fix builing on EL8 +- Build all python bindings for all flavors +- Make minion reconnecting on changing master IP (bsc#1228182) + +- Added: + * make-minion-reconnecting-on-changing-master-ip-bsc-1.patch + ------------------------------------------------------------------- Thu Oct 17 14:02:57 UTC 2024 - Marek Czernek diff --git a/salt.spec b/salt.spec index 312e30e..69f3f7f 100644 --- a/salt.spec +++ b/salt.spec @@ -1,7 +1,7 @@ # # spec file for package salt # -# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2024 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -12,10 +12,21 @@ # license that conforms to the Open Source Definition (Version 1.9) # published by the Open Source Initiative. -# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# Please submit bugfixes or comments via https://bugs.opensuse.org/ # + + %global debug_package %{nil} +%if 0%{?suse_version} > 1500 +%bcond_without libalternatives +%else +%bcond_with libalternatives +%endif +%if 0%{?sle_version} >= 150400 || 0%{?suse_version} >= 1600 +%define _alternatives 1 +%endif + %global flavor @BUILD_FLAVOR@%{nil} %if "%{flavor}" == "testsuite" %define psuffix -test @@ -28,7 +39,6 @@ %else %bcond_with systemd %endif -%{!?python3_sitelib: %global python3_sitelib %(python3 -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")} %if 0%{?suse_version} > 1110 %bcond_without bash_completion %bcond_without fish_completion @@ -41,13 +51,41 @@ %bcond_without docs %bcond_with builddocs +%if %{without systemd} +%define service_del_preun echo %{*} +%endif + +%{?sle15allpythons} +%define skip_python2 1 +%if 0%{?rhel} == 8 || (0%{?suse_version} == 1500 && 0%{?sle_version} < 150400) +%define __python3_bin_suffix 3.6 +%if 0%{?rhel} == 8 +%define __python3 /usr/libexec/platform-python +%else +%define __python3 /usr/bin/python3 +%endif +%define python_module() python3-%** +%define python_files() -n python3-%1 +%define python_subpackages %{nil} +%define python_sitelib %python3_sitelib +%define python_expand(+abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-=) %{lua: \ +local args = rpm.expand("%**")\ +local python_bin = rpm.expand("%__python3")\ +local python_bin_suffix = rpm.expand("%__python3_bin_suffix")\ +args = args:gsub("$python_bin_suffix", python_bin_suffix)\ +args = args:gsub("$python_sitelib", "python3_sitelib")\ +args = args:gsub("$python", python_bin)\ +print(rpm.expand(args .. "\\n"))\ +} +%define _nosinglespec 1 +%endif Name: salt%{psuffix} Version: 3006.0 Release: 0 Summary: A parallel remote execution system License: Apache-2.0 Group: System/Management -Url: https://saltproject.io/ +URL: https://saltproject.io/ Source: v%{version}.tar.gz Source1: README.SUSE Source2: salt-tmpfiles.d @@ -454,6 +492,12 @@ Patch144: fix-x509-private-key-tests-and-test_suse-on-sle12-68.patch Patch145: enhance-cleanup-mechanism-after-salt-bundle-upgrade-.patch # PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/commit/9683260d61668da8559ecde6caf63a52fedd8790 Patch146: handle-logger-flushing-already-closed-file-686.patch +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/66422 +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/66757 +# PATCH-FIX_UPSTREAM: https://github.com/saltstack/salt/pull/66760 +Patch147: make-minion-reconnecting-on-changing-master-ip-bsc-1.patch +# PATCH-FIX_OPENSUSE: https://github.com/openSUSE/salt/pull/690 +Patch148: revert-setting-selinux-context-for-minion-service-bs.patch ### IMPORTANT: The line below is used as a snippet marker. Do not touch it. ### SALT PATCHES LIST END @@ -464,7 +508,11 @@ BuildRequires: logrotate BuildRequires: fdupes %endif +%if 0%{?_alternatives} +Requires: %{name}-call = %{version}-%{release} +%else Requires: python3-%{name} = %{version}-%{release} +%endif Obsoletes: python2-%{name} Requires(pre): %{_sbindir}/groupadd @@ -473,7 +521,6 @@ Provides: user(salt) Provides: group(salt) %if 0%{?suse_version} -Requires(pre): %fillup_prereq Requires(pre): shadow %endif @@ -499,10 +546,6 @@ Requires: iproute %if %{with systemd} BuildRequires: pkgconfig(systemd) %{?systemd_ordering} -%else -%if 0%{?suse_version} -Requires(pre): %insserv_prereq -%endif %endif %if %{with fish_completion} @@ -526,6 +569,9 @@ BuildRequires: zsh BuildRequires: yum %endif +%define python_subpackage_only 1 +%python_subpackages + %description Salt is a distributed remote execution system used to execute commands and query data. It was developed in order to bring the best solutions found in @@ -536,7 +582,11 @@ servers, handle them quickly and through a simple and manageable interface. %if "%{flavor}" != "testsuite" +%if 0%{?_nosinglespec} %package -n python3-salt +%else +%package -n python-salt +%endif Summary: python3 library for salt Group: System/Management Requires: %{name} = %{version}-%{release} @@ -544,62 +594,76 @@ BuildRequires: python-rpm-macros %if 0%{?rhel} == 8 BuildRequires: platform-python %else -BuildRequires: python3 +BuildRequires: %{python_module base} %endif -BuildRequires: python3-devel -BuildRequires: python3-setuptools +BuildRequires: %{python_module setuptools} # requirements/base.txt %if 0%{?rhel} || 0%{?fedora} BuildRequires: python3-jinja2 +BuildRequires: python3-m2crypto BuildRequires: python3-markupsafe BuildRequires: python3-msgpack > 0.3 BuildRequires: python3-zmq >= 2.2.0 -BuildRequires: python3-m2crypto %else -BuildRequires: python3-Jinja2 -BuildRequires: python3-MarkupSafe -BuildRequires: python3-msgpack-python > 0.3 -BuildRequires: python3-pyzmq >= 2.2.0 +BuildRequires: %{python_module Jinja2} +BuildRequires: %{python_module MarkupSafe} +BuildRequires: %{python_module msgpack-python > 0.3} +BuildRequires: %{python_module pyzmq > 2.2.0} %if 0%{?suse_version} >= 1500 -BuildRequires: python3-M2Crypto +BuildRequires: %{python_module M2Crypto} %else -BuildRequires: python3-pycrypto >= 2.6.1 +BuildRequires: %{python_module pycrypto >= 2.6.1} %endif %endif -BuildRequires: python3-PyYAML -BuildRequires: python3-psutil -BuildRequires: python3-requests >= 1.0.0 -BuildRequires: python3-distro -BuildRequires: python3-looseversion -BuildRequires: python3-packaging -BuildRequires: python3-contextvars +BuildRequires: %{python_module PyYAML} +BuildRequires: %{python_module psutil} +BuildRequires: %{python_module requests >= 1.0.0} +BuildRequires: %{python_module distro} +BuildRequires: %{python_module looseversion} +BuildRequires: %{python_module packaging} +BuildRequires: %{python_module contextvars} # requirements/zeromq.txt %if %{with test} -BuildRequires: python3-boto >= 2.32.1 -BuildRequires: %{python3-mock if %python-base < 3.8} -BuildRequires: python3-moto >= 0.3.6 -BuildRequires: python3-pip -BuildRequires: python3-salt-testing >= 2015.2.16 -BuildRequires: python3-unittest2 -BuildRequires: python3-xml +BuildRequires: %{python_module boto >= 2.32.1} +BuildRequires: %{python_module mock if %python-base < 3.8} +BuildRequires: %{python_module moto >= 0.3.6} +BuildRequires: %{python_module pip} +BuildRequires: %{python_module salt-testing >= 2015.2.16} +BuildRequires: %{python_module unittest2} +BuildRequires: %{python_module xml} %endif %if %{with builddocs} -BuildRequires: python3-sphinx +BuildRequires: %{python_module sphinx} %endif %if 0%{?rhel} == 8 Requires: platform-python %else -Requires: python3 +%if 0%{?_nosinglespec} +Requires: %{python_module base} +%else +Requires: python-base %endif +%endif + +%if 0%{?_alternatives} +%if %{with libalternatives} +Requires: alts +BuildRequires: alts +%else +Requires(post): update-alternatives +Requires(postun):update-alternatives +%endif +%endif + # requirements/base.txt %if 0%{?rhel} || 0%{?fedora} Requires: python3-jinja2 -Requires: yum +Requires: python3-m2crypto Requires: python3-markupsafe Requires: python3-msgpack > 0.3 -Requires: python3-m2crypto Requires: python3-zmq >= 2.2.0 +Requires: yum %if 0%{?rhel} == 8 || 0%{?fedora} >= 30 Requires: dnf @@ -608,42 +672,78 @@ Requires: dnf Requires: yum-plugin-security %endif %else # SUSE -Requires: python3-Jinja2 -Requires: python3-MarkupSafe -Requires: python3-msgpack-python > 0.3 +%if 0%{?_nosinglespec} +Requires: %{python_module Jinja2} +Requires: %{python_module MarkupSafe} +Requires: %{python_module msgpack-python > 0.3} %if 0%{?suse_version} >= 1500 -Requires: python3-M2Crypto +Requires: %{python_module M2Crypto} %else -Requires: python3-pycrypto >= 2.6.1 +Requires: %{python_module pycrypto >= 2.6.1} +%endif +Requires: %{python_module pyzmq >= 2.2.0} +%else +Requires: python-Jinja2 +Requires: python-MarkupSafe +Requires: python-msgpack-python > 0.3 +%if 0%{?suse_version} >= 1500 +Requires: python-M2Crypto +%else +Requires: python-pycrypto >= 2.6.1 +%endif +Requires: python-pyzmq >= 2.2.0 %endif -Requires: python3-pyzmq >= 2.2.0 %endif # end of RHEL / SUSE specific section -Requires: python3-jmespath -Requires: python3-PyYAML -Requires: python3-psutil -Requires: python3-requests >= 1.0.0 -Requires: python3-distro -Requires: python3-looseversion -Requires: python3-packaging -Requires: python3-contextvars +%if 0%{?_nosinglespec} +Recommends: %{python_module jmespath} +Requires: %{python_module PyYAML} +Requires: %{python_module psutil} +Requires: %{python_module requests >= 1.0.0} +Requires: %{python_module distro} +Requires: %{python_module looseversion} +Requires: %{python_module packaging} +Requires: %{python_module contextvars} %if 0%{?suse_version} # required for zypper.py -Requires: python3-rpm -Requires(pre): libzypp(plugin:system) >= 0 -Requires: python3-zypp-plugin +Requires: %{python_module rpm} # requirements/opt.txt (not all) # Suggests: python-MySQL-python ## Disabled for now, originally Recommended -Suggests: python3-timelib -Suggests: python3-gnupg +Suggests: %{python_module timelib} +Suggests: %{python_module gnupg} +%endif +%else +Recommends: python-jmespath +Requires: python-PyYAML +Requires: python-psutil +Requires: python-requests >= 1.0.0 +Requires: python-distro +Requires: python-looseversion +Requires: python-packaging +Requires: python-contextvars +%if 0%{?suse_version} +# required for zypper.py +Requires: python-rpm +# requirements/opt.txt (not all) +# Suggests: python-MySQL-python ## Disabled for now, originally Recommended +Suggests: python-timelib +Suggests: python-gnupg # requirements/zeromq.txt %endif +%endif # %if 0%{?suse_version} # python-xml is part of python-base in all rhel versions -Requires: python3-xml -Suggests: python3-Mako -Recommends: python3-netaddr -Recommends: python3-pyinotify +%if 0%{?_nosinglespec} +Requires: %{python_module xml} +Suggests: %{python_module Mako} +Recommends: %{python_module netaddr} +Recommends: %{python_module pyinotify} +%else +Requires: python-xml +Suggests: python-Mako +Recommends: python-netaddr +Recommends: python-pyinotify +%endif %endif # Required by Salt modules @@ -653,9 +753,19 @@ Requires: file Recommends: man Recommends: python3-passlib -Provides: bundled(python3-tornado) = 4.5.3 +%if 0%{?_nosinglespec} +Provides: bundled(%{python_module tornado}) = 4.5.3 +%else +Provides: bundled(python-tornado) = 4.5.3 +%endif +Provides: %{name}-call = %{version}-%{release} + +%if 0%{?_nosinglespec} %description -n python3-salt +%else +%description -n python-salt +%endif Python3 specific files for salt %package api @@ -716,14 +826,7 @@ Requires: pmtools %endif %if %{with systemd} %{?systemd_requires} -BuildRequires: systemd -%else -%if 0%{?suse_version} -Requires(pre): %insserv_prereq -%endif -%endif -%if 0%{?suse_version} -Requires(pre): %fillup_prereq +BuildRequires: systemd %endif %description master @@ -738,16 +841,13 @@ Requires: %{name} = %{version}-%{release} %if 0%{?suse_version} > 1500 || 0%{?sle_version} > 150000 Requires: (%{name}-transactional-update = %{version}-%{release} if read-only-root-fs) %endif +%if 0%{?suse_version} +Requires: python3-zypp-plugin +Requires(pre): libzypp(plugin:system) >= 0 +%endif %if %{with systemd} %{?systemd_requires} -%else -%if 0%{?suse_version} -Requires(pre): %insserv_prereq -%endif -%endif -%if 0%{?suse_version} -Requires(pre): %fillup_prereq %endif %description minion @@ -760,13 +860,6 @@ Group: System/Management Requires: %{name} = %{version}-%{release} %if %{with systemd} %{?systemd_requires} -%else -%if 0%{?suse_version} -Requires(pre): %insserv_prereq -%endif -%endif -%if 0%{?suse_version} -Requires(pre): %fillup_prereq %endif %description proxy @@ -776,7 +869,6 @@ Examples include network gear that has an API but runs a proprietary OS, devices with limited CPU or memory, or devices that could run a minion, but for security reasons, will not. - %package syndic Summary: The syndic component for saltstack Group: System/Management @@ -784,13 +876,6 @@ Requires: %{name} = %{version}-%{release} Requires: %{name}-master = %{version}-%{release} %if %{with systemd} %{?systemd_requires} -%else -%if 0%{?suse_version} -Requires(pre): %insserv_prereq -%endif -%endif -%if 0%{?suse_version} -Requires(pre): %fillup_prereq %endif %description syndic @@ -808,13 +893,6 @@ Recommends: sshpass %endif %if %{with systemd} %{?systemd_requires} -%else -%if 0%{?suse_version} -Requires(pre): %insserv_prereq -%endif -%endif -%if 0%{?suse_version} -Requires(pre): %fillup_prereq %endif %description ssh @@ -891,44 +969,73 @@ list of active executors. This package add the configuration file. %if "%{flavor}" == "testsuite" -%package -n python3-salt-testsuite +%if 0%{?_nosinglespec} +%package -n %{python_module salt-testsuite} +%else +%package -n python-salt-testsuite +%endif Summary: Unit and integration tests for Salt %if 0%{?rhel} == 8 BuildRequires: platform-python %else -BuildRequires: python3 +BuildRequires: %{python_module base} %endif -BuildRequires: python3-devel -BuildRequires: python3-setuptools +BuildRequires: %{python_module setuptools} Requires: salt = %{version} -Recommends: python3-CherryPy -Requires: python3-Genshi -Requires: python3-Mako +%if 0%{?_nosinglespec} +Recommends: %{python_module CherryPy} +Requires: %{python_module Genshi} +Requires: %{python_module Mako} %if !0%{?suse_version} > 1600 || 0%{?centos} -Requires: python3-boto +Requires: %{python_module boto} %endif -Requires: python3-boto3 -Requires: python3-docker +Requires: %{python_module boto3} +Requires: %{python_module docker} %if 0%{?suse_version} < 1600 -Requires: python3-mock +Requires: %{python_module mock} +%endif +Requires: %{python_module pygit2} +Requires: %{python_module pytest >= 7.0.1} +Requires: %{python_module pytest-httpserver} +Requires: %{python_module pytest-salt-factories >= 1.0.0~rc21} +Requires: %{python_module pytest-subtests} +Requires: %{python_module testinfra} +Requires: %{python_module yamllint} +Requires: %{python_module pip} +%else +Recommends: python-CherryPy +Requires: python-Genshi +Requires: python-Mako +%if !0%{?suse_version} > 1600 || 0%{?centos} +Requires: python-boto +%endif +Requires: python-boto3 +Requires: python-docker +%if 0%{?suse_version} < 1600 +Requires: python-mock +%endif +Requires: python-pygit2 +Requires: python-pytest >= 7.0.1 +Requires: python-pytest-httpserver +Requires: python-pytest-salt-factories >= 1.0.0~rc21 +Requires: python-pytest-subtests +Requires: python-testinfra +Requires: python-yamllint +Requires: python-pip %endif -Requires: python3-pygit2 -Requires: python3-pytest >= 7.0.1 -Requires: python3-pytest-httpserver -Requires: python3-pytest-salt-factories >= 1.0.0~rc21 -Requires: python3-pytest-subtests -Requires: python3-testinfra -Requires: python3-yamllint -Requires: python3-pip Requires: docker Requires: openssh Requires: git Obsoletes: %{name}-tests +%if 0%{?_nosinglespec} %description -n python3-salt-testsuite +%else +%description -n python-salt-testsuite +%endif Collection of unit, functional, and integration tests for %{name}. %endif @@ -949,8 +1056,10 @@ cp %{S:6} . %if 0%{?fedora} || 0%{?rhel} export PATH=/usr/bin:$PATH %endif -python3 setup.py --with-salt-version=%{version} --salt-transport=both build -mv build _build.python3 +%{python_expand # +$python setup.py --with-salt-version=%{version} --salt-transport=both build +mv build _build.%{$python_bin_suffix} +} %if %{with docs} && %{without builddocs} # extract docs from the tarball @@ -970,16 +1079,18 @@ cd doc && make html && rm _build/html/.buildinfo && rm _build/html/_images/proxy %install %if "%{flavor}" != "testsuite" -mv _build.python3 build -python3 setup.py --salt-transport=both install --prefix=%{_prefix} --root=%{buildroot} -mv build _build.python3 +%{python_expand # +mv _build.%{$python_bin_suffix} build +$python setup.py --salt-transport=both install --prefix=%{_prefix} --root=%{buildroot} +mv build _build.%{$python_bin_suffix} -DEF_PYPATH=_build.python3/scripts-*/ +DEF_PYPATH=_build.%{$python_bin_suffix}/scripts-*/ rm -f %{buildroot}%{_bindir}/* for script in $DEF_PYPATH/*; do install -m 0755 $script %{buildroot}%{_bindir} done +} ## create missing directories install -Dd -m 0750 %{buildroot}%{_localstatedir}/cache/salt/cloud @@ -1015,18 +1126,22 @@ install -Dd -m 0755 %{buildroot}%{_sbindir} install -Dd -m 0755 %{buildroot}%{_sysconfdir}/logrotate.d/ # Install salt-support profiles -install -Dpm 0644 salt/cli/support/profiles/* %{buildroot}%{python3_sitelib}/salt/cli/support/profiles +%{python_expand # +install -Dpm 0644 salt/cli/support/profiles/* %{buildroot}%{$python_sitelib}/salt/cli/support/profiles +} %endif %if "%{flavor}" == "testsuite" # Install Salt tests -install -Dd %{buildroot}%{python3_sitelib}/salt-testsuite -cp -a tests %{buildroot}%{python3_sitelib}/salt-testsuite/ +%{python_expand # +install -Dd %{buildroot}%{$python_sitelib}/salt-testsuite +cp -a tests %{buildroot}%{$python_sitelib}/salt-testsuite/ # Remove runtests.py which is not used as deprecated method of running the tests -rm %{buildroot}%{python3_sitelib}/salt-testsuite/tests/runtests.py +rm %{buildroot}%{$python_sitelib}/salt-testsuite/tests/runtests.py # Copy conf files to the testsuite as they are used by the tests -cp -a conf %{buildroot}%{python3_sitelib}/salt-testsuite/ +cp -a conf %{buildroot}%{$python_sitelib}/salt-testsuite/ +} %endif %if "%{flavor}" != "testsuite" @@ -1074,24 +1189,8 @@ ln -s service %{buildroot}%{_sbindir}/rcsalt-syndic ln -s service %{buildroot}%{_sbindir}/rcsalt-minion ln -s service %{buildroot}%{_sbindir}/rcsalt-api install -Dpm 644 %{S:2} %{buildroot}/usr/lib/tmpfiles.d/salt.conf -%else -mkdir -p %{buildroot}%{_initddir} -## install init scripts -install -Dpm 0755 pkg/old/suse/salt-master %{buildroot}%{_initddir}/salt-master -install -Dpm 0755 pkg/old/suse/salt-syndic %{buildroot}%{_initddir}/salt-syndic -install -Dpm 0755 pkg/old/suse/salt-minion %{buildroot}%{_initddir}/salt-minion -install -Dpm 0755 pkg/old/suse/salt-api %{buildroot}%{_initddir}/salt-api -ln -sf %{_initddir}/salt-master %{buildroot}%{_sbindir}/rcsalt-master -ln -sf %{_initddir}/salt-syndic %{buildroot}%{_sbindir}/rcsalt-syndic -ln -sf %{_initddir}/salt-minion %{buildroot}%{_sbindir}/rcsalt-minion -ln -sf %{_initddir}/salt-api %{buildroot}%{_sbindir}/rcsalt-api %endif -## Install sysV salt-minion watchdog for SLES11 and RHEL6 -%if 0%{?rhel} == 6 || 0%{?suse_version} == 1110 -install -Dpm 0755 scripts/suse/watchdog/salt-daemon-watcher %{buildroot}%{_bindir}/salt-daemon-watcher -%endif - # ## install config files install -Dpm 0640 conf/minion %{buildroot}%{_sysconfdir}/salt/minion @@ -1137,9 +1236,20 @@ install -Dpm 0640 conf/suse/standalone-formulas-configuration.conf %{buildroot}% %if 0%{?suse_version} > 1020 %fdupes %{buildroot}%{_docdir} -%fdupes %{buildroot}%{python3_sitelib} +%python_expand %fdupes %{buildroot}%{$python_sitelib} %endif +%if 0%{?_alternatives} +%python_clone -a %{buildroot}%{_bindir}/salt-call +%endif + +%endif + +%check +%if %{with test} +%{python_expand # +$python setup.py test --runtests-opts=-u +} %endif %if "%{flavor}" != "testsuite" @@ -1153,6 +1263,10 @@ getent passwd salt >/dev/null || %{_sbindir}/useradd -r -g salt -d $S_HOME -s /b if [[ -d "$S_PHOME/.ssh" ]]; then mv $S_PHOME/.ssh $S_HOME fi +%if 0%{?_alternatives} +[ -h %{_bindir}/salt-call ] || rm -f %{_bindir}/salt-call +%python_libalternatives_reset_alternative salt-call +%endif %post %if %{with systemd} @@ -1185,14 +1299,9 @@ dbus-uuidgen --ensure %if %{with systemd} %if 0%{?suse_version} %service_add_post salt-proxy@.service -%fillup_only %else %systemd_post salt-proxy@.service %endif -%else -%if 0%{?suse_version} -%fillup_and_insserv -%endif %endif %postun proxy @@ -1202,11 +1311,6 @@ dbus-uuidgen --ensure %else %systemd_postun_with_restart salt-proxy@.service %endif -%else -%if 0%{?suse_version} -%insserv_cleanup -%restart_on_update salt-proxy -%endif %endif %preun syndic @@ -1216,15 +1320,6 @@ dbus-uuidgen --ensure %else %systemd_preun salt-syndic.service %endif -%else -%if 0%{?suse_version} -%stop_on_removal salt-syndic -%else - if [ $1 -eq 0 ] ; then - /sbin/service salt-syndic stop >/dev/null 2>&1 - /sbin/chkconfig --del salt-syndic - fi -%endif %endif %pre syndic @@ -1238,14 +1333,9 @@ dbus-uuidgen --ensure %if %{with systemd} %if 0%{?suse_version} %service_add_post salt-syndic.service -%fillup_only %else %systemd_post salt-syndic.service %endif -%else -%if 0%{?suse_version} -%fillup_and_insserv -%endif %endif %postun syndic @@ -1255,11 +1345,6 @@ dbus-uuidgen --ensure %else %systemd_postun_with_restart salt-syndic.service %endif -%else -%if 0%{?suse_version} -%insserv_cleanup -%restart_on_update salt-syndic -%endif %endif %preun master @@ -1269,15 +1354,6 @@ dbus-uuidgen --ensure %else %systemd_preun salt-master.service %endif -%else -%if 0%{?suse_version} -%stop_on_removal salt-master -%else - if [ $1 -eq 0 ] ; then - /sbin/service salt-master stop >/dev/null 2>&1 - /sbin/chkconfig --del salt-master - fi -%endif %endif %pre master @@ -1313,16 +1389,9 @@ if [ "${systemd_ver%%.*}" -lt 228 ]; then fi %if 0%{?suse_version} %service_add_post salt-master.service -%fillup_only %else %systemd_post salt-master.service %endif -%else -%if 0%{?suse_version} -%fillup_and_insserv -%else - /sbin/chkconfig --add salt-master -%endif %endif %postun master @@ -1332,15 +1401,6 @@ fi %else %systemd_postun_with_restart salt-master.service %endif -%else -%if 0%{?suse_version} -%restart_on_update salt-master -%insserv_cleanup -%else - if [ "$1" -ge "1" ] ; then - /sbin/service salt-master condrestart >/dev/null 2>&1 || : - fi -%endif %endif %preun minion @@ -1350,15 +1410,6 @@ fi %else %systemd_preun salt-minion.service %endif -%else -%if 0%{?suse_version} -%stop_on_removal salt-minion -%else - if [ $1 -eq 0 ] ; then - /sbin/service salt-minion stop >/dev/null 2>&1 - /sbin/chkconfig --del salt-minion - fi -%endif %endif %pre minion @@ -1372,16 +1423,9 @@ fi %if %{with systemd} %if 0%{?suse_version} %service_add_post salt-minion.service -%fillup_only %else %systemd_post salt-minion.service %endif -%else -%if 0%{?suse_version} -%fillup_and_insserv -%else - /sbin/chkconfig --add salt-minion -%endif %endif %postun minion @@ -1391,15 +1435,6 @@ fi %else %systemd_postun_with_restart salt-minion.service %endif -%else -%if 0%{?suse_version} -%insserv_cleanup -%restart_on_update salt-minion -%else - if [ "$1" -ge "1" ] ; then - /sbin/service salt-minion condrestart >/dev/null 2>&1 || : - fi -%endif %endif %preun api @@ -1427,10 +1462,6 @@ fi %else %systemd_post salt-api.service %endif -%else -%if 0%{?suse_version} -%fillup_and_insserv -%endif %endif %postun api @@ -1440,14 +1471,25 @@ fi %else %systemd_postun_with_restart salt-api.service %endif -%else -%if 0%{?suse_version} -%insserv_cleanup -%restart_on_update -%endif %endif -%posttrans -n python3-salt +%if 0%{?_alternatives} +%pre -n python-salt +[ -h %{_bindir}/salt-call ] || rm -f %{_bindir}/salt-call +%python_libalternatives_reset_alternative salt-call + +%post -n python-salt +%python_install_alternative salt-call + +%postun -n python-salt +%python_uninstall_alternative salt-call +%endif + +%if 0%{?_nosinglespec} +%posttrans -n %{python_module salt} +%else +%posttrans -n python-salt +%endif # force re-generate a new thin.tgz rm -f %{_localstatedir}/cache/salt/master/thin/version rm -f %{_localstatedir}/cache/salt/minion/thin/version @@ -1455,11 +1497,9 @@ rm -f %{_localstatedir}/cache/salt/minion/thin/version %files api %defattr(-,root,root) %{_bindir}/salt-api -%{_sbindir}/rcsalt-api %if %{with systemd} +%{_sbindir}/rcsalt-api %{_unitdir}/salt-api.service -%else -%{_initddir}/salt-api %endif %{_mandir}/man1/salt-api.1.* @@ -1485,11 +1525,9 @@ rm -f %{_localstatedir}/cache/salt/minion/thin/version %defattr(-,root,root) %{_bindir}/salt-syndic %{_mandir}/man1/salt-syndic.1.gz -%{_sbindir}/rcsalt-syndic %if %{with systemd} +%{_sbindir}/rcsalt-syndic %{_unitdir}/salt-syndic.service -%else -%{_initddir}/salt-syndic %endif %files minion @@ -1501,7 +1539,9 @@ rm -f %{_localstatedir}/cache/salt/minion/thin/version %dir %attr(0750, root, root) %{_sysconfdir}/salt/minion.d/ %dir %attr(0750, root, root) %{_sysconfdir}/salt/pki/minion/ %dir %attr(0750, root, root) %{_localstatedir}/cache/salt/minion/ +%if %{with systemd} %{_sbindir}/rcsalt-minion +%endif # Install plugin only on SUSE machines %if 0%{?suse_version} @@ -1522,13 +1562,6 @@ rm -f %{_localstatedir}/cache/salt/minion/thin/version %if %{with systemd} %{_unitdir}/salt-minion.service -%else -%config(noreplace) %{_initddir}/salt-minion -%endif - -## Install sysV salt-minion watchdog for SLES11 and RHEL6 -%if 0%{?rhel} == 6 || 0%{?suse_version} == 1110 -%{_bindir}/salt-daemon-watcher %endif %files proxy @@ -1554,11 +1587,9 @@ rm -f %{_localstatedir}/cache/salt/minion/thin/version %if 0%{?suse_version} <= 1500 %config(noreplace) %{_sysconfdir}/sysconfig/SuSEfirewall2.d/services/salt %endif -%{_sbindir}/rcsalt-master %if %{with systemd} +%{_sbindir}/rcsalt-master %{_unitdir}/salt-master.service -%else -%config(noreplace) %{_initddir}/salt-master %endif # %config(noreplace) %attr(0640, root, salt) %{_sysconfdir}/salt/master @@ -1584,7 +1615,9 @@ rm -f %{_localstatedir}/cache/salt/minion/thin/version %files %defattr(-,root,root,-) %{_bindir}/spm +%if ! 0%{?_alternatives} %{_bindir}/salt-call +%endif %{_bindir}/salt-support %{_mandir}/man1/salt-call.1.gz %{_mandir}/man1/spm.1.gz @@ -1603,13 +1636,16 @@ rm -f %{_localstatedir}/cache/salt/minion/thin/version %endif %{_mandir}/man1/salt.1.* -%files -n python3-salt +%files %{python_files salt} %defattr(-,root,root,-) -%dir %{python3_sitelib}/salt -%dir %{python3_sitelib}/salt-*.egg-info -%{python3_sitelib}/salt/* -%{python3_sitelib}/salt-*.egg-info/* -%exclude %{python3_sitelib}/salt/cloud/deploy/*.sh +%if 0%{?_alternatives} +%python_alternative %{_bindir}/salt-call +%endif +%dir %{python_sitelib}/salt +%dir %{python_sitelib}/salt-*.egg-info +%{python_sitelib}/salt/* +%{python_sitelib}/salt-*.egg-info/* +%exclude %{python_sitelib}/salt/cloud/deploy/*.sh %if %{with docs} %files doc @@ -1656,10 +1692,8 @@ rm -f %{_localstatedir}/cache/salt/minion/thin/version %endif %if "%{flavor}" == "testsuite" -%files -n python3-salt-testsuite -%{python3_sitelib}/salt-testsuite +%files %{python_files salt-testsuite} +%{python_sitelib}/salt-testsuite %endif %changelog - -