HEX
Server: Apache
System: Linux br384.hostgator.com.br 4.19.286-203.ELK.el7.x86_64 #1 SMP Wed Jun 14 04:33:55 CDT 2023 x86_64
User: regi8665 (2159)
PHP: 8.3.30
Disabled: NONE
Upload Files
File: //usr/libexec/kcare/python/kcarectl/update_utils.py
# Copyright (c) Cloud Linux Software, Inc
# Licensed under CLOUD LINUX LICENSE AGREEMENT
# http://cloudlinux.com/docs/LICENCE.TXT

import functools
import json
import os
import time

from . import config, constants, log_utils, utils
from .py23 import json_loads_nstr

if False:  # pragma: no cover
    from typing import Any, Callable, Dict  # noqa: F401

STATUS_CHANGE_GAP_DELAY = 5 * 60  # 5 minute


def touch_status_gap_file(filename='.kcarestatus'):
    status_filepath = os.path.join(constants.PATCH_CACHE, filename)
    utils.atomic_write(status_filepath, utils.timestamp_str())


def status_gap_passed(filename='.kcarestatus'):
    status_filepath = os.path.join(constants.PATCH_CACHE, filename)
    if os.path.isfile(status_filepath):
        with open(status_filepath, 'r') as sfile:
            try:
                timestamp = int(sfile.read())
                if int(timestamp) + config.STATUS_CHANGE_GAP + STATUS_CHANGE_GAP_DELAY > time.time():
                    return False
            except Exception:
                pass
    return True


def _check_component(component):
    # type: (str) -> None
    if component not in ('kernel', 'libcare'):
        raise ValueError('Unknown update status component: {0}'.format(component))


def _load_update_status():
    # type: () -> Dict[str, Any]
    content = utils.read_file(constants.UPDATE_STATUS_PATH)
    if content is None:
        return {}

    try:
        result = json_loads_nstr(content)  # type: Dict[str, Any]
        return result
    except (ValueError, TypeError):
        log_utils.kcarelog.warning('Failed to parse update status file')
        return {}


def save_update_status(component, error):
    # type: (str, str) -> None
    _check_component(component)
    try:
        data = _load_update_status()
        data[component] = {
            'error': error,
            'timestamp': int(time.time()),
        }
        utils.atomic_write(constants.UPDATE_STATUS_PATH, json.dumps(data))
    except Exception:
        log_utils.kcarelog.warning('Failed to save update status', exc_info=True)


def _error_status(err):
    # type: (Exception) -> str
    status = getattr(err, 'status', '')
    if status:
        return status

    return str(err)


def track_update_status(component):
    # type: (str) -> Callable[..., Any]
    _check_component(component)

    def decorator(fn):  # type: (Callable[..., Any]) -> Callable[..., Any]
        @functools.wraps(fn)
        def inner(*args, **kwargs):  # type: (Any, Any) -> Any
            try:
                result = fn(*args, **kwargs)
            except Exception as err:
                save_update_status(component, error=_error_status(err))
                raise

            save_update_status(component, error='')
            return result

        return inner

    return decorator


def read_update_error(component):
    # type: (str) -> str
    _check_component(component)
    try:
        data = _load_update_status()
        error = data.get(component, {}).get('error', '')  # type: str
        return error[: constants.UPDATE_ERROR_MAX_LENGTH]
    except Exception:
        log_utils.kcarelog.warning('Failed to read update status', exc_info=True)
        return ''