Compare commits
7 Commits
09baf6d1e8
...
d4ce9c3ee3
| Author | SHA1 | Date |
|---|---|---|
|
|
d4ce9c3ee3 | |
|
|
8bebeb619a | |
|
|
115df98905 | |
|
|
5721e56237 | |
|
|
3ebc728fb9 | |
|
|
46732216eb | |
|
|
1f2095ce1a |
101
api/gitapi.py
101
api/gitapi.py
|
|
@ -77,7 +77,7 @@ REPOSITORIES_BASE_PATH = "/opt/opengnsys/ogrepository/oggit/git/oggit/"
|
||||||
|
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
tasks = {}
|
tasks = {}
|
||||||
|
tasks_max = 1024
|
||||||
|
|
||||||
# Create an instance of the Flask class
|
# Create an instance of the Flask class
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
@ -93,6 +93,24 @@ executor = Executor(app)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def add_task(future):
|
||||||
|
task_id = uuid.uuid4().hex
|
||||||
|
task_data = {
|
||||||
|
"future" : future,
|
||||||
|
"start_time" : time.time()
|
||||||
|
}
|
||||||
|
|
||||||
|
while len(tasks) >= tasks_max:
|
||||||
|
oldest_task_id = min(tasks, key=lambda k: tasks[k]['start_time'])
|
||||||
|
task = tasks[task_id]["future"]
|
||||||
|
if task.running():
|
||||||
|
log.error("Cancelling still running task %s, maximum task limit of %i reached", task_id, tasks_max)
|
||||||
|
task.cancel()
|
||||||
|
|
||||||
|
del tasks[oldest_task_id]
|
||||||
|
|
||||||
|
tasks[task_id] = task_data
|
||||||
|
return task_id
|
||||||
|
|
||||||
def do_repo_backup(repo, params):
|
def do_repo_backup(repo, params):
|
||||||
"""
|
"""
|
||||||
|
|
@ -110,7 +128,10 @@ def do_repo_backup(repo, params):
|
||||||
bool: True if the backup was successful.
|
bool: True if the backup was successful.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
gitrepo = git.Repo(f"{REPOSITORIES_BASE_PATH}/{repo}.git")
|
git_repo_path = f"{REPOSITORIES_BASE_PATH}/{repo}.git"
|
||||||
|
git_repo = git.Repo(git_repo_path)
|
||||||
|
git_repo.git.config('--global', '--add', 'safe.directory', git_repo_path)
|
||||||
|
|
||||||
|
|
||||||
ssh = paramiko.SSHClient()
|
ssh = paramiko.SSHClient()
|
||||||
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
|
|
@ -120,7 +141,7 @@ def do_repo_backup(repo, params):
|
||||||
|
|
||||||
|
|
||||||
with sftp.file(params["filename"], mode='wb+') as remote_file:
|
with sftp.file(params["filename"], mode='wb+') as remote_file:
|
||||||
gitrepo.archive(remote_file, format="tar.gz")
|
git_repo.archive(remote_file, format="tar.gz")
|
||||||
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
@ -139,13 +160,16 @@ def do_repo_sync(repo, params):
|
||||||
- "remote_ref" (str): The name of the remote reference.
|
- "remote_ref" (str): The name of the remote reference.
|
||||||
- "summary" (str): A summary of the push operation for the reference.
|
- "summary" (str): A summary of the push operation for the reference.
|
||||||
"""
|
"""
|
||||||
gitrepo = git.Repo(f"{REPOSITORIES_BASE_PATH}/{repo}.git")
|
git_repo_path = f"{REPOSITORIES_BASE_PATH}/{repo}.git"
|
||||||
|
git_repo = git.Repo(git_repo_path)
|
||||||
|
git_repo.git.config('--global', '--add', 'safe.directory', git_repo_path)
|
||||||
|
|
||||||
|
|
||||||
# Recreate the remote every time, it might change
|
# Recreate the remote every time, it might change
|
||||||
if "backup" in gitrepo.remotes:
|
if "backup" in git_repo.remotes:
|
||||||
gitrepo.delete_remote("backup")
|
git_repo.delete_remote("backup")
|
||||||
|
|
||||||
backup_repo = gitrepo.create_remote("backup", params["remote_repository"])
|
backup_repo = git_repo.create_remote("backup", params["remote_repository"])
|
||||||
pushed_references = backup_repo.push("*:*")
|
pushed_references = backup_repo.push("*:*")
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
|
|
@ -165,11 +189,11 @@ def do_repo_gc(repo):
|
||||||
Returns:
|
Returns:
|
||||||
bool: True if the garbage collection command was executed successfully.
|
bool: True if the garbage collection command was executed successfully.
|
||||||
"""
|
"""
|
||||||
gitrepo = git.Repo(f"{REPOSITORIES_BASE_PATH}/{repo}.git")
|
git_repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git")
|
||||||
|
git_repo = git.Repo(git_repo_path)
|
||||||
gitrepo.git.gc()
|
git_repo.git.config('--global', '--add', 'safe.directory', git_repo_path)
|
||||||
return True
|
|
||||||
|
|
||||||
|
git_repo.git.gc()
|
||||||
|
|
||||||
@app.errorhandler(HTTPException)
|
@app.errorhandler(HTTPException)
|
||||||
def handle_exception(e):
|
def handle_exception(e):
|
||||||
|
|
@ -279,6 +303,7 @@ class GitRepositories(Resource):
|
||||||
data = request.json
|
data = request.json
|
||||||
|
|
||||||
if data is None:
|
if data is None:
|
||||||
|
log.error("Can't create repository, JSON post data missing")
|
||||||
return {"error" : "Parameters missing"}, 400
|
return {"error" : "Parameters missing"}, 400
|
||||||
|
|
||||||
repo = data["name"]
|
repo = data["name"]
|
||||||
|
|
@ -325,15 +350,16 @@ class GitRepoSync(Resource):
|
||||||
data = request.json
|
data = request.json
|
||||||
|
|
||||||
if data is None:
|
if data is None:
|
||||||
|
log.error("Can't create repository, JSON post data missing")
|
||||||
return {"error" : "Parameters missing"}, 400
|
return {"error" : "Parameters missing"}, 400
|
||||||
|
|
||||||
if not "remote_repository" in data:
|
if not "remote_repository" in data:
|
||||||
|
log.error("Can't create repository, parameter 'remote_repository' missing")
|
||||||
return {"error" : "Parameter 'remote_repository' missing"}, 400
|
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 = add_task(future)
|
||||||
tasks[task_id] = future
|
|
||||||
|
|
||||||
log.info("Starting synchronization of repository %s, task %s", repo, task_id, extra = {"repository" : repo, "task_id" : task_id})
|
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
|
||||||
|
|
@ -373,6 +399,7 @@ class GitRepoBackup(Resource):
|
||||||
|
|
||||||
data = request.json
|
data = request.json
|
||||||
if data is None:
|
if data is None:
|
||||||
|
log.error("Can't create repository, JSON post data missing")
|
||||||
return {"error" : "Parameters missing"}, 400
|
return {"error" : "Parameters missing"}, 400
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -381,8 +408,7 @@ class GitRepoBackup(Resource):
|
||||||
|
|
||||||
|
|
||||||
future = executor.submit(do_repo_backup, repo, data)
|
future = executor.submit(do_repo_backup, repo, data)
|
||||||
task_id = str(uuid.uuid4())
|
task_id = add_task(future)
|
||||||
tasks[task_id] = future
|
|
||||||
|
|
||||||
log.info("Starting backup of repository %s, task %s", repo, task_id, extra = {"repository" : repo, "task_id" : task_id})
|
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
|
||||||
|
|
@ -411,8 +437,7 @@ class GitRepoCompact(Resource):
|
||||||
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 = add_task(future)
|
||||||
tasks[task_id] = future
|
|
||||||
|
|
||||||
log.info("Starting compaction of repository %s, task %s", repo, task_id, extra = {"repository" : repo, "task_id" : task_id})
|
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
|
||||||
|
|
@ -437,7 +462,7 @@ class GitTaskStatus(Resource):
|
||||||
log.error("Task %s was not found", task_id, extra = {"task_id" : task_id})
|
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]["future"]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if future.done():
|
if future.done():
|
||||||
|
|
@ -506,6 +531,8 @@ class GitRepoBranches(Resource):
|
||||||
return {"error": "Repository not found"}, 404
|
return {"error": "Repository not found"}, 404
|
||||||
|
|
||||||
git_repo = git.Repo(repo_path)
|
git_repo = git.Repo(repo_path)
|
||||||
|
git_repo.git.config('--global', '--add', 'safe.directory', repo_path)
|
||||||
|
|
||||||
|
|
||||||
branches = []
|
branches = []
|
||||||
for branch in git_repo.branches:
|
for branch in git_repo.branches:
|
||||||
|
|
@ -516,7 +543,36 @@ class GitRepoBranches(Resource):
|
||||||
"branches": branches
|
"branches": branches
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@git_ns.route('/repositories/<repo>/branches/<branch>')
|
||||||
|
class GitRepoBranchesDeleter(Resource):
|
||||||
|
def delete(self, repo, branch):
|
||||||
|
"""Delete a given branch in a given repository
|
||||||
|
|
||||||
|
Args:
|
||||||
|
repo (str): The name of the repository.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Response: A JSON response containing a list of branch names or an error message if the repository is not found.
|
||||||
|
- 200: A JSON object with a "status" key containing "deleted"
|
||||||
|
- 404: A JSON object with an "error" key containing the message "Repository not found" or "Branch not found"
|
||||||
|
"""
|
||||||
|
|
||||||
|
repo_path = os.path.join(REPOSITORIES_BASE_PATH, repo + ".git")
|
||||||
|
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
|
||||||
|
|
||||||
|
git_repo = git.Repo(repo_path)
|
||||||
|
git_repo.git.config('--global', '--add', 'safe.directory', repo_path)
|
||||||
|
|
||||||
|
|
||||||
|
if not branch in git_repo.branches:
|
||||||
|
log.error("Can't delete branch %s, not found in repository %s", branch, repo, extra = {"repository" : repo, "branch" : branch})
|
||||||
|
return {"error": "Branch not found"}, 404
|
||||||
|
|
||||||
|
git_repo.delete_head(branch)
|
||||||
|
log.info("Branch %s of repository %s deleted", branch, repo, extra = {"repository" : repo, "branch" : branch})
|
||||||
|
return {"status": "deleted"}, 200
|
||||||
|
|
||||||
|
|
||||||
@git_ns.route('/health')
|
@git_ns.route('/health')
|
||||||
|
|
@ -557,6 +613,15 @@ class GitStatus(Resource):
|
||||||
"active_tasks" : len(tasks)
|
"active_tasks" : len(tasks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@app.after_request
|
||||||
|
def after_request(response):
|
||||||
|
log.info("Request from %s: %s %s %s %s", request.remote_addr, request.method, request.scheme, request.full_path, response.status,
|
||||||
|
extra = {"remote_addr" : request.remote_addr, "method" : request.method, "scheme" : request.scheme, "full_path" : request.full_path, "status" : response.status})
|
||||||
|
|
||||||
|
if debug_enabled:
|
||||||
|
log.debug("Response: %s", response.data, extra = {"response" : response.data})
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
api.add_namespace(git_ns)
|
api.add_namespace(git_ns)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -248,10 +248,6 @@ class OpengnsysGitInstaller:
|
||||||
"""Ignorar requisito de clave de ssh para el instalador"""
|
"""Ignorar requisito de clave de ssh para el instalador"""
|
||||||
self.ignoresshkey = value
|
self.ignoresshkey = value
|
||||||
|
|
||||||
def set_usesshkey(self, value):
|
|
||||||
"""Usar clave de ssh especificada"""
|
|
||||||
self.usesshkey = value
|
|
||||||
|
|
||||||
def set_basepath(self, value):
|
def set_basepath(self, value):
|
||||||
"""Establece ruta base de OpenGnsys
|
"""Establece ruta base de OpenGnsys
|
||||||
Valor por defecto: /opt/opengnsys
|
Valor por defecto: /opt/opengnsys
|
||||||
|
|
@ -478,38 +474,33 @@ class OpengnsysGitInstaller:
|
||||||
self.__logger.debug("Using provided initrd file %s", initrd_file)
|
self.__logger.debug("Using provided initrd file %s", initrd_file)
|
||||||
client_initrd_path = initrd_file
|
client_initrd_path = initrd_file
|
||||||
|
|
||||||
if self.usesshkey:
|
self.__logger.debug("Extracting key from %s", client_initrd_path)
|
||||||
with open(self.usesshkey, 'r') as f:
|
|
||||||
public_key = f.read().strip()
|
|
||||||
|
|
||||||
|
if os.path.isfile(client_initrd_path):
|
||||||
|
#os.makedirs(temp_dir, exist_ok=True)
|
||||||
|
#os.chdir(self.temp_dir.name)
|
||||||
|
self.__logger.debug("Uncompressing %s", client_initrd_path)
|
||||||
|
public_key = None
|
||||||
|
with libarchive.file_reader(client_initrd_path) as initrd:
|
||||||
|
for file in initrd:
|
||||||
|
self.__logger.debug("File: %s", file)
|
||||||
|
|
||||||
|
pathname = file.pathname;
|
||||||
|
if pathname.startswith("./"):
|
||||||
|
pathname = pathname[2:]
|
||||||
|
|
||||||
|
if pathname in self.key_paths_dict:
|
||||||
|
self.__logger.info("Found key %s, extracting", pathname)
|
||||||
|
|
||||||
|
data = bytearray()
|
||||||
|
for block in file.get_blocks():
|
||||||
|
data = data + block
|
||||||
|
public_key = data.decode('utf-8').strip()
|
||||||
|
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
self.__logger.debug("Extracting key from %s", client_initrd_path)
|
print(f"Failed to find initrd at {client_initrd_path}")
|
||||||
|
exit(2)
|
||||||
if os.path.isfile(client_initrd_path):
|
|
||||||
#os.makedirs(temp_dir, exist_ok=True)
|
|
||||||
#os.chdir(self.temp_dir.name)
|
|
||||||
self.__logger.debug("Uncompressing %s", client_initrd_path)
|
|
||||||
public_key = None
|
|
||||||
with libarchive.file_reader(client_initrd_path) as initrd:
|
|
||||||
for file in initrd:
|
|
||||||
self.__logger.debug("File: %s", file)
|
|
||||||
|
|
||||||
pathname = file.pathname;
|
|
||||||
if pathname.startswith("./"):
|
|
||||||
pathname = pathname[2:]
|
|
||||||
|
|
||||||
if pathname in self.key_paths_dict:
|
|
||||||
self.__logger.info("Found key %s, extracting", pathname)
|
|
||||||
|
|
||||||
data = bytearray()
|
|
||||||
for block in file.get_blocks():
|
|
||||||
data = data + block
|
|
||||||
public_key = data.decode('utf-8').strip()
|
|
||||||
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
print(f"Failed to find initrd at {client_initrd_path}")
|
|
||||||
exit(2)
|
|
||||||
|
|
||||||
if not public_key:
|
if not public_key:
|
||||||
self.__logger.warning("Failed to find a SSH key")
|
self.__logger.warning("Failed to find a SSH key")
|
||||||
|
|
@ -961,7 +952,7 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
parser.add_argument('--testmode', action='store_true', help="Modo de prueba")
|
parser.add_argument('--testmode', action='store_true', help="Modo de prueba")
|
||||||
parser.add_argument('--ignoresshkey', action='store_true', help="Ignorar clave de SSH")
|
parser.add_argument('--ignoresshkey', action='store_true', help="Ignorar clave de SSH")
|
||||||
parser.add_argument('--usesshkey', type=str, help="Usar clave SSH especificada")
|
parser.add_argument('--use-ssh-key', metavar="FILE", type=str, help="Add the SSH key from the specified file")
|
||||||
parser.add_argument('--test-createuser', action='store_true')
|
parser.add_argument('--test-createuser', action='store_true')
|
||||||
parser.add_argument('--extract-ssh-key', action='store_true', help="Extract SSH key from oglive squashfs")
|
parser.add_argument('--extract-ssh-key', action='store_true', help="Extract SSH key from oglive squashfs")
|
||||||
parser.add_argument('--set-ssh-key', action='store_true', help="Read SSH key from oglive squashfs and set it in Forgejo")
|
parser.add_argument('--set-ssh-key', action='store_true', help="Read SSH key from oglive squashfs and set it in Forgejo")
|
||||||
|
|
@ -993,7 +984,6 @@ if __name__ == '__main__':
|
||||||
installer = OpengnsysGitInstaller()
|
installer = OpengnsysGitInstaller()
|
||||||
installer.set_testmode(args.testmode)
|
installer.set_testmode(args.testmode)
|
||||||
installer.set_ignoresshkey(args.ignoresshkey)
|
installer.set_ignoresshkey(args.ignoresshkey)
|
||||||
installer.set_usesshkey(args.usesshkey)
|
|
||||||
|
|
||||||
logger.debug("Inicio de instalación")
|
logger.debug("Inicio de instalación")
|
||||||
|
|
||||||
|
|
@ -1012,6 +1002,13 @@ if __name__ == '__main__':
|
||||||
print(f"{key}")
|
print(f"{key}")
|
||||||
elif args.set_ssh_key:
|
elif args.set_ssh_key:
|
||||||
installer.add_ssh_key_from_squashfs(oglive_num=args.oglive, squashfs_file=args.squashfs_file, oglive_file = args.oglive_file or args.oglive_url)
|
installer.add_ssh_key_from_squashfs(oglive_num=args.oglive, squashfs_file=args.squashfs_file, oglive_file = args.oglive_file or args.oglive_url)
|
||||||
|
elif args.use_ssh_key:
|
||||||
|
with open(args.use_ssh_key, 'r', encoding='utf-8') as ssh_key_file:
|
||||||
|
ssh_key_data = ssh_key_file.read().strip()
|
||||||
|
(keytype, keydata, description) = ssh_key_data.split(" ", 2)
|
||||||
|
|
||||||
|
installer.add_forgejo_sshkey(f"{keytype} {keydata}", description)
|
||||||
|
|
||||||
elif args.set_ssh_key_in_initrd:
|
elif args.set_ssh_key_in_initrd:
|
||||||
installer.set_ssh_key_in_initrd()
|
installer.set_ssh_key_in_initrd()
|
||||||
elif args.get_image_paths:
|
elif args.get_image_paths:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue