Initial forgejo install
parent
9a0faff058
commit
48161614f1
|
@ -240,7 +240,9 @@ class GitRepositories(Resource):
|
|||
|
||||
|
||||
installer = OpengnsysGitInstaller()
|
||||
installer.init_git_repo(repo + ".git")
|
||||
installer.add_forgejo_repo(repo)
|
||||
|
||||
#installer.init_git_repo(repo + ".git")
|
||||
|
||||
|
||||
return jsonify({"status": "Repository created"}), 201
|
||||
|
|
|
@ -1628,6 +1628,7 @@ class OpengnsysGitLibrary:
|
|||
"""
|
||||
Restore the repository to the state it had before the non-committed modifications
|
||||
"""
|
||||
self.logger.info("Undoing any user changes to the filesystem")
|
||||
repo = git.Repo(path)
|
||||
|
||||
repo.head.reset(index=True, working_tree=True)
|
||||
|
|
|
@ -10,9 +10,21 @@ import subprocess
|
|||
import sys
|
||||
import pwd
|
||||
import grp
|
||||
from termcolor import colored, cprint
|
||||
from termcolor import cprint
|
||||
import git
|
||||
import libarchive
|
||||
import urllib.request
|
||||
import pathlib
|
||||
import socket
|
||||
import time
|
||||
import requests
|
||||
|
||||
|
||||
#FORGEJO_VERSION="8.0.3"
|
||||
FORGEJO_VERSION="9.0.0"
|
||||
FORGEJO_URL=f"https://codeberg.org/forgejo/forgejo/releases/download/v{FORGEJO_VERSION}/forgejo-{FORGEJO_VERSION}-linux-amd64"
|
||||
|
||||
|
||||
|
||||
|
||||
def show_error(*args):
|
||||
|
@ -27,6 +39,7 @@ def show_error(*args):
|
|||
"""
|
||||
cprint(*args, "red", attrs = ["bold"], file=sys.stderr)
|
||||
|
||||
|
||||
class RequirementException(Exception):
|
||||
"""Excepción que indica que nos falta algún requisito
|
||||
|
||||
|
@ -102,11 +115,16 @@ class OpengnsysGitInstaller:
|
|||
self.git_basedir = "base.git"
|
||||
self.ssh_user = "opengnsys"
|
||||
self.ssh_group = "opengnsys"
|
||||
self.email = "OpenGnsys@opengnsys.com"
|
||||
self.forgejo_password = "opengnsys"
|
||||
self.forgejo_port = 3000
|
||||
|
||||
|
||||
self.ssh_homedir = pwd.getpwnam(self.ssh_user).pw_dir
|
||||
self.ssh_uid = pwd.getpwnam(self.ssh_user).pw_uid
|
||||
self.ssh_gid = grp.getgrnam(self.ssh_group).gr_gid
|
||||
self.temp_dir = None
|
||||
self.script_path = os.path.realpath(os.path.dirname(__file__))
|
||||
|
||||
# Possible names for SSH key
|
||||
self.key_paths = ["scripts/ssl/id_rsa.pub", "scripts/ssl/id_ed25519.pub", "scripts/ssl/id_ecdsa.pub", "scripts/ssl/id_ed25519_sk.pub", "scripts/ssl/id_ecdsa_sk.pub"]
|
||||
|
@ -180,7 +198,7 @@ class OpengnsysGitInstaller:
|
|||
|
||||
self.__logger.info("Configurando repositorio de GIT")
|
||||
repo.config_writer().set_value("user", "name", "OpenGnsys").release()
|
||||
repo.config_writer().set_value("user", "email", "OpenGnsys@opengnsys.com").release()
|
||||
repo.config_writer().set_value("user", "email", self.email).release()
|
||||
|
||||
self._recursive_chown(repo_path, ouid=self.ssh_uid, ogid=self.ssh_gid)
|
||||
|
||||
|
@ -209,6 +227,71 @@ class OpengnsysGitInstaller:
|
|||
for filename in filenames:
|
||||
os.chown(os.path.join(dirpath, filename), uid=ouid, gid=ogid)
|
||||
|
||||
def _wait_for_port(self, host, port):
|
||||
self.__logger.info("Waiting for %s:%i to be up", host, port)
|
||||
|
||||
timeout = 60
|
||||
start_time = time.time()
|
||||
|
||||
ready = False
|
||||
while not ready and (time.time() - start_time) < 60:
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
s.connect((host, port))
|
||||
ready = True
|
||||
s.close()
|
||||
except TimeoutError:
|
||||
self.__logger.debug("Timed out, no connection yet.")
|
||||
except OSError as oserr:
|
||||
self.__logger.debug("%s, no connection yet. %.1f seconds left.", oserr.strerror, timeout - (time.time() - start_time))
|
||||
|
||||
time.sleep(0.1)
|
||||
|
||||
if ready:
|
||||
self.__logger.info("Connection established.")
|
||||
else:
|
||||
self.__logger.error("Timed out waiting for connection!")
|
||||
raise TimeoutError("Timed out waiting for connection!")
|
||||
|
||||
|
||||
def _extract_ssh_key(self):
|
||||
public_key=""
|
||||
|
||||
INITRD = "oginitrd.img"
|
||||
|
||||
tftp_dir = os.path.join(self.base_path, "tftpboot")
|
||||
default_num = self.oglive.get_default()
|
||||
default_client = self.oglive.get_clients()[default_num]
|
||||
client_initrd_path = os.path.join(tftp_dir, default_client, INITRD)
|
||||
|
||||
#self.temp_dir = self._get_tempdir()
|
||||
|
||||
if self.usesshkey:
|
||||
with open(self.usesshkey, 'r') as f:
|
||||
public_key = f.read().strip()
|
||||
|
||||
else:
|
||||
if os.path.isfile(client_initrd_path):
|
||||
#os.makedirs(temp_dir, exist_ok=True)
|
||||
#os.chdir(self.temp_dir.name)
|
||||
self.__logger.debug("Descomprimiendo %s", client_initrd_path)
|
||||
public_key = None
|
||||
with libarchive.file_reader(client_initrd_path) as initrd:
|
||||
for file in initrd:
|
||||
#self.__logger.debug("Archivo: %s", file)
|
||||
|
||||
if file.pathname in self.key_paths_dict:
|
||||
data = bytearray()
|
||||
for block in file.get_blocks():
|
||||
data = data + block
|
||||
public_key = data.decode('utf-8').strip()
|
||||
|
||||
break
|
||||
else:
|
||||
print(f"No se encuentra la imagen de initrd {client_initrd_path}")
|
||||
exit(2)
|
||||
|
||||
return public_key
|
||||
|
||||
def install(self):
|
||||
"""Instalar
|
||||
|
@ -338,6 +421,182 @@ class OpengnsysGitInstaller:
|
|||
for DIR in ["base.git", "linux.git", "windows.git"]: #, "LinAcl", "WinAcl"]:
|
||||
self._recursive_chown(os.path.join(ogdir_images, DIR), ouid=self.ssh_uid, ogid=self.ssh_gid)
|
||||
|
||||
def _install_template(self, template, destination, keysvalues):
|
||||
|
||||
self.__logger.info("Writing template %s into %s", template, destination)
|
||||
|
||||
data = ""
|
||||
with open(template, "r", encoding="utf-8") as template_file:
|
||||
data = template_file.read()
|
||||
|
||||
for key in keysvalues.keys():
|
||||
data = data.replace("{" + key + "}", keysvalues[key])
|
||||
|
||||
with open(destination, "w+", encoding="utf-8") as out_file:
|
||||
out_file.write(data)
|
||||
|
||||
def _runcmd(self, cmd):
|
||||
self.__logger.debug("Running: %s", cmd)
|
||||
|
||||
ret = subprocess.run(cmd, check=True,capture_output=True, encoding='utf-8')
|
||||
return ret.stdout.strip()
|
||||
|
||||
def install_forgejo(self):
|
||||
self.__logger.info("Installing Forgejo")
|
||||
|
||||
|
||||
|
||||
|
||||
bin_path = os.path.join(self.base_path, "bin", "forgejo")
|
||||
conf_dir_path = os.path.join(self.base_path, "etc", "forgejo")
|
||||
|
||||
|
||||
lfs_dir_path = os.path.join(self.base_path, "images", "lfs")
|
||||
forgejo_work_dir_path = os.path.join(self.base_path, "var", "run", "forgejo")
|
||||
forgejo_db_dir_path = os.path.join(self.base_path, "var", "run", "forgejo")
|
||||
forgejo_db_path = os.path.join(forgejo_db_dir_path, "forgejo.db")
|
||||
|
||||
forgejo_log_dir_path = os.path.join(self.base_path, "log", "forgejo")
|
||||
|
||||
|
||||
conf_path = os.path.join(conf_dir_path, "app.ini")
|
||||
|
||||
self.__logger.debug("Stopping opengnsys-forgejo service")
|
||||
subprocess.run(["systemctl", "stop", "opengnsys-forgejo"], check=False)
|
||||
|
||||
if not os.path.exists(bin_path):
|
||||
self.__logger.debug("Downloading from %s into %s", FORGEJO_URL, bin_path)
|
||||
urllib.request.urlretrieve(FORGEJO_URL, bin_path)
|
||||
os.chmod(bin_path, 0o755)
|
||||
|
||||
|
||||
|
||||
if os.path.exists(forgejo_db_path):
|
||||
self.__logger.debug("Removing old configuration")
|
||||
os.unlink(forgejo_db_path)
|
||||
else:
|
||||
self.__logger.debug("Old configuration not present, ok.")
|
||||
|
||||
self.__logger.debug("Creating directories")
|
||||
|
||||
pathlib.Path(conf_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(lfs_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(forgejo_work_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(forgejo_db_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
pathlib.Path(forgejo_log_dir_path).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
os.chown(lfs_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
os.chown(forgejo_work_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
os.chown(forgejo_db_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
os.chown(forgejo_log_dir_path, self.ssh_uid, self.ssh_gid)
|
||||
|
||||
data = {
|
||||
"forgejo_user" : self.ssh_user,
|
||||
"forgejo_group" : self.ssh_group,
|
||||
"forgejo_port" : str(self.forgejo_port),
|
||||
"forgejo_bin" : bin_path,
|
||||
"forgejo_app_ini" : conf_path,
|
||||
"forgejo_work_path" : forgejo_work_dir_path,
|
||||
"forgejo_db_path" : forgejo_db_path,
|
||||
"forgejo_repository_root" : os.path.join(self.base_path, "images"),
|
||||
"forgejo_lfs_path" : lfs_dir_path,
|
||||
"forgejo_log_path" : forgejo_log_dir_path,
|
||||
"forgejo_hostname" : self._runcmd("hostname"),
|
||||
"forgejo_lfs_jwt_secret" : self._runcmd([bin_path,"generate", "secret", "LFS_JWT_SECRET"]),
|
||||
"forgejo_jwt_secret" : self._runcmd([bin_path,"generate", "secret", "JWT_SECRET"]),
|
||||
"forgejo_internal_token" : self._runcmd([bin_path,"generate", "secret", "INTERNAL_TOKEN"]),
|
||||
"forgejo_secret_key" : self._runcmd([bin_path,"generate", "secret", "SECRET_KEY"])
|
||||
}
|
||||
|
||||
self._install_template(os.path.join(self.script_path, "forgejo-app.ini"), conf_path, data)
|
||||
self._install_template(os.path.join(self.script_path, "forgejo.service"), "/etc/systemd/system/opengnsys-forgejo.service", data)
|
||||
|
||||
|
||||
self.__logger.debug("Reloading systemd and starting service")
|
||||
subprocess.run(["systemctl", "daemon-reload"], check=True)
|
||||
subprocess.run(["systemctl", "enable", "opengnsys-forgejo"], check=True)
|
||||
subprocess.run(["systemctl", "restart", "opengnsys-forgejo"], check=True)
|
||||
|
||||
self.__logger.info("Waiting for forgejo to start")
|
||||
self._wait_for_port("localhost", self.forgejo_port)
|
||||
|
||||
|
||||
self.__logger.info("Configuring forgejo")
|
||||
|
||||
def run_forge_cmd(args):
|
||||
cmd = [bin_path, "--config", conf_path] + args
|
||||
self.__logger.debug("Running command: %s", cmd)
|
||||
|
||||
ret = subprocess.run(cmd, check=False, capture_output=True, encoding='utf-8', user=self.ssh_user)
|
||||
if ret.returncode == 0:
|
||||
return ret.stdout.strip()
|
||||
else:
|
||||
self.__logger.error("Failed to run command: %s, return code %i", cmd, ret.returncode)
|
||||
self.__logger.error("stdout: %s", ret.stdout)
|
||||
self.__logger.error("stderr: %s", ret.stderr)
|
||||
raise RuntimeError("Failed to run necessary command")
|
||||
|
||||
run_forge_cmd(["admin", "doctor", "check"])
|
||||
|
||||
run_forge_cmd(["admin", "user", "create", "--username", self.ssh_user, "--password", self.forgejo_password, "--email", self.email])
|
||||
|
||||
token = run_forge_cmd(["admin", "user", "generate-access-token", "--username", self.ssh_user, "-t", "gitapi", "--scopes", "all", "--raw"])
|
||||
|
||||
with open(os.path.join(self.base_path, "etc", "ogGitApiToken.cfg"), "w+", encoding='utf-8') as token_file:
|
||||
token_file.write(token)
|
||||
|
||||
|
||||
ssh_key = self._extract_ssh_key()
|
||||
|
||||
self.add_forgejo_sshkey(ssh_key, "Default key")
|
||||
|
||||
|
||||
def add_forgejo_repo(self, repository_name, description = ""):
|
||||
token = ""
|
||||
with open(os.path.join(self.base_path, "etc", "ogGitApiToken.cfg"), "r", encoding='utf-8') as token_file:
|
||||
token = token_file.read().strip()
|
||||
|
||||
self.__logger.info("Adding repository %s for Forgejo", repository_name)
|
||||
|
||||
r = requests.post(
|
||||
f"http://localhost:{self.forgejo_port}/api/v1/user/repos",
|
||||
json={
|
||||
"auto_init" : False,
|
||||
"default_branch" : "main",
|
||||
"description" : description,
|
||||
"name" : repository_name,
|
||||
"private" : False
|
||||
}, headers={
|
||||
'Authorization' : f"token {token}"
|
||||
},
|
||||
timeout = 60
|
||||
)
|
||||
|
||||
self.__logger.info("Request status was %i", r.status_code)
|
||||
|
||||
def add_forgejo_sshkey(self, pubkey, description = ""):
|
||||
token = ""
|
||||
with open(os.path.join(self.base_path, "etc", "ogGitApiToken.cfg"), "r", encoding='utf-8') as token_file:
|
||||
token = token_file.read().strip()
|
||||
|
||||
self.__logger.info("Adding SSH key to Forgejo: %s", pubkey)
|
||||
|
||||
r = requests.post(
|
||||
f"http://localhost:{self.forgejo_port}/api/v1/user/keys",
|
||||
json={
|
||||
"key" : pubkey,
|
||||
"read_only" : False,
|
||||
"title" : description
|
||||
}, headers={
|
||||
'Authorization' : f"token {token}"
|
||||
},
|
||||
timeout = 60
|
||||
)
|
||||
|
||||
self.__logger.info("Request status was %i", r.status_code)
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -350,6 +609,9 @@ if __name__ == '__main__':
|
|||
prog="OpenGnsys Installer",
|
||||
description="Script para la instalación del repositorio git",
|
||||
)
|
||||
parser.add_argument('--forgejo-only', action='store_true', help="Solo instalar forgejo")
|
||||
parser.add_argument('--forgejo-addrepos', action='store_true', help="Solo agregar repositorios forgejo")
|
||||
|
||||
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('--usesshkey', type=str, help="Usar clave SSH especificada")
|
||||
|
@ -364,7 +626,13 @@ if __name__ == '__main__':
|
|||
logger.debug("Inicio de instalación")
|
||||
|
||||
try:
|
||||
installer.install()
|
||||
if args.forgejo_only:
|
||||
installer.install_forgejo()
|
||||
elif args.forgejo_addrepos:
|
||||
installer.add_forgejo_repo("linux")
|
||||
else:
|
||||
installer.install()
|
||||
installer.install_forgejo()
|
||||
except RequirementException as req:
|
||||
show_error(f"Requisito para la instalación no satisfecho: {req.message}")
|
||||
exit(1)
|
||||
|
|
Loading…
Reference in New Issue