1
1
# -*- coding: utf-8 -*-
2
2
# vim: noai:ts=4:sw=4:expandtab
3
3
4
+ import hashlib
5
+ import json
4
6
import os
5
7
import logging
6
8
import subprocess
@@ -16,6 +18,47 @@ class PodmanError(Exception):
16
18
"""
17
19
18
20
21
+ def podman_get_oci_digest (image , logger = None , podman_binary = None ):
22
+ """
23
+ Get sha256 digest of RootFS layers. This must be identical for
24
+ all images containing same order of layers, thus it can be used
25
+ as the check that we've loaded same image.
26
+ """
27
+ logger = logger or logging .getLogger ()
28
+ podman = podman_binary or "/usr/bin/podman"
29
+ logger .info ("Calculating %s image OCI digest" , image )
30
+ check = [podman , "image" , "inspect" , image ]
31
+ result = subprocess .run (check , stdout = subprocess .PIPE ,
32
+ stderr = subprocess .PIPE , check = False ,
33
+ encoding = "utf8" )
34
+ if result .returncode :
35
+ logger .error ("Can't get %s podman image digest: %s" , image , result .stderr )
36
+ return None
37
+ result = result .stdout .strip ()
38
+
39
+ try :
40
+ data = json .loads (result )[0 ]
41
+ except json .JSONDecodeError :
42
+ logger .error ("The manifest data of %s are not json-formatted." , image )
43
+ return None
44
+
45
+ if 'RootFS' not in data :
46
+ logger .error ("RootFS section of %s is missing." , image )
47
+ return None
48
+ if data ['RootFS' ]['Type' ] != 'layers' :
49
+ logger .error ("Unexpected format for RootFS in %s." , image )
50
+ return None
51
+
52
+ # data which should be sufficient to confirm the image
53
+ data = {
54
+ 'RootFS' : data ['RootFS' ],
55
+ 'Config' : data ['Config' ],
56
+ }
57
+ # convert to json string with ordered dicts and create hash
58
+ data = json .dumps (data , sort_keys = True )
59
+ return hashlib .sha256 (data .encode ()).hexdigest ()
60
+
61
+
19
62
def podman_check_native_image_architecture (image , logger = None , podman_binary = None ):
20
63
"""
21
64
Return True if image's architecture is "native" for this host.
@@ -132,26 +175,21 @@ def mounted_image(self):
132
175
subprocess .run (cmd_umount , stdout = subprocess .PIPE ,
133
176
stderr = subprocess .PIPE , check = True )
134
177
135
- def get_image_digest (self ):
178
+ def get_oci_digest (self ):
136
179
"""
137
- Get the "sha256:..." string for the image we work with.
180
+ Get sha256 digest of RootFS layers. This must be identical for
181
+ all images containing same order of layers, thus it can be used
182
+ as the check that we've loaded same image.
138
183
"""
139
184
the_image = self .image
140
185
if the_image .startswith ("oci-archive:" ):
141
186
# We can't query digest from tarball directly, but note
142
187
# the image needs to be tagged first!
143
188
the_image = self ._tagged_id
144
- check = [self .podman_binary , "image" , "inspect" , the_image ,
145
- "--format" , "{{ .Digest }}" ]
146
- result = subprocess .run (check , stdout = subprocess .PIPE ,
147
- stderr = subprocess .PIPE , check = False ,
148
- encoding = "utf8" )
149
- if result .returncode :
150
- raise PodmanError (f"Can't get { the_image } podman image digest: { result .stderr } " )
151
- result = result .stdout .strip ()
152
- if len (result .splitlines ()) != 1 :
153
- raise PodmanError (f"The digest of { the_image } image is not a single-line string" )
154
- return result
189
+ digest = podman_get_oci_digest (the_image , logger = getLog ())
190
+ if digest is None :
191
+ raise PodmanError (f"Getting OCI digest for image { self .image } failed" )
192
+ return digest
155
193
156
194
def check_native_image_architecture (self ):
157
195
"""
0 commit comments