feature(services): allow moving uninitialized services

This commit is contained in:
Houkime 2024-06-19 13:30:37 +00:00
parent 6afaefbb41
commit f57eda5237
3 changed files with 91 additions and 4 deletions

View file

@ -2,6 +2,7 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from typing import List, Optional from typing import List, Optional
from os.path import exists
from selfprivacy_api import utils from selfprivacy_api import utils
from selfprivacy_api.services.config_item import ServiceConfigItem from selfprivacy_api.services.config_item import ServiceConfigItem
@ -237,6 +238,16 @@ class Service(ABC):
storage_used += get_storage_usage(folder) storage_used += get_storage_usage(folder)
return storage_used return storage_used
@classmethod
def has_folders(cls) -> int:
"""
If there are no folders on disk, moving is noop
"""
for folder in cls.get_folders():
if exists(folder):
return True
return False
@classmethod @classmethod
def get_dns_records(cls, ip4: str, ip6: Optional[str]) -> List[ServiceDnsRecord]: def get_dns_records(cls, ip4: str, ip6: Optional[str]) -> List[ServiceDnsRecord]:
subdomain = cls.get_subdomain() subdomain = cls.get_subdomain()
@ -358,7 +369,10 @@ class Service(ABC):
binds = self.binds() binds = self.binds()
if binds == []: if binds == []:
raise MoveError("nothing to move") raise MoveError("nothing to move")
check_binds(current_volume_name, binds)
# It is ok if service is uninitialized, we will just reregister it
if self.has_folders():
check_binds(current_volume_name, binds)
def do_move_to_volume( def do_move_to_volume(
self, self,
@ -401,7 +415,18 @@ class Service(ABC):
service_name = self.get_display_name() service_name = self.get_display_name()
report_progress(0, job, "Performing pre-move checks...") report_progress(0, job, "Performing pre-move checks...")
self.assert_can_move(volume) self.assert_can_move(volume)
if not self.has_folders():
self.set_location(volume)
Jobs.update(
job=job,
status=JobStatus.FINISHED,
result=f"{service_name} moved successfully(no folders).",
status_text=f"NOT starting {service_name}",
progress=100,
)
return job
report_progress(5, job, f"Stopping {service_name}...") report_progress(5, job, f"Stopping {service_name}...")
assert self is not None assert self is not None

View file

@ -5,6 +5,7 @@ import subprocess
from typing import List from typing import List
from os import path from os import path
from pathlib import Path
# from enum import Enum # from enum import Enum
@ -73,8 +74,11 @@ class DummyService(Service):
@classmethod @classmethod
def status_file(cls) -> str: def status_file(cls) -> str:
dir = cls.folders[0] dir = cls.folders[0]
# we do not REALLY want to store our state in our declared folders # We do not want to store our state in our declared folders
return path.join(dir, "..", "service_status") # Because they are moved and tossed in tests wildly
parent = Path(dir).parent
return path.join(parent, "service_status")
@classmethod @classmethod
def set_status(cls, status: ServiceStatus): def set_status(cls, status: ServiceStatus):

View file

@ -2,7 +2,7 @@ import pytest
import shutil import shutil
from typing import Generator from typing import Generator
from os import mkdir from os import mkdir, rmdir
from selfprivacy_api.utils.block_devices import BlockDevices from selfprivacy_api.utils.block_devices import BlockDevices
@ -605,6 +605,11 @@ def test_graphql_move_service_without_folders_on_old_volume(
mock_lsblk_devices, mock_lsblk_devices,
dummy_service: DummyService, dummy_service: DummyService,
): ):
"""
Situation when you have folders in the filetree but they are not mounted
but just folders
"""
target = "sda1" target = "sda1"
BlockDevices().update() BlockDevices().update()
assert BlockDevices().get_block_device(target) is not None assert BlockDevices().get_block_device(target) is not None
@ -618,6 +623,59 @@ def test_graphql_move_service_without_folders_on_old_volume(
assert "sda2/test_service is not found" in data["message"] assert "sda2/test_service is not found" in data["message"]
def test_move_empty(
authorized_client, generic_userdata, mock_check_volume, dummy_service, fp
):
"""
A reregister of uninitialized service with no data.
No binds in place yet, and no rebuilds should happen.
"""
origin = "sda1"
target = "sda2"
assert BlockDevices().get_block_device(target) is not None
assert BlockDevices().get_block_device(origin) is not None
dummy_service.set_drive(origin)
dummy_service.set_simulated_moves(False)
dummy_service.disable()
unit_name = "sp-nixos-rebuild.service"
rebuild_command = ["systemctl", "start", unit_name]
prepare_nixos_rebuild_calls(fp, unit_name)
# We will NOT be mounting and remounting folders
mount_command = ["mount", fp.any()]
unmount_command = ["umount", fp.any()]
fp.pass_command(mount_command, 2)
fp.pass_command(unmount_command, 2)
# We will NOT be changing ownership
chown_command = ["chown", fp.any()]
fp.pass_command(chown_command, 2)
# We have virtual binds encapsulating our understanding where this should go.
assert len(dummy_service.binds()) == 2
# Remove all folders
for folder in dummy_service.get_folders():
shutil.rmtree(folder)
# They are virtual and unaffected by folder removal
assert len(dummy_service.binds()) == 2
mutation_response = api_move(authorized_client, dummy_service, target)
data = get_data(mutation_response)["services"]["moveService"]
assert_ok(data)
assert data["service"] is not None
assert fp.call_count(rebuild_command) == 0
assert fp.call_count(mount_command) == 0
assert fp.call_count(unmount_command) == 0
assert fp.call_count(chown_command) == 0
def test_graphql_move_service( def test_graphql_move_service(
authorized_client, generic_userdata, mock_check_volume, dummy_service_with_binds, fp authorized_client, generic_userdata, mock_check_volume, dummy_service_with_binds, fp
): ):