forked from release-engineering/pubtools-pulplib
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmaintenance.py
223 lines (174 loc) · 7.36 KB
/
maintenance.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
import logging
import datetime
import os
import jsonschema
from frozenlist2 import frozenlist
from pubtools.pulplib._impl import compat_attr as attr
from .attr import pulp_attrib
from .convert import read_timestamp, write_timestamp
from ..schema import load_schema
from .common import InvalidDataException
LOG = logging.getLogger("pubtools.pulplib")
USER = os.environ.get("USER")
HOSTNAME = os.environ.get("HOSTNAME")
@attr.s(kw_only=True, frozen=True)
class MaintenanceEntry(object):
"""Details about the maintenance status of a specific repository.
.. versionadded:: 1.4.0
"""
repo_id = pulp_attrib(type=str)
"""ID of repository in maintenance.
Note: there is no guarantee that a repository of this ID currently exists
in the Pulp server."""
message = pulp_attrib(default=None, type=str)
"""Why this repository is in maintenance."""
owner = pulp_attrib(default=None, type=str)
"""Who set this repository in maintenance mode."""
started = pulp_attrib(default=None, type=datetime.datetime)
""":class:`~datetime.datetime` in UTC at when the maintenance started."""
@attr.s(kw_only=True, frozen=True)
class MaintenanceReport(object):
"""Represents the maintenance status of Pulp repositories.
On release-engineering Pulp servers, it's possible to put individual repositories
into "maintenance mode". When in maintenance mode, external publishes of a repository
will be blocked. Other operations remain possible.
This object holds information on the set of repositories currently in maintenance mode.
.. versionadded:: 1.4.0
"""
_OWNER = "%s@%s" % (USER, HOSTNAME) if all([USER, HOSTNAME]) else "pubtools.pulplib"
_SCHEMA = load_schema("maintenance")
last_updated = pulp_attrib(default=None, type=datetime.datetime)
""":class:`~datetime.datetime` in UTC when this report was last updated,
if it's the first time the report is created, current time is used."""
last_updated_by = pulp_attrib(default=None, type=str)
"""Person/party who updated the report last time."""
entries = pulp_attrib(
default=attr.Factory(frozenlist), type=list, converter=frozenlist
)
"""A list of :class:`MaintenanceEntry` objects, indicating
which repositories are in maintenance mode and details.
If empty, then it means no repositories are in maintenance mode.
"""
@entries.validator
def _check_duplicates(self, _, value):
# check if there's duplicate entries
repo_ids = [entry.repo_id for entry in value]
if len(repo_ids) != len(set(repo_ids)):
raise ValueError("Duplicate entries")
@classmethod
def _from_data(cls, data):
"""Create a new report with raw data
Args:
data (dict):
A dict containing a raw representation of the maintenance status.
Returns:
a new instance of ``cls``
Raises:
InvalidDataException
If the provided ``data`` fails validation against an expected schema.
"""
try:
jsonschema.validate(instance=data, schema=cls._SCHEMA)
except jsonschema.exceptions.ValidationError as error:
LOG.exception("%s.from_data invoked with invalid data", cls.__name__)
raise InvalidDataException(str(error))
entries = []
for repo_id, details in data["repos"].items():
entries.append(
MaintenanceEntry(
repo_id=repo_id,
message=details["message"],
owner=details["owner"],
started=read_timestamp(details["started"]),
)
)
maintenance = cls(
last_updated=read_timestamp(data["last_updated"]),
last_updated_by=data["last_updated_by"],
entries=entries,
)
return maintenance
def _export_dict(self):
"""export a raw dictionary of maintenance report"""
report = {
"last_updated": write_timestamp(self.last_updated),
"last_updated_by": self.last_updated_by or self._OWNER,
"repos": {},
}
for entry in self.entries:
report["repos"].update(
{
entry.repo_id: {
"message": entry.message,
"owner": entry.owner,
"started": write_timestamp(entry.started),
}
}
)
return report
def add(self, repo_ids, **kwargs):
"""Add entries to maintenance report and update the timestamp. Every
entry added to the report represents a repository in maintenance mode.
Args:
repo_ids (list[str]):
A list of repository ids. New entries with these repository ids will
be added to the maintenance report.
Note: it's users' responsibility to make sure the repository exists in
the Pulp server, this method doesn't check for the existence of repositories.
message (str) (optional):
Reason why put the repo to maintenance.
owner (str) (optional):
Who set the maintenance mode.
Returns:
:class:`~pubtools.pulplib.MaintenanceReport`
A copy of this maintenance report with added repositories.
"""
message = kwargs.get("message") or "Maintenance mode is enabled"
owner = kwargs.get("owner") or self._OWNER
to_add = []
for repo in repo_ids:
to_add.append(
MaintenanceEntry(
repo_id=repo,
owner=owner,
message=message,
started=datetime.datetime.utcnow(),
)
)
entries = list(self.entries)
entries.extend(to_add)
# filter out duplicated entries. Filtering is in reverse order, which
# means existed entries will be replaced by newer ones with same repo_id
filtered_entries = []
entry_ids = set()
for entry in reversed(entries):
if entry.repo_id not in entry_ids:
filtered_entries.append(entry)
entry_ids.add(entry.repo_id)
return attr.evolve(
self,
entries=filtered_entries,
last_updated_by=owner,
last_updated=datetime.datetime.utcnow(),
)
def remove(self, repo_ids, **kwargs):
"""Remove entries from the maintenance report. Remove entries means the
removing corresponding repositories from maintenance mode.
Args:
repo_ids (list[str]):
A list of repository ids. Entries match repository ids will be removed
from the maintenance report.
owner (str) (optional):
Who unset the maintenance mode.
Returns:
:class:`~pubtools.pulplib.MaintenanceReport`
A copy of this maintenance report with removed repositories.
"""
owner = kwargs.get("owner") or self._OWNER
repo_ids = set(repo_ids)
# convert to set, make checking faster
new_entries = []
for entry in self.entries:
if entry.repo_id not in repo_ids:
new_entries.append(entry)
return attr.evolve(self, last_updated_by=owner, entries=new_entries)