python3-cyberfusion-queue-support 2.7__tar.gz → 3.0__tar.gz

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.
Files changed (55) hide show
  1. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/PKG-INFO +10 -5
  2. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/README.md +9 -4
  3. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/pyproject.toml +2 -1
  4. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/__init__.py +16 -6
  5. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/database.py +28 -4
  6. python3_cyberfusion_queue_support-3.0/src/cyberfusion/QueueSupport/enums.py +7 -0
  7. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/__init__.py +7 -0
  8. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/chmod.py +2 -0
  9. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/chown.py +2 -0
  10. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/command.py +2 -0
  11. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/copy.py +2 -0
  12. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/database_create.py +2 -0
  13. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/database_drop.py +2 -0
  14. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/database_user_drop.py +2 -0
  15. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/database_user_ensure_state.py +6 -4
  16. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/database_user_grant_grant.py +2 -0
  17. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/database_user_grant_revoke.py +2 -0
  18. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/mkdir.py +2 -0
  19. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/move.py +2 -0
  20. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/rmtree.py +15 -0
  21. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/systemd_daemon_reload.py +2 -0
  22. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/systemd_tmp_files_create.py +2 -0
  23. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/systemd_unit_disable.py +2 -0
  24. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/systemd_unit_enable.py +2 -0
  25. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/systemd_unit_reload.py +2 -0
  26. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/systemd_unit_restart.py +2 -0
  27. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/systemd_unit_start.py +2 -0
  28. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/systemd_unit_stop.py +2 -0
  29. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/items/unlink.py +2 -0
  30. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/migrations/env.py +3 -1
  31. python3_cyberfusion_queue_support-3.0/src/cyberfusion/QueueSupport/migrations/versions/03d4e411c575_add_fail_silently.py +30 -0
  32. python3_cyberfusion_queue_support-3.0/src/cyberfusion/QueueSupport/migrations/versions/2f4316506856_add_cascade_deletes.py +67 -0
  33. python3_cyberfusion_queue_support-3.0/src/cyberfusion/QueueSupport/migrations/versions/52d1b17abcd0_add_status.py +32 -0
  34. python3_cyberfusion_queue_support-3.0/src/cyberfusion/QueueSupport/migrations/versions/8406a0af7394_set_status_on_existing_queue_processes.py +24 -0
  35. python3_cyberfusion_queue_support-3.0/src/cyberfusion/QueueSupport/scripts/__init__.py +0 -0
  36. python3_cyberfusion_queue_support-3.0/src/cyberfusion/QueueSupport/scripts/purge_queues.py +21 -0
  37. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/settings.py +2 -0
  38. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/python3_cyberfusion_queue_support.egg-info/PKG-INFO +10 -5
  39. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/python3_cyberfusion_queue_support.egg-info/SOURCES.txt +7 -0
  40. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/python3_cyberfusion_queue_support.egg-info/entry_points.txt +1 -0
  41. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/setup.cfg +0 -0
  42. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/encoders.py +0 -0
  43. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/exceptions/__init__.py +0 -0
  44. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/interfaces.py +0 -0
  45. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/migrations/__init__.py +0 -0
  46. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/migrations/versions/571e55ab5ed5_initial_migration.py +0 -0
  47. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/migrations/versions/8023b9eecdd1_add_traceback_to_queue_items.py +0 -0
  48. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/migrations/versions/9ae29b5db790_add_string_to_queue_item_outcomes.py +0 -0
  49. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/migrations/versions/__init__.py +0 -0
  50. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/outcomes.py +0 -0
  51. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/sentinels.py +0 -0
  52. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/cyberfusion/QueueSupport/utilities.py +0 -0
  53. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/python3_cyberfusion_queue_support.egg-info/dependency_links.txt +0 -0
  54. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/python3_cyberfusion_queue_support.egg-info/requires.txt +0 -0
  55. {python3_cyberfusion_queue_support-2.7 → python3_cyberfusion_queue_support-3.0}/src/python3_cyberfusion_queue_support.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python3-cyberfusion-queue-support
3
- Version: 2.7
3
+ Version: 3.0
4
4
  Summary: Library to queue actions.
5
5
  Author-email: Cyberfusion <support@cyberfusion.io>
6
6
  Project-URL: Source, https://github.com/CyberfusionIO/python3-cyberfusion-queue-support
@@ -29,16 +29,19 @@ For example, suppose the file `/tmp/example.txt` should be chmodded to 0600. You
29
29
  ... see what the chmod _would_ do with 'preview mode':
30
30
 
31
31
  ```
32
- >>> queue.process(preview=False)
32
+ >>> process, outcomes = queue.process(preview=False)
33
+ >>> outcomes
33
34
  [<cyberfusion.QueueSupport.outcomes.ChmodItemModeChangeOutcome object at 0x7f947e8ef510>]
34
- >>> print(queue.process(preview=True)[0])
35
+ >>> process, outcomes = queue.process(preview=True)
36
+ >>> print(outcomes[0])
35
37
  Change mode of /tmp/example.txt from 0o644 to 0o600
36
38
  ```
37
39
 
38
40
  ... then actually run the chmod:
39
41
 
40
42
  ```
41
- >>> print(queue.process(preview=False)[0])
43
+ >>> process, outcomes = queue.process(preview=False)
44
+ >>> print(outcomes[0])
42
45
  Change mode of /tmp/example2.txt from 0o644 to 0o600
43
46
  ```
44
47
 
@@ -172,7 +175,9 @@ queue.add(item)
172
175
 
173
176
  preview = True or False
174
177
 
175
- outcomes = queue.process(preview=preview)
178
+ process, outcomes = queue.process(preview=preview)
179
+
180
+ print(process.status)
176
181
 
177
182
  for outcome in outcomes:
178
183
  print(str(outcome))
@@ -16,16 +16,19 @@ For example, suppose the file `/tmp/example.txt` should be chmodded to 0600. You
16
16
  ... see what the chmod _would_ do with 'preview mode':
17
17
 
18
18
  ```
19
- >>> queue.process(preview=False)
19
+ >>> process, outcomes = queue.process(preview=False)
20
+ >>> outcomes
20
21
  [<cyberfusion.QueueSupport.outcomes.ChmodItemModeChangeOutcome object at 0x7f947e8ef510>]
21
- >>> print(queue.process(preview=True)[0])
22
+ >>> process, outcomes = queue.process(preview=True)
23
+ >>> print(outcomes[0])
22
24
  Change mode of /tmp/example.txt from 0o644 to 0o600
23
25
  ```
24
26
 
25
27
  ... then actually run the chmod:
26
28
 
27
29
  ```
28
- >>> print(queue.process(preview=False)[0])
30
+ >>> process, outcomes = queue.process(preview=False)
31
+ >>> print(outcomes[0])
29
32
  Change mode of /tmp/example2.txt from 0o644 to 0o600
30
33
  ```
31
34
 
@@ -159,7 +162,9 @@ queue.add(item)
159
162
 
160
163
  preview = True or False
161
164
 
162
- outcomes = queue.process(preview=preview)
165
+ process, outcomes = queue.process(preview=preview)
166
+
167
+ print(process.status)
163
168
 
164
169
  for outcome in outcomes:
165
170
  print(str(outcome))
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python3-cyberfusion-queue-support"
7
- version = "2.7"
7
+ version = "3.0"
8
8
  description = "Library to queue actions."
9
9
  readme = "README.md"
10
10
  authors = [
@@ -23,6 +23,7 @@ dependencies = [
23
23
 
24
24
  [project.scripts]
25
25
  queue-support-migrate = "cyberfusion.QueueSupport.database:run_migrations"
26
+ queue-support-purge-queues = "cyberfusion.QueueSupport.scripts.purge_queues:main"
26
27
 
27
28
  [tool.coverage.run]
28
29
  omit = [
@@ -4,6 +4,7 @@ import logging
4
4
  from dataclasses import dataclass
5
5
  from typing import List
6
6
  import traceback
7
+
7
8
  from cyberfusion.QueueSupport.database import (
8
9
  Queue as QueueModel,
9
10
  QueueItem,
@@ -11,6 +12,7 @@ from cyberfusion.QueueSupport.database import (
11
12
  QueueItemOutcome,
12
13
  QueueProcess,
13
14
  )
15
+ from cyberfusion.QueueSupport.enums import QueueProcessStatus
14
16
 
15
17
  from cyberfusion.QueueSupport.interfaces import OutcomeInterface
16
18
  from cyberfusion.QueueSupport.items import _Item
@@ -66,6 +68,7 @@ class Queue:
66
68
  type=item.__class__.__name__,
67
69
  reference=item.reference,
68
70
  hide_outcomes=item.hide_outcomes,
71
+ fail_silently=item.fail_silently,
69
72
  deduplicated=deduplicated,
70
73
  attributes=item,
71
74
  traceback=None,
@@ -75,13 +78,12 @@ class Queue:
75
78
 
76
79
  self.item_mappings.append(QueueItemMapping(item, object_))
77
80
 
78
- def process(self, preview: bool) -> List[OutcomeInterface]:
81
+ def process(self, preview: bool) -> tuple[QueueProcess, list[OutcomeInterface]]:
79
82
  """Process items."""
80
83
  logger.debug("Processing items")
81
84
 
82
85
  process_object = QueueProcess(
83
- queue_id=self.queue_database_object.id,
84
- preview=preview,
86
+ queue_id=self.queue_database_object.id, preview=preview, status=None
85
87
  )
86
88
 
87
89
  self._database_session.add(process_object)
@@ -123,9 +125,12 @@ class Queue:
123
125
 
124
126
  self._database_session.add(item_mapping.database_object)
125
127
 
126
- # Don't fulfill other queue items
128
+ if item_mapping.database_object.fail_silently:
129
+ process_object.status = QueueProcessStatus.WARNING
130
+ else:
131
+ process_object.status = QueueProcessStatus.FATAL
127
132
 
128
- break
133
+ break
129
134
 
130
135
  outcomes.extend(item_outcomes)
131
136
 
@@ -144,6 +149,11 @@ class Queue:
144
149
 
145
150
  logger.debug("Processed items")
146
151
 
152
+ if not process_object.status:
153
+ process_object.status = QueueProcessStatus.SUCCESS
154
+
155
+ self._database_session.add(process_object)
156
+
147
157
  self._database_session.commit()
148
158
 
149
- return outcomes
159
+ return process_object, outcomes
@@ -1,4 +1,5 @@
1
1
  import json
2
+ from sqlalchemy import Enum
2
3
 
3
4
  from alembic.config import Config
4
5
  import os
@@ -16,6 +17,7 @@ from sqlalchemy import event
16
17
  from sqlalchemy.types import JSON
17
18
 
18
19
  from cyberfusion.QueueSupport.encoders import CustomEncoder
20
+ from cyberfusion.QueueSupport.enums import QueueProcessStatus
19
21
  from cyberfusion.QueueSupport.settings import settings
20
22
 
21
23
 
@@ -87,10 +89,12 @@ class Queue(BaseModel):
87
89
  queue_items = relationship(
88
90
  "QueueItem",
89
91
  back_populates="queue",
92
+ cascade="all, delete",
90
93
  )
91
94
  queue_processes = relationship(
92
95
  "QueueProcess",
93
96
  back_populates="queue",
97
+ cascade="all, delete",
94
98
  )
95
99
 
96
100
 
@@ -99,13 +103,19 @@ class QueueProcess(BaseModel):
99
103
 
100
104
  __tablename__ = "queue_processes"
101
105
 
102
- queue_id = Column(Integer, ForeignKey("queues.id"), nullable=False)
106
+ queue_id = Column(
107
+ Integer,
108
+ ForeignKey("queues.id", ondelete="CASCADE"),
109
+ nullable=False,
110
+ )
103
111
  preview = Column(Boolean, nullable=False)
112
+ status = Column(Enum(QueueProcessStatus), nullable=True)
104
113
 
105
114
  queue = relationship("Queue", back_populates="queue_processes")
106
115
  queue_item_outcomes = relationship(
107
116
  "QueueItemOutcome",
108
117
  back_populates="queue_process",
118
+ cascade="all, delete",
109
119
  )
110
120
 
111
121
 
@@ -114,10 +124,15 @@ class QueueItem(BaseModel):
114
124
 
115
125
  __tablename__ = "queue_items"
116
126
 
117
- queue_id = Column(Integer, ForeignKey("queues.id"), nullable=False)
127
+ queue_id = Column(
128
+ Integer,
129
+ ForeignKey("queues.id", ondelete="CASCADE"),
130
+ nullable=False,
131
+ )
118
132
  type = Column(String(length=255), nullable=False)
119
133
  reference = Column(String(length=255), nullable=True)
120
134
  hide_outcomes = Column(Boolean, nullable=False)
135
+ fail_silently = Column(Boolean, nullable=False)
121
136
  deduplicated = Column(Boolean, nullable=False)
122
137
  attributes = Column(JSON, nullable=False)
123
138
  traceback = Column(String(), nullable=True)
@@ -126,6 +141,7 @@ class QueueItem(BaseModel):
126
141
  queue_item_outcomes = relationship(
127
142
  "QueueItemOutcome",
128
143
  back_populates="queue_item",
144
+ cascade="all, delete",
129
145
  )
130
146
 
131
147
 
@@ -134,8 +150,16 @@ class QueueItemOutcome(BaseModel):
134
150
 
135
151
  __tablename__ = "queue_item_outcomes"
136
152
 
137
- queue_item_id = Column(Integer, ForeignKey("queue_items.id"), nullable=False)
138
- queue_process_id = Column(Integer, ForeignKey("queue_processes.id"), nullable=False)
153
+ queue_item_id = Column(
154
+ Integer,
155
+ ForeignKey("queue_items.id", ondelete="CASCADE"),
156
+ nullable=False,
157
+ )
158
+ queue_process_id = Column(
159
+ Integer,
160
+ ForeignKey("queue_processes.id", ondelete="CASCADE"),
161
+ nullable=False,
162
+ )
139
163
  type = Column(String(length=255), nullable=False)
140
164
  attributes = Column(JSON, nullable=False)
141
165
  string = Column(String(length=255), nullable=False)
@@ -0,0 +1,7 @@
1
+ import enum
2
+
3
+
4
+ class QueueProcessStatus(enum.StrEnum):
5
+ SUCCESS = "success"
6
+ FATAL = "fatal"
7
+ WARNING = "warning"
@@ -18,16 +18,23 @@ class _Item(ItemInterface, ABC):
18
18
  *,
19
19
  reference: Optional[str] = None,
20
20
  hide_outcomes: bool = False,
21
+ fail_silently: bool = False,
21
22
  ) -> None: # pragma: no cover
22
23
  """Set attributes."""
23
24
  self._reference = reference
24
25
  self._hide_outcomes = hide_outcomes
26
+ self._fail_silently = fail_silently
25
27
 
26
28
  @property
27
29
  def hide_outcomes(self) -> bool:
28
30
  """Get if outcomes should be hidden."""
29
31
  return self._hide_outcomes
30
32
 
33
+ @property
34
+ def fail_silently(self) -> bool:
35
+ """Get if queue process should be stopped on exception."""
36
+ return self._fail_silently
37
+
31
38
  @property
32
39
  def reference(self) -> Optional[str]:
33
40
  """Get free-form reference, for users' own administrations."""
@@ -22,12 +22,14 @@ class ChmodItem(_Item):
22
22
  mode: int,
23
23
  reference: Optional[str] = None,
24
24
  hide_outcomes: bool = False,
25
+ fail_silently: bool = False,
25
26
  ) -> None:
26
27
  """Set attributes."""
27
28
  self.path = path
28
29
  self.mode = mode
29
30
  self._reference = reference
30
31
  self._hide_outcomes = hide_outcomes
32
+ self._fail_silently = fail_silently
31
33
 
32
34
  if os.path.islink(self.path):
33
35
  raise PathIsSymlinkError(self.path)
@@ -39,6 +39,7 @@ class ChownItem(_Item):
39
39
  group_name: str,
40
40
  reference: Optional[str] = None,
41
41
  hide_outcomes: bool = False,
42
+ fail_silently: bool = False,
42
43
  ) -> None:
43
44
  """Set attributes."""
44
45
  self.path = path
@@ -46,6 +47,7 @@ class ChownItem(_Item):
46
47
  self.group_name = group_name
47
48
  self._reference = reference
48
49
  self._hide_outcomes = hide_outcomes
50
+ self._fail_silently = fail_silently
49
51
 
50
52
  if os.path.islink(self.path):
51
53
  raise PathIsSymlinkError(self.path)
@@ -20,11 +20,13 @@ class CommandItem(_Item):
20
20
  command: List[str],
21
21
  reference: Optional[str] = None,
22
22
  hide_outcomes: bool = False,
23
+ fail_silently: bool = False,
23
24
  ) -> None:
24
25
  """Set attributes."""
25
26
  self.command = command
26
27
  self._reference = reference
27
28
  self._hide_outcomes = hide_outcomes
29
+ self._fail_silently = fail_silently
28
30
 
29
31
  @property
30
32
  def outcomes(self) -> List[CommandItemRunOutcome]:
@@ -23,12 +23,14 @@ class CopyItem(_Item):
23
23
  destination: str,
24
24
  reference: Optional[str] = None,
25
25
  hide_outcomes: bool = False,
26
+ fail_silently: bool = False,
26
27
  ) -> None:
27
28
  """Set attributes."""
28
29
  self.source = source
29
30
  self.destination = destination
30
31
  self._reference = reference
31
32
  self._hide_outcomes = hide_outcomes
33
+ self._fail_silently = fail_silently
32
34
 
33
35
  if os.path.islink(self.source):
34
36
  raise PathIsSymlinkError(self.source)
@@ -22,12 +22,14 @@ class DatabaseCreateItem(_Item):
22
22
  name: str,
23
23
  reference: Optional[str] = None,
24
24
  hide_outcomes: bool = False,
25
+ fail_silently: bool = False,
25
26
  ) -> None:
26
27
  """Set attributes."""
27
28
  self.server_software_name = server_software_name
28
29
  self.name = name
29
30
  self._reference = reference
30
31
  self._hide_outcomes = hide_outcomes
32
+ self._fail_silently = fail_silently
31
33
 
32
34
  self.database = Database(
33
35
  support=DatabaseSupport(server_software_names=[self.server_software_name]),
@@ -22,12 +22,14 @@ class DatabaseDropItem(_Item):
22
22
  name: str,
23
23
  reference: Optional[str] = None,
24
24
  hide_outcomes: bool = False,
25
+ fail_silently: bool = False,
25
26
  ) -> None:
26
27
  """Set attributes."""
27
28
  self.server_software_name = server_software_name
28
29
  self.name = name
29
30
  self._reference = reference
30
31
  self._hide_outcomes = hide_outcomes
32
+ self._fail_silently = fail_silently
31
33
 
32
34
  self.database = Database(
33
35
  support=DatabaseSupport(server_software_names=[self.server_software_name]),
@@ -27,6 +27,7 @@ class DatabaseUserDropItem(_Item):
27
27
  host: Optional[str] = None,
28
28
  reference: Optional[str] = None,
29
29
  hide_outcomes: bool = False,
30
+ fail_silently: bool = False,
30
31
  ) -> None:
31
32
  """Set attributes."""
32
33
  self.server_software_name = server_software_name
@@ -34,6 +35,7 @@ class DatabaseUserDropItem(_Item):
34
35
  self.host = host
35
36
  self._reference = reference
36
37
  self._hide_outcomes = hide_outcomes
38
+ self._fail_silently = fail_silently
37
39
 
38
40
  self.database_user = DatabaseUser(
39
41
  server=Server(
@@ -29,14 +29,16 @@ class DatabaseUserEnsureStateItem(_Item):
29
29
  host: Optional[str] = None,
30
30
  reference: Optional[str] = None,
31
31
  hide_outcomes: bool = False,
32
+ fail_silently: bool = False,
32
33
  ) -> None:
33
34
  """Set attributes."""
34
35
  self.server_software_name = server_software_name
35
36
  self.name = name
36
- self.password = password
37
+ self._password = password
37
38
  self.host = host
38
39
  self._reference = reference
39
40
  self._hide_outcomes = hide_outcomes
41
+ self._fail_silently = fail_silently
40
42
 
41
43
  self.database_user = DatabaseUser(
42
44
  server=Server(
@@ -46,7 +48,7 @@ class DatabaseUserEnsureStateItem(_Item):
46
48
  ),
47
49
  name=self.name,
48
50
  server_software_name=self.server_software_name,
49
- password=self.password,
51
+ password=self._password,
50
52
  host=self.host,
51
53
  )
52
54
 
@@ -66,7 +68,7 @@ class DatabaseUserEnsureStateItem(_Item):
66
68
  database_user=self.database_user
67
69
  )
68
70
  )
69
- elif self.database_user._get_password() != self.password:
71
+ elif self.database_user._get_password() != self._password:
70
72
  outcomes.append(
71
73
  DatabaseUserEnsureStateItemEditPasswordOutcome(
72
74
  database_user=self.database_user
@@ -96,5 +98,5 @@ class DatabaseUserEnsureStateItem(_Item):
96
98
  other.server_software_name == self.server_software_name
97
99
  and other.name == self.name
98
100
  and other.host == self.host
99
- and other.password == self.password
101
+ and other._password == self._password
100
102
  )
@@ -31,6 +31,7 @@ class DatabaseUserGrantGrantItem(_Item):
31
31
  table_name: Optional[str],
32
32
  reference: Optional[str] = None,
33
33
  hide_outcomes: bool = False,
34
+ fail_silently: bool = False,
34
35
  ) -> None:
35
36
  """Set attributes."""
36
37
  self.server_software_name = server_software_name
@@ -41,6 +42,7 @@ class DatabaseUserGrantGrantItem(_Item):
41
42
  self.table_name = table_name
42
43
  self._reference = reference
43
44
  self._hide_outcomes = hide_outcomes
45
+ self._fail_silently = fail_silently
44
46
 
45
47
  self._database = Database(
46
48
  support=DatabaseSupport(server_software_names=[self.server_software_name]),
@@ -31,6 +31,7 @@ class DatabaseUserGrantRevokeItem(_Item):
31
31
  table_name: Optional[str],
32
32
  reference: Optional[str] = None,
33
33
  hide_outcomes: bool = False,
34
+ fail_silently: bool = False,
34
35
  ) -> None:
35
36
  """Set attributes."""
36
37
  self.server_software_name = server_software_name
@@ -41,6 +42,7 @@ class DatabaseUserGrantRevokeItem(_Item):
41
42
  self.table_name = table_name
42
43
  self._reference = reference
43
44
  self._hide_outcomes = hide_outcomes
45
+ self._fail_silently = fail_silently
44
46
 
45
47
  self._database = Database(
46
48
  support=DatabaseSupport(server_software_names=[self.server_software_name]),
@@ -20,11 +20,13 @@ class MkdirItem(_Item):
20
20
  path: str,
21
21
  reference: Optional[str] = None,
22
22
  hide_outcomes: bool = False,
23
+ fail_silently: bool = False,
23
24
  ) -> None:
24
25
  """Set attributes."""
25
26
  self.path = path
26
27
  self._reference = reference
27
28
  self._hide_outcomes = hide_outcomes
29
+ self._fail_silently = fail_silently
28
30
 
29
31
  if os.path.islink(self.path):
30
32
  raise PathIsSymlinkError(self.path)
@@ -22,12 +22,14 @@ class MoveItem(_Item):
22
22
  destination: str,
23
23
  reference: Optional[str] = None,
24
24
  hide_outcomes: bool = False,
25
+ fail_silently: bool = False,
25
26
  ) -> None:
26
27
  """Set attributes."""
27
28
  self.source = source
28
29
  self.destination = destination
29
30
  self._reference = reference
30
31
  self._hide_outcomes = hide_outcomes
32
+ self._fail_silently = fail_silently
31
33
 
32
34
  if os.path.islink(self.source):
33
35
  raise PathIsSymlinkError(self.source)
@@ -3,6 +3,7 @@
3
3
  import logging
4
4
  import os
5
5
  import shutil
6
+ from pathlib import Path
6
7
  from typing import List, Optional
7
8
 
8
9
  from cyberfusion.QueueSupport.exceptions import PathIsSymlinkError
@@ -19,17 +20,31 @@ class RmTreeItem(_Item):
19
20
  self,
20
21
  *,
21
22
  path: str,
23
+ min_depth: int,
22
24
  reference: Optional[str] = None,
23
25
  hide_outcomes: bool = False,
26
+ fail_silently: bool = False,
24
27
  ) -> None:
25
28
  """Set attributes."""
26
29
  self.path = path
27
30
  self._reference = reference
28
31
  self._hide_outcomes = hide_outcomes
32
+ self._fail_silently = fail_silently
29
33
 
30
34
  if os.path.islink(self.path):
31
35
  raise PathIsSymlinkError(self.path)
32
36
 
37
+ if not os.path.isabs(path):
38
+ raise ValueError("Path must be absolute")
39
+
40
+ if min_depth < 1:
41
+ raise ValueError("min_depth must be greater than 0")
42
+
43
+ depth = len(Path(os.path.normpath(path)).parents)
44
+
45
+ if depth < min_depth:
46
+ raise ValueError(f"Path doesn't have enough depth: {depth} < {min_depth}")
47
+
33
48
  @property
34
49
  def outcomes(self) -> List[RmTreeItemRemoveOutcome]:
35
50
  """Get outcomes of calling self.fulfill."""
@@ -21,10 +21,12 @@ class SystemdDaemonReloadItem(_Item):
21
21
  *,
22
22
  reference: Optional[str] = None,
23
23
  hide_outcomes: bool = False,
24
+ fail_silently: bool = False,
24
25
  ) -> None:
25
26
  """Set attributes."""
26
27
  self._reference = reference
27
28
  self._hide_outcomes = hide_outcomes
29
+ self._fail_silently = fail_silently
28
30
 
29
31
  @property
30
32
  def outcomes(self) -> List[SystemdDaemonReloadItemReloadOutcome]:
@@ -21,11 +21,13 @@ class SystemdTmpFilesCreateItem(_Item):
21
21
  path: str,
22
22
  reference: Optional[str] = None,
23
23
  hide_outcomes: bool = False,
24
+ fail_silently: bool = False,
24
25
  ) -> None:
25
26
  """Set attributes."""
26
27
  self.path = path
27
28
  self._reference = reference
28
29
  self._hide_outcomes = hide_outcomes
30
+ self._fail_silently = fail_silently
29
31
 
30
32
  @property
31
33
  def outcomes(self) -> List[SystemdTmpFilesCreateItemCreateOutcome]:
@@ -21,11 +21,13 @@ class SystemdUnitDisableItem(_Item):
21
21
  name: str,
22
22
  reference: Optional[str] = None,
23
23
  hide_outcomes: bool = False,
24
+ fail_silently: bool = False,
24
25
  ) -> None:
25
26
  """Set attributes."""
26
27
  self.name = name
27
28
  self._reference = reference
28
29
  self._hide_outcomes = hide_outcomes
30
+ self._fail_silently = fail_silently
29
31
 
30
32
  self.unit = Unit(self.name)
31
33
 
@@ -21,11 +21,13 @@ class SystemdUnitEnableItem(_Item):
21
21
  name: str,
22
22
  reference: Optional[str] = None,
23
23
  hide_outcomes: bool = False,
24
+ fail_silently: bool = False,
24
25
  ) -> None:
25
26
  """Set attributes."""
26
27
  self.name = name
27
28
  self._reference = reference
28
29
  self._hide_outcomes = hide_outcomes
30
+ self._fail_silently = fail_silently
29
31
 
30
32
  self.unit = Unit(self.name)
31
33
 
@@ -21,11 +21,13 @@ class SystemdUnitReloadItem(_Item):
21
21
  name: str,
22
22
  reference: Optional[str] = None,
23
23
  hide_outcomes: bool = False,
24
+ fail_silently: bool = False,
24
25
  ) -> None:
25
26
  """Set attributes."""
26
27
  self.name = name
27
28
  self._reference = reference
28
29
  self._hide_outcomes = hide_outcomes
30
+ self._fail_silently = fail_silently
29
31
 
30
32
  self.unit = Unit(self.name)
31
33
 
@@ -21,11 +21,13 @@ class SystemdUnitRestartItem(_Item):
21
21
  name: str,
22
22
  reference: Optional[str] = None,
23
23
  hide_outcomes: bool = False,
24
+ fail_silently: bool = False,
24
25
  ) -> None:
25
26
  """Set attributes."""
26
27
  self.name = name
27
28
  self._reference = reference
28
29
  self._hide_outcomes = hide_outcomes
30
+ self._fail_silently = fail_silently
29
31
 
30
32
  self.unit = Unit(self.name)
31
33
 
@@ -21,11 +21,13 @@ class SystemdUnitStartItem(_Item):
21
21
  name: str,
22
22
  reference: Optional[str] = None,
23
23
  hide_outcomes: bool = False,
24
+ fail_silently: bool = False,
24
25
  ) -> None:
25
26
  """Set attributes."""
26
27
  self.name = name
27
28
  self._reference = reference
28
29
  self._hide_outcomes = hide_outcomes
30
+ self._fail_silently = fail_silently
29
31
 
30
32
  self.unit = Unit(self.name)
31
33
 
@@ -19,11 +19,13 @@ class SystemdUnitStopItem(_Item):
19
19
  name: str,
20
20
  reference: Optional[str] = None,
21
21
  hide_outcomes: bool = False,
22
+ fail_silently: bool = False,
22
23
  ) -> None:
23
24
  """Set attributes."""
24
25
  self.name = name
25
26
  self._reference = reference
26
27
  self._hide_outcomes = hide_outcomes
28
+ self._fail_silently = fail_silently
27
29
 
28
30
  self.unit = Unit(self.name)
29
31
 
@@ -20,11 +20,13 @@ class UnlinkItem(_Item):
20
20
  path: str,
21
21
  reference: Optional[str] = None,
22
22
  hide_outcomes: bool = False,
23
+ fail_silently: bool = False,
23
24
  ) -> None:
24
25
  """Set attributes."""
25
26
  self.path = path
26
27
  self._reference = reference
27
28
  self._hide_outcomes = hide_outcomes
29
+ self._fail_silently = fail_silently
28
30
 
29
31
  if os.path.islink(self.path):
30
32
  raise PathIsSymlinkError(self.path)
@@ -37,7 +37,9 @@ def run_migrations_online() -> None:
37
37
  )
38
38
 
39
39
  with connectable.connect() as connection:
40
- context.configure(connection=connection, target_metadata=target_metadata)
40
+ context.configure(
41
+ connection=connection, target_metadata=target_metadata, render_as_batch=True
42
+ )
41
43
 
42
44
  with context.begin_transaction():
43
45
  context.run_migrations()
@@ -0,0 +1,30 @@
1
+ """Add fail_silently
2
+
3
+ Revision ID: 03d4e411c575
4
+ Revises: 8406a0af7394
5
+ Create Date: 2025-08-27 00:44:17.788937
6
+
7
+ """
8
+
9
+ from alembic import op
10
+ import sqlalchemy as sa
11
+
12
+
13
+ # revision identifiers, used by Alembic.
14
+ revision = "03d4e411c575"
15
+ down_revision = "8406a0af7394"
16
+ branch_labels = None
17
+ depends_on = None
18
+
19
+
20
+ def upgrade() -> None:
21
+ op.add_column(
22
+ "queue_items",
23
+ sa.Column(
24
+ "fail_silently", sa.Boolean(), server_default=sa.text("0"), nullable=False
25
+ ),
26
+ )
27
+
28
+
29
+ def downgrade() -> None:
30
+ op.drop_column("queue_items", "fail_silently")
@@ -0,0 +1,67 @@
1
+ """Add cascade deletes
2
+
3
+ Revision ID: 2f4316506856
4
+ Revises: 03d4e411c575
5
+ Create Date: 2025-08-28 16:31:42.976092
6
+
7
+ """
8
+
9
+ from alembic import op
10
+
11
+
12
+ # revision identifiers, used by Alembic.
13
+ revision = "2f4316506856"
14
+ down_revision = "03d4e411c575"
15
+ branch_labels = None
16
+ depends_on = None
17
+
18
+
19
+ def upgrade() -> None:
20
+ with op.batch_alter_table("queue_item_outcomes", schema=None) as batch_op:
21
+ batch_op.drop_constraint(
22
+ "fk_queue_item_outcomes_queue_process_id_queue_processes",
23
+ type_="foreignkey",
24
+ )
25
+ batch_op.drop_constraint(
26
+ "fk_queue_item_outcomes_queue_item_id_queue_items", type_="foreignkey"
27
+ )
28
+ batch_op.create_foreign_key(
29
+ batch_op.f("fk_queue_item_outcomes_queue_item_id_queue_items"),
30
+ "queue_items",
31
+ ["queue_item_id"],
32
+ ["id"],
33
+ ondelete="CASCADE",
34
+ )
35
+ batch_op.create_foreign_key(
36
+ batch_op.f("fk_queue_item_outcomes_queue_process_id_queue_processes"),
37
+ "queue_processes",
38
+ ["queue_process_id"],
39
+ ["id"],
40
+ ondelete="CASCADE",
41
+ )
42
+
43
+ with op.batch_alter_table("queue_items", schema=None) as batch_op:
44
+ batch_op.drop_constraint("fk_queue_items_queue_id_queues", type_="foreignkey")
45
+ batch_op.create_foreign_key(
46
+ batch_op.f("fk_queue_items_queue_id_queues"),
47
+ "queues",
48
+ ["queue_id"],
49
+ ["id"],
50
+ ondelete="CASCADE",
51
+ )
52
+
53
+ with op.batch_alter_table("queue_processes", schema=None) as batch_op:
54
+ batch_op.drop_constraint(
55
+ "fk_queue_processes_queue_id_queues", type_="foreignkey"
56
+ )
57
+ batch_op.create_foreign_key(
58
+ batch_op.f("fk_queue_processes_queue_id_queues"),
59
+ "queues",
60
+ ["queue_id"],
61
+ ["id"],
62
+ ondelete="CASCADE",
63
+ )
64
+
65
+
66
+ def downgrade() -> None:
67
+ pass
@@ -0,0 +1,32 @@
1
+ """Add status
2
+
3
+ Revision ID: 52d1b17abcd0
4
+ Revises: 8023b9eecdd1
5
+ Create Date: 2025-08-28 11:48:46.168058
6
+
7
+ """
8
+
9
+ from alembic import op
10
+ import sqlalchemy as sa
11
+
12
+
13
+ # revision identifiers, used by Alembic.
14
+ revision = "52d1b17abcd0"
15
+ down_revision = "8023b9eecdd1"
16
+ branch_labels = None
17
+ depends_on = None
18
+
19
+
20
+ def upgrade() -> None:
21
+ op.add_column(
22
+ "queue_processes",
23
+ sa.Column(
24
+ "status",
25
+ sa.Enum("SUCCESS", "FATAL", "WARNING", name="queueprocessstatus"),
26
+ nullable=True,
27
+ ),
28
+ )
29
+
30
+
31
+ def downgrade() -> None:
32
+ op.drop_column("queue_processes", "status")
@@ -0,0 +1,24 @@
1
+ """Set status on existing queue processes
2
+
3
+ Revision ID: 8406a0af7394
4
+ Revises: 52d1b17abcd0
5
+ Create Date: 2025-08-28 11:56:22.861704
6
+
7
+ """
8
+
9
+ from alembic import op
10
+
11
+
12
+ # revision identifiers, used by Alembic.
13
+ revision = "8406a0af7394"
14
+ down_revision = "52d1b17abcd0"
15
+ branch_labels = None
16
+ depends_on = None
17
+
18
+
19
+ def upgrade() -> None:
20
+ op.execute("UPDATE queue_processes SET status = 'SUCCESS'")
21
+
22
+
23
+ def downgrade() -> None:
24
+ pass
@@ -0,0 +1,21 @@
1
+ import datetime
2
+
3
+ from cyberfusion.QueueSupport.database import make_database_session, Queue
4
+ from cyberfusion.QueueSupport.settings import settings
5
+
6
+
7
+ def main() -> None:
8
+ database_session = make_database_session()
9
+
10
+ purge_before_date = datetime.datetime.now() - datetime.timedelta(
11
+ days=settings.queue_purge_days
12
+ )
13
+
14
+ queues = (
15
+ database_session.query(Queue).filter(Queue.created_at < purge_before_date).all()
16
+ )
17
+
18
+ for queue in queues:
19
+ database_session.delete(queue)
20
+
21
+ database_session.commit()
@@ -4,6 +4,8 @@ from pydantic import BaseSettings
4
4
  class Settings(BaseSettings):
5
5
  database_path: str = "sqlite:///./queue-support.db"
6
6
 
7
+ queue_purge_days: int = 30
8
+
7
9
  class Config:
8
10
  env_prefix = "queue_support_"
9
11
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python3-cyberfusion-queue-support
3
- Version: 2.7
3
+ Version: 3.0
4
4
  Summary: Library to queue actions.
5
5
  Author-email: Cyberfusion <support@cyberfusion.io>
6
6
  Project-URL: Source, https://github.com/CyberfusionIO/python3-cyberfusion-queue-support
@@ -29,16 +29,19 @@ For example, suppose the file `/tmp/example.txt` should be chmodded to 0600. You
29
29
  ... see what the chmod _would_ do with 'preview mode':
30
30
 
31
31
  ```
32
- >>> queue.process(preview=False)
32
+ >>> process, outcomes = queue.process(preview=False)
33
+ >>> outcomes
33
34
  [<cyberfusion.QueueSupport.outcomes.ChmodItemModeChangeOutcome object at 0x7f947e8ef510>]
34
- >>> print(queue.process(preview=True)[0])
35
+ >>> process, outcomes = queue.process(preview=True)
36
+ >>> print(outcomes[0])
35
37
  Change mode of /tmp/example.txt from 0o644 to 0o600
36
38
  ```
37
39
 
38
40
  ... then actually run the chmod:
39
41
 
40
42
  ```
41
- >>> print(queue.process(preview=False)[0])
43
+ >>> process, outcomes = queue.process(preview=False)
44
+ >>> print(outcomes[0])
42
45
  Change mode of /tmp/example2.txt from 0o644 to 0o600
43
46
  ```
44
47
 
@@ -172,7 +175,9 @@ queue.add(item)
172
175
 
173
176
  preview = True or False
174
177
 
175
- outcomes = queue.process(preview=preview)
178
+ process, outcomes = queue.process(preview=preview)
179
+
180
+ print(process.status)
176
181
 
177
182
  for outcome in outcomes:
178
183
  print(str(outcome))
@@ -4,6 +4,7 @@ setup.cfg
4
4
  src/cyberfusion/QueueSupport/__init__.py
5
5
  src/cyberfusion/QueueSupport/database.py
6
6
  src/cyberfusion/QueueSupport/encoders.py
7
+ src/cyberfusion/QueueSupport/enums.py
7
8
  src/cyberfusion/QueueSupport/interfaces.py
8
9
  src/cyberfusion/QueueSupport/outcomes.py
9
10
  src/cyberfusion/QueueSupport/sentinels.py
@@ -35,10 +36,16 @@ src/cyberfusion/QueueSupport/items/systemd_unit_stop.py
35
36
  src/cyberfusion/QueueSupport/items/unlink.py
36
37
  src/cyberfusion/QueueSupport/migrations/__init__.py
37
38
  src/cyberfusion/QueueSupport/migrations/env.py
39
+ src/cyberfusion/QueueSupport/migrations/versions/03d4e411c575_add_fail_silently.py
40
+ src/cyberfusion/QueueSupport/migrations/versions/2f4316506856_add_cascade_deletes.py
41
+ src/cyberfusion/QueueSupport/migrations/versions/52d1b17abcd0_add_status.py
38
42
  src/cyberfusion/QueueSupport/migrations/versions/571e55ab5ed5_initial_migration.py
39
43
  src/cyberfusion/QueueSupport/migrations/versions/8023b9eecdd1_add_traceback_to_queue_items.py
44
+ src/cyberfusion/QueueSupport/migrations/versions/8406a0af7394_set_status_on_existing_queue_processes.py
40
45
  src/cyberfusion/QueueSupport/migrations/versions/9ae29b5db790_add_string_to_queue_item_outcomes.py
41
46
  src/cyberfusion/QueueSupport/migrations/versions/__init__.py
47
+ src/cyberfusion/QueueSupport/scripts/__init__.py
48
+ src/cyberfusion/QueueSupport/scripts/purge_queues.py
42
49
  src/python3_cyberfusion_queue_support.egg-info/PKG-INFO
43
50
  src/python3_cyberfusion_queue_support.egg-info/SOURCES.txt
44
51
  src/python3_cyberfusion_queue_support.egg-info/dependency_links.txt
@@ -1,2 +1,3 @@
1
1
  [console_scripts]
2
2
  queue-support-migrate = cyberfusion.QueueSupport.database:run_migrations
3
+ queue-support-purge-queues = cyberfusion.QueueSupport.scripts.purge_queues:main