diff --git a/Dockerfile b/Dockerfile index 832c3e3..2da5983 100644 --- a/Dockerfile +++ b/Dockerfile @@ -42,10 +42,6 @@ COPY src/ src/ COPY config/ config/ COPY composer.json . - - - - # Install dependencies RUN composer install COPY .env .env diff --git a/api/gitapi.py b/api/gitapi.py index 6458150..82f97e9 100644 --- a/api/gitapi.py +++ b/api/gitapi.py @@ -10,6 +10,7 @@ from flask import Flask, request from flask_executor import Executor import subprocess from flask import stream_with_context, Response +import paramiko repositories_base_path = "/opt/opengnsys/images" @@ -22,17 +23,23 @@ executor = Executor(app) tasks = {} + def do_repo_backup(repo, params): - user = params["ssh_user"] - server = params["ssh_server"] - filename = params["filename"] + gitrepo = git.Repo(f"{repositories_base_path}/{repo}.git") - os.chdir(repositories_base_path) - os.chdir(f"{repo}.git") + ssh = paramiko.SSHClient() + ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - subprocess.run(f"git archive --format=tar HEAD | ssh {user}@{server} -T \"cat > {filename}\"", shell=True, check=True) - #sleep(10) + ssh.connect(params["ssh_server"], params["ssh_port"], params["ssh_user"]) + sftp = ssh.open_sftp() + + + with sftp.file(params["filename"], mode='wb+') as remote_file: + gitrepo.archive(remote_file, format="tar.gz") + + + return True def do_repo_sync(repo, params): gitrepo = git.Repo(f"{repositories_base_path}/{repo}.git") @@ -51,6 +58,12 @@ def do_repo_sync(repo, params): return results +def do_repo_gc(repo): + gitrepo = git.Repo(f"{repositories_base_path}/{repo}.git") + + gitrepo.git.gc() + return True + # Define a route for the root URL @app.route('/') @@ -131,9 +144,9 @@ def backup_repo(repo): if data is None: return jsonify({"error" : "Parameters missing"}), 400 - dest_server = data["ssh_server"] - dest_user = data["ssh_user"] - dest_file = data["filename"] + + if not "ssh_port" in data: + data["ssh_port"] = 22 future = executor.submit(do_repo_backup, repo, data) @@ -144,6 +157,20 @@ def backup_repo(repo): #return jsonify({"status": "Started backup", "repository" : repo, "ssh_server" : dest_server, "ssh_user" : dest_user, "filename" : dest_file}), 200 +@app.route('/repositories//gc', methods=['POST']) +def gc_repo(repo): + + repo_path = os.path.join(repositories_base_path, repo + ".git") + if not os.path.isdir(repo_path): + return jsonify({"error": "Repository not found"}), 404 + + future = executor.submit(do_repo_gc, repo) + task_id = str(uuid.uuid4()) + tasks[task_id] = future + + return jsonify({"status": "started", "task_id" : task_id}), 200 + + @app.route('/tasks//status') def tasks_status(task_id): if not task_id in tasks: @@ -201,6 +228,6 @@ def health_check(): # Run the Flask app if __name__ == '__main__': - app.run(debug=True) + app.run(debug=True, host='0.0.0.0') diff --git a/api/requirements.txt b/api/requirements.txt index 985e8b7..5c63be4 100644 --- a/api/requirements.txt +++ b/api/requirements.txt @@ -1,17 +1,25 @@ +bcrypt==4.0.1 +cffi==1.15.1 click==8.0.4 colorterm==0.3 +contextvars==2.4 +cryptography==40.0.2 dataclasses==0.8 Flask==2.0.3 Flask-Executor==1.0.0 gitdb==4.0.9 GitPython==3.1.20 +immutables==0.19 importlib-metadata==4.8.3 itsdangerous==2.0.1 Jinja2==3.0.3 libarchive==0.4.7 MarkupSafe==2.0.1 nose==1.3.7 +paramiko==3.5.0 pkg_resources==0.0.0 +pycparser==2.21 +PyNaCl==1.5.0 smmap==5.0.0 termcolor==1.1.0 typing_extensions==4.1.1 diff --git a/docker/test/mock_api.py b/docker/test/mock_api.py new file mode 100644 index 0000000..533cbe3 --- /dev/null +++ b/docker/test/mock_api.py @@ -0,0 +1,76 @@ +from flask import Flask, jsonify, request + +app = Flask(__name__) + +# Simulación de /repositories - Obtención de lista de repositorios +@app.route('/repositories', methods=['GET']) +def get_repositories(): + # Mock de respuesta exitosa + return jsonify({ + "repositories": ["repo1", "repo2", "repo3"] + }), 200 + +# Simulación de /repositories/ - Creación de repositorio +@app.route('/repositories/', methods=['PUT']) +def create_repo(repo): + # Mock de respuesta de repositorio ya existente + return jsonify({"status": "Repository already exists"}), 200 + +# Simulación de /repositories//sync - Sincronización de repositorio +@app.route('/repositories//sync', methods=['POST']) +def sync_repo(repo): + data = request.json + dest_repo = data["remote_repository"] + + # Mock de respuesta exitosa + return jsonify({ + "status": "Started synchronization", + "repository": repo, + "destination_repository": dest_repo + }), 200 + +# Simulación de /repositories//backup - Realización de backup +@app.route('/repositories//backup', methods=['POST']) +def backup_repo(repo): + data = request.json + dest_server = data["ssh_server"] + dest_user = data["ssh_user"] + dest_file = data["filename"] + + # Mock de respuesta exitosa + return jsonify({ + "status": "Started backup", + "repository": repo, + "ssh_server": dest_server, + "ssh_user": dest_user, + "filename": dest_file + }), 200 + +# Simulación de /repositories/ - Eliminación de repositorio +@app.route('/repositories/', methods=['DELETE']) +def delete_repo(repo): + # Mock de respuesta exitosa + return jsonify({"status": "Repository deleted"}), 200 + +# Simulación de /repositories//branches - Obtención de branches del repositorio +@app.route('/repositories//branches', methods=['GET']) +def get_repository_branches(repo): + # Mock de respuesta con branches de ejemplo + return jsonify({ + "branches": ["main", "dev", "feature-xyz"] + }), 200 + +# Simulación de la verificación de salud del servicio +@app.route('/health', methods=['GET']) +def health_check(): + return jsonify({"status": "OK"}), 200 + +# Simulación de la raíz de la API +@app.route('/', methods=['GET']) +def home(): + return jsonify({ + "message": "OpenGnsys Git API Mock" + }), 200 + +if __name__ == '__main__': + app.run(host='0.0.0.0', port=5001, debug=True)