From 5bc1bb61fdeb88c8e597322fbacbc8458d5c97ee Mon Sep 17 00:00:00 2001 From: Tyler Levy Conde Date: Wed, 4 Sep 2024 11:20:52 -0600 Subject: [PATCH] Add repo managing --- src/soluble/salt/repo.py | 144 +++++++++++++++++++++++++++ src/soluble/salt/ssh.py | 18 +++- src/soluble/soluble/init.py | 1 + src/soluble_master/soluble/master.py | 6 ++ src/soluble_minion/soluble/minion.py | 6 ++ 5 files changed, 172 insertions(+), 3 deletions(-) create mode 100644 src/soluble/salt/repo.py diff --git a/src/soluble/salt/repo.py b/src/soluble/salt/repo.py new file mode 100644 index 0000000..cdc6949 --- /dev/null +++ b/src/soluble/salt/repo.py @@ -0,0 +1,144 @@ +# Anything higher than 20 is assumed to be ubuntu, lower than 20 is debian +DEBIAN_UBUNTU_MAJOR_RELEASE_CUTTOFF = 18 + +DEBIAN_REPO = "deb http://repo.saltproject.io/py3/ubuntu/{{ grains['osmajorrelease'] }}/amd64/latest {{ grains['oscodename'] }} main" +DEBIAN_KEY = "https://repo.saltproject.io/py3/ubuntu/{{ grains['osmajorrelease'] }}/amd64/latest/SALTSTACK-GPG-KEY.pub" +DEBIAN_TARGET = ( + f"os_family:Debian|osmajorrelease:>={DEBIAN_UBUNTU_MAJOR_RELEASE_CUTTOFF}" +) + +UBUNTU_REPO = "deb http://repo.saltproject.io/py3/debian/{{ grains['osmajorrelease'] }}/amd64/latest {{ grains['oscodename'] }} main" +UBUNTU_KEY = "https://repo.saltproject.io/py3/debian/{{ grains['osmajorrelease'] }}/amd64/latest/SALTSTACK-GPG-KEY.pub" +UBUNTU_TARGET = ( + f"os_family:Debian|osmajorrelease:<{DEBIAN_UBUNTU_MAJOR_RELEASE_CUTTOFF}" +) + +REDHAT_REPO = "https://repo.saltproject.io/py3/redhat/el{{ grains['osmajorrelease'] }}/x86_64/latest/" +REDHAT_KEY = "https://repo.saltproject.io/py3/redhat/el{{ grains['osmajorrelease'] }}/x86_64/latest/SALTSTACK-GPG-KEY.pub" +REDHAT_TARGET = "os_family:RedHat" + +AMAZON_REPO = "https://repo.saltproject.io/py3/amazon/2/x86_64/latest/" +AMAZON_KEY = ( + "https://repo.saltproject.io/py3/amazon/2/x86_64/latest/SALTSTACK-GPG-KEY.pub" +) +AMAZON_TARGET = "os:Amazon" + + +async def setup(hub, name: str): + """ + Add the SaltStack repository and install Salt based on the target OS. + First, target Ubuntu-based systems, then fall back to other Debian-based systems (e.g., Debian, SteamOS). + """ + # TODO this functionality is not quite ready + return + hub.log.info("Setting up SaltStack repository on target(s)...") + + # Handle Ubuntu-based systems (Pop!_OS, etc.) + await hub.salt.ssh.run_command( + name, + "state.single", + "pkgrepo.managed", + f'name="{DEBIAN_REPO}"', + f'key_url="{DEBIAN_KEY}"', + "refresh=True", + "--delimiter='|'", + target=DEBIAN_TARGET, + target_type="grain_pcre", + ) + + # Fallback to other Debian-based systems + await hub.salt.ssh.run_command( + name, + "state.single", + "pkgrepo.managed", + f'name="{UBUNTU_REPO}"', + f'key_url="{UBUNTU_KEY}"', + "refresh=True", + "--delimiter='|'", + target=UBUNTU_TARGET, + target_type="grain_pcre", + ) + + # Target RedHat/CentOS systems + await hub.salt.ssh.run_command( + name, + "state.single", + "pkgrepo.managed", + f'name="{REDHAT_REPO}"', + f'gpgkey="{REDHAT_KEY}"', + "gpgcheck=1", + "refresh=True", + target=REDHAT_TARGET, + target_type="grain", + ) + + # Target Amazon Linux systems + await hub.salt.ssh.run_command( + name, + "state.single", + "pkgrepo.managed", + f'name="{AMAZON_REPO}"', + f'gpgkey="{AMAZON_KEY}"', + "gpgcheck=1", + "refresh=True", + target=AMAZON_TARGET, + target_type="grain", + ) + + hub.log.info("SaltStack repository set up successfully.") + + +async def teardown(hub, name: str): + """ + Remove the SaltStack repository based on the target OS. + First, target Ubuntu-based systems, then fall back to other Debian-based systems (e.g., Debian, SteamOS). + Finally, handle RedHat-based and Amazon Linux systems. + """ + # TODO this functionality is not quite ready + return + hub.log.info("Removing SaltStack repository from target(s)...") + + # Handle Ubuntu-based systems (Pop!_OS, etc.) + await hub.salt.ssh.run_command( + name, + "state.single", + "pkgrepo.absent", + f'name="{UBUNTU_REPO}"', + "--delimiter='|'", + target=UBUNTU_TARGET, + target_type="grain_pcre", + ) + + # Fallback to other Debian-based systems + await hub.salt.ssh.run_command( + name, + "state.single", + "pkgrepo.absent", + f'name="{DEBIAN_REPO}"', + "--delimiter='|'", + target=DEBIAN_TARGET, + target_type="grain_pcre", + ) + + # Remove repository for RedHat/CentOS systems + await hub.salt.ssh.run_command( + name, + "state.single", + "pkgrepo.absent", + f'name="{REDHAT_REPO}"', + target=REDHAT_TARGET, + target_type="grain", + ) + + # Remove repository for Amazon Linux systems + await hub.salt.ssh.run_command( + name, + "state.single", + "pkgrepo.absent", + f'name="{AMAZON_REPO}"', + target=AMAZON_TARGET, + target_type="grain", + ) + + hub.log.info("SaltStack repository removed successfully.") + return 0 diff --git a/src/soluble/salt/ssh.py b/src/soluble/salt/ssh.py index 46a8204..e5adb8a 100644 --- a/src/soluble/salt/ssh.py +++ b/src/soluble/salt/ssh.py @@ -2,12 +2,15 @@ async def run_command( hub, name: str, command: str, - *, + *args, capture_output: bool = True, hard_fail: bool = True, + target: str = None, + target_type: str = "glob", ) -> dict[str, object]: """Run a salt-ssh command asynchronously, handle all prompts, and return the output.""" - target = hub.soluble.RUN[name].ssh_target + if target is None: + target = hub.soluble.RUN[name].ssh_target roster = hub.soluble.RUN[name].roster_file escalate = hub.soluble.RUN[name].escalate config_dir = hub.soluble.RUN[name].salt_config_dir @@ -16,7 +19,16 @@ async def run_command( cmd = hub.soluble.RUN[name].salt_ssh_bin assert cmd, "Could not find salt-ssh" - full_command = f"{cmd} '{target}' --roster-file={roster} {command} --log-level={hub.OPT.pop_config.log_level} {options}" + # Add targeting logic based on the passed target_type + if target_type == "grain": + target_option = f"--grain '{target}'" + elif target_type == "grain_pcre": + target_option = f"--grain-pcre '{target}'" + else: + target_option = f"'{target}'" + + full_command = f"{cmd} {target_option} --roster-file={roster} {command} --log-level={hub.OPT.pop_config.log_level} {options} {' '.join(args)}" + if capture_output: full_command += " --no-color --out=json" if config_dir: diff --git a/src/soluble/soluble/init.py b/src/soluble/soluble/init.py index 60892c7..14926a2 100644 --- a/src/soluble/soluble/init.py +++ b/src/soluble/soluble/init.py @@ -122,6 +122,7 @@ async def setup(hub, name: str): async def run(hub, name: str) -> int: """This is where a soluble plugin runs its primary function""" hub.log.info("Soluble run") + # TODO do a salt-ssh --raw await hub.salt.ssh.run_command(name, f"test.ping", capture_output=False) return 0 diff --git a/src/soluble_master/soluble/master.py b/src/soluble_master/soluble/master.py index 7b01ce3..fc917c7 100644 --- a/src/soluble_master/soluble/master.py +++ b/src/soluble_master/soluble/master.py @@ -16,6 +16,9 @@ async def setup(hub, name: str): f"state.single file.managed source=file://{config.master_config} name=/etc/salt/master", ) + # Set up salt repo if necessary + await hub.salt.repo.setup(name) + # Install Salt on the target hub.log.info("Installing Salt on target(s)...") await hub.salt.ssh.run_command(name, "state.single pkg.installed name=salt-master") @@ -56,6 +59,9 @@ async def teardown(hub, name: str): hub.log.info("Uninstalling Salt from target(s)...") await hub.salt.ssh.run_command(name, "state.single pkg.removed name=salt-master") + # Remove the salt repo if necessary + await hub.salt.repo.teardown(name) + # Remove the master configuration file hub.log.info("Removing master configuration from target(s)...") await hub.salt.ssh.run_command( diff --git a/src/soluble_minion/soluble/minion.py b/src/soluble_minion/soluble/minion.py index 372dfde..bb52347 100644 --- a/src/soluble_minion/soluble/minion.py +++ b/src/soluble_minion/soluble/minion.py @@ -21,6 +21,9 @@ async def setup(hub, name: str): f'state.single cmd.run name="echo {minion_id} > /etc/salt/minion_id"', ) + # Set up salt repo if necessary + await hub.salt.repo.setup(name) + # Install Salt on the target hub.log.info("Installing Salt on target(s)...") await hub.salt.ssh.run_command(name, "state.single pkg.installed name=salt-minion") @@ -76,6 +79,9 @@ async def teardown(hub, name: str): name, "state.single pkg.removed name=salt-minion", hard_fail=False ) + # Remove the salt repo if necessary + await hub.salt.repo.teardown(name) + # Remove the minion configuration file hub.log.info("Removing minion configuration from target(s)...") await hub.salt.ssh.run_command(