Improvements for logging and error handling

ogrepository-integration
Vadim vtroshchinskiy 2025-01-29 09:45:26 +01:00
parent 14cd2d4363
commit 73118501b3
1 changed files with 66 additions and 2 deletions

View File

@ -59,6 +59,16 @@ from flask_executor import Executor
from flask_restx import Api, Resource, fields from flask_restx import Api, Resource, fields
#from flasgger import Swagger #from flasgger import Swagger
import paramiko import paramiko
import logging
from systemd.journal import JournalHandler
debug_enabled = False
log = logging.getLogger('gitapi')
log.addHandler(JournalHandler())
log.setLevel(logging.INFO)
log.info("Started")
REPOSITORIES_BASE_PATH = "/opt/opengnsys/ogrepository/oggit/git/oggit/" REPOSITORIES_BASE_PATH = "/opt/opengnsys/ogrepository/oggit/git/oggit/"
@ -158,6 +168,35 @@ def do_repo_gc(repo):
return True return True
@app.errorhandler(Exception)
def handle_exception(e):
"""Return JSON for errors"""
# start with the correct headers and status code from the error
response = e.get_response()
errid = uuid.uuid4()
if debug_enabled:
response = {
"errcode": e.code,
"errname": e.name,
"description": e.description,
}
else:
response = {
"errcode" : 500,
"errname" : "Internal error",
"description": f"Please see the log for error {errid}",
"error_id" : errid
}
log.error("Error ID %s: code %i, name %s, description %s", errid, e.code, e.name, e.description, extra = { "error_id" : errid, "errcode" : e.code, "errname" : e.name, "description" : e.description })
# response.content_type = "application/json"
return response
# Define a route for the root URL # Define a route for the root URL
@api.route('/') @api.route('/')
class GitLib(Resource): class GitLib(Resource):
@ -170,6 +209,8 @@ class GitLib(Resource):
Returns: Returns:
Response: A Flask JSON response containing a welcome message. Response: A Flask JSON response containing a welcome message.
""" """
log.info("Root URL accessed")
return { return {
"message": "OpenGnsys Git API" "message": "OpenGnsys Git API"
} }
@ -197,6 +238,7 @@ class GitRepositories(Resource):
""" """
if not os.path.isdir(REPOSITORIES_BASE_PATH): if not os.path.isdir(REPOSITORIES_BASE_PATH):
log.error("Can't list repositories. Repository storage at %s not found", REPOSITORIES_BASE_PATH, extra = {"path" : REPOSITORIES_BASE_PATH})
return {"error": "Repository storage not found, git functionality may not be installed."}, 500 return {"error": "Repository storage not found, git functionality may not be installed."}, 500
repos = [] repos = []
@ -208,6 +250,7 @@ class GitRepositories(Resource):
repos = repos + [name] repos = repos + [name]
log.info("Returning %i repositories", len(repos))
return { return {
"repositories": repos "repositories": repos
} }
@ -236,6 +279,7 @@ class GitRepositories(Resource):
repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git") repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git")
if os.path.isdir(repo_path): if os.path.isdir(repo_path):
log.error("Can't create repository %s, already exists at %s", repo, repo_path, extra = {"repository" : repo, "path" : repo_path})
return {"status": "Repository already exists"}, 200 return {"status": "Repository already exists"}, 200
@ -244,7 +288,7 @@ class GitRepositories(Resource):
#installer.init_git_repo(repo + ".git") #installer.init_git_repo(repo + ".git")
log.info("Repository %s created", repo, extra = {"repository" : repo})
return {"status": "Repository created"}, 201 return {"status": "Repository created"}, 201
@ -268,6 +312,7 @@ class GitRepoSync(Resource):
""" """
repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git") repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git")
if not os.path.isdir(repo_path): if not os.path.isdir(repo_path):
log.error("Can't sync repository %s, not found. Looked in %s", repo, repo_path, extra = {"repository" : repo, "path" : repo_path })
return {"error": "Repository not found"}, 404 return {"error": "Repository not found"}, 404
@ -276,9 +321,15 @@ class GitRepoSync(Resource):
if data is None: if data is None:
return {"error" : "Parameters missing"}, 400 return {"error" : "Parameters missing"}, 400
if not "remote_repository" in data:
return {"error" : "Parameter 'remote_repository' missing"}, 400
future = executor.submit(do_repo_sync, repo, data) future = executor.submit(do_repo_sync, repo, data)
task_id = str(uuid.uuid4()) task_id = str(uuid.uuid4())
tasks[task_id] = future tasks[task_id] = future
log.info("Starting synchronization of repository %s, task %s", repo, task_id, extra = {"repository" : repo, "task_id" : task_id})
return {"status": "started", "task_id" : task_id}, 200 return {"status": "started", "task_id" : task_id}, 200
@ -310,6 +361,7 @@ class GitRepoBackup(Resource):
""" """
repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git") repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git")
if not os.path.isdir(repo_path): if not os.path.isdir(repo_path):
log.error("Can't backup repository %s, not found. Looked in %s", repo, repo_path, extra = {"repository" : repo, "path" : repo_path })
return {"error": "Repository not found"}, 404 return {"error": "Repository not found"}, 404
@ -326,6 +378,7 @@ class GitRepoBackup(Resource):
task_id = str(uuid.uuid4()) task_id = str(uuid.uuid4())
tasks[task_id] = future tasks[task_id] = future
log.info("Starting backup of repository %s, task %s", repo, task_id, extra = {"repository" : repo, "task_id" : task_id})
return {"status": "started", "task_id" : task_id}, 200 return {"status": "started", "task_id" : task_id}, 200
@git_ns.route('/repositories/<repo>/compact', methods=['POST']) @git_ns.route('/repositories/<repo>/compact', methods=['POST'])
@ -348,12 +401,14 @@ class GitRepoCompact(Resource):
""" """
repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git") repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git")
if not os.path.isdir(repo_path): if not os.path.isdir(repo_path):
log.error("Can't compact repository %s, not found. Looked in %s", repo, repo_path, extra = {"repository" : repo, "path" : repo_path })
return {"error": "Repository not found"}, 404 return {"error": "Repository not found"}, 404
future = executor.submit(do_repo_gc, repo) future = executor.submit(do_repo_gc, repo)
task_id = str(uuid.uuid4()) task_id = str(uuid.uuid4())
tasks[task_id] = future tasks[task_id] = future
log.info("Starting compaction of repository %s, task %s", repo, task_id, extra = {"repository" : repo, "task_id" : task_id})
return {"status": "started", "task_id" : task_id}, 200 return {"status": "started", "task_id" : task_id}, 200
@ -373,14 +428,17 @@ class GitTaskStatus(Resource):
- If the task is still in progress, returns a 202 status indicating the task is in progress. - If the task is still in progress, returns a 202 status indicating the task is in progress.
""" """
if not task_id in tasks: if not task_id in tasks:
log.error("Task %s was not found", task_id, extra = {"task_id" : task_id})
return {"error": "Task not found"}, 404 return {"error": "Task not found"}, 404
future = tasks[task_id] future = tasks[task_id]
if future.done(): if future.done():
result = future.result() result = future.result()
log.info("Returning completion of task %s", task_id, extra = {"task_id" : task_id})
return {"status" : "completed", "result" : result}, 200 return {"status" : "completed", "result" : result}, 200
else: else:
log.info("Task %s is still in progress", task_id, extra = {"task_id" : task_id})
return {"status" : "in progress"}, 202 return {"status" : "in progress"}, 202
@ -405,10 +463,12 @@ class GitRepo(Resource):
""" """
repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git") repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git")
if not os.path.isdir(repo_path): if not os.path.isdir(repo_path):
log.error("Can't delete repository %s, not found. Looked in %s", repo, repo_path, extra = {"repository" : repo, "path" : repo_path })
return {"error": "Repository not found"}, 404 return {"error": "Repository not found"}, 404
shutil.rmtree(repo_path) shutil.rmtree(repo_path)
log.info("Deleted repository %s", repo, extra = {"repository" : repo})
return {"status": "Repository deleted"}, 200 return {"status": "Repository deleted"}, 200
@ -430,6 +490,7 @@ class GitRepoBranches(Resource):
""" """
repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git") repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git")
if not os.path.isdir(repo_path): if not os.path.isdir(repo_path):
log.error("Can't get branches of repository repository %s, not found. Looked in %s", repo, repo_path, extra = {"repository" : repo, "path" : repo_path })
return {"error": "Repository not found"}, 404 return {"error": "Repository not found"}, 404
git_repo = git.Repo(repo_path) git_repo = git.Repo(repo_path)
@ -438,7 +499,7 @@ class GitRepoBranches(Resource):
for branch in git_repo.branches: for branch in git_repo.branches:
branches = branches + [branch.name] branches = branches + [branch.name]
log.info("Returning %i branches", len(branches))
return { return {
"branches": branches "branches": branches
} }
@ -460,6 +521,7 @@ class GitHealth(Resource):
active and functional. active and functional.
""" """
log.info("Health check endpoint called")
return { return {
"status": "OK" "status": "OK"
} }
@ -476,6 +538,8 @@ class GitStatus(Resource):
Response: A JSON response with status information Response: A JSON response with status information
""" """
log.info("Status endpoint called")
return { return {
"uptime" : time.time() - start_time, "uptime" : time.time() - start_time,
"active_tasks" : len(tasks) "active_tasks" : len(tasks)