python3-cyberfusion-queue-support 1.1.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,72 @@
1
+ """Classes for queue."""
2
+
3
+ import logging
4
+ from typing import List
5
+
6
+ from cyberfusion.QueueSupport.exceptions import QueueFulfillFailed
7
+ from cyberfusion.QueueSupport.interfaces import OutcomeInterface
8
+ from cyberfusion.QueueSupport.items import _Item
9
+
10
+ logger = logging.getLogger(__name__)
11
+
12
+
13
+ class Queue:
14
+ """Represents queue."""
15
+
16
+ def __init__(self) -> None:
17
+ """Set attributes."""
18
+ self.items: List[_Item] = []
19
+
20
+ def add(self, item: _Item, *, move_duplicate_last: bool = True) -> None:
21
+ """Add item to queue."""
22
+ if item not in self.items:
23
+ self.items.append(item)
24
+
25
+ logger.info("Added item to queue (reference: '%s')", item.reference)
26
+ else:
27
+ # If item already in queue, move to last place
28
+
29
+ if move_duplicate_last:
30
+ self.items.append(self.items.pop(self.items.index(item)))
31
+
32
+ logger.info(
33
+ "Added item to queue (reference: '%s') (moving duplicate last)",
34
+ item.reference,
35
+ )
36
+ else:
37
+ logger.info(
38
+ "Didn't add item to queue (reference: '%s') (already present)",
39
+ item.reference,
40
+ )
41
+
42
+ def process(self, preview: bool) -> List[OutcomeInterface]:
43
+ """Process items."""
44
+ logger.info("Processing items")
45
+
46
+ results = []
47
+
48
+ for item in self.items:
49
+ logger.info("Processing item '%s'", item.reference)
50
+
51
+ if not item.hide_outcomes:
52
+ results.extend(item.outcomes)
53
+
54
+ if not preview:
55
+ try:
56
+ logger.info("Fulfilling item '%s'", item.reference)
57
+
58
+ item.fulfill()
59
+
60
+ logger.info("Fulfilled item '%s'", item.reference)
61
+ except QueueFulfillFailed:
62
+ raise
63
+ except Exception as e:
64
+ raise QueueFulfillFailed(
65
+ item,
66
+ ) from e
67
+
68
+ logger.info("Processed item '%s'", item.reference)
69
+
70
+ logger.info("Processed items")
71
+
72
+ return results
@@ -0,0 +1,39 @@
1
+ """Exceptions."""
2
+
3
+ from dataclasses import dataclass
4
+ from typing import List
5
+
6
+ from cyberfusion.QueueSupport.items import _Item
7
+
8
+
9
+ class ItemError(Exception):
10
+ """Issues with item."""
11
+
12
+ pass
13
+
14
+
15
+ @dataclass
16
+ class PathIsSymlinkError(ItemError):
17
+ """Path is symlink."""
18
+
19
+ path: str
20
+
21
+
22
+ @dataclass
23
+ class QueueFulfillFailed(Exception):
24
+ """Error occurred while fulfilling queue."""
25
+
26
+ item: _Item
27
+
28
+
29
+ @dataclass
30
+ class CommandQueueFulfillFailed(QueueFulfillFailed):
31
+ """Error occurred while fulfilling queue, with command item."""
32
+
33
+ command: List[str]
34
+ stdout: str
35
+ stderr: str
36
+
37
+ def __str__(self) -> str:
38
+ """Get string representation."""
39
+ return f"Command:\n\n{self.command}\n\nStdout:\n\n{self.stdout}\n\nStderr:\n\n{self.stderr}"
@@ -0,0 +1,38 @@
1
+ """Interfaces."""
2
+
3
+ from abc import ABCMeta, abstractmethod
4
+ from typing import List
5
+
6
+
7
+ class OutcomeInterface(metaclass=ABCMeta):
8
+ """Interface for outcomes."""
9
+
10
+ @abstractmethod
11
+ def __str__(self) -> str: # pragma: no cover
12
+ """Get human-readable string."""
13
+ pass
14
+
15
+ @abstractmethod
16
+ def __eq__(self, other: object) -> bool: # pragma: no cover
17
+ """Get equality based on attributes."""
18
+ pass
19
+
20
+
21
+ class ItemInterface(metaclass=ABCMeta):
22
+ """Interface for items."""
23
+
24
+ @property
25
+ @abstractmethod
26
+ def outcomes(self) -> List[OutcomeInterface]: # pragma: no cover
27
+ """Get outcomes of calling self.fulfill."""
28
+ pass
29
+
30
+ @abstractmethod
31
+ def fulfill(self) -> None: # pragma: no cover
32
+ """Fulfill outcomes."""
33
+ pass
34
+
35
+ @abstractmethod
36
+ def __eq__(self, other: object) -> bool: # pragma: no cover
37
+ """Get equality based on attributes."""
38
+ pass
@@ -0,0 +1,22 @@
1
+ """Items."""
2
+
3
+ import logging
4
+ from typing import Optional
5
+
6
+ from cyberfusion.QueueSupport.interfaces import ItemInterface
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ class _Item(ItemInterface):
12
+ """Represents base item."""
13
+
14
+ @property
15
+ def hide_outcomes(self) -> bool:
16
+ """Get if outcomes should be hidden."""
17
+ return self._hide_outcomes
18
+
19
+ @property
20
+ def reference(self) -> Optional[str]:
21
+ """Get free-form reference, for users' own administrations."""
22
+ return self._reference
@@ -0,0 +1,74 @@
1
+ """Item."""
2
+
3
+ import logging
4
+ import os
5
+ from typing import List, Optional
6
+
7
+ from cyberfusion.QueueSupport.exceptions import PathIsSymlinkError
8
+ from cyberfusion.QueueSupport.interfaces import OutcomeInterface
9
+ from cyberfusion.QueueSupport.items import _Item
10
+ from cyberfusion.QueueSupport.outcomes import ChmodItemModeChangeOutcome
11
+ from cyberfusion.QueueSupport.utilities import get_decimal_permissions
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class ChmodItem(_Item):
17
+ """Represents item."""
18
+
19
+ def __init__(
20
+ self,
21
+ *,
22
+ path: str,
23
+ mode: int,
24
+ reference: Optional[str] = None,
25
+ hide_outcomes: bool = False,
26
+ ) -> None:
27
+ """Set attributes."""
28
+ self.path = path
29
+ self.mode = mode
30
+ self._reference = reference
31
+ self._hide_outcomes = hide_outcomes
32
+
33
+ if os.path.islink(self.path):
34
+ raise PathIsSymlinkError(self.path)
35
+
36
+ @property
37
+ def outcomes(self) -> List[OutcomeInterface]:
38
+ """Get outcomes of calling self.fulfill."""
39
+ outcomes = []
40
+
41
+ if not os.path.exists(self.path):
42
+ outcomes.append(
43
+ ChmodItemModeChangeOutcome(
44
+ path=self.path, old_mode=None, new_mode=self.mode
45
+ )
46
+ )
47
+ else:
48
+ old_mode = get_decimal_permissions(self.path)
49
+ mode_changed = old_mode != self.mode
50
+
51
+ if mode_changed:
52
+ outcomes.append(
53
+ ChmodItemModeChangeOutcome(
54
+ path=self.path, old_mode=old_mode, new_mode=self.mode
55
+ )
56
+ )
57
+
58
+ return outcomes
59
+
60
+ def fulfill(self) -> None:
61
+ """Fulfill outcomes."""
62
+ mode_change_outcomes = [
63
+ x for x in self.outcomes if isinstance(x, ChmodItemModeChangeOutcome)
64
+ ]
65
+
66
+ if mode_change_outcomes:
67
+ os.chmod(mode_change_outcomes[0].path, mode_change_outcomes[0].new_mode)
68
+
69
+ def __eq__(self, other: object) -> bool:
70
+ """Get equality based on attributes."""
71
+ if not isinstance(other, ChmodItem):
72
+ return False
73
+
74
+ return other.path == self.path and other.mode == self.mode
@@ -0,0 +1,140 @@
1
+ """Item."""
2
+
3
+ import grp
4
+ import logging
5
+ import os
6
+ import pwd
7
+ from grp import getgrgid
8
+ from pwd import getpwuid
9
+ from typing import List, Optional
10
+
11
+ from cyberfusion.QueueSupport.exceptions import PathIsSymlinkError
12
+ from cyberfusion.QueueSupport.interfaces import OutcomeInterface
13
+ from cyberfusion.QueueSupport.items import _Item
14
+ from cyberfusion.QueueSupport.outcomes import (
15
+ ChownItemGroupChangeOutcome,
16
+ ChownItemOwnerChangeOutcome,
17
+ )
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ def get_uid(username: str) -> int:
23
+ """Get UID by username."""
24
+ return pwd.getpwnam(username).pw_uid
25
+
26
+
27
+ def get_gid(group_name: str) -> int:
28
+ """Get GID by group name."""
29
+ return grp.getgrnam(group_name).gr_gid
30
+
31
+
32
+ class ChownItem(_Item):
33
+ """Represents item."""
34
+
35
+ def __init__(
36
+ self,
37
+ *,
38
+ path: str,
39
+ owner_name: str,
40
+ group_name: str,
41
+ reference: Optional[str] = None,
42
+ hide_outcomes: bool = False,
43
+ ) -> None:
44
+ """Set attributes."""
45
+ self.path = path
46
+ self.owner_name = owner_name
47
+ self.group_name = group_name
48
+ self._reference = reference
49
+ self._hide_outcomes = hide_outcomes
50
+
51
+ if os.path.islink(self.path):
52
+ raise PathIsSymlinkError(self.path)
53
+
54
+ @property
55
+ def outcomes(self) -> List[OutcomeInterface]:
56
+ """Get outcomes of calling self.fulfill."""
57
+ outcomes = []
58
+
59
+ if not os.path.exists(self.path):
60
+ outcomes.extend(
61
+ [
62
+ ChownItemOwnerChangeOutcome(
63
+ path=self.path,
64
+ old_owner_name=None,
65
+ new_owner_name=self.owner_name,
66
+ ),
67
+ ChownItemGroupChangeOutcome(
68
+ path=self.path,
69
+ old_group_name=None,
70
+ new_group_name=self.group_name,
71
+ ),
72
+ ]
73
+ )
74
+ else:
75
+ try:
76
+ old_owner_name = getpwuid(os.stat(self.path).st_uid).pw_name
77
+ except KeyError:
78
+ old_owner_name = "(no user with UID exists)"
79
+
80
+ try:
81
+ old_group_name = getgrgid(os.stat(self.path).st_gid).gr_name
82
+ except KeyError:
83
+ old_group_name = "(no group with GID exists)"
84
+
85
+ owner_name_changed = old_owner_name != self.owner_name
86
+ group_name_changed = old_group_name != self.group_name
87
+
88
+ if owner_name_changed:
89
+ outcomes.append(
90
+ ChownItemOwnerChangeOutcome(
91
+ path=self.path,
92
+ old_owner_name=old_owner_name,
93
+ new_owner_name=self.owner_name,
94
+ )
95
+ )
96
+
97
+ if group_name_changed:
98
+ outcomes.append(
99
+ ChownItemGroupChangeOutcome(
100
+ path=self.path,
101
+ old_group_name=old_group_name,
102
+ new_group_name=self.group_name,
103
+ )
104
+ )
105
+
106
+ return outcomes
107
+
108
+ def fulfill(self) -> None:
109
+ """Fulfill outcomes."""
110
+ owner_name_change_outcomes = [
111
+ x for x in self.outcomes if isinstance(x, ChownItemOwnerChangeOutcome)
112
+ ]
113
+ group_name_change_outcomes = [
114
+ x for x in self.outcomes if isinstance(x, ChownItemGroupChangeOutcome)
115
+ ]
116
+
117
+ if owner_name_change_outcomes:
118
+ os.chown(
119
+ owner_name_change_outcomes[0].path,
120
+ uid=get_uid(owner_name_change_outcomes[0].new_owner_name),
121
+ gid=-1,
122
+ )
123
+
124
+ if group_name_change_outcomes:
125
+ os.chown(
126
+ group_name_change_outcomes[0].path,
127
+ uid=-1,
128
+ gid=get_gid(group_name_change_outcomes[0].new_group_name),
129
+ )
130
+
131
+ def __eq__(self, other: object) -> bool:
132
+ """Get equality based on attributes."""
133
+ if not isinstance(other, ChownItem):
134
+ return False
135
+
136
+ return (
137
+ other.path == self.path
138
+ and other.owner_name == self.owner_name
139
+ and other.group_name == self.group_name
140
+ )
@@ -0,0 +1,67 @@
1
+ """Item."""
2
+
3
+ import logging
4
+ import subprocess
5
+ from typing import List, Optional
6
+
7
+ from cyberfusion.QueueSupport.exceptions import CommandQueueFulfillFailed
8
+ from cyberfusion.QueueSupport.interfaces import OutcomeInterface
9
+ from cyberfusion.QueueSupport.items import _Item
10
+ from cyberfusion.QueueSupport.outcomes import CommandItemRunOutcome
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class CommandItem(_Item):
16
+ """Represents item."""
17
+
18
+ def __init__(
19
+ self,
20
+ *,
21
+ command: List[str],
22
+ reference: Optional[str] = None,
23
+ hide_outcomes: bool = False,
24
+ ) -> None:
25
+ """Set attributes."""
26
+ self.command = command
27
+ self._reference = reference
28
+ self._hide_outcomes = hide_outcomes
29
+
30
+ @property
31
+ def outcomes(self) -> List[OutcomeInterface]:
32
+ """Get outcomes of calling self.fulfill."""
33
+ outcomes = []
34
+
35
+ outcomes.append(CommandItemRunOutcome(command=self.command))
36
+
37
+ return outcomes
38
+
39
+ def fulfill(self) -> None:
40
+ """Fulfill outcomes."""
41
+ run_outcomes = [
42
+ x for x in self.outcomes if isinstance(x, CommandItemRunOutcome)
43
+ ]
44
+
45
+ command = run_outcomes[0].command
46
+
47
+ try:
48
+ output = subprocess.run(
49
+ command,
50
+ check=True,
51
+ text=True,
52
+ capture_output=True,
53
+ )
54
+
55
+ logger.info("Command stdout: %s", output.stdout)
56
+ logger.info("Command stderr: %s", output.stderr)
57
+ except subprocess.CalledProcessError as e:
58
+ raise CommandQueueFulfillFailed(
59
+ self, command=command, stdout=e.stdout, stderr=e.stderr
60
+ ) from e
61
+
62
+ def __eq__(self, other: object) -> bool:
63
+ """Get equality based on attributes."""
64
+ if not isinstance(other, CommandItem):
65
+ return False
66
+
67
+ return other.command == self.command
@@ -0,0 +1,61 @@
1
+ """Item."""
2
+
3
+ import logging
4
+ import os
5
+ import shutil
6
+ from typing import List, Optional
7
+
8
+ from cyberfusion.QueueSupport.exceptions import PathIsSymlinkError
9
+ from cyberfusion.QueueSupport.interfaces import OutcomeInterface
10
+ from cyberfusion.QueueSupport.items import _Item
11
+ from cyberfusion.QueueSupport.outcomes import CopyItemCopyOutcome
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class CopyItem(_Item):
17
+ """Represents item."""
18
+
19
+ def __init__(
20
+ self,
21
+ *,
22
+ source: str,
23
+ destination: str,
24
+ reference: Optional[str] = None,
25
+ hide_outcomes: bool = False,
26
+ ) -> None:
27
+ """Set attributes."""
28
+ self.source = source
29
+ self.destination = destination
30
+ self._reference = reference
31
+ self._hide_outcomes = hide_outcomes
32
+
33
+ if os.path.islink(self.source):
34
+ raise PathIsSymlinkError(self.source)
35
+
36
+ if os.path.islink(self.destination):
37
+ raise PathIsSymlinkError(self.destination)
38
+
39
+ @property
40
+ def outcomes(self) -> List[OutcomeInterface]:
41
+ """Get outcomes of calling self.fulfill."""
42
+ outcomes = []
43
+
44
+ outcomes.append(
45
+ CopyItemCopyOutcome(source=self.source, destination=self.destination)
46
+ )
47
+
48
+ return outcomes
49
+
50
+ def fulfill(self) -> None:
51
+ """Fulfill outcomes."""
52
+ copy_outcomes = [x for x in self.outcomes if isinstance(x, CopyItemCopyOutcome)]
53
+
54
+ shutil.copyfile(copy_outcomes[0].source, copy_outcomes[0].destination)
55
+
56
+ def __eq__(self, other: object) -> bool:
57
+ """Get equality based on attributes."""
58
+ if not isinstance(other, CopyItem):
59
+ return False
60
+
61
+ return other.source == self.source and other.destination == self.destination
@@ -0,0 +1,61 @@
1
+ """Item."""
2
+
3
+ import logging
4
+ import os
5
+ from typing import List, Optional
6
+
7
+ from cyberfusion.QueueSupport.exceptions import PathIsSymlinkError
8
+ from cyberfusion.QueueSupport.interfaces import OutcomeInterface
9
+ from cyberfusion.QueueSupport.items import _Item
10
+ from cyberfusion.QueueSupport.outcomes import MkdirItemCreateOutcome
11
+
12
+ logger = logging.getLogger(__name__)
13
+
14
+
15
+ class MkdirItem(_Item):
16
+ """Represents item."""
17
+
18
+ def __init__(
19
+ self,
20
+ *,
21
+ path: str,
22
+ reference: Optional[str] = None,
23
+ hide_outcomes: bool = False,
24
+ ) -> None:
25
+ """Set attributes."""
26
+ self.path = path
27
+ self._reference = reference
28
+ self._hide_outcomes = hide_outcomes
29
+
30
+ if os.path.islink(self.path):
31
+ raise PathIsSymlinkError(self.path)
32
+
33
+ @property
34
+ def outcomes(self) -> List[OutcomeInterface]:
35
+ """Get outcomes of calling self.fulfill."""
36
+ outcomes = []
37
+
38
+ if not os.path.isdir(self.path):
39
+ outcomes.append(
40
+ MkdirItemCreateOutcome(
41
+ path=self.path,
42
+ )
43
+ )
44
+
45
+ return outcomes
46
+
47
+ def fulfill(self) -> None:
48
+ """Fulfill outcomes."""
49
+ create_outcomes = [
50
+ x for x in self.outcomes if isinstance(x, MkdirItemCreateOutcome)
51
+ ]
52
+
53
+ if create_outcomes:
54
+ os.mkdir(create_outcomes[0].path)
55
+
56
+ def __eq__(self, other: object) -> bool:
57
+ """Get equality based on attributes."""
58
+ if not isinstance(other, MkdirItem):
59
+ return False
60
+
61
+ return other.path == self.path
@@ -0,0 +1,61 @@
1
+ """Item."""
2
+
3
+ import logging
4
+ import os
5
+ import shutil
6
+ from typing import List, Optional
7
+
8
+ from cyberfusion.QueueSupport.exceptions import PathIsSymlinkError
9
+ from cyberfusion.QueueSupport.interfaces import OutcomeInterface
10
+ from cyberfusion.QueueSupport.items import _Item
11
+ from cyberfusion.QueueSupport.outcomes import MoveItemMoveOutcome
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class MoveItem(_Item):
17
+ """Represents item."""
18
+
19
+ def __init__(
20
+ self,
21
+ *,
22
+ source: str,
23
+ destination: str,
24
+ reference: Optional[str] = None,
25
+ hide_outcomes: bool = False,
26
+ ) -> None:
27
+ """Set attributes."""
28
+ self.source = source
29
+ self.destination = destination
30
+ self._reference = reference
31
+ self._hide_outcomes = hide_outcomes
32
+
33
+ if os.path.islink(self.source):
34
+ raise PathIsSymlinkError(self.source)
35
+
36
+ if os.path.islink(self.destination):
37
+ raise PathIsSymlinkError(self.destination)
38
+
39
+ @property
40
+ def outcomes(self) -> List[OutcomeInterface]:
41
+ """Get outcomes of calling self.fulfill."""
42
+ outcomes = []
43
+
44
+ outcomes.append(
45
+ MoveItemMoveOutcome(source=self.source, destination=self.destination)
46
+ )
47
+
48
+ return outcomes
49
+
50
+ def fulfill(self) -> None:
51
+ """Fulfill outcomes."""
52
+ move_outcomes = [x for x in self.outcomes if isinstance(x, MoveItemMoveOutcome)]
53
+
54
+ shutil.move(move_outcomes[0].source, move_outcomes[0].destination)
55
+
56
+ def __eq__(self, other: object) -> bool:
57
+ """Get equality based on attributes."""
58
+ if not isinstance(other, MoveItem):
59
+ return False
60
+
61
+ return other.source == self.source and other.destination == self.destination
@@ -0,0 +1,55 @@
1
+ """Item."""
2
+
3
+ import logging
4
+ from typing import List, Optional
5
+
6
+ from cyberfusion.QueueSupport.interfaces import OutcomeInterface
7
+ from cyberfusion.QueueSupport.items import _Item
8
+ from cyberfusion.QueueSupport.outcomes import (
9
+ SystemdTmpFilesCreateItemCreateOutcome,
10
+ )
11
+ from cyberfusion.SystemdSupport.tmp_files import TmpFileConfigurationFile
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ class SystemdTmpFilesCreateItem(_Item):
17
+ """Represents item."""
18
+
19
+ def __init__(
20
+ self,
21
+ *,
22
+ path: str,
23
+ reference: Optional[str] = None,
24
+ hide_outcomes: bool = False,
25
+ ) -> None:
26
+ """Set attributes."""
27
+ self.path = path
28
+ self._reference = reference
29
+ self._hide_outcomes = hide_outcomes
30
+
31
+ @property
32
+ def outcomes(self) -> List[OutcomeInterface]:
33
+ """Get outcomes of calling self.fulfill."""
34
+ outcomes = []
35
+
36
+ outcomes.append(SystemdTmpFilesCreateItemCreateOutcome(path=self.path))
37
+
38
+ return outcomes
39
+
40
+ def fulfill(self) -> None:
41
+ """Fulfill outcomes."""
42
+ systemd_tmp_files_create_outcomes = [
43
+ x
44
+ for x in self.outcomes
45
+ if isinstance(x, SystemdTmpFilesCreateItemCreateOutcome)
46
+ ]
47
+
48
+ TmpFileConfigurationFile(systemd_tmp_files_create_outcomes[0].path).create()
49
+
50
+ def __eq__(self, other: object) -> bool:
51
+ """Get equality based on attributes."""
52
+ if not isinstance(other, SystemdTmpFilesCreateItem):
53
+ return False
54
+
55
+ return other.path == self.path