ScriptCollection 3.5.119__py3-none-any.whl → 3.5.121__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.
- ScriptCollection/CertificateUpdater.py +5 -2
- ScriptCollection/GeneralUtilities.py +56 -11
- ScriptCollection/ImageUpdater.py +477 -0
- ScriptCollection/SCLog.py +34 -20
- ScriptCollection/ScriptCollectionCore.py +47 -130
- ScriptCollection/TasksForCommonProjectStructure.py +31 -29
- {scriptcollection-3.5.119.dist-info → scriptcollection-3.5.121.dist-info}/METADATA +1 -1
- scriptcollection-3.5.121.dist-info/RECORD +17 -0
- {scriptcollection-3.5.119.dist-info → scriptcollection-3.5.121.dist-info}/WHEEL +1 -1
- scriptcollection-3.5.119.dist-info/RECORD +0 -16
- {scriptcollection-3.5.119.dist-info → scriptcollection-3.5.121.dist-info}/entry_points.txt +0 -0
- {scriptcollection-3.5.119.dist-info → scriptcollection-3.5.121.dist-info}/top_level.txt +0 -0
@@ -24,7 +24,7 @@ class CertificateUpdater:
|
|
24
24
|
|
25
25
|
def __init__(self, domains: list[str], email: str, current_file: str, arguments: list[str]):
|
26
26
|
self.__sc = ScriptCollectionCore()
|
27
|
-
self.maximal_age_of_certificates_in_days =
|
27
|
+
self.maximal_age_of_certificates_in_days = 15
|
28
28
|
self.__domains = domains
|
29
29
|
self.__email = email
|
30
30
|
self.__current_folder = current_file
|
@@ -126,7 +126,7 @@ class CertificateUpdater:
|
|
126
126
|
def __get_last_certificate_update_date(self) -> datetime:
|
127
127
|
if os.path.exists(self.__last_update_timestamp_file):
|
128
128
|
filecontent = GeneralUtilities.read_text_from_file(self.__last_update_timestamp_file)
|
129
|
-
return GeneralUtilities.string_to_datetime(filecontent.replace("\r",
|
129
|
+
return GeneralUtilities.string_to_datetime(filecontent.replace("\r", GeneralUtilities.empty_string).replace("\n", GeneralUtilities.empty_string))
|
130
130
|
else:
|
131
131
|
return datetime(year=1970, month=1, day=1)
|
132
132
|
|
@@ -142,5 +142,8 @@ class CertificateUpdater:
|
|
142
142
|
args = parser.parse_args(self.__arguments)
|
143
143
|
now = datetime.now()
|
144
144
|
if (self.__get_last_certificate_update_date()+timedelta(days=self.maximal_age_of_certificates_in_days)) < now or args.force:
|
145
|
+
GeneralUtilities.write_message_to_stdout(f"Update certificates...")
|
145
146
|
self.__update_certificates()
|
146
147
|
self.__set_last_certificate_update_date(now)
|
148
|
+
else:
|
149
|
+
GeneralUtilities.write_message_to_stdout(f"Certificates are already up to date.")
|
@@ -38,6 +38,8 @@ class GeneralUtilities:
|
|
38
38
|
__datetime_format: str = "%Y-%m-%dT%H:%M:%S"
|
39
39
|
__date_format: str = "%Y-%m-%d"
|
40
40
|
|
41
|
+
empty_string: str = ""
|
42
|
+
|
41
43
|
@staticmethod
|
42
44
|
def get_modest_dark_url() -> str:
|
43
45
|
return "https://aniondev.github.io/CDN/ScriptCollectionDesigns/ModestDark/Style.css"
|
@@ -46,6 +48,10 @@ class GeneralUtilities:
|
|
46
48
|
def is_generic(t: typing.Type):
|
47
49
|
return hasattr(t, "__origin__")
|
48
50
|
|
51
|
+
@staticmethod
|
52
|
+
def is_debugger_attached():
|
53
|
+
return sys.gettrace() is not None
|
54
|
+
|
49
55
|
@staticmethod
|
50
56
|
def check_arguments(function):
|
51
57
|
def __check_function(*args, **named_args):
|
@@ -234,6 +240,32 @@ class GeneralUtilities:
|
|
234
240
|
text = GeneralUtilities.replace_underscores_in_text(text, replacements)
|
235
241
|
GeneralUtilities.write_text_to_file(file, text, encoding)
|
236
242
|
|
243
|
+
@staticmethod
|
244
|
+
def print_text(text: str, print_to_stdout: bool = True):
|
245
|
+
GeneralUtilities.__print_text_to_console(text, print_to_stdout)
|
246
|
+
|
247
|
+
@staticmethod
|
248
|
+
def print_text_in_green(text: str, print_to_stdout: bool = True):
|
249
|
+
GeneralUtilities.__print_text_to_console(f"\033[32m{text}\033[0m", print_to_stdout)
|
250
|
+
|
251
|
+
@staticmethod
|
252
|
+
def print_text_in_yellow(text: str, print_to_stdout: bool = True):
|
253
|
+
GeneralUtilities.__print_text_to_console(f"\033[33m{text}\033[0m", print_to_stdout)
|
254
|
+
|
255
|
+
@staticmethod
|
256
|
+
def print_text_in_red(text: str, print_to_stdout: bool = True):
|
257
|
+
GeneralUtilities.__print_text_to_console(f"\033[31m{text}\033[0m", print_to_stdout)
|
258
|
+
|
259
|
+
@staticmethod
|
260
|
+
def print_text_in_cyan(text: str, print_to_stdout: bool = True):
|
261
|
+
GeneralUtilities.__print_text_to_console(f"\033[36m{text}\033[0m", print_to_stdout)
|
262
|
+
|
263
|
+
@staticmethod
|
264
|
+
def __print_text_to_console(text: str, print_to_stdout: bool = True):
|
265
|
+
output = sys.stdout if print_to_stdout else sys.stderr
|
266
|
+
output.write(text)
|
267
|
+
output.flush()
|
268
|
+
|
237
269
|
@staticmethod
|
238
270
|
@check_arguments
|
239
271
|
def reconfigure_standrd_input_and_outputs():
|
@@ -243,16 +275,29 @@ class GeneralUtilities:
|
|
243
275
|
|
244
276
|
@staticmethod
|
245
277
|
@check_arguments
|
246
|
-
def
|
247
|
-
|
248
|
-
|
278
|
+
def write_message_to_stdout_advanced(message: str, add_empty_lines: bool = True, adapt_lines: bool = True, append_linebreak: bool = True):
|
279
|
+
new_line_character: str = "\n" if append_linebreak else ""
|
280
|
+
for line in GeneralUtilities.string_to_lines(message, add_empty_lines, adapt_lines):
|
281
|
+
sys.stdout.write(GeneralUtilities.str_none_safe(line)+new_line_character)
|
249
282
|
sys.stdout.flush()
|
250
283
|
|
284
|
+
@staticmethod
|
285
|
+
@check_arguments
|
286
|
+
def write_message_to_stdout(message: str):
|
287
|
+
GeneralUtilities.write_message_to_stdout_advanced(message, True, True, True)
|
288
|
+
|
289
|
+
@staticmethod
|
290
|
+
@check_arguments
|
291
|
+
def write_message_to_stderr_advanced(message: str, add_empty_lines: bool = True, adapt_lines: bool = True, append_linebreak: bool = True):
|
292
|
+
new_line_character: str = "\n" if append_linebreak else ""
|
293
|
+
for line in GeneralUtilities.string_to_lines(message, add_empty_lines, adapt_lines):
|
294
|
+
sys.stderr.write(GeneralUtilities.str_none_safe(line)+new_line_character)
|
295
|
+
sys.stderr.flush()
|
296
|
+
|
251
297
|
@staticmethod
|
252
298
|
@check_arguments
|
253
299
|
def write_message_to_stderr(message: str):
|
254
|
-
|
255
|
-
sys.stderr.flush()
|
300
|
+
GeneralUtilities.write_message_to_stderr_advanced(message, True, True, True)
|
256
301
|
|
257
302
|
@staticmethod
|
258
303
|
@check_arguments
|
@@ -260,7 +305,7 @@ class GeneralUtilities:
|
|
260
305
|
if GeneralUtilities.string_has_content(os_error.filename2):
|
261
306
|
secondpath = f" {os_error.filename2}"
|
262
307
|
else:
|
263
|
-
secondpath =
|
308
|
+
secondpath = GeneralUtilities.empty_string
|
264
309
|
return f"Related path(s): {os_error.filename}{secondpath}"
|
265
310
|
|
266
311
|
@staticmethod
|
@@ -315,7 +360,7 @@ class GeneralUtilities:
|
|
315
360
|
return True
|
316
361
|
type_of_argument = type(argument)
|
317
362
|
if type_of_argument == str:
|
318
|
-
return argument ==
|
363
|
+
return argument == GeneralUtilities.empty_string
|
319
364
|
else:
|
320
365
|
raise ValueError(f"expected string-variable in argument of string_is_none_or_empty but the type was '{str(type_of_argument)}'")
|
321
366
|
|
@@ -325,7 +370,7 @@ class GeneralUtilities:
|
|
325
370
|
if GeneralUtilities.string_is_none_or_empty(string):
|
326
371
|
return True
|
327
372
|
else:
|
328
|
-
return string.strip() ==
|
373
|
+
return string.strip() == GeneralUtilities.empty_string
|
329
374
|
|
330
375
|
@staticmethod
|
331
376
|
@check_arguments
|
@@ -378,7 +423,7 @@ class GeneralUtilities:
|
|
378
423
|
if GeneralUtilities.file_ends_with_content(file):
|
379
424
|
return "\n"
|
380
425
|
else:
|
381
|
-
return
|
426
|
+
return GeneralUtilities.empty_string
|
382
427
|
|
383
428
|
@staticmethod
|
384
429
|
@check_arguments
|
@@ -861,7 +906,7 @@ class GeneralUtilities:
|
|
861
906
|
result = list()
|
862
907
|
if list_as_string is not None:
|
863
908
|
list_as_string = list_as_string.strip()
|
864
|
-
if list_as_string ==
|
909
|
+
if list_as_string == GeneralUtilities.empty_string:
|
865
910
|
pass
|
866
911
|
elif separator in list_as_string:
|
867
912
|
for item in list_as_string.split(separator):
|
@@ -922,7 +967,7 @@ class GeneralUtilities:
|
|
922
967
|
if positive:
|
923
968
|
return "✅"
|
924
969
|
else:
|
925
|
-
return
|
970
|
+
return GeneralUtilities.empty_string
|
926
971
|
|
927
972
|
@staticmethod
|
928
973
|
@check_arguments
|
@@ -0,0 +1,477 @@
|
|
1
|
+
from abc import abstractmethod
|
2
|
+
from enum import Enum
|
3
|
+
import json
|
4
|
+
import re
|
5
|
+
from urllib.parse import quote
|
6
|
+
import yaml
|
7
|
+
import requests
|
8
|
+
from packaging import version as ve
|
9
|
+
from packaging.version import Version
|
10
|
+
from .GeneralUtilities import GeneralUtilities
|
11
|
+
|
12
|
+
|
13
|
+
class VersionEcholon(Enum):
|
14
|
+
Patch = 0
|
15
|
+
LatestPatchOrLatestMinor = 1
|
16
|
+
LatestPatchOrLatestMinorOrNextMajor = 2
|
17
|
+
Newest = 3
|
18
|
+
|
19
|
+
|
20
|
+
class ImageUpdaterHelper:
|
21
|
+
|
22
|
+
@staticmethod
|
23
|
+
@GeneralUtilities.check_arguments
|
24
|
+
def _internal_filter_for_major_and_minor_versions(versions: list[Version], major: int, minor: int) -> Version:
|
25
|
+
return [v for v in versions if v.major == major and v.minor == minor]
|
26
|
+
|
27
|
+
@staticmethod
|
28
|
+
@GeneralUtilities.check_arguments
|
29
|
+
def _internal_filter_for_major_versions(versions: list[Version], major: int) -> Version:
|
30
|
+
return [v for v in versions if v.major == major]
|
31
|
+
|
32
|
+
@staticmethod
|
33
|
+
@GeneralUtilities.check_arguments
|
34
|
+
def _internal_get_latest_patch_version(newer_versions: list[Version], current_version: Version) -> Version:
|
35
|
+
candidates = ImageUpdaterHelper._internal_filter_for_major_and_minor_versions(newer_versions, current_version.major, current_version.minor)
|
36
|
+
if len(candidates) == 0:
|
37
|
+
return current_version
|
38
|
+
result = ImageUpdaterHelper.get_latest_version(candidates)
|
39
|
+
return result
|
40
|
+
|
41
|
+
@staticmethod
|
42
|
+
@GeneralUtilities.check_arguments
|
43
|
+
def _internal_get_latest_patch_or_latest_minor_version(newer_versions: list[Version], current_version: Version) -> Version:
|
44
|
+
candidates = ImageUpdaterHelper._internal_filter_for_major_versions(newer_versions, current_version.major)
|
45
|
+
if len(candidates) == 0:
|
46
|
+
return current_version
|
47
|
+
result = ImageUpdaterHelper.get_latest_version(candidates)
|
48
|
+
return result
|
49
|
+
|
50
|
+
@staticmethod
|
51
|
+
@GeneralUtilities.check_arguments
|
52
|
+
def _internal_get_latest_patch_or_latest_minor_or_next_major_version(newer_versions: list[Version], current_version: Version) -> Version:
|
53
|
+
candidates = ImageUpdaterHelper._internal_filter_for_major_versions(newer_versions, current_version.major+1)
|
54
|
+
if 0 < len(candidates):
|
55
|
+
result = ImageUpdaterHelper.get_latest_version(candidates)
|
56
|
+
return result
|
57
|
+
else:
|
58
|
+
candidates = ImageUpdaterHelper._internal_filter_for_major_versions(newer_versions, current_version.major)
|
59
|
+
if len(candidates) == 0:
|
60
|
+
return current_version
|
61
|
+
result = ImageUpdaterHelper.get_latest_version(candidates)
|
62
|
+
return result
|
63
|
+
|
64
|
+
@staticmethod
|
65
|
+
@GeneralUtilities.check_arguments
|
66
|
+
def filter_considering_echolog(newer_versions: list[Version], current_version: Version, version_echolon: VersionEcholon) -> Version:
|
67
|
+
if version_echolon == VersionEcholon.Patch:
|
68
|
+
return ImageUpdaterHelper._internal_get_latest_patch_version(newer_versions, current_version)
|
69
|
+
elif version_echolon == VersionEcholon.LatestPatchOrLatestMinor:
|
70
|
+
return ImageUpdaterHelper._internal_get_latest_patch_or_latest_minor_version(newer_versions, current_version)
|
71
|
+
elif version_echolon == VersionEcholon.LatestPatchOrLatestMinorOrNextMajor:
|
72
|
+
return ImageUpdaterHelper._internal_get_latest_patch_or_latest_minor_or_next_major_version(newer_versions, current_version)
|
73
|
+
elif version_echolon == VersionEcholon.Newest:
|
74
|
+
return ImageUpdaterHelper.get_latest_version(newer_versions)
|
75
|
+
else:
|
76
|
+
raise ValueError(f"Unknown version-echolon")
|
77
|
+
|
78
|
+
@staticmethod
|
79
|
+
@GeneralUtilities.check_arguments
|
80
|
+
def get_latest_version(versions: list[Version]) -> Version:
|
81
|
+
result = max(versions)
|
82
|
+
return result
|
83
|
+
|
84
|
+
@staticmethod
|
85
|
+
@GeneralUtilities.check_arguments
|
86
|
+
def get_latest_version_from_versiontrings(version_strings: list[str]) -> str:
|
87
|
+
parsed = [ve.parse(v) for v in version_strings]
|
88
|
+
result = max(parsed)
|
89
|
+
return str(result)
|
90
|
+
|
91
|
+
@staticmethod
|
92
|
+
@GeneralUtilities.check_arguments
|
93
|
+
def filter_for_newer_versions(comparison_version: Version, versions_to_filter: list[Version]) -> list[Version]:
|
94
|
+
result = [v for v in versions_to_filter if comparison_version < v]
|
95
|
+
return result
|
96
|
+
|
97
|
+
@staticmethod
|
98
|
+
@GeneralUtilities.check_arguments
|
99
|
+
def get_versions_in_docker_hub(image: str, search_string: str, filter_regex: str, maximal_amount_of_items_to_load: int = 250) -> list[Version]:
|
100
|
+
if "/" not in image:
|
101
|
+
image = f"library/{image}"
|
102
|
+
response = requests.get(f"https://hub.docker.com/v2/repositories/{quote(image)}/tags?name={quote(search_string)}&ordering=last_updated&page=1&page_size={str(maximal_amount_of_items_to_load)}", timeout=20, headers={'Cache-Control': 'no-cache'})
|
103
|
+
if response.status_code != 200:
|
104
|
+
raise ValueError(f"Failed to fetch data from Docker Hub: {response.status_code}")
|
105
|
+
response_text = response.text
|
106
|
+
data = json.loads(response_text)
|
107
|
+
tags: list[str] = [tag["name"] for tag in data["results"] if re.match(filter_regex, tag["name"])]
|
108
|
+
versions = [tag.split("-")[0] for tag in tags]
|
109
|
+
result = [ve.parse(v) for v in versions]
|
110
|
+
return result
|
111
|
+
|
112
|
+
|
113
|
+
class ConcreteImageUpdater:
|
114
|
+
|
115
|
+
@abstractmethod
|
116
|
+
@GeneralUtilities.check_arguments
|
117
|
+
def version_to_tag(self, version: Version) -> str:
|
118
|
+
raise NotImplementedError
|
119
|
+
|
120
|
+
@abstractmethod
|
121
|
+
@GeneralUtilities.check_arguments
|
122
|
+
def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
|
123
|
+
raise NotImplementedError
|
124
|
+
|
125
|
+
@abstractmethod
|
126
|
+
@GeneralUtilities.check_arguments
|
127
|
+
def get_supported_images(self) -> list[str]:
|
128
|
+
raise NotImplementedError
|
129
|
+
|
130
|
+
@abstractmethod
|
131
|
+
@GeneralUtilities.check_arguments
|
132
|
+
def get_version_from_tag(self, image: str, tag: str) -> Version:
|
133
|
+
raise NotImplementedError
|
134
|
+
|
135
|
+
|
136
|
+
class ConcreteImageUpdaterForNginx(ConcreteImageUpdater):
|
137
|
+
|
138
|
+
@GeneralUtilities.check_arguments
|
139
|
+
def version_to_tag(self, version: Version) -> str:
|
140
|
+
raise NotImplementedError
|
141
|
+
|
142
|
+
@GeneralUtilities.check_arguments
|
143
|
+
def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
|
144
|
+
raise NotImplementedError
|
145
|
+
|
146
|
+
@GeneralUtilities.check_arguments
|
147
|
+
def get_supported_images(self) -> list[str]:
|
148
|
+
return ["nginx"]
|
149
|
+
|
150
|
+
@GeneralUtilities.check_arguments
|
151
|
+
def get_version_from_tag(self, image: str, tag: str) -> Version:
|
152
|
+
raise NotImplementedError
|
153
|
+
|
154
|
+
|
155
|
+
class ConcreteImageUpdaterForGitLab(ConcreteImageUpdater):
|
156
|
+
|
157
|
+
@GeneralUtilities.check_arguments
|
158
|
+
def version_to_tag(self, version: Version) -> str:
|
159
|
+
raise NotImplementedError
|
160
|
+
|
161
|
+
@GeneralUtilities.check_arguments
|
162
|
+
def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
|
163
|
+
raise NotImplementedError
|
164
|
+
|
165
|
+
@GeneralUtilities.check_arguments
|
166
|
+
def get_supported_images(self) -> list[str]:
|
167
|
+
return ["gitlab/gitlab-ce", "gitlab/gitlab-ee"]
|
168
|
+
|
169
|
+
@GeneralUtilities.check_arguments
|
170
|
+
def get_version_from_tag(self, image: str, tag: str) -> Version:
|
171
|
+
raise NotImplementedError
|
172
|
+
|
173
|
+
|
174
|
+
class ConcreteImageUpdaterForRegistry(ConcreteImageUpdater):
|
175
|
+
|
176
|
+
@GeneralUtilities.check_arguments
|
177
|
+
def version_to_tag(self, version: Version) -> str:
|
178
|
+
raise NotImplementedError
|
179
|
+
|
180
|
+
@GeneralUtilities.check_arguments
|
181
|
+
def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
|
182
|
+
raise NotImplementedError
|
183
|
+
|
184
|
+
@abstractmethod
|
185
|
+
@GeneralUtilities.check_arguments
|
186
|
+
def get_supported_images(self) -> list[str]:
|
187
|
+
return [] # TODO
|
188
|
+
|
189
|
+
@GeneralUtilities.check_arguments
|
190
|
+
def get_version_from_tag(self, image: str, tag: str) -> Version:
|
191
|
+
raise NotImplementedError
|
192
|
+
|
193
|
+
|
194
|
+
class ConcreteImageUpdaterForPrometheus(ConcreteImageUpdater):
|
195
|
+
|
196
|
+
@GeneralUtilities.check_arguments
|
197
|
+
def version_to_tag(self, version: Version) -> str:
|
198
|
+
raise NotImplementedError
|
199
|
+
|
200
|
+
@GeneralUtilities.check_arguments
|
201
|
+
def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
|
202
|
+
raise NotImplementedError
|
203
|
+
|
204
|
+
@GeneralUtilities.check_arguments
|
205
|
+
def get_supported_images(self) -> list[str]:
|
206
|
+
return [] # TODO
|
207
|
+
|
208
|
+
@GeneralUtilities.check_arguments
|
209
|
+
def get_version_from_tag(self, image: str, tag: str) -> Version:
|
210
|
+
raise NotImplementedError
|
211
|
+
|
212
|
+
|
213
|
+
class ConcreteImageUpdaterForPrometheusBlackboxExporter(ConcreteImageUpdater):
|
214
|
+
|
215
|
+
@GeneralUtilities.check_arguments
|
216
|
+
def version_to_tag(self, version: Version) -> str:
|
217
|
+
raise NotImplementedError
|
218
|
+
|
219
|
+
@GeneralUtilities.check_arguments
|
220
|
+
def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
|
221
|
+
raise NotImplementedError
|
222
|
+
|
223
|
+
@GeneralUtilities.check_arguments
|
224
|
+
def get_supported_images(self) -> list[str]:
|
225
|
+
return [] # TODO
|
226
|
+
|
227
|
+
@GeneralUtilities.check_arguments
|
228
|
+
def get_version_from_tag(self, image: str, tag: str) -> Version:
|
229
|
+
raise NotImplementedError
|
230
|
+
|
231
|
+
|
232
|
+
class ConcreteImageUpdaterForPrometheusNginxExporter(ConcreteImageUpdater):
|
233
|
+
|
234
|
+
@GeneralUtilities.check_arguments
|
235
|
+
def version_to_tag(self, version: Version) -> str:
|
236
|
+
raise NotImplementedError
|
237
|
+
|
238
|
+
@GeneralUtilities.check_arguments
|
239
|
+
def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
|
240
|
+
raise NotImplementedError
|
241
|
+
|
242
|
+
@GeneralUtilities.check_arguments
|
243
|
+
def get_supported_images(self) -> list[str]:
|
244
|
+
return [] # TODO
|
245
|
+
|
246
|
+
@GeneralUtilities.check_arguments
|
247
|
+
def get_version_from_tag(self, image: str, tag: str) -> Version:
|
248
|
+
raise NotImplementedError
|
249
|
+
|
250
|
+
|
251
|
+
class ConcreteImageUpdaterForPrometheusNodeExporter(ConcreteImageUpdater):
|
252
|
+
|
253
|
+
@GeneralUtilities.check_arguments
|
254
|
+
def version_to_tag(self, version: Version) -> str:
|
255
|
+
raise NotImplementedError
|
256
|
+
|
257
|
+
@GeneralUtilities.check_arguments
|
258
|
+
def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
|
259
|
+
raise NotImplementedError
|
260
|
+
|
261
|
+
@GeneralUtilities.check_arguments
|
262
|
+
def get_supported_images(self) -> list[str]:
|
263
|
+
return [] # TODO
|
264
|
+
|
265
|
+
@GeneralUtilities.check_arguments
|
266
|
+
def get_version_from_tag(self, image: str, tag: str) -> Version:
|
267
|
+
raise NotImplementedError
|
268
|
+
|
269
|
+
|
270
|
+
class ConcreteImageUpdaterForKeycloak(ConcreteImageUpdater):
|
271
|
+
|
272
|
+
@GeneralUtilities.check_arguments
|
273
|
+
def version_to_tag(self, version: Version) -> str:
|
274
|
+
raise NotImplementedError
|
275
|
+
|
276
|
+
@GeneralUtilities.check_arguments
|
277
|
+
def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
|
278
|
+
raise NotImplementedError
|
279
|
+
|
280
|
+
@GeneralUtilities.check_arguments
|
281
|
+
def get_supported_images(self) -> list[str]:
|
282
|
+
return [] # TODO
|
283
|
+
|
284
|
+
@GeneralUtilities.check_arguments
|
285
|
+
def get_version_from_tag(self, image: str, tag: str) -> Version:
|
286
|
+
raise NotImplementedError
|
287
|
+
|
288
|
+
|
289
|
+
class ConcreteImageUpdaterForMariaDB(ConcreteImageUpdater):
|
290
|
+
|
291
|
+
@GeneralUtilities.check_arguments
|
292
|
+
def version_to_tag(self, version: Version) -> str:
|
293
|
+
return f"{version.major}.{version.minor}.{version.micro}"
|
294
|
+
|
295
|
+
@GeneralUtilities.check_arguments
|
296
|
+
def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
|
297
|
+
versions = ImageUpdaterHelper.get_versions_in_docker_hub(image, ".", "^\\d+\\.\\d+\\.\\d+$", 999)
|
298
|
+
newer_versions = ImageUpdaterHelper.filter_for_newer_versions(current_version, versions)
|
299
|
+
result = ImageUpdaterHelper.filter_considering_echolog(newer_versions, current_version, version_echolon)
|
300
|
+
return result
|
301
|
+
|
302
|
+
@GeneralUtilities.check_arguments
|
303
|
+
def get_supported_images(self) -> list[str]:
|
304
|
+
return ["mariadb"]
|
305
|
+
|
306
|
+
@GeneralUtilities.check_arguments
|
307
|
+
def get_version_from_tag(self, image: str, tag: str) -> Version:
|
308
|
+
return ve.parse(tag)
|
309
|
+
|
310
|
+
|
311
|
+
class ConcreteImageUpdaterForPostgreSQL(ConcreteImageUpdater):
|
312
|
+
|
313
|
+
@GeneralUtilities.check_arguments
|
314
|
+
def version_to_tag(self, version: Version) -> str:
|
315
|
+
raise NotImplementedError
|
316
|
+
|
317
|
+
@GeneralUtilities.check_arguments
|
318
|
+
def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
|
319
|
+
raise NotImplementedError
|
320
|
+
|
321
|
+
@GeneralUtilities.check_arguments
|
322
|
+
def get_supported_images(self) -> list[str]:
|
323
|
+
return [] # TODO
|
324
|
+
|
325
|
+
@GeneralUtilities.check_arguments
|
326
|
+
def get_version_from_tag(self, image: str, tag: str) -> Version:
|
327
|
+
raise NotImplementedError
|
328
|
+
|
329
|
+
|
330
|
+
class ConcreteImageUpdaterForAdminer(ConcreteImageUpdater):
|
331
|
+
|
332
|
+
@GeneralUtilities.check_arguments
|
333
|
+
def version_to_tag(self, version: Version) -> str:
|
334
|
+
return f"{version.major}.{version.minor}.{version.micro}"
|
335
|
+
|
336
|
+
@GeneralUtilities.check_arguments
|
337
|
+
def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> Version:
|
338
|
+
versions = ImageUpdaterHelper.get_versions_in_docker_hub(image, ".", "^\\d+\\.\\d+\\.\\d+$", 999)
|
339
|
+
newer_versions = ImageUpdaterHelper.filter_for_newer_versions(current_version, versions)
|
340
|
+
result = ImageUpdaterHelper.filter_considering_echolog(newer_versions, current_version, version_echolon)
|
341
|
+
return result
|
342
|
+
|
343
|
+
@GeneralUtilities.check_arguments
|
344
|
+
def get_supported_images(self) -> list[str]:
|
345
|
+
return ["adminer"]
|
346
|
+
|
347
|
+
@GeneralUtilities.check_arguments
|
348
|
+
def get_version_from_tag(self, image: str, tag: str) -> Version:
|
349
|
+
return ve.parse(tag)
|
350
|
+
|
351
|
+
|
352
|
+
class ImageUpdater:
|
353
|
+
|
354
|
+
updater: list[ConcreteImageUpdater] = None
|
355
|
+
|
356
|
+
def __init__(self):
|
357
|
+
self.updater = list[ConcreteImageUpdater]()
|
358
|
+
|
359
|
+
def add_default_mapper(self) -> None:
|
360
|
+
self.updater.append(ConcreteImageUpdaterForNginx())
|
361
|
+
self.updater.append(ConcreteImageUpdaterForGitLab())
|
362
|
+
self.updater.append(ConcreteImageUpdaterForRegistry())
|
363
|
+
self.updater.append(ConcreteImageUpdaterForPrometheus())
|
364
|
+
self.updater.append(ConcreteImageUpdaterForPrometheusBlackboxExporter())
|
365
|
+
self.updater.append(ConcreteImageUpdaterForPrometheusNginxExporter())
|
366
|
+
self.updater.append(ConcreteImageUpdaterForPrometheusNodeExporter())
|
367
|
+
self.updater.append(ConcreteImageUpdaterForKeycloak())
|
368
|
+
self.updater.append(ConcreteImageUpdaterForMariaDB())
|
369
|
+
self.updater.append(ConcreteImageUpdaterForPostgreSQL())
|
370
|
+
self.updater.append(ConcreteImageUpdaterForAdminer())
|
371
|
+
|
372
|
+
@GeneralUtilities.check_arguments
|
373
|
+
def check_service_for_newest_version(self, dockercompose_file: str, service_name: str) -> bool:
|
374
|
+
imagename, existing_tag, existing_version = self.get_current_version_of_service_from_docker_compose_file(dockercompose_file, service_name) # pylint:disable=unused-variable
|
375
|
+
newest_version, newest_tag = self.get_latest_version_of_image(imagename, VersionEcholon.Newest, existing_version) # pylint:disable=unused-variable
|
376
|
+
if existing_version < newest_version:
|
377
|
+
GeneralUtilities.write_message_to_stdout(f"Service {service_name} with image {imagename} uses tag {existing_version}. The newest available version of this image is {newest_version}.")
|
378
|
+
return True
|
379
|
+
else:
|
380
|
+
return False
|
381
|
+
|
382
|
+
@GeneralUtilities.check_arguments
|
383
|
+
def check_for_newest_version(self, dockercompose_file: str, excluded_services: list[str] = []) -> bool:
|
384
|
+
all_services = self.get_services_from_docker_compose_file(dockercompose_file)
|
385
|
+
services_to_check = [service for service in all_services if service not in all_services]
|
386
|
+
newer_version_available: bool = False
|
387
|
+
for service_to_check in services_to_check:
|
388
|
+
if self.check_service_for_newest_version(dockercompose_file, service_to_check):
|
389
|
+
newer_version_available = True
|
390
|
+
return newer_version_available
|
391
|
+
|
392
|
+
@GeneralUtilities.check_arguments
|
393
|
+
def update_all_services_in_docker_compose_file(self, dockercompose_file: str, version_echolon: VersionEcholon, except_services: list[str] = []):
|
394
|
+
all_services = self.get_services_from_docker_compose_file(dockercompose_file)
|
395
|
+
services_to_update = [service for service in all_services if service not in except_services]
|
396
|
+
self.update_services_in_docker_compose_file(dockercompose_file, services_to_update, version_echolon)
|
397
|
+
|
398
|
+
@GeneralUtilities.check_arguments
|
399
|
+
def update_services_in_docker_compose_file(self, dockercompose_file: str, service_names: list[str], version_echolon: VersionEcholon):
|
400
|
+
for service_name in service_names:
|
401
|
+
if self.service_has_image_information(dockercompose_file, service_name):
|
402
|
+
self.update_service_in_docker_compose_file(dockercompose_file, service_name, version_echolon)
|
403
|
+
|
404
|
+
@GeneralUtilities.check_arguments
|
405
|
+
def service_has_image_information(self, dockercompose_file: str, service_name: str) -> bool:
|
406
|
+
with open(dockercompose_file, 'r', encoding="utf-8") as file:
|
407
|
+
compose_data = yaml.safe_load(file)
|
408
|
+
service = compose_data.get('services', {}).get(service_name, {})
|
409
|
+
image = service.get('image', None)
|
410
|
+
return image is not None
|
411
|
+
|
412
|
+
@GeneralUtilities.check_arguments
|
413
|
+
def update_service_in_docker_compose_file(self, dockercompose_file: str, service_name: str, version_echolon: VersionEcholon):
|
414
|
+
imagename, existing_tag, existing_version = self.get_current_version_of_service_from_docker_compose_file(dockercompose_file, service_name) # pylint:disable=unused-variable
|
415
|
+
result = self.get_latest_version_of_image(imagename, version_echolon, existing_version)
|
416
|
+
newest_version = result[0]
|
417
|
+
newest_tag = result[1]
|
418
|
+
if existing_version < newest_version:
|
419
|
+
|
420
|
+
with open(dockercompose_file, 'r', encoding="utf-8") as f:
|
421
|
+
compose_data = yaml.safe_load(f)
|
422
|
+
|
423
|
+
services = compose_data.get("services", {})
|
424
|
+
if service_name not in services:
|
425
|
+
raise ValueError(f"Service '{service_name}' not found.")
|
426
|
+
|
427
|
+
image = services[service_name].get("image")
|
428
|
+
if not image:
|
429
|
+
raise ValueError(f"Service '{service_name}' does not have an image-field.")
|
430
|
+
|
431
|
+
imagename = image.split(":")[0]
|
432
|
+
services[service_name]["image"] = imagename+":"+newest_tag
|
433
|
+
|
434
|
+
with open(dockercompose_file, 'w', encoding="utf-8") as f:
|
435
|
+
yaml.dump(compose_data, f, default_flow_style=False)
|
436
|
+
|
437
|
+
@GeneralUtilities.check_arguments
|
438
|
+
def get_current_version_of_service_from_docker_compose_file(self, dockercompose_file: str, service_name: str) -> tuple[str, str, Version]: # returns (image,existing_tag,existing_version)
|
439
|
+
with open(dockercompose_file, 'r', encoding="utf-8") as file:
|
440
|
+
compose_data = yaml.safe_load(file)
|
441
|
+
service = compose_data.get('services', {}).get(service_name, {})
|
442
|
+
image = str(service.get('image', None))
|
443
|
+
if image:
|
444
|
+
if ':' in image:
|
445
|
+
name, tag = image.rsplit(':', 1)
|
446
|
+
else:
|
447
|
+
name, tag = image, 'latest'
|
448
|
+
return name, tag, self.get_docker_version_from_tag(name, tag)
|
449
|
+
else:
|
450
|
+
raise ValueError(f"Service '{service_name}' in '{dockercompose_file}'")
|
451
|
+
|
452
|
+
@GeneralUtilities.check_arguments
|
453
|
+
def __get_updater_for_image(self, image: str) -> str:
|
454
|
+
for updater in self.updater:
|
455
|
+
if image in updater.get_supported_images():
|
456
|
+
return updater
|
457
|
+
raise ValueError(f"No updater available for image '{image}'")
|
458
|
+
|
459
|
+
@GeneralUtilities.check_arguments
|
460
|
+
def get_docker_version_from_tag(self, image: str, tag: str) -> Version:
|
461
|
+
updater: ImageUpdater = self.__get_updater_for_image(image)
|
462
|
+
return updater.get_version_from_tag(image, tag)
|
463
|
+
|
464
|
+
@GeneralUtilities.check_arguments
|
465
|
+
def get_latest_version_of_image(self, image: str, version_echolon: VersionEcholon, current_version: Version) -> tuple[Version, str]:
|
466
|
+
updater: ImageUpdater = self.__get_updater_for_image(image)
|
467
|
+
newest_version: Version = updater.get_latest_version_of_image(image, version_echolon, current_version)
|
468
|
+
newest_tag: str = updater.version_to_tag(newest_version)
|
469
|
+
return (newest_version, newest_tag)
|
470
|
+
|
471
|
+
@GeneralUtilities.check_arguments
|
472
|
+
def get_services_from_docker_compose_file(self, dockercompose_file: str) -> list[str]:
|
473
|
+
with open(dockercompose_file, 'r', encoding="utf-8") as f:
|
474
|
+
compose_data = yaml.safe_load(f)
|
475
|
+
services = compose_data.get('services', {})
|
476
|
+
result = list(services.keys())
|
477
|
+
return result
|