Skip to content
This repository has been archived by the owner on Jan 24, 2025. It is now read-only.

Commit

Permalink
Add integration test for init
Browse files Browse the repository at this point in the history
  • Loading branch information
Akm0d committed Aug 30, 2024
1 parent 011c94c commit b568e1f
Show file tree
Hide file tree
Showing 7 changed files with 139 additions and 24 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ soluble = "soluble.scripts:start"

[tool.pytest.ini_options]
testpaths = "tests"
addopts = "--tb native --full-trace --color=yes -vv"
addopts = "--tb native --full-trace --color=yes -vv -s"
asyncio_mode = "auto"

[tool.hatch.envs.lint]
Expand Down
2 changes: 1 addition & 1 deletion src/soluble/salt/ssh.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ async def run_command(
cmd = hub.lib.shutil.which("salt-ssh")
assert cmd, "Could not find salt-ssh"

full_command = f'{cmd} "{target}" --roster-file={roster} {command} --log-level=quiet --hard-crash {options}'
full_command = f"{cmd} '{target}' --roster-file={roster} {command} --log-level=quiet --hard-crash {options}"
if capture_output:
full_command += " --no-color --out=json"
if config_dir:
Expand Down
18 changes: 13 additions & 5 deletions src/soluble/soluble/init.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ def __init__(hub):
hub.pop.sub.add(python_import="salt", sub=hub.lib)
hub.pop.sub.add(python_import="socket", sub=hub.lib)
hub.pop.sub.add(python_import="tempfile", sub=hub.lib)
hub.pop.sub.add(python_import="typing", sub=hub.lib)
hub.pop.sub.add(python_import="shlex", sub=hub.lib)
hub.pop.sub.add(python_import="sys", sub=hub.lib)
hub.pop.sub.add(python_import="uuid", sub=hub.lib)
Expand Down Expand Up @@ -54,13 +55,20 @@ def cli(hub):
if value is None:
continue

salt_ssh_opts.append(opts["options"][0])
if isinstance(value, tuple):
for v in value:
salt_ssh_opts.append(opts["options"][0])
if " " in v:
v = f"'{v}'"
salt_ssh_opts.append(str(v))
else:
salt_ssh_opts.append(opts["options"][0])

# This is a flag
if isinstance(value, bool):
continue
# This is a flag
if isinstance(value, bool):
continue

salt_ssh_opts.append(str(value))
salt_ssh_opts.append(str(value))

kwargs["salt_ssh_options"] = salt_ssh_opts

Expand Down
111 changes: 109 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
from unittest import mock

import pop.hub
import pytest


@pytest.fixture(scope="session", name="hub")
def integration_hub(hub):
@pytest.fixture(scope="function", name="hub")
def integration_hub():
hub = pop.hub.Hub()
hub.pop.sub.add("tests.helpers", subname="test")
hub.pop.sub.add(dyne_name="soluble")

hub.pop.sub.add(python_import="asyncio", sub=hub.lib)
hub.pop.sub.add(python_import="asyncssh", sub=hub.lib)
hub.pop.sub.add(python_import="docker", sub=hub.lib)
hub.pop.sub.add(python_import="pathlib", sub=hub.lib)
hub.pop.sub.add(python_import="pytest", sub=hub.lib)
hub.pop.sub.add(python_import="pwd", sub=hub.lib)
hub.pop.sub.add(python_import="uuid", sub=hub.lib)
hub.pop.sub.add(python_import="sys", sub=hub.lib)
hub.pop.sub.add(python_import="socket", sub=hub.lib)
Expand All @@ -21,3 +25,106 @@ def integration_hub(hub):
hub.pop.config.load(["soluble"], cli="soluble", parse_cli=False)

yield hub


@pytest.fixture(scope="function", autouse=True)
def cleanup_containers(hub):
# Yield first, after the test, clean up the containers
try:
yield
finally:
for container in hub.test.CONTAINER.values():
container.stop()
container.remove()


@pytest.fixture(scope="function", autouse=True)
def salt_config_dir(hub):
# Create a temporary directory for the configuration files
tempdir = hub.lib.tempfile.TemporaryDirectory()

# Get the current user's username
user = hub.lib.pwd.getpwuid(hub.lib.os.getuid()).pw_name

# Define the paths within the temp directory
config_dir = tempdir.name
root_dir = hub.lib.os.path.join(config_dir, "salt")
pki_dir_master = hub.lib.os.path.join(root_dir, "pki", "master")
pki_dir_minion = hub.lib.os.path.join(root_dir, "pki", "minion")
cachedir_master = hub.lib.os.path.join(root_dir, "cache", "master")
cachedir_minion = hub.lib.os.path.join(root_dir, "cache", "minion")
logs_master = hub.lib.os.path.join(root_dir, "logs", "master.log")
logs_minion = hub.lib.os.path.join(root_dir, "logs", "minion.log")

# Ensure all necessary directories exist
hub.lib.os.makedirs(pki_dir_master, exist_ok=True)
hub.lib.os.makedirs(pki_dir_minion, exist_ok=True)
hub.lib.os.makedirs(cachedir_master, exist_ok=True)
hub.lib.os.makedirs(cachedir_minion, exist_ok=True)
hub.lib.os.makedirs(hub.lib.os.path.dirname(logs_master), exist_ok=True)
hub.lib.os.makedirs(hub.lib.os.path.dirname(logs_minion), exist_ok=True)

# Master configuration
master_config = {
"user": user,
"root_dir": root_dir,
"pki_dir": pki_dir_master,
"cachedir": cachedir_master,
"log_file": logs_master,
}

# Minion configuration
minion_config = {
"user": user,
"master": "localhost",
"root_dir": root_dir,
"pki_dir": pki_dir_minion,
"cachedir": cachedir_minion,
"log_file": logs_minion,
}

# Write the master and minion configs to files
master_config_path = hub.lib.os.path.join(config_dir, "master")
minion_config_path = hub.lib.os.path.join(config_dir, "minion")

with open(master_config_path, "w") as f:
hub.lib.yaml.safe_dump(master_config, f)

with open(minion_config_path, "w") as f:
hub.lib.yaml.safe_dump(minion_config, f)

# Store this on the nub
hub.test.SALT_CONFIG_DIR = config_dir

# Yield the config directory for use in other fixtures
yield config_dir

# Cleanup the temporary directory after the session
tempdir.cleanup()


@pytest.fixture(scope="function")
async def salt_master(hub, salt_config_dir):
# Start the Salt master using the configuration
process = await hub.lib.asyncio.create_subprocess_exec(
"salt-master",
"-c",
salt_config_dir,
"-ldebug",
)

await hub.lib.asyncio.sleep(5)

print("Done setting salt master")
yield

# Stop the Salt master after tests are done
process.terminate()

try:
# Wait for the process to terminate gracefully
await process.wait()
except hub.lib.asyncio.CancelledError:
# Ensure the process is fully stopped if wait fails
process.kill()
await process.wait()
15 changes: 6 additions & 9 deletions tests/helpers/cmd.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
async def run(hub, subcommand: str, target: str = "*", *args):
with hub.test.container.roster() as rf:
command = f"{hub.lib.sys.executable} -m soluble -R {rf} {subcommand} '{target}' {' '.join(args)}"
command = f"{hub.lib.sys.executable} -m soluble --ssh-option='-o StrictHostKeyChecking=no' --log-level=debug --salt-config-dir {hub.test.SALT_CONFIG_DIR} -R {rf} {subcommand} '{target}' {' '.join(args)}"

# Run the command asynchronously
process = await hub.lib.asyncio.create_subprocess_shell(
command,
stdout=hub.lib.asyncio.subprocess.PIPE,
stderr=hub.lib.asyncio.subprocess.PIPE,
)

await hub.lib.asyncio.sleep(2)

# Capture the stdout and stderr
stdout, stderr = await process.communicate()
stdout, _ = await process.communicate()

# Decode the output
stdout = stdout.decode()
stderr = stderr.decode()

# Assert the process was successful
assert (
process.returncode == 0
), f"Command failed with code {process.returncode}: {stderr}"
print(stdout)
assert not await process.wait(), "Command failed"

# Return the captured stdout
return stdout
9 changes: 5 additions & 4 deletions tests/helpers/container.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

def __init__(hub):
hub.test.ROSTER = {}
hub.test.CONTAINER = {}


def next_free_port(hub, host, port: int = 2222) -> int:
Expand Down Expand Up @@ -78,12 +79,12 @@ async def create(hub, username: str = "user", password: str = "pass"):
container.remove()
raise RuntimeError("Could not connect to container")

hub.test.ROSTER[target_name] = container
hub.test.ROSTER[target_name] = {
"name": target_name,
"host": "localhost",
"port": port,
"username": username,
"password": password,
"container": container,
}

return hub.test.ROSTER[target_name]
Expand All @@ -94,7 +95,7 @@ def roster(hub):
"""
Return roster file for all created containers
"""
roster = hub.test.ROSTER
with hub.lib.tempfile.NamedTemporaryFile(suffix=".yaml") as fh:
roster = hub.test.ROSTER.copy()
with hub.lib.tempfile.NamedTemporaryFile("w+", suffix=".yaml", delete=False) as fh:
hub.lib.yaml.safe_dump(roster, fh)
yield fh.name
6 changes: 4 additions & 2 deletions tests/soluble/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ def test_help(hub):
hub.soluble.init.cli()


async def test_cli(hub):
async def test_cli(hub, salt_master):
print("creating container")
await hub.test.container.create()
assert await hub.test.cmd.run("init")
print("running command")
await hub.test.cmd.run("init")

0 comments on commit b568e1f

Please sign in to comment.