From 236862982307ede317c973815750203d322a42f4 Mon Sep 17 00:00:00 2001 From: Nikhil Dhandre Date: Fri, 8 May 2020 00:10:24 +0530 Subject: [PATCH 1/2] Add support of podman --- miqsel/conf.yaml | 11 ++-- miqsel/config.py | 15 ++++- miqsel/env.py | 2 +- miqsel/server.py | 154 +++++++++++++++++++++++------------------------ 4 files changed, 96 insertions(+), 86 deletions(-) diff --git a/miqsel/conf.yaml b/miqsel/conf.yaml index 71b3cba..7238f25 100644 --- a/miqsel/conf.yaml +++ b/miqsel/conf.yaml @@ -1,13 +1,16 @@ container: - image: cfmeqe/cfme_sel_stable:latest + client: auto + data_dir: None + image: quay.io/redhatqe/selenium-standalone name: selenium_container - project: null + network: None + project: None server_port: 4444 + viewer: auto vnc_port: 5999 - data_dir: None miq: appliances: - - hostname: null + - hostname: null browser: webdriver: Remote webdriver_options: diff --git a/miqsel/config.py b/miqsel/config.py index d9f8ae9..03b788d 100644 --- a/miqsel/config.py +++ b/miqsel/config.py @@ -21,6 +21,11 @@ def write(self, cfg): with open(self.conf_file, "w") as ymlfile: return safe_dump(cfg, ymlfile, default_flow_style=False) + @property + def container(self): + cfg = self.read() + return cfg["container"] + @click.command(help="Configure Miq Selenium Server") def config(): @@ -32,9 +37,12 @@ def config(): cfg["container"]["project"] = click.prompt( "Miq project working directory", default=cfg["container"]["project"] ) + cfg["container"]["client"] = click.prompt( + "Container Engine [podman/docker]", default=cfg["container"]["client"] + ) cfg["container"]["name"] = click.prompt("Container name", default=cfg["container"]["name"]) cfg["container"]["image"] = click.prompt( - "Docker selenium driver image", default=cfg["container"]["image"] + "Selenium container image", default=cfg["container"]["image"] ) cfg["container"]["vnc_port"] = click.prompt( "VNC running on port?", default=cfg["container"]["vnc_port"] @@ -42,8 +50,11 @@ def config(): cfg["container"]["server_port"] = click.prompt( "Selenium server running on port?", default=cfg["container"]["server_port"] ) + cfg["container"]["network"] = click.prompt( + "Container network", default=cfg["container"]["network"] + ) cfg["container"]["data_dir"] = click.prompt( - "Testing data mount to directory '/var/tmp'", default=cfg["container"]["data_dir"] + "Testing data mount to directory '/data'", default=cfg["container"]["data_dir"] ) conf.write(cfg=cfg) click.echo("Configuration saved successfully...") diff --git a/miqsel/env.py b/miqsel/env.py index 25dabfb..0dfe4a7 100644 --- a/miqsel/env.py +++ b/miqsel/env.py @@ -30,7 +30,7 @@ def env_file(self): if os.path.isdir("conf"): # First check miqsel running from project dir return os.path.join("conf", "env.local.yaml") - elif self.project: + elif self.project != "None": # Second check miqsel project path set or not return os.path.join(self.project, "conf", "env.local.yaml") else: diff --git a/miqsel/server.py b/miqsel/server.py index e33285e..8b8d824 100644 --- a/miqsel/server.py +++ b/miqsel/server.py @@ -1,41 +1,48 @@ +import shutil +import subprocess import time -from subprocess import PIPE -from subprocess import Popen import click -import docker from miqsel.config import Configuration from miqsel.env import LocalEnv +CLIENTS = ["podman", "docker"] +VNC_VIEWERS = ["vncviewer", "vinagre", "xdg-open"] -class Server(object): + +class SeleniumContainer(object): """Selenium Server Management""" - def __init__(self): - self.client = docker.from_env(version="auto") - self.cfg = Configuration().read() + def __init__( + self, image=None, name=None, server_port=None, vnc_port=None, data_dir=None, network=None + ): + self.cfg = Configuration().container + self.image = image or self.cfg["image"] + self.name = name or self.cfg["name"] + self.server_port = server_port or self.cfg["server_port"] + self.vnc_port = vnc_port or self.cfg["vnc_port"] + self.data_dir = data_dir or self.cfg["data_dir"] + self.network = network or self.cfg["network"] @property - def container(self): - """ - Get running selenium container - :return: if container running return container object else None - """ - - try: - return self.client.containers.get(self.cfg["container"]["name"]) - except docker.errors.NotFound: - return None + def client(self): + _client = self.cfg["client"] + + if _client == "auto": + try: + _client = next(c for c in CLIENTS if shutil.which(c)) + except StopIteration: + click.echo(f"No container client found on machine. Install one of {CLIENTS}") + if shutil.which(_client): + return _client + else: + click.echo(f"Container client {_client} not found") @property - def hostname(self): - """ - Get ip allocated to container - :return: If container running return ip else None - """ - - return self.container.attrs["NetworkSettings"]["IPAddress"] if self.container else None + def is_running(self): + cmd = subprocess.run([self.client, "ps"], stdout=subprocess.PIPE) + return self.name in cmd.stdout.decode() @property def executor(self): @@ -44,11 +51,7 @@ def executor(self): :return: If container running return executor url else None """ - return ( - f"http://{self.hostname}:{self.cfg['container']['server_port']}/wd/hub" - if self.hostname - else None - ) + return f"http://localhost:{self.server_port}/wd/hub" @property def vnc(self): @@ -57,45 +60,40 @@ def vnc(self): :return: If container running return vnc url else None """ - return f"{self.hostname}:{self.cfg['container']['vnc_port']}" if self.hostname else None + return f"localhost:{self.vnc_port}" def start(self, **kwargs): """Start selenium container""" - - img = self.cfg["container"]["image"] - name = self.cfg["container"]["name"] - mount_dir = self.cfg["container"]["data_dir"] - - if mount_dir != "None": - kwargs.update({"volumes": {mount_dir: {"bind": "/var/tmp", "mode": "ro"}}}) - - if not self.container: - if not self.client.images.list(name=img): - click.echo("Pulling docker images...") - click.echo("It will take some time; Please wait...") - - self.client.containers.run(img, name=name, detach=True, auto_remove=True, **kwargs) - - time.sleep(5) - - t0 = time.time() - while True: - if self.hostname: - break - elif time.time() > (t0 + 30): - click.echo("Timeout: Fail to get hostname. Check for selenium server status") - exit(0) - - elif getattr(self.container, "status", None) == "exited": - self.container.start() - else: - click.echo("Need to check container status...") + if not self.is_running: + cmd = [ + self.client, + "run", + "-d", + "--rm", + "--shm-size=2g", + "--expose", + "5999", + "--expose", + "4444", + "-p", + f"{self.vnc_port}:5999", + "-p", + f"{self.server_port}:4444", + "--name", + self.name, + ] + if self.network != "None": + cmd.extend(["--network", self.network]) + if self.data_dir != "None": + cmd.extend(["-v", f"{self.data_dir}:/data:z"]) + cmd.append(self.image) + subprocess.run(cmd) + # hack some time to stable + time.sleep(2) def stop(self): """Stop selenium container if running""" - - if getattr(self.container, "status", None) == "running": - self.container.stop() + subprocess.run([self.client, "stop", self.name]) @property def status(self): @@ -103,18 +101,14 @@ def status(self): Get status of selenium container :return: container status if running else stopped """ - - if self.container: - return self.container.status - else: - return "stopped" + return "running" if self.is_running else "stopped" @click.command(help="Status of Selenium Server") def status(): """status command""" - miq = Server() + miq = SeleniumContainer() click.echo(miq.status) @@ -123,8 +117,8 @@ def status(): def start(ctx): """start command""" - miq = Server() - if miq.status == "stopped": + miq = SeleniumContainer() + if not miq.is_running: try: miq.start() click.echo("Selenium Server started") @@ -143,8 +137,8 @@ def start(ctx): def stop(): """stop command""" - miq = Server() - if miq.status == "running": + miq = SeleniumContainer() + if miq.is_running: miq.stop() click.echo("Selenium Server stopped") else: @@ -155,7 +149,7 @@ def stop(): def executor(): """executor url command""" - miq = Server() + miq = SeleniumContainer() if miq.status == "running": click.echo(miq.executor) else: @@ -166,7 +160,7 @@ def executor(): def vnc(): """vnc url command""" - miq = Server() + miq = SeleniumContainer() if miq.status == "running": click.echo(miq.vnc) else: @@ -181,11 +175,13 @@ def viewer(url): :param url: Server url with port """ - url = url if url else Server().vnc + url = url if url else SeleniumContainer().vnc + try: + _viewer = next(v for v in VNC_VIEWERS if shutil.which(v)) + except StopIteration: + click.echo(f"No vnc viewer found. Install one of {VNC_VIEWERS}") + if url: - try: - Popen(["vncviewer", url], stdout=PIPE) - except FileNotFoundError: - click.echo("Need vnc viewer... Check README") + subprocess.Popen([_viewer, url], stdout=subprocess.PIPE) else: click.echo("Server not running...") From e0a8204411ed66dc84070c8a1db36f8202d012a9 Mon Sep 17 00:00:00 2001 From: Nikhil Dhandre Date: Fri, 8 May 2020 01:05:08 +0530 Subject: [PATCH 2/2] fix tests --- README.md | 40 ++++++++++------------------------------ setup.py | 3 ++- tests/test_configured.py | 6 +++--- 3 files changed, 15 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 43d50d0..d7dd459 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -

miqsel

+

miqsel

Miq Selenium Server

@@ -16,46 +16,26 @@ src="https://pepy.tech/badge/miqsel"> src="https://img.shields.io/badge/code%20style-black-000000.svg">

-Simple command line application to spin [selenium docker container](https://hub.docker.com/r/cfmeqe/cfme_sel_stable) and provide `vnc` access. +Simple command line application to spin [selenium container](http://quay.io/redhatqe/selenium-standalone) and provide `vnc` access. -## Prerequisite: -1. **Docker**: +### Prerequisite: +1. **Podman/Docker**: - For setting `docker` environment follow below steps: + Make sure `podman` or `docker` running on your system. + - [Podman Installation](https://podman.io/getting-started/installation.html) + - [Docker Installation](https://docs.docker.com/engine/install/) - - [Fedora](https://developer.fedoraproject.org/tools/docker/docker-installation.html) +2. **VNC Viewer**: - * Set Repository: - ``` - sudo dnf -y install dnf-plugins-core - sudo dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo - ``` - - * Install below packages: - - ``` - sudo dnf -y install docker-ce - sudo systemctl start docker - sudo systemctl enable docker - ``` - - * Run docker with non-root User: - ``` - sudo groupadd docker - sudo usermod -aG docker - ``` - - - [Ubuntu](https://docs.docker.com/install/linux/docker-ce/ubuntu/) and [others](https://docs.docker.com/install/) - -2. **TigerVNC Viewer**: + You can choose any vnc viewer but most of user used _tigervnc_. - Fedora: ``` sudo dnf install tigervnc ``` - [TigerVNC link for other Distro's](http://tigervnc.bphinz.com/nightly/) -## Installation: +### Installation: ```bash pip install miqsel --user ``` diff --git a/setup.py b/setup.py index d4080c1..5e92b62 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ with open("README.md") as readme_file: readme = readme_file.read() -install_requirements = ["Click>=5.0", "docker>=3.1", "ruamel.yaml~=0.15"] +install_requirements = ["Click>=5.0", "ruamel.yaml~=0.15"] setup_requirements = ["setuptools_scm"] @@ -20,6 +20,7 @@ "Natural Language :: English", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3 :: Only", "Topic :: Software Development :: Quality Assurance", "Topic :: Software Development :: Testing", diff --git a/tests/test_configured.py b/tests/test_configured.py index 8c568d3..fc8ba07 100644 --- a/tests/test_configured.py +++ b/tests/test_configured.py @@ -28,7 +28,7 @@ def test_miqsel(state, proj_dir, ensure_stopped): assert os.path.isfile(path=env_path) with open(env_path, "r") as f: - assert "command_executor: http://172" in f.read() + assert "command_executor: http://localhost:4444/wd/hub" in f.read() out, _, returncode = miqsel_cmd("miqsel status") assert returncode == 0 @@ -76,7 +76,7 @@ def test_executor_url(proj_dir, ensure_stopped): miqsel_cmd("miqsel start") out, _, returncode = miqsel_cmd("miqsel executor") assert returncode == 0 - assert out.strip() == "http://172.17.0.2:4444/wd/hub" + assert out.strip() == "http://localhost:4444/wd/hub" def test_vnc_url(proj_dir, ensure_stopped): @@ -85,4 +85,4 @@ def test_vnc_url(proj_dir, ensure_stopped): miqsel_cmd("miqsel start") out, _, returncode = miqsel_cmd("miqsel vnc") assert returncode == 0 - assert out.strip() == "172.17.0.2:5999" + assert out.strip() == "localhost:5999"