Skip to content

Commit

Permalink
Create the helpers needed to run the tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Akm0d committed Aug 28, 2024
1 parent 05b95e2 commit 07c1e6b
Show file tree
Hide file tree
Showing 5 changed files with 157 additions and 4 deletions.
29 changes: 26 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,45 @@ on:

jobs:
build:

runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.10", "3.11"]

services:
docker:
image: docker:20.10.7
options: --privileged
ports:
- 2375:2375
env:
DOCKER_TLS_CERTDIR: ""

steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}

- name: Install Docker CLI
run: |
sudo apt-get update
sudo apt-get install -y docker-ce-cli
- name: Install dependencies
run: |
python -m pip install .[test]
python -m pip install --upgrade pip
pip install .[test]
- name: Set Docker Host
run: |
echo "DOCKER_HOST=tcp://localhost:2375" >> $GITHUB_ENV
- name: Test with pytest
env:
DOCKER_HOST: tcp://localhost:2375
run: |
pytest
pytest --maxfail=5 -v
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,13 @@ There are three basic soluble plugins: `init`, `minion`, and `master`.

- **Soluble Master Example:**

Running the soluble `master` plugin requires you to install `soluble` with the `master` extras
if you aren't running the command from a salt master:

```bash
pip install soluble[master]
```

This will spin up a master on the roster targets until you hit CTRL-C.

```bash
Expand Down
14 changes: 13 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,22 @@ packages = [
[project.optional-dependencies]
test = [
"asynctest",
"asyncssh",
"cryptography>=39.0.1,<42.0.0",
"docker",
"mock",
"pidfile>=0.1.1",
"psutil>=5.6.3",
"pytest>=6.2.5",
"pytest-asyncio",
"pytest-pop>=8.0",
"pytest-httpserver",
"pytest-pop>=11.0.0",
"pytest-tempdir",
"salt",
]

master = [
"salt",
]

# Specify console scripts
Expand Down
10 changes: 10 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,18 @@

@pytest.fixture(scope="session", name="hub")
def integration_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="pytest", 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)
hub.pop.sub.add(python_import="tempfile", sub=hub.lib)

with mock.patch("sys.argv", ["soluble"]):
hub.pop.config.load(["soluble"], cli="soluble", parse_cli=False)

Expand Down
101 changes: 101 additions & 0 deletions tests/helpers/container.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import contextlib


def __init__(hub):
hub.test.TARGETS = {}


def next_free_port(hub, host, port: int = 2222) -> int:
for i in range(1000):
with hub.lib.socket.socket(
hub.lib.socket.AF_INET, hub.lib.socket.SOCK_STREAM
) as sock:
sock.settimeout(2)
try:
sock.bind((host, port))
break
except OSError:
port += 2
else:
raise RuntimeError(f"Unable to find an available port on {host}")

return port


async def create_ssh_target(
hub, host: str, username: str = "user", password: str = "pass"
):
client = hub.lib.docker.from_env()
port = hub.test.container.next_free_port(host)
target_name = f"soluble_test_{hub.lib.uuid.uuid4()}"
pugid = "0" if username == "root" else "1000"

container = client.containers.run(
"linuxserver/openssh-server:latest",
command=["/bin/sh", "-c", "while true; do sleep 1; done"],
detach=True,
ports={"2222/tcp": port},
hostname=target_name,
network="bridge",
environment={
"PUID": pugid,
"PGID": pugid,
"TZ": "Etc/UTC",
"SUDO_ACCESS": "true",
"PASSWORD_ACCESS": "true",
"USER_NAME": username,
"USER_PASSWORD": password,
},
)

# Enable SSH Tunneling
container.exec_run(
cmd="sed -i 's/^AllowTcpForwarding no/AllowTcpForwarding yes/' /etc/ssh/sshd_config",
privileged=True,
detach=True,
)
if username == "root":
container.exec_run(
cmd="sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config",
privileged=True,
detach=True,
)

# Wait for SSH service to be available
for _ in range(60):
try:
async with hub.lib.asyncssh.connect(
host=host,
port=port,
username=username,
password=password,
known_hosts=None,
):
break
except:
await hub.lib.asyncio.sleep(1)
else:
container.stop()
container.remove()
raise RuntimeError("Could not connect to container")

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

return hub.test.TARGETS[target_name]


@contextlib.contextmanager
def roster(hub):
"""
Return roster file for all created containers
"""
roster = hub.test.TARGETS
with hub.lib.tempfile.NamedTemporaryFile(suffix=".yaml") as fh:
hub.lib.yaml.safe_dump(roster, fh)
yield fh.name

0 comments on commit 07c1e6b

Please sign in to comment.