diff --git a/.gitignore b/.gitignore index e535394..5872390 100644 --- a/.gitignore +++ b/.gitignore @@ -198,7 +198,7 @@ cython_debug/ # Ignore dynamically constructed swagger APIs .swagger-codegen* -src/portainer/* +src/portainer_api/* # These files do need to be included though: !src/portainer/pyproject.toml !src/portainer/build.py diff --git a/deploy b/deploy index 846f397..f5500ef 100755 --- a/deploy +++ b/deploy @@ -4,6 +4,9 @@ from typing import Optional import typer # type: ignore import subprocess import portainer # type: ignore +import docker # type: ignore +import tomllib +from pathlib import Path def docker_deploy_core(stack_name: Optional[str] = "core"): @@ -11,19 +14,53 @@ def docker_deploy_core(stack_name: Optional[str] = "core"): subprocess.run(["docker", "stack", "deploy", "-c", "docker-compose.yaml", stack_name]) +def fetch_repository_url() -> str: + """Fetches the repository url from the pyproject.toml file""" + with open("pyproject.toml", "rb") as f: + return tomllib.load(f)["tool"]["poetry"]["repository"] + + def docker_deploy_stack(username: str, password: str, stack_name: Optional[str] = "stack"): """Deploys the stack using the portainer api, to allow for complete control over the stack""" - breakpoint() #! TODO: Implement way of using portainer api to deploy stack + # Create an API client + client = portainer.ApiClient() + client.configuration.host = "http://127.0.0.1:9000/api" + # Authenticate the client + auth = portainer.AuthApi(client) + auth_token = auth.authenticate_user( + portainer.AuthAuthenticatePayload( + username=username, + password=password, + ), + ) + client.configuration.api_key["Authorization"] = auth_token.jwt + client.configuration.api_key_prefix["Authorization"] = "Bearer" + # Get the endpoint ID for the local docker endpoint + endpoints = portainer.EndpointsApi(client) + endpoint_id = next(filter(lambda e: e.name == "local", endpoints.endpoint_list())).id + # Then, deploy the stack using the API + stacks = portainer.StacksApi(client) + stacks.stack_create_docker_swarm_repository( + endpoint_id=endpoint_id, + body = portainer.StacksSwarmStackFromGitRepositoryPayload( + auto_update=portainer.PortainerAutoUpdateSettings( + interval="60m", + ), + name=stack_name, + compose_file="stack.yaml", + swarm_id=docker.from_env().swarm.id, + repository_url=fetch_repository_url(), + ) + ) + # breakpoint() #! TODO: Implement way of using portainer api to deploy stack # subprocess.run(["docker", "stack", "deploy", "-c", "stack.yaml", stack_name]) -def docker_deploy_all(core_name: Optional[str] = "core", stack_name: Optional[str] = "stack"): +def docker_deploy_all(username: str, password: str, core_name: Optional[str] = "core", stack_name: Optional[str] = "stack"): """Deploys the core services and the stack""" docker_deploy_core(core_name) - docker_deploy_stack(stack_name) + docker_deploy_stack(username, password, stack_name) if __name__ == "__main__": - # typer.run(docker_deploy_all) - # docker_deploy_core("core") - typer.run(docker_deploy_stack) + typer.run(docker_deploy_all) diff --git a/pyproject.toml b/pyproject.toml index 57ed4a4..0416bff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,15 +4,16 @@ version = "0.1.0" description = "" authors = ["Cian Hughes "] readme = "README.md" +repository = "https://github.com/Cian-H/I-Form_Research_Server_Stack" packages = [ { include = "deploy", from = "." }, - # { include = "portainer", from = "src" }, ] [tool.poetry.dependencies] python = "^3.11" typer = "^0.9.0" -portainer = {path = "src/portainer"} +portainer = {path = "src/portainer_api"} +docker = "^7.0.0" [tool.poetry.group.dev.dependencies] snoop = "^0.4.3" diff --git a/src/portainer/build.py b/src/portainer/build.py deleted file mode 100644 index f3d80f3..0000000 --- a/src/portainer/build.py +++ /dev/null @@ -1,50 +0,0 @@ -from pathlib import Path -import requests # type: ignore -import docker # type: ignore - - -def fetch_portainer_api_spec(version: str): - """Gets and caches the portainer api spec for a given version""" - if not Path(f".cache/portainer_api/{version}/swagger.json").exists(): - Path(f".cache/portainer_api/{version}").mkdir(parents=True, exist_ok=True) - json_request = requests.get(f"https://api.swaggerhub.com/apis/portainer/portainer-ce/{version}/swagger.json") - json_request.raise_for_status() - with open(f".cache/portainer_api/{version}/swagger.json", "w") as f: - f.write(json_request.text) - - -def build_portainer_api(version: str): - """Builds the portainer api""" - # Path("src/portainer_api").mkdir(parents=True, exist_ok=True) - docker_client = docker.from_env() - docker_client.containers.run( - "swaggerapi/swagger-codegen-cli", - "generate -i /schemas/swagger.json -l python -o /local -D packageName=portainer", - mounts = [ - docker.types.Mount( - target="/local", - source=str(Path().resolve()), - type="bind" - ), - docker.types.Mount( - target="/schemas", - source=str(Path(f".cache/portainer_api/{version}").resolve()), - type="bind" - ), - ], - auto_remove=True, - ) - - -def init_portainer_api(): - c = docker.from_env().containers.run("portainer/portainer-ce:latest", detach=True, tty=True, remove=True) - portainer_version = c.exec_run("/portainer --version").output.decode().strip() - c.stop() - fetch_portainer_api_spec(portainer_version) - build_portainer_api(portainer_version) - -def build(): - init_portainer_api() - -if __name__ == "__main__": - build() \ No newline at end of file diff --git a/src/portainer/pyproject.toml b/src/portainer/pyproject.toml deleted file mode 100644 index c3250a8..0000000 --- a/src/portainer/pyproject.toml +++ /dev/null @@ -1,24 +0,0 @@ -[tool.poetry] -name = "portainer" -version = "0.1.0" -description = "" -authors = ["Cian Hughes "] - -[tool.poetry.dependencies] -python = "^3.11" -requests = "^2.31.0" -docker = "^7.0.0" - -[tool.poetry.build] -script = "build.py" -generate-setup-file = false - -[build-system] -requires = ["poetry-core", "setuptools", "requests", "docker"] -build-backend = "poetry.core.masonry.api" - -[project] -dynamic = ["dependencies"] - -[tool.setuptools.dynamic] -dependencies = {file = ["requirements.txt"]} diff --git a/src/portainer_backup/build.py b/src/portainer_backup/build.py deleted file mode 100644 index 6537c26..0000000 --- a/src/portainer_backup/build.py +++ /dev/null @@ -1,87 +0,0 @@ -from typing import Dict, Any -from pathlib import Path -import requests # type: ignore -import docker # type: ignore - - -def portainer_login(username: str, password: str) -> Dict[str, Any]: - """Logs into portainer and returns a session and the portainer version""" - auth_login = {"username": username, "password": password} - auth_response = requests.post("http://127.0.0.1:9000/api/auth", json=auth_login) - auth_response.raise_for_status() - auth_token = auth_response.json()["jwt"] - session = requests.Session() - session.headers.update({"Authorization": f"Bearer {auth_token}"}) - portainer_response = session.get("http://127.0.0.1:9000/api/system/version") - portainer_response.raise_for_status() - portainer_version = portainer_response.json()["ServerVersion"] - return {"session": session, "version": portainer_version, "auth_token": auth_token} - - -def fetch_portainer_api_spec(version: str): # -> Dict[str, Any]: - """Gets and caches the portainer api spec for a given version""" - if not Path(f".cache/portainer_api/{version}/swagger.json").exists(): - Path(f".cache/portainer_api/{version}").mkdir(parents=True, exist_ok=True) - json_request = requests.get(f"https://api.swaggerhub.com/apis/portainer/portainer-ce/{version}/swagger.json") - json_request.raise_for_status() - with open(f".cache/portainer_api/{version}/swagger.json", "w") as f: - f.write(json_request.text) - # with open(f".cache/portainer_api/{version}/swagger.json") as f: - # return f.read() - - -# def init_portainer_api(version: str, auth_token: str): # -> openapi.Swagger: -# """ -# Returns a swagger api for portainer which follows the schema outlined here: -# https://app.swaggerhub.com/apis/portainer/portainer-ce/2.19.4 -# """ -# # spec = get_portainer_api_spec(version) -# # api = openapi.loads(spec) -# # api.validate() -# # return api -# app = App.create(f"https://api.swaggerhub.com/apis/portainer/portainer-ce/{version}/swagger.json") -# auth = Security(app) -# auth.update_with("ApiKeyAuth", auth_token) -# client = Client(auth) -# return app, client - -# def create_portainer_api(version: str, auth_token: str): -# spec = get_portainer_api_spec(version) -# api = openapi.loads(spec) -# api.validate() -# return api - -def build_portainer_api(version: str): - """Builds the portainer api""" - # Path("src/portainer_api").mkdir(parents=True, exist_ok=True) - docker_client = docker.from_env() - docker_client.containers.run( - "swaggerapi/swagger-codegen-cli", - "generate -i /schemas/swagger.json -l python -o /local -D packageName=portainer", - mounts = [ - docker.types.Mount( - target="/local", - source=str(Path().resolve()), - type="bind" - ), - docker.types.Mount( - target="/schemas", - source=str(Path(f".cache/portainer_api/{version}").resolve()), - type="bind" - ), - ], - auto_remove=True, - ) - - -def init_portainer_api(username: str, password: str): - portainer_session = portainer_login(username, password) - fetch_portainer_api_spec(portainer_session["version"]) - build_portainer_api(portainer_session["version"]) - -def build(): - init_portainer_api("admin", "%a7DtJkb&^fG%aZKGNvga5V&yBU$#UYBfGjU*pu!v2HS288&kwbR7Gpd@A5MjWr2") - -if __name__ == "__main__": - build() - # import portainer # noqa: F401 \ No newline at end of file diff --git a/src/portainer_backup/pyproject.toml b/src/portainer_backup/pyproject.toml deleted file mode 100644 index 7e714b3..0000000 --- a/src/portainer_backup/pyproject.toml +++ /dev/null @@ -1,22 +0,0 @@ -[tool.poetry] -name = "portainer" -version = "0.1.0" -description = "" -authors = ["Cian Hughes "] -# packages = [ -# { include = "src" }, -# ] - -[tool.poetry.dependencies] -python = "^3.11" -requests = "^2.31.0" -docker = "^7.0.0" -six = "^1.16.0" - -[tool.poetry.build] -script = "build.py" -generate-setup-file = false - -[build-system] -requires = ["poetry-core", "setuptools", "requests", "docker"] -build-backend = "poetry.core.masonry.api"