assemblyline-core 4.5.1.dev202__tar.gz → 4.5.1.dev203__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.

Potentially problematic release.


This version of assemblyline-core might be problematic. Click here for more details.

Files changed (88) hide show
  1. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/PKG-INFO +1 -1
  2. assemblyline-core-4.5.1.dev203/assemblyline_core/VERSION +1 -0
  3. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/scaler/scaler_server.py +8 -1
  4. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/updater/helper.py +114 -72
  5. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core.egg-info/PKG-INFO +1 -1
  6. assemblyline-core-4.5.1.dev202/assemblyline_core/VERSION +0 -1
  7. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/LICENCE.md +0 -0
  8. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/README.md +0 -0
  9. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/__init__.py +0 -0
  10. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/alerter/__init__.py +0 -0
  11. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/alerter/processing.py +0 -0
  12. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/alerter/run_alerter.py +0 -0
  13. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/archiver/__init__.py +0 -0
  14. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/archiver/run_archiver.py +0 -0
  15. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/badlist_client.py +0 -0
  16. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/dispatching/__init__.py +0 -0
  17. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/dispatching/__main__.py +0 -0
  18. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/dispatching/client.py +0 -0
  19. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/dispatching/dispatcher.py +0 -0
  20. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/dispatching/schedules.py +0 -0
  21. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/dispatching/timeout.py +0 -0
  22. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/expiry/__init__.py +0 -0
  23. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/expiry/run_expiry.py +0 -0
  24. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/ingester/__init__.py +0 -0
  25. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/ingester/__main__.py +0 -0
  26. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/ingester/constants.py +0 -0
  27. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/ingester/ingester.py +0 -0
  28. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/metrics/__init__.py +0 -0
  29. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/metrics/es_metrics.py +0 -0
  30. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/metrics/heartbeat_formatter.py +0 -0
  31. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/metrics/helper.py +0 -0
  32. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/metrics/metrics_server.py +0 -0
  33. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/metrics/run_heartbeat_manager.py +0 -0
  34. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/metrics/run_metrics_aggregator.py +0 -0
  35. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/metrics/run_statistics_aggregator.py +0 -0
  36. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/plumber/__init__.py +0 -0
  37. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/plumber/run_plumber.py +0 -0
  38. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/replay/__init__.py +0 -0
  39. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/replay/client.py +0 -0
  40. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/replay/creator/__init__.py +0 -0
  41. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/replay/creator/run.py +0 -0
  42. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/replay/creator/run_worker.py +0 -0
  43. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/replay/loader/__init__.py +0 -0
  44. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/replay/loader/run.py +0 -0
  45. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/replay/loader/run_worker.py +0 -0
  46. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/replay/replay.py +0 -0
  47. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/safelist_client.py +0 -0
  48. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/scaler/__init__.py +0 -0
  49. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/scaler/collection.py +0 -0
  50. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/scaler/controllers/__init__.py +0 -0
  51. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/scaler/controllers/docker_ctl.py +0 -0
  52. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/scaler/controllers/interface.py +0 -0
  53. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/scaler/controllers/kubernetes_ctl.py +0 -0
  54. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/scaler/run_scaler.py +0 -0
  55. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/server_base.py +0 -0
  56. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/signature_client.py +0 -0
  57. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/submission_client.py +0 -0
  58. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/tasking_client.py +0 -0
  59. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/updater/__init__.py +0 -0
  60. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/updater/run_updater.py +0 -0
  61. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/vacuum/__init__.py +0 -0
  62. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/vacuum/crawler.py +0 -0
  63. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/vacuum/department_map.py +0 -0
  64. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/vacuum/safelist.py +0 -0
  65. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/vacuum/stream_map.py +0 -0
  66. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/vacuum/worker.py +0 -0
  67. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/workflow/__init__.py +0 -0
  68. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core/workflow/run_workflow.py +0 -0
  69. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core.egg-info/SOURCES.txt +0 -0
  70. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core.egg-info/dependency_links.txt +0 -0
  71. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core.egg-info/requires.txt +0 -0
  72. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/assemblyline_core.egg-info/top_level.txt +0 -0
  73. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/setup.cfg +0 -0
  74. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/setup.py +0 -0
  75. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/test/test_alerter.py +0 -0
  76. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/test/test_badlist_client.py +0 -0
  77. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/test/test_dispatcher.py +0 -0
  78. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/test/test_expiry.py +0 -0
  79. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/test/test_plumber.py +0 -0
  80. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/test/test_replay.py +0 -0
  81. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/test/test_safelist_client.py +0 -0
  82. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/test/test_scaler.py +0 -0
  83. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/test/test_scheduler.py +0 -0
  84. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/test/test_signature_client.py +0 -0
  85. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/test/test_simulation.py +0 -0
  86. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/test/test_vacuum.py +0 -0
  87. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/test/test_worker_ingest.py +0 -0
  88. {assemblyline-core-4.5.1.dev202 → assemblyline-core-4.5.1.dev203}/test/test_worker_submit.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: assemblyline-core
3
- Version: 4.5.1.dev202
3
+ Version: 4.5.1.dev203
4
4
  Summary: Assemblyline 4 - Core components
5
5
  Home-page: https://github.com/CybercentreCanada/assemblyline-core/
6
6
  Author: CCCS Assemblyline development team
@@ -0,0 +1 @@
1
+ 4.5.1.dev203
@@ -37,6 +37,7 @@ from assemblyline.common.forge import get_classification, get_service_queue, get
37
37
  from assemblyline.common.constants import SCALER_TIMEOUT_QUEUE, SERVICE_STATE_HASH, ServiceStatus
38
38
  from assemblyline.common.version import FRAMEWORK_VERSION, SYSTEM_VERSION
39
39
  from assemblyline.common.isotime import now_as_iso
40
+ from assemblyline_core.updater.helper import get_registry_config
40
41
  from assemblyline_core.scaler.controllers import KubernetesController
41
42
  from assemblyline_core.scaler.controllers.interface import ContainerEvent, ServiceControlError
42
43
  from assemblyline_core.server_base import ServiceStage, ThreadedCoreBase
@@ -518,6 +519,12 @@ class ScalerServer(ThreadedCoreBase):
518
519
  for var in default_settings.environment:
519
520
  if var.name not in set_keys:
520
521
  docker_config.environment.append(var)
522
+
523
+ # Set authentication to registry to pull the image
524
+ auth_config = get_registry_config(docker_config, self.config)
525
+ docker_config.registry_username = auth_config['username']
526
+ docker_config.registry_password = auth_config['password']
527
+
521
528
  return docker_config
522
529
 
523
530
  # noinspection PyBroadException
@@ -537,7 +544,7 @@ class ScalerServer(ThreadedCoreBase):
537
544
  if not service.version.startswith(system_spec):
538
545
  # If FW and SYS version don't prefix in the service version, we can't guarantee the
539
546
  # service is compatible. Disable and treat it as incompatible due to service version.
540
- self.log.warning("Disabling service with incompatible version. "
547
+ self.log.warning(f"Disabling {service.name} with incompatible version. "
541
548
  f"[{service.version} != '{system_spec}.X.{service.update_channel}Y'].")
542
549
  disable_incompatible_service()
543
550
  elif service.update_config and service.update_config.wait_for_update and not service.update_config.sources:
@@ -5,13 +5,15 @@ import string
5
5
  import time
6
6
 
7
7
  from assemblyline.common.version import FRAMEWORK_VERSION, SYSTEM_VERSION
8
- from assemblyline.odm.models.config import Config as SystemConfig
9
- from assemblyline.odm.models.service import Service as ServiceConfig
8
+ from assemblyline.odm.models.config import Config as SystemConfig, ServiceRegistry
9
+ from assemblyline.odm.models.service import Service as ServiceConfig, DockerConfig
10
10
 
11
11
  from base64 import b64encode
12
12
  from collections import defaultdict
13
13
  from logging import Logger
14
+ from typing import Dict, List
14
15
  from packaging.version import parse, Version
16
+ from urllib.parse import urlencode
15
17
 
16
18
  DEFAULT_DOCKER_REGISTRY = "hub.docker.com"
17
19
 
@@ -129,9 +131,36 @@ REGISTRY_TYPE_MAPPING = {
129
131
  'harbor': HarborRegistry()
130
132
  }
131
133
 
132
-
133
- def get_latest_tag_for_service(
134
- service_config: ServiceConfig, system_config: SystemConfig, logger: Logger, prefix: str = ""):
134
+ def get_registry_config(docker_config: DockerConfig, system_config: SystemConfig) -> Dict[str, str]:
135
+ server = docker_config.image.split("/", 1)[0]
136
+
137
+ # Prioritize authentication given as a system configuration
138
+ registries: List[ServiceRegistry] = system_config.services.registries or []
139
+ for registry in registries:
140
+ if server.startswith(registry.name):
141
+ # Return authentication credentials and the type of registry
142
+ return dict(username=registry.username, password=registry.password, type=registry.type)
143
+
144
+ # Otherwise return what's configured for the service
145
+ return dict(username=docker_config.registry_username, password=docker_config.registry_password,
146
+ type=docker_config.registry_type)
147
+
148
+ def get_latest_tag_for_service(service_config: ServiceConfig, system_config: SystemConfig, logger: Logger,
149
+ prefix: str = ""):
150
+ """
151
+ Retrieves the latest compatible tag for a given service from its respective Docker registry.
152
+ This function processes the image name to determine the correct server, authenticates
153
+ with the registry, and then fetches the latest tag that matches the service's framework
154
+ and system version constraints. It handles different registry configurations including
155
+ private and public ones.
156
+ Args:
157
+ service_config (ServiceConfig): The configuration object for the service which includes Docker configuration.
158
+ system_config (SystemConfig): System-wide configuration which includes service updates and registry configurations.
159
+ logger (Logger): Logger object for logging messages.
160
+ prefix (str, optional): A prefix string to prepend to log messages for clarity in logs.
161
+ Returns:
162
+ tuple: Returns a tuple of the final image name, the latest tag, and authentication config used for the Docker registry.
163
+ """
135
164
  def process_image(image):
136
165
  # Find which server to search in
137
166
  server = image.split("/")[0]
@@ -151,10 +180,9 @@ def get_latest_tag_for_service(
151
180
 
152
181
  # Split repo name without the tag
153
182
  image_name = image_name.rsplit(":", 1)[0]
154
-
155
183
  return server, image_name
156
184
 
157
- # Extract info
185
+ # Extract and process service information
158
186
  service_name = service_config.name
159
187
  image = service_config.docker_config.image
160
188
  update_channel = service_config.update_channel
@@ -167,36 +195,23 @@ def get_latest_tag_for_service(
167
195
 
168
196
  # Get authentication
169
197
  auth = None
170
- auth_config = None
171
198
  server, image_name = process_image(searchable_image)
172
199
 
173
- if not (service_config.docker_config.registry_username and service_config.docker_config.registry_password):
174
- # If the passed in service configuration is missing registry credentials, check against system configuration
175
- for registry in system_config.services.registries:
176
- if server.startswith(registry['name']):
177
- # Apply the credentials that the system is configured to use with the registry
178
- service_config.docker_config.registry_username = registry['username']
179
- service_config.docker_config.registry_password = registry['password']
180
- service_config.docker_config.registry_type = registry['type']
181
- break
182
-
183
- if service_config.docker_config.registry_username and service_config.docker_config.registry_password:
184
- # We're authenticating using Basic Auth
185
- auth_config = {
186
- 'username': service_config.docker_config.registry_username,
187
- 'password': service_config.docker_config.registry_password
188
- }
189
- upass = f"{service_config.docker_config.registry_username}:{service_config.docker_config.registry_password}"
200
+ # Generate 'Authenication' header value for pulling tag list from registry
201
+ auth_config = get_registry_config(service_config.docker_config, system_config)
202
+ registry_type = auth_config.pop('type')
203
+ if auth_config['username'] and auth_config['password']:
204
+ upass = f"{auth_config['username']}:{auth_config['password']}"
190
205
  auth = f"Basic {b64encode(upass.encode()).decode()}"
191
- elif service_config.docker_config.registry_password:
206
+ elif auth_config['password']:
192
207
  # We're assuming that if only a password is given, then this is a token
193
- auth = f"Bearer {service_config.docker_config.registry_password}"
208
+ auth = f"Bearer {auth_config['password']}"
194
209
 
195
210
  if server.endswith(".azurecr.io"):
196
211
  # This is an Azure Container Registry based on the server name
197
212
  registry = AzureContainerRegistry()
198
213
  else:
199
- registry = REGISTRY_TYPE_MAPPING[service_config.docker_config.registry_type]
214
+ registry = REGISTRY_TYPE_MAPPING[registry_type]
200
215
  token_server = None
201
216
  proxies = None
202
217
  for reg_conf in system_config.core.updater.registry_configs:
@@ -206,31 +221,28 @@ def get_latest_tag_for_service(
206
221
  break
207
222
 
208
223
  if server == DEFAULT_DOCKER_REGISTRY:
209
- tags = _get_dockerhub_tags(image_name, update_channel, proxies)
224
+ tags = _get_dockerhub_tags(image_name, update_channel, prefix, proxies, logger=logger)
210
225
  else:
211
226
  tags = registry._get_proprietary_registry_tags(server, image_name, auth,
212
227
  not system_config.services.allow_insecure_registry,
213
228
  proxies, token_server)
214
- tag_name = None
215
229
 
216
230
  # Pre-filter tags to only consider 'compatible' tags relative to the running system
217
- tags = [t for t in tags
218
- if re.match(f"({FRAMEWORK_VERSION})\.({SYSTEM_VERSION})\.\\d+\.({update_channel})\\d+", t)]
219
-
231
+ tags = [tag for tag in tags
232
+ if re.match(f"({FRAMEWORK_VERSION})\.({SYSTEM_VERSION})\.\\d+\.({update_channel})\\d+", tag)]
220
233
  if not tags:
221
- logger.warning(f"{prefix}Cannot fetch latest tag for service {service_name} - {image_name}"
234
+ logger.warning(f"{prefix} Cannot fetch latest tag for service {service_name} - {image_name}" \
222
235
  f" => [server: {server}, repo_name: {image_name}, channel: {update_channel}]")
223
- else:
224
- for t in tags:
225
- t_version = Version(t.replace(update_channel, ""))
226
- # Tag name gets assigned to the first viable option then relies on comparison to get the latest
227
- if not tag_name:
228
- tag_name = t
229
- elif t_version.major == FRAMEWORK_VERSION and t_version.minor == SYSTEM_VERSION and \
230
- t_version > parse(tag_name.replace(update_channel, "")):
231
- tag_name = t
232
-
233
- logger.info(f"{prefix}Latest {service_name} tag on {update_channel.upper()} channel is: {tag_name}")
236
+ tag_name = None
237
+ for tag in tags:
238
+ t_version = Version(tag.replace(update_channel, ""))
239
+ # Tag name gets assigned to the first viable option then relies on comparison to get the latest
240
+ if not tag_name:
241
+ tag_name = tag
242
+ elif t_version.major == FRAMEWORK_VERSION and t_version.minor == SYSTEM_VERSION and \
243
+ t_version > parse(tag_name.replace(update_channel, "")):
244
+ tag_name = tag
245
+ logger.info(f"{prefix}Latest {service_name} tag on {update_channel.upper()} channel is: {tag_name}")
234
246
 
235
247
  # Fix service image for use in Kubernetes
236
248
  image_variables = defaultdict(str)
@@ -244,43 +256,73 @@ def get_latest_tag_for_service(
244
256
 
245
257
  return image_name, tag_name, auth_config
246
258
 
247
-
248
- # Default for obtaining tags from DockerHub
249
- def _get_dockerhub_tags(image_name, update_channel, proxies=None):
250
- # Find latest tag for each types
251
- rv = []
259
+ def _get_dockerhub_tags(image_name, update_channel, prefix, proxies=None, docker_registry=DEFAULT_DOCKER_REGISTRY,
260
+ logger: Logger=None):
261
+ """
262
+ Retrieve DockerHub tags for a specific image and update channel.
263
+ Args:
264
+ image_name (str): Full name of the image (including namespace).
265
+ update_channel (str): Specific channel to filter tags (e.g., stable, beta).
266
+ proxies (dict, optional): Proxy configuration.
267
+ docker_registry (str, optional): Docker registry URL. Defaults to DEFAULT_DOCKER_REGISTRY.
268
+ Returns:
269
+ list: A list of tag names that match the given criteria, or None if an error occurs.
270
+ """
252
271
  namespace, repository = image_name.split('/', 1)
253
- url = f"https://{DEFAULT_DOCKER_REGISTRY}/v2/namespaces/{namespace}/repositories/{repository}/tags" \
254
- f"?page_size=50&page=1&name={update_channel}&ordering=last_updated"
255
-
272
+ base_url = f"https://{docker_registry}/v2/namespaces/{namespace}/repositories/{repository}/tags"
273
+ query_params = {
274
+ 'page_size': 50,
275
+ 'page': 1,
276
+ 'name': update_channel,
277
+ 'ordering': 'last_updated'
278
+ }
279
+ url = f"{base_url}?{urlencode(query_params)}"
256
280
  while True:
257
- resp = requests.get(url, proxies=proxies)
258
- if resp.ok:
259
- resp_data = resp.json()
281
+ try:
282
+ response = requests.get(url, proxies=proxies)
283
+ response.raise_for_status() # Raises an HTTPError for bad responses
284
+ response_data = response.json()
285
+ tags = []
260
286
  if namespace == "cccs":
261
287
  # The tags are coming from a repository managed by the Assemblyline team
262
- for x in resp_data['results']:
263
- tag_name = x['name']
264
- if tag_name == f"{FRAMEWORK_VERSION}.{SYSTEM_VERSION}.{update_channel}":
288
+ for tag_info in response_data['results']:
289
+ tag_name = tag_info.get('name')
290
+ if tag_name and tag_name == f"{FRAMEWORK_VERSION}.{SYSTEM_VERSION}.{update_channel}":
265
291
  # Ignore tag aliases containing the update_channel
266
292
  continue
267
-
268
- if tag_name.startswith(f"{FRAMEWORK_VERSION}.{SYSTEM_VERSION}"):
293
+ if tag_name and tag_name.startswith(f"{FRAMEWORK_VERSION}.{SYSTEM_VERSION}"):
269
294
  # Because the order of paging is based on `last_updated`,
270
295
  # we can return the first valid candidate
296
+ logger.info(f"{prefix}Valid tag found for {repository}: {tag_name}")
271
297
  return [tag_name]
272
298
  else:
273
299
  # The tags are coming from another repository, so we're don't know the ordering of tags
274
- rv.extend([x['name'] for x in resp_data['results']])
275
- # Page until there are no results left
276
- url = resp_data.get('next', None)
277
- if url is None:
300
+ tags.extend(tag_info.get('name') for tag_info in response_data['results'] if tag_info.get('name'))
301
+ url = response_data.get('next')
302
+ if not url:
303
+ logger.info(f"{prefix}No more pages left to fetch.")
278
304
  break
279
- elif resp.status_code == 429:
280
- # Based on https://docs.docker.com/docker-hub/api/latest/#tag/rate-limiting
281
- # We've hit the rate limit so we have to wait and try again later
282
- time.sleep(int(resp.headers['retry-after']) - int(time.time()))
283
- else:
305
+ except requests.exceptions.HTTPError as e:
306
+ if response.status_code == 429:
307
+ # Based on https://docs.docker.com/docker-hub/api/latest/#tag/rate-limiting
308
+ # We've hit the rate limit so we have to wait and try again later
309
+ logger.warning(f"{prefix}Rate limit reached for DockerHub. Retrying after sleep..")
310
+ time.sleep(int(response.headers['retry-after']) - int(time.time()))
311
+ else:
312
+ logger.error(f"{prefix}HTTP error occurred: {e}")
284
313
  break
285
-
286
- return rv
314
+ except requests.exceptions.ConnectionError as e:
315
+ logger.error(f"{prefix}Connection error occurred: {e}")
316
+ break
317
+ except requests.exceptions.Timeout as e:
318
+ logger.error(f"{prefix}Request timed out.")
319
+ break
320
+ except requests.exceptions.RequestException as e:
321
+ logger.error(f"{prefix}An error occurred during requests: {e}")
322
+ break
323
+ except ValueError as e:
324
+ logger.error(f"{prefix}JSON decode error: {e}")
325
+ break
326
+ if tags:
327
+ logger.info(f"{prefix}Tags retrieved successfully: {tags}")
328
+ return tags
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: assemblyline-core
3
- Version: 4.5.1.dev202
3
+ Version: 4.5.1.dev203
4
4
  Summary: Assemblyline 4 - Core components
5
5
  Home-page: https://github.com/CybercentreCanada/assemblyline-core/
6
6
  Author: CCCS Assemblyline development team
@@ -1 +0,0 @@
1
- 4.5.1.dev202