mirror of
https://git.selfprivacy.org/SelfPrivacy/selfprivacy-rest-api.git
synced 2024-11-10 13:03:11 +00:00
feature(services): allow moving uninitialized services
This commit is contained in:
parent
6afaefbb41
commit
f57eda5237
|
@ -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,6 +369,9 @@ class Service(ABC):
|
||||||
binds = self.binds()
|
binds = self.binds()
|
||||||
if binds == []:
|
if binds == []:
|
||||||
raise MoveError("nothing to move")
|
raise MoveError("nothing to move")
|
||||||
|
|
||||||
|
# It is ok if service is uninitialized, we will just reregister it
|
||||||
|
if self.has_folders():
|
||||||
check_binds(current_volume_name, binds)
|
check_binds(current_volume_name, binds)
|
||||||
|
|
||||||
def do_move_to_volume(
|
def do_move_to_volume(
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
):
|
):
|
||||||
|
|
Loading…
Reference in a new issue