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.
@@ -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 = 60
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", "").replace("\n", ""))
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 write_message_to_stdout(message: str):
247
- for line in GeneralUtilities.string_to_lines(message):
248
- sys.stdout.write(GeneralUtilities.str_none_safe(line)+"\n")
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
- sys.stderr.write(GeneralUtilities.str_none_safe(message)+"\n")
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