mirror of
https://github.com/Cian-H/I-Form_Server_Node_Deployer.git
synced 2025-12-22 22:22:02 +00:00
Fully working locally
This commit is contained in:
181
autoignition.py
Normal file
181
autoignition.py
Normal file
@@ -0,0 +1,181 @@
|
||||
from fnmatch import fnmatch
|
||||
import io
|
||||
from pathlib import Path
|
||||
import tarfile
|
||||
import time
|
||||
from typing import Annotated
|
||||
|
||||
import docker
|
||||
import git
|
||||
from selenium import webdriver
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
import typer
|
||||
|
||||
from debug import debug_mode
|
||||
|
||||
|
||||
CLIENT = docker.from_env(version="auto")
|
||||
SELENIUM_INIT_MESSAGE = "INFO [Standalone.execute] - Started Selenium Standalone"
|
||||
FUELIGNITION_INIT_MESSAGE = "ready in *ms."
|
||||
FUELIGNITION_BUILD_DIR = Path("build/fuel-ignition")
|
||||
FUELIGNITION_URL = (
|
||||
"http://localhost:3000/fuel-ignition/edit" # "https://opensuse.github.io/fuel-ignition/edit"
|
||||
)
|
||||
CWD_MOUNTDIR = Path("/host_cwd")
|
||||
|
||||
|
||||
def create_driver():
|
||||
driver = webdriver.Remote(
|
||||
"http://127.0.0.1:4444",
|
||||
options=webdriver.FirefoxOptions(),
|
||||
)
|
||||
driver.implicitly_wait(10)
|
||||
return driver
|
||||
|
||||
|
||||
def convert_json_via_fuelignition(container, driver, fuelignition_json, img_path):
|
||||
driver.get(FUELIGNITION_URL)
|
||||
# Navigate to "Load Settings from" and upload the json
|
||||
load_from = driver.find_element(By.NAME, "load_from")
|
||||
load_from.send_keys(str(CWD_MOUNTDIR / fuelignition_json))
|
||||
# Walk through page structure to find, scroll to and click "Convert and Download"
|
||||
export = driver.find_element(By.ID, "export")
|
||||
export_divs = export.find_elements(By.TAG_NAME, "div")
|
||||
convert_div = export_divs[9]
|
||||
convert_button = convert_div.find_element(By.TAG_NAME, "button")
|
||||
# Ensure "Downloads" is empty if it exists
|
||||
container.exec_run("[ -d /home/seluser/Downloads/* ] && rm /home/seluser/Downloads/*")
|
||||
# A hacky way of scrolling to the element, but is only way i can find right now
|
||||
convert_button.location_once_scrolled_into_view
|
||||
time.sleep(1)
|
||||
w = WebDriverWait(driver, 10)
|
||||
w.until_not(EC.invisibility_of_element(convert_button))
|
||||
w.until(EC.element_to_be_clickable(convert_button))
|
||||
convert_button.click()
|
||||
# Now, wait for the file to be downloaded
|
||||
while container.exec_run("ls /home/seluser/Downloads/").exit_code != 0:
|
||||
time.sleep(0.1)
|
||||
while ".img.part" in container.exec_run("ls /home/seluser/Downloads/").output.decode():
|
||||
time.sleep(0.1)
|
||||
image_file = container.exec_run("ls /home/seluser/Downloads/").output.decode().split()[0]
|
||||
# Finally, fetch the image file from the container
|
||||
client_image_path = f"/home/seluser/Downloads/{image_file}"
|
||||
host_image_path = Path().cwd() / img_path
|
||||
if host_image_path.exists():
|
||||
host_image_path.unlink()
|
||||
filestream = container.get_archive(client_image_path)[0]
|
||||
# unpack the tarfile in memory
|
||||
bytestream = io.BytesIO(b"".join(chunk for chunk in filestream))
|
||||
bytestream.seek(0)
|
||||
tar = tarfile.open(fileobj=bytestream)
|
||||
with open(host_image_path, "wb+") as f:
|
||||
f.write(tar.extractfile(tar.getmembers()[0].name).read())
|
||||
|
||||
|
||||
def build_fuelignition():
|
||||
# Make sure the local fuel-ignition repo is up to date
|
||||
if (not FUELIGNITION_BUILD_DIR.exists()) or (len(tuple(FUELIGNITION_BUILD_DIR.iterdir())) == 0):
|
||||
repo = git.Repo.clone_from(
|
||||
"https://github.com/openSUSE/fuel-ignition.git",
|
||||
FUELIGNITION_BUILD_DIR,
|
||||
branch="main",
|
||||
)
|
||||
else:
|
||||
repo = git.Repo(FUELIGNITION_BUILD_DIR)
|
||||
repo.remotes.origin.update()
|
||||
repo.remotes.origin.pull()
|
||||
# Then, build the docker image using the Dockerfile in the repo
|
||||
# * For the container to build, we need to use a patched Dockerfile
|
||||
# * The patch manually creates a "fuelignition" usergroup
|
||||
# * From reading up, this change to how docker creates usergroups
|
||||
# * appears to have been introduced in engine version 9.3.0 and above?
|
||||
# * For now, we're applying the patch @>=9.3.0 and can change later if needed
|
||||
engine_version = tuple(
|
||||
map(
|
||||
int,
|
||||
next(filter(lambda x: x.get("Name") == "Engine", CLIENT.version()["Components"]))[
|
||||
"Version"
|
||||
].split("."),
|
||||
)
|
||||
)
|
||||
root_container = (engine_version[0] > 9) or (engine_version[0] == 9 and engine_version[1] >= 3)
|
||||
dockerfile = "Dockerfile"
|
||||
if root_container:
|
||||
dockerfile = "../../templates/patched.dockerfile"
|
||||
CLIENT.images.build(
|
||||
path=str(FUELIGNITION_BUILD_DIR),
|
||||
dockerfile=dockerfile,
|
||||
tag="fuel-ignition",
|
||||
network_mode="host",
|
||||
buildargs={"CONTAINER_USERID": "1000"},
|
||||
rm=True,
|
||||
quiet=False,
|
||||
)
|
||||
|
||||
|
||||
def json_to_img(fuelignition_json: str, img_path: str) -> None:
|
||||
selenium_container = None
|
||||
fuelignition_container = None
|
||||
try:
|
||||
# Initialise containers
|
||||
selenium_container = CLIENT.containers.run(
|
||||
"selenium/standalone-firefox:latest",
|
||||
detach=True,
|
||||
remove=True,
|
||||
ports={4444: 4444, 7900: 7900},
|
||||
mounts=[
|
||||
docker.types.Mount(
|
||||
target=str(CWD_MOUNTDIR),
|
||||
source=str(Path.cwd().absolute()),
|
||||
type="bind",
|
||||
)
|
||||
],
|
||||
)
|
||||
build_fuelignition()
|
||||
fuelignition_container = CLIENT.containers.run(
|
||||
"fuel-ignition",
|
||||
detach=True,
|
||||
remove=True,
|
||||
network_mode=f"container:{selenium_container.id}",
|
||||
)
|
||||
# Wait for the containers to finish starting up
|
||||
while SELENIUM_INIT_MESSAGE not in selenium_container.logs().decode():
|
||||
time.sleep(0.1)
|
||||
while not fnmatch(
|
||||
fuelignition_container.logs().decode().strip().split("\n")[-1].strip(),
|
||||
FUELIGNITION_INIT_MESSAGE,
|
||||
):
|
||||
time.sleep(0.1)
|
||||
# Now, create the webdriver and convert the json to an img
|
||||
driver = create_driver()
|
||||
convert_json_via_fuelignition(selenium_container, driver, fuelignition_json, img_path)
|
||||
driver.quit()
|
||||
except Exception as e:
|
||||
raise e
|
||||
finally:
|
||||
if selenium_container is not None:
|
||||
selenium_container.kill()
|
||||
if fuelignition_container is not None:
|
||||
fuelignition_container.kill()
|
||||
|
||||
|
||||
def main(
|
||||
fuelignition_json: Annotated[
|
||||
str, typer.Option(help="The fuel-ignition json for configuring the disk image", prompt=True)
|
||||
],
|
||||
img_path: Annotated[
|
||||
str, typer.Option(help="The file to output the disk image to", prompt=True)
|
||||
],
|
||||
debug: Annotated[bool, typer.Option(help="Enable debug mode")] = False,
|
||||
) -> None:
|
||||
debug_mode(debug)
|
||||
f = json_to_img
|
||||
if debug:
|
||||
f = ss(f) # noqa: F821, # type: ignore #? ss is installed in debug_mode
|
||||
f(fuelignition_json, img_path)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
typer.run(main)
|
||||
Reference in New Issue
Block a user