From bfc6ee55927e3e516d6e7d83be4081856260285b Mon Sep 17 00:00:00 2001 From: James Lu Date: Fri, 27 Dec 2024 12:06:48 +0800 Subject: [PATCH] fix(e2e): use backup target API if settings are removed ref: longhorn/longhorn 5411, 10043 Signed-off-by: James Lu --- e2e/libs/backupstore/base.py | 117 +++++++++++++++++++++++++++++++++++ e2e/libs/setting/setting.py | 73 +++++++++++++++++++--- 2 files changed, 180 insertions(+), 10 deletions(-) diff --git a/e2e/libs/backupstore/base.py b/e2e/libs/backupstore/base.py index 05775406e8..8671f86998 100644 --- a/e2e/libs/backupstore/base.py +++ b/e2e/libs/backupstore/base.py @@ -1,12 +1,42 @@ from abc import ABC, abstractmethod import hashlib import os +import re from kubernetes import client +from utility.utility import get_longhorn_client + +SECOND = 1 +MINUTE = 60 * SECOND +HOUR = 60 * MINUTE + +duration_u = { + "s": SECOND, + "m": MINUTE, + "h": HOUR, +} + + +def from_k8s_duration_to_seconds(duration_s): + # k8s duration string format such as "3h5m30s" + total = 0 + pattern_str = r'([0-9]+)([smh]+)' + pattern = re.compile(pattern_str) + matches = pattern.findall(duration_s) + if not len(matches): + raise Exception("Invalid duration {}".format(duration_s)) + + for v, u in matches: + total += int(v) * duration_u[u] + + return total + class Base(ABC): + DEFAULT_BACKUPTARGET = "default" + def __init__(self): self.core_api = client.CoreV1Api() backupstore = os.environ.get('LONGHORN_BACKUPSTORE') @@ -34,6 +64,54 @@ def backup_volume_path(self, volume_name): return backupstore_bv_path + def get_backupstore_url(self): + return get_longhorn_client().by_id_backupTarget( + self.DEFAULT_BACKUPTARGET).backupTargetURL + + def get_backupstore_secret(self): + return get_longhorn_client().by_id_backupTarget( + self.DEFAULT_BACKUPTARGET).credentialSecret + + def get_backupstore_poll_interval(self): + k8s_duration = get_longhorn_client().by_id_backupTarget( + self.DEFAULT_BACKUPTARGET).pollInterval + return from_k8s_duration_to_seconds(k8s_duration) + + def set_backupstore_url(self, url): + bt = get_longhorn_client().by_id_backupTarget( + self.DEFAULT_BACKUPTARGET) + p_interval = from_k8s_duration_to_seconds(bt.pollInterval) + self.update_backupstore(url=url, + credential_secret=bt.credentialSecret, + poll_interval=p_interval) + + def set_backupstore_secret(self, secret): + bt = get_longhorn_client().by_id_backupTarget( + self.DEFAULT_BACKUPTARGET) + p_interval = from_k8s_duration_to_seconds(bt.pollInterval) + self.update_backupstore(url=bt.backupTargetURL, + credential_secret=secret, + poll_interval=p_interval) + + def set_backupstore_poll_interval(self, poll_interval): + bt = get_longhorn_client().by_id_backupTarget( + self.DEFAULT_BACKUPTARGET) + self.update_backupstore(url=bt.backupTargetURL, + credential_secret=bt.credentialSecret, + poll_interval=poll_interval) + + def reset_backupstore(self): + self.update_backupstore() + + def update_backupstore(self, + url="", credential_secret="", poll_interval=300): + backup_target = get_longhorn_client().by_id_backupTarget( + self.DEFAULT_BACKUPTARGET) + backup_target.backupTargetUpdate(name=self.DEFAULT_BACKUPTARGET, + backupTargetURL=url, + credentialSecret=credential_secret, + pollInterval=str(poll_interval)) + @abstractmethod def get_backup_volume_prefix(self, volume_name): return NotImplemented @@ -77,3 +155,42 @@ def delete_random_backup_block(self): @abstractmethod def count_backup_block_files(self): return NotImplemented + + +class BackupStore(Base): + + def __init__(self): + super().__init__() + + def get_backup_volume_prefix(self, volume_name): + return NotImplemented + + def get_backup_cfg_file_path(self, volume_name, backup_name): + return NotImplemented + + def get_volume_cfg_file_path(self, volume_name): + return NotImplemented + + def get_backup_blocks_dir(self, volume_name): + return NotImplemented + + def create_file_in_backupstore(self): + return NotImplemented + + def write_backup_cfg_file(self, volume_name, backup_name, data): + return NotImplemented + + def delete_file_in_backupstore(self): + return NotImplemented + + def delete_backup_cfg_file(self): + return NotImplemented + + def delete_volume_cfg_file(self): + return NotImplemented + + def delete_random_backup_block(self): + return NotImplemented + + def count_backup_block_files(self): + return NotImplemented diff --git a/e2e/libs/setting/setting.py b/e2e/libs/setting/setting.py index d479b11f56..a33295466e 100644 --- a/e2e/libs/setting/setting.py +++ b/e2e/libs/setting/setting.py @@ -5,14 +5,25 @@ from utility.utility import get_retry_count_and_interval from utility.utility import logging +from backupstore.base import BackupStore + + class Setting: SETTING_BACKUP_TARGET = "backup-target" SETTING_BACKUP_TARGET_CREDENTIAL_SECRET = "backup-target-credential-secret" SETTING_BACKUPSTORE_POLL_INTERVAL = "backupstore-poll-interval" + SETTING_BACKUP_TARGET_NOT_SUPPORTED = \ + f"setting {SETTING_BACKUP_TARGET} is not supported" + SETTING_BACKUP_TARGET_CREDENTIAL_SECRET_NOT_SUPPORTED = \ + f"setting {SETTING_BACKUP_TARGET_CREDENTIAL_SECRET} is not supported" + SETTING_SETTING_BACKUPSTORE_POLL_INTERVAL_NOT_SUPPORTED = \ + f"setting {SETTING_BACKUPSTORE_POLL_INTERVAL} is not supported" + def __init__(self): self.retry_count, self.retry_interval = get_retry_count_and_interval() + self.backupstore = BackupStore() def update_setting(self, key, value, retry=True): if retry: @@ -43,23 +54,65 @@ def set_backupstore(self): backupstore = self.get_backupstore_url() if backupstore: backupsettings = backupstore.split("$") - self.update_setting(self.SETTING_BACKUP_TARGET, backupsettings[0]) - if len(backupsettings) > 1: - self.update_setting(self.SETTING_BACKUP_TARGET_CREDENTIAL_SECRET, backupsettings[1]) - poll_interval = self.get_backupstore_poll_interval() - self.update_setting(self.SETTING_BACKUPSTORE_POLL_INTERVAL, poll_interval) + try: + self.update_setting(self.SETTING_BACKUP_TARGET, + backupsettings[0], + retry=False) + if len(backupsettings) > 1: + self.update_setting( + self.SETTING_BACKUP_TARGET_CREDENTIAL_SECRET, + backupsettings[1], + retry=False) + + self.update_setting(self.SETTING_BACKUPSTORE_POLL_INTERVAL, + poll_interval, + retry=False) + except Exception as e: + if self.SETTING_BACKUP_TARGET_NOT_SUPPORTED in e.error.message: + self.backupstore.set_backupstore_url(backupsettings[0]) + if len(backupsettings) > 1: + self.backupstore.set_backupstore_secret( + backupsettings[1]) + self.backupstore.set_backupstore_poll_interval( + poll_interval) + else: + logging(e) def reset_backupstore(self): - self.update_setting(self.SETTING_BACKUP_TARGET, "") - self.update_setting(self.SETTING_BACKUP_TARGET_CREDENTIAL_SECRET, "") - self.update_setting(self.SETTING_BACKUPSTORE_POLL_INTERVAL, "300") + try: + self.update_setting(self.SETTING_BACKUP_TARGET, "", retry=False) + self.update_setting(self.SETTING_BACKUP_TARGET_CREDENTIAL_SECRET, + "", + retry=False) + self.update_setting(self.SETTING_BACKUPSTORE_POLL_INTERVAL, + "300", + retry=False) + except Exception as e: + if self.SETTING_BACKUP_TARGET_NOT_SUPPORTED in e.error.message: + self.backupstore.reset_backupstore() + else: + logging(e) def get_backup_target(self): - return self.get_setting(self.SETTING_BACKUP_TARGET) + try: + return self.get_setting(self.SETTING_BACKUP_TARGET) + except Exception as e: + if self.SETTING_BACKUP_TARGET_NOT_SUPPORTED in e.error.message: + return self.backupstore.get_backupstore_url() + else: + logging(e) def get_secret(self): - return self.get_setting(self.SETTING_BACKUP_TARGET_CREDENTIAL_SECRET) + try: + return self.get_setting( + self.SETTING_BACKUP_TARGET_CREDENTIAL_SECRET) + except Exception as e: + if (self.SETTING_BACKUP_TARGET_CREDENTIAL_SECRET_NOT_SUPPORTED + in e.error.message): + return self.backupstore.get_backupstore_secret() + else: + logging(e) def reset_settings(self): client = get_longhorn_client()