@@ -44,17 +44,19 @@ class Image(object):
4444 FORMAT = None
4545 """ Image format version """
4646
47- def __init__ (self , log , docker , image , from_layer , tmp_dir = None , tag = None ):
47+ def __init__ (self , log , docker , image , from_layer , tmp_dir = None , tag = None , rebase = None ):
4848 self .log = log
4949 self .debug = self .log .isEnabledFor (logging .DEBUG )
5050 self .docker = docker
5151 self .image = image
5252 self .from_layer = from_layer
5353 self .tag = tag
54+ self .rebase = rebase
5455 self .image_name = None
5556 self .image_tag = None
5657 self .squash_id = None
5758
59+
5860 # Workaround for https://play.golang.org/p/sCsWMXYxqy
5961 #
6062 # Golang doesn't add padding to microseconds when marshaling
@@ -71,6 +73,7 @@ def __init__(self, log, docker, image, from_layer, tmp_dir=None, tag=None):
7173
7274 def squash (self ):
7375 self ._before_squashing ()
76+ self .log .info ("Squashing image '%s'..." % self .image )
7477 ret = self ._squash ()
7578 self ._after_squashing ()
7679
@@ -94,12 +97,14 @@ def _initialize_directories(self):
9497
9598 # Temporary location on the disk of the old, unpacked *image*
9699 self .old_image_dir = os .path .join (self .tmp_dir , "old" )
100+ # Temporary location on the disk of the rebase, unpacked *image*
101+ self .rebase_image_dir = os .path .join (self .tmp_dir , "rebase" )
97102 # Temporary location on the disk of the new, unpacked, squashed *image*
98103 self .new_image_dir = os .path .join (self .tmp_dir , "new" )
99104 # Temporary location on the disk of the squashed *layer*
100105 self .squashed_dir = os .path .join (self .new_image_dir , "squashed" )
101106
102- for d in self .old_image_dir , self .new_image_dir :
107+ for d in self .old_image_dir , self .new_image_dir , self . rebase_image_dir :
103108 os .makedirs (d )
104109
105110 def _squash_id (self , layer ):
@@ -152,8 +157,16 @@ def _before_squashing(self):
152157 try :
153158 self .old_image_id = self .docker .inspect_image (self .image )['Id' ]
154159 except SquashError :
155- raise SquashError (
156- "Could not get the image ID to squash, please check provided 'image' argument: %s" % self .image )
160+ raise SquashError ("Could not get the image ID to squash, "
161+ "please check provided 'image' argument: %s" % self .image )
162+
163+ if self .rebase :
164+ # The image id or name of the image to rebase to
165+ try :
166+ self .rebase = self .docker .inspect_image (self .rebase )['Id' ]
167+ except SquashError :
168+ raise SquashError ("Could not get the image ID to rebase to, "
169+ "please check provided 'rebase' argument: %s" % self .rebase )
157170
158171 self .old_image_layers = []
159172
@@ -166,32 +179,36 @@ def _before_squashing(self):
166179 self .log .debug ("Old layers: %s" , self .old_image_layers )
167180
168181 # By default - squash all layers.
169- if self .from_layer == None :
182+ if self .from_layer is None :
170183 self .from_layer = len (self .old_image_layers )
171184
172185 try :
173186 number_of_layers = int (self .from_layer )
174187
175- self .log .debug (
176- "We detected number of layers as the argument to squash" )
188+ self .log .debug ("We detected number of layers as the argument to squash" )
177189 except ValueError :
178190 self .log .debug ("We detected layer as the argument to squash" )
179191
180192 squash_id = self ._squash_id (self .from_layer )
181193
182194 if not squash_id :
183- raise SquashError (
184- "The %s layer could not be found in the %s image" % (self .from_layer , self .image ))
195+ raise SquashError ("The %s layer could not be found in the %s image" % (self .from_layer , self .image ))
185196
186- number_of_layers = len (self .old_image_layers ) - \
187- self .old_image_layers .index (squash_id ) - 1
197+ number_of_layers = len (self .old_image_layers ) - self .old_image_layers .index (squash_id ) - 1
188198
189199 self ._validate_number_of_layers (number_of_layers )
190200
191201 marker = len (self .old_image_layers ) - number_of_layers
192202
193203 self .layers_to_squash = self .old_image_layers [marker :]
194- self .layers_to_move = self .old_image_layers [:marker ]
204+ if self .rebase :
205+ self .layers_to_move = []
206+ self ._read_layers (self .layers_to_move , self .rebase )
207+ self .layers_to_move .reverse ()
208+ else :
209+ self .layers_to_move = self .old_image_layers [:marker ]
210+
211+ self .old_image_squash_marker = marker
195212
196213 self .log .info ("Checking if squashing is necessary..." )
197214
@@ -201,18 +218,18 @@ def _before_squashing(self):
201218 if len (self .layers_to_squash ) == 1 :
202219 raise SquashUnnecessaryError ("Single layer marked to squash, no squashing is required" )
203220
204- self .log .info ("Attempting to squash last %s layers..." ,
205- number_of_layers )
221+ self .log .info ("Attempting to squash last %s layers%s ..." , number_of_layers ,
222+ " rebasing on %s" % self . rebase if self . rebase else "" )
206223 self .log .debug ("Layers to squash: %s" , self .layers_to_squash )
207224 self .log .debug ("Layers to move: %s" , self .layers_to_move )
208225
209226 # Fetch the image and unpack it on the fly to the old image directory
210227 self ._save_image (self .old_image_id , self .old_image_dir )
228+ if self .rebase :
229+ self ._save_image (self .rebase , self .rebase_image_dir )
211230
212231 self .size_before = self ._dir_size (self .old_image_dir )
213232
214- self .log .info ("Squashing image '%s'..." % self .image )
215-
216233 def _after_squashing (self ):
217234 self .log .debug ("Removing from disk already squashed layers..." )
218235 shutil .rmtree (self .old_image_dir , ignore_errors = True )
@@ -698,7 +715,7 @@ def _squash_layers(self, layers_to_squash, layers_to_move):
698715
699716 # Find all files in layers that we don't squash
700717 files_in_layers_to_move = self ._files_in_layers (
701- layers_to_move , self .old_image_dir )
718+ layers_to_move , self .old_image_dir if not self . rebase else self . rebase_image_dir )
702719
703720 with tarfile .open (self .squashed_tar , 'w' , format = tarfile .PAX_FORMAT ) as squashed_tar :
704721 to_skip = []
0 commit comments