3737CONTAINER_STATS_CACHE_TTL_SECONDS = 10
3838DISK_USAGE_CACHE = {}
3939DISK_USAGE_CACHE_TTL_SECONDS = 300
40+ IMAGE_VERSION_CACHE = {}
41+ IMAGE_VERSION_CACHE_TTL_SECONDS = 3600
4042DISK_USAGE_INFLIGHT = set ()
4143DISK_USAGE_INFLIGHT_LOCK = threading .Lock ()
4244
@@ -80,6 +82,22 @@ def set_cached_disk_usage(host_id, data):
8082 DISK_USAGE_CACHE [host_id ] = {'timestamp' : time .time (), 'data' : data }
8183
8284
85+ def get_cached_image_version (cache_key ):
86+ entry = IMAGE_VERSION_CACHE .get (cache_key )
87+ if not entry :
88+ return None
89+ if time .time () - entry ['timestamp' ] > IMAGE_VERSION_CACHE_TTL_SECONDS :
90+ return None
91+ return entry ['data' ]
92+
93+
94+ def set_cached_image_version (cache_key , version , source ):
95+ IMAGE_VERSION_CACHE [cache_key ] = {
96+ 'timestamp' : time .time (),
97+ 'data' : (version , source )
98+ }
99+
100+
83101def refresh_disk_usage_async (host_id , docker_client ):
84102 def run ():
85103 try :
@@ -204,6 +222,18 @@ def strip_image_tag(image_name):
204222
205223 return f"{ prefix } /{ last } " if prefix else last
206224
225+ def _extract_version_from_labels (labels , source ):
226+ for key , value in (labels or {}).items ():
227+ if not value :
228+ continue
229+ key_lower = key .lower ()
230+ if key_lower in VERSION_LABEL_KEYS or 'version' in key_lower :
231+ match = VERSION_IN_TEXT_RE .search (str (value ))
232+ if match :
233+ return match .group (0 ), source
234+ return None , None
235+
236+
207237def get_image_version (container , image_name , docker_client = None ):
208238 """Best-effort version from image labels, falling back to version-like tags."""
209239 labels = {}
@@ -214,10 +244,9 @@ def get_image_version(container, image_name, docker_client=None):
214244 except Exception :
215245 labels = {}
216246
217- for key in VERSION_LABEL_KEYS :
218- value = labels .get (key )
219- if value :
220- return value .strip (), 'label'
247+ version , source = _extract_version_from_labels (labels , 'label' )
248+ if version :
249+ return version , source
221250
222251 ref_name = labels .get ('org.opencontainers.image.ref.name' , '' )
223252 if ref_name :
@@ -244,10 +273,9 @@ def get_image_version(container, image_name, docker_client=None):
244273 image_id = container .image .id if container .image else None
245274 image_obj = docker_client .images .get (image_id ) if image_id else None
246275 image_labels = image_obj .attrs .get ('Config' , {}).get ('Labels' , {}) if image_obj else {}
247- for key in VERSION_LABEL_KEYS :
248- value = (image_labels or {}).get (key )
249- if value :
250- return value .strip (), 'label'
276+ version , source = _extract_version_from_labels (image_labels , 'label' )
277+ if version :
278+ return version , source
251279 ref_name = (image_labels or {}).get ('org.opencontainers.image.ref.name' , '' )
252280 if ref_name :
253281 match = VERSION_IN_TEXT_RE .search (ref_name )
@@ -278,6 +306,20 @@ def get_image_version(container, image_name, docker_client=None):
278306 if match :
279307 return match .group (0 ), 'tag'
280308
309+ if docker_client and image_name and ':' in image_name :
310+ cache_key = image_name
311+ cached = get_cached_image_version (cache_key )
312+ if cached :
313+ return cached
314+ try :
315+ registry_data = docker_client .images .get_registry_data (image_name )
316+ registry_version = get_registry_version (registry_data )
317+ if registry_version and registry_version != 'unknown' :
318+ set_cached_image_version (cache_key , registry_version , 'registry' )
319+ return registry_version , 'registry'
320+ except Exception :
321+ pass
322+
281323 return 'unknown' , 'unknown'
282324
283325def get_registry_version (registry_data ):
0 commit comments