update_manager: refactor AppDeloy class

Don't require the `app_params` argument, instead dynamically
generate the configuration from a dict.  This simiplifies AppDeploy
initialization as the internally generated configurations can be
read in the same way as those supplied in moonraker.conf.

Signed-off-by:  Eric Callahan <arksine.code@gmail.com>
This commit is contained in:
Eric Callahan 2022-03-27 08:07:32 -04:00
parent 54081612ca
commit e9e86c3042
No known key found for this signature in database
GPG Key ID: 7027245FBBDDF59A
6 changed files with 99 additions and 109 deletions

View File

@ -8,7 +8,6 @@ from __future__ import annotations
import pathlib
import shutil
import hashlib
import json
import logging
from .base_deploy import BaseDeploy
@ -28,7 +27,7 @@ if TYPE_CHECKING:
CHANNEL_TO_TYPE = {
"stable": "zip",
"beta": "zip_beta",
"beta": "git_repo",
"dev": "git_repo"
}
TYPE_TO_CHANNEL = {
@ -38,26 +37,14 @@ TYPE_TO_CHANNEL = {
}
class AppDeploy(BaseDeploy):
def __init__(self,
config: ConfigHelper,
cmd_helper: CommandHelper,
app_params: Optional[Dict[str, Any]]
) -> None:
def __init__(self, config: ConfigHelper, cmd_helper: CommandHelper) -> None:
super().__init__(config, cmd_helper, prefix="Application")
self.config = config
self.app_params = app_params
cfg_hash = self._calc_config_hash()
super().__init__(config, cmd_helper, prefix="Application",
cfg_hash=cfg_hash)
self.debug = self.cmd_helper.is_debug_enabled()
if app_params is not None:
self.channel: str = app_params['channel']
self.path: pathlib.Path = pathlib.Path(
app_params['path']).expanduser().resolve()
executable: Optional[str] = app_params['executable']
self.type = CHANNEL_TO_TYPE[self.channel]
else:
self.type = config.get('type')
self.channel = TYPE_TO_CHANNEL[self.type]
self.channel = config.get(
"channel", TYPE_TO_CHANNEL[self.type]
)
self.path = pathlib.Path(
config.get('path')).expanduser().resolve()
executable = config.get('env', None)
@ -136,15 +123,6 @@ class AppDeploy(BaseDeploy):
self._is_valid = storage.get('is_valid', False)
return storage
def _calc_config_hash(self) -> str:
cfg_hash = self.config.get_hash()
if self.app_params is None:
return cfg_hash.hexdigest()
else:
app_bytes = json.dumps(self.app_params).encode()
cfg_hash.update(app_bytes)
return cfg_hash.hexdigest()
def _verify_path(self,
config: ConfigHelper,
option: str,

View File

@ -0,0 +1,62 @@
# Moonraker/Klipper update configuration
#
# Copyright (C) 2022 Eric Callahan <arksine.code@gmail.com>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
from __future__ import annotations
import os
import sys
import copy
from typing import (
TYPE_CHECKING,
Dict
)
if TYPE_CHECKING:
from confighelper import ConfigHelper
from components.database import MoonrakerDatabase
MOONRAKER_PATH = os.path.normpath(os.path.join(
os.path.dirname(__file__), "../../.."))
KLIPPER_DEFAULT_PATH = os.path.expanduser("~/klipper")
KLIPPER_DEFAULT_EXEC = os.path.expanduser("~/klippy-env/bin/python")
BASE_CONFIG: Dict[str, Dict[str, str]] = {
"moonraker": {
"origin": "https://github.com/arksine/moonraker.git",
"requirements": "scripts/moonraker-requirements.txt",
"venv_args": "-p python3",
"install_script": "scripts/install-moonraker.sh",
"host_repo": "arksine/moonraker",
"env": sys.executable,
"path": MOONRAKER_PATH,
"managed_services": "moonraker"
},
"klipper": {
"moved_origin": "https://github.com/kevinoconnor/klipper.git",
"origin": "https://github.com/Klipper3d/klipper.git",
"requirements": "scripts/klippy-requirements.txt",
"venv_args": "-p python2",
"install_script": "scripts/install-octopi.sh",
"host_repo": "arksine/moonraker",
"managed_services": "klipper"
}
}
def get_base_configuration(config: ConfigHelper, channel: str) -> ConfigHelper:
server = config.get_server()
base_cfg = copy.deepcopy(BASE_CONFIG)
app_type = "zip" if channel == "stable" else "git_repo"
base_cfg["moonraker"]["channel"] = channel
base_cfg["moonraker"]["type"] = app_type
base_cfg["klipper"]["channel"] = channel
base_cfg["klipper"]["type"] = app_type
db: MoonrakerDatabase = server.lookup_component('database')
base_cfg["klipper"]["path"] = db.get_item(
"moonraker", "update_manager.klipper_path", KLIPPER_DEFAULT_PATH
).result()
base_cfg["klipper"]["env"] = db.get_item(
"moonraker", "update_manager.klipper_exec", KLIPPER_DEFAULT_EXEC
).result()
return config.read_supplemental_dict(base_cfg)

View File

@ -28,12 +28,8 @@ if TYPE_CHECKING:
from .update_manager import CommandHelper
class GitDeploy(AppDeploy):
def __init__(self,
config: ConfigHelper,
cmd_helper: CommandHelper,
app_params: Optional[Dict[str, Any]] = None
) -> None:
super().__init__(config, cmd_helper, app_params)
def __init__(self, config: ConfigHelper, cmd_helper: CommandHelper) -> None:
super().__init__(config, cmd_helper)
self.repo = GitRepo(cmd_helper, self.path, self.name,
self.origin, self.moved_origin)
if self.type != 'git_repo':
@ -41,7 +37,7 @@ class GitDeploy(AppDeploy):
@staticmethod
async def from_application(app: AppDeploy) -> GitDeploy:
new_app = GitDeploy(app.config, app.cmd_helper, app.app_params)
new_app = GitDeploy(app.config, app.cmd_helper)
await new_app.reinstall()
return new_app

View File

@ -1,17 +0,0 @@
# Supplemental configuration for Moonraker's Update Manager
# component. This file should not be modified.
[update_manager moonraker]
origin: https://github.com/arksine/moonraker.git
requirements: scripts/moonraker-requirements.txt
venv_args: -p python3
install_script: scripts/install-moonraker.sh
host_repo: arksine/moonraker
[update_manager klipper]
moved_origin: https://github.com/kevinoconnor/klipper.git
origin: https://github.com/Klipper3d/klipper.git
requirements: scripts/klippy-requirements.txt
venv_args: -p python2
install_script: scripts/install-octopi.sh
host_repo: arksine/moonraker

View File

@ -9,13 +9,13 @@ import asyncio
import os
import pathlib
import logging
import sys
import shutil
import zipfile
import time
import tempfile
import re
from thirdparty.packagekit import enums as PkEnum
from . import base_config
from .base_deploy import BaseDeploy
from .app_deploy import AppDeploy
from .git_deploy import GitDeploy
@ -51,13 +51,6 @@ if TYPE_CHECKING:
from dbus_next.aio import ProxyInterface
JsonType = Union[List[Any], Dict[str, Any]]
MOONRAKER_PATH = os.path.normpath(os.path.join(
os.path.dirname(__file__), "../../.."))
SUPPLEMENTAL_CFG_PATH = os.path.join(
os.path.dirname(__file__), "update_manager.conf")
KLIPPER_DEFAULT_PATH = os.path.expanduser("~/klipper")
KLIPPER_DEFAULT_EXEC = os.path.expanduser("~/klippy-env/bin/python")
# Check To see if Updates are necessary each hour
UPDATE_REFRESH_INTERVAL = 3600.
# Perform auto refresh no later than 4am
@ -73,46 +66,30 @@ class UpdateManager:
def __init__(self, config: ConfigHelper) -> None:
self.server = config.get_server()
self.event_loop = self.server.get_event_loop()
self.app_config = config.read_supplemental_config(
SUPPLEMENTAL_CFG_PATH)
auto_refresh_enabled = config.getboolean('enable_auto_refresh', False)
self.channel = config.get('channel', "dev")
if self.channel not in ["dev", "beta"]:
raise config.error(
f"Unsupported channel '{self.channel}' in section"
" [update_manager]")
self.app_config = base_config.get_base_configuration(
config, self.channel
)
auto_refresh_enabled = config.getboolean('enable_auto_refresh', False)
self.cmd_helper = CommandHelper(config)
self.updaters: Dict[str, BaseDeploy] = {}
if config.getboolean('enable_system_updates', True):
self.updaters['system'] = PackageDeploy(config, self.cmd_helper)
db: DBComp = self.server.lookup_component('database')
kpath = db.get_item("moonraker", "update_manager.klipper_path",
KLIPPER_DEFAULT_PATH).result()
kenv_path = db.get_item("moonraker", "update_manager.klipper_exec",
KLIPPER_DEFAULT_EXEC).result()
self.updaters['moonraker'] = get_deploy_class(MOONRAKER_PATH)(
self.app_config[f"update_manager moonraker"], self.cmd_helper,
{
'channel': self.channel,
'path': MOONRAKER_PATH,
'executable': sys.executable
}
)
mcfg = self.app_config["moonraker"]
kcfg = self.app_config["klipper"]
mclass = get_deploy_class(mcfg.get("path"))
self.updaters['moonraker'] = mclass(mcfg, self.cmd_helper)
kclass = BaseDeploy
if (
os.path.exists(kpath) and
os.path.exists(kenv_path)
os.path.exists(kcfg.get("path")) and
os.path.exists(kcfg.get("env"))
):
self.updaters['klipper'] = get_deploy_class(kpath)(
self.app_config[f"update_manager klipper"], self.cmd_helper,
{
'channel': self.channel,
'path': kpath,
'executable': kenv_path
}
)
else:
self.updaters['klipper'] = BaseDeploy(
self.app_config[f"update_manager klipper"], self.cmd_helper)
kclass = get_deploy_class(kcfg.get("path"))
self.updaters['klipper'] = kclass(kcfg, self.cmd_helper)
# TODO: The below check may be removed when invalid config options
# raise a config error.
@ -135,8 +112,8 @@ class UpdateManager:
self.updaters[name] = WebClientDeploy(cfg, self.cmd_helper)
elif client_type in ["git_repo", "zip", "zip_beta"]:
path = os.path.expanduser(cfg.get('path'))
self.updaters[name] = get_deploy_class(path)(
cfg, self.cmd_helper)
dclass = get_deploy_class(path)
self.updaters[name] = dclass(cfg, self.cmd_helper)
else:
raise config.error(
f"Invalid type '{client_type}' for section [{section}]")
@ -215,14 +192,12 @@ class UpdateManager:
db: DBComp = self.server.lookup_component('database')
db.insert_item("moonraker", "update_manager.klipper_path", kpath)
db.insert_item("moonraker", "update_manager.klipper_exec", executable)
kcfg = self.app_config["klipper"]
kcfg.set_option("path", kpath)
kcfg.set_option("env", executable)
need_notification = not isinstance(kupdater, AppDeploy)
self.updaters['klipper'] = get_deploy_class(kpath)(
self.app_config[f"update_manager klipper"], self.cmd_helper,
{
'channel': self.channel,
'path': kpath,
'executable': executable
})
kclass = get_deploy_class(kpath)
self.updaters['klipper'] = kclass(kcfg, self.cmd_helper)
async with self.cmd_request_lock:
umdb = self.cmd_helper.get_umdb()
await umdb.pop('klipper', None)

View File

@ -35,12 +35,8 @@ RINFO_KEYS = [
]
class ZipDeploy(AppDeploy):
def __init__(self,
config: ConfigHelper,
cmd_helper: CommandHelper,
app_params: Optional[Dict[str, Any]] = None
) -> None:
super().__init__(config, cmd_helper, app_params)
def __init__(self, config: ConfigHelper, cmd_helper: CommandHelper) -> None:
super().__init__(config, cmd_helper)
self.official_repo: str = "?"
self.owner: str = "?"
# Extract repo from origin for validation
@ -60,7 +56,7 @@ class ZipDeploy(AppDeploy):
@staticmethod
async def from_application(app: AppDeploy) -> ZipDeploy:
new_app = ZipDeploy(app.config, app.cmd_helper, app.app_params)
new_app = ZipDeploy(app.config, app.cmd_helper)
await new_app.reinstall()
return new_app