From 1d8b72235a92a2a56d1a7b94b006c8d9a9feccef Mon Sep 17 00:00:00 2001 From: Jake Moshenko Date: Fri, 26 Aug 2016 14:46:18 -0400 Subject: [PATCH] Add a helper method to Image to parse ancestor string. --- data/database.py | 6 ++++++ data/model/image.py | 6 ++---- data/model/repository.py | 2 +- endpoints/api/image.py | 24 +++++++++++------------ util/imagetree.py | 25 +++++++++++------------- util/migrate/backfill_aggregate_sizes.py | 2 +- 6 files changed, 33 insertions(+), 32 deletions(-) diff --git a/data/database.py b/data/database.py index 367dac336..8e34d21ea 100644 --- a/data/database.py +++ b/data/database.py @@ -616,6 +616,12 @@ class Image(BaseModel): (('security_indexed_engine', 'security_indexed'), False), ) + def ancestor_id_list(self): + """ Returns an integer list of ancestor ids, ordered chronologically from + root to direct parent. + """ + return map(int, self.ancestors.split('/')[1:-1]) + _ImageProxy.initialize(Image) diff --git a/data/model/image.py b/data/model/image.py index c11e7178b..2fc8f837d 100644 --- a/data/model/image.py +++ b/data/model/image.py @@ -397,9 +397,7 @@ def get_repo_image_by_storage_checksum(namespace, repository_name, storage_check def get_image_layers(image): """ Returns a list of the full layers of an image, including itself (if specified), sorted from base image outward. """ - ancestors = image.ancestors.split('/')[1:-1] - image_ids = [ancestor_id for ancestor_id in ancestors if ancestor_id] - image_ids.append(str(image.id)) + image_ids = image.ancestor_id_list() + [image.id] query = (ImageStoragePlacement .select(ImageStoragePlacement, Image, ImageStorage, ImageStorageLocation) @@ -410,7 +408,7 @@ def get_image_layers(image): .where(Image.id << image_ids)) image_list = list(invert_placement_query_results(query)) - image_list.sort(key=lambda image: image_ids.index(str(image.id))) + image_list.sort(key=lambda img: image_ids.index(img.id)) return image_list diff --git a/data/model/repository.py b/data/model/repository.py index aaceb74e6..e0bf7234b 100644 --- a/data/model/repository.py +++ b/data/model/repository.py @@ -157,7 +157,7 @@ def garbage_collect_repo(repo): def gen_referenced_ancestors(): for tagged_image in tagged_images: # The ancestor list is in the format '/1/2/3/', extract just the ids - ancestor_id_strings = tagged_image.ancestors.split('/')[1:-1] + ancestor_id_strings = tagged_image.ancestor_list() for img_id_str in ancestor_id_strings: yield int(img_id_str) yield tagged_image.id diff --git a/endpoints/api/image.py b/endpoints/api/image.py index 48875b271..0d6e59425 100644 --- a/endpoints/api/image.py +++ b/endpoints/api/image.py @@ -13,7 +13,7 @@ def image_view(image, image_map, include_ancestors=True): command = image.command def docker_id(aid): - if not aid or not aid in image_map: + if aid not in image_map: return '' return image_map[aid].docker_image_id @@ -30,14 +30,14 @@ def image_view(image, image_map, include_ancestors=True): if include_ancestors: # Calculate the ancestors string, with the DBID's replaced with the docker IDs. - ancestors = [docker_id(a) for a in image.ancestors.split('/')] - image_data['ancestors'] = '/'.join(ancestors) + ancestors = [docker_id(a) for a in image.ancestor_id_list()] + image_data['ancestors'] = '/{0}/'.format('/'.join(ancestors)) return image_data def historical_image_view(image, image_map): - ancestors = [image_map[a] for a in image.ancestors.split('/')[1:-1]] + ancestors = [image_map[a] for a in image.ancestor_id_list()] normal_view = image_view(image, image_map) normal_view['history'] = [image_view(parent, image_map, False) for parent in ancestors] return normal_view @@ -58,23 +58,23 @@ class RepositoryImageList(RepositoryParamResource): all_images = model.image.get_repository_images_without_placements(repo) all_tags = model.tag.list_repository_tags(namespace, repository) - tags_by_image_id = defaultdict(list) + tags_by_docker_id = defaultdict(list) found_image_ids = set() for tag in all_tags: - tags_by_image_id[tag.image.docker_image_id].append(tag.name) - found_image_ids.add(str(tag.image.id)) - found_image_ids.update(tag.image.ancestors.split('/')[1:-1]) + tags_by_docker_id[tag.image.docker_image_id].append(tag.name) + found_image_ids.add(tag.image.id) + found_image_ids.update(tag.image.ancestor_id_list()) image_map = {} filtered_images = [] for image in all_images: - if str(image.id) in found_image_ids: - image_map[str(image.id)] = image + if image.id in found_image_ids: + image_map[image.id] = image filtered_images.append(image) def add_tags(image_json): - image_json['tags'] = tags_by_image_id[image_json['id']] + image_json['tags'] = tags_by_docker_id[image_json['id']] return image_json return { @@ -98,7 +98,7 @@ class RepositoryImage(RepositoryParamResource): # Lookup all the ancestor images for the image. image_map = {} for current_image in model.image.get_parent_images(namespace, repository, image): - image_map[str(current_image.id)] = current_image + image_map[current_image.id] = current_image return historical_image_view(image, image_map) diff --git a/util/imagetree.py b/util/imagetree.py index a45ac29d1..9df68b3d1 100644 --- a/util/imagetree.py +++ b/util/imagetree.py @@ -1,3 +1,6 @@ +from collections import defaultdict + + class ImageTreeNode(object): """ A node in the image tree. """ def __init__(self, image, child_map): @@ -9,7 +12,7 @@ class ImageTreeNode(object): @property def children(self): - return self._child_map.get(str(self.image.id), []) + return self._child_map[self.image.id] def add_tag(self, tag): self.tags.append(tag) @@ -20,18 +23,18 @@ class ImageTree(object): def __init__(self, all_images, all_tags, base_filter=None): self._image_map = {} - self._child_map = {} + self._child_map = defaultdict(list) self._build(all_images, all_tags, base_filter) - def _build(self, all_images, all_tags, base_filter=None): + def _build(self, all_images, all_tags, base_filter=None): # Build nodes for each of the images. for image in all_images: - ancestors = image.ancestors.split('/')[1:-1] + ancestors = image.ancestor_id_list() # Filter any unneeded images. if base_filter is not None: - if image.id != base_filter and not str(base_filter) in ancestors: + if image.id != base_filter and not base_filter in ancestors: continue # Create the node for the image. @@ -39,11 +42,8 @@ class ImageTree(object): self._image_map[image.id] = image_node # Add the node to the child map for its parent image (if any). - parent_image_id = image.ancestors.split('/')[-2] if image.ancestors else None - if parent_image_id: - if not parent_image_id in self._child_map: - self._child_map[parent_image_id] = [] - + parent_image_id = image.parent_id + if parent_image_id is not None: self._child_map[parent_image_id].append(image_node) # Build the tag map. @@ -54,7 +54,6 @@ class ImageTree(object): image_node.add_tag(tag.name) - def find_longest_path(self, image_id, checker): """ Returns a list of images representing the longest path that matches the given checker function, starting from the given image_id *exclusive*. @@ -65,7 +64,6 @@ class ImageTree(object): return self._find_longest_path(start_node, checker, -1)[1:] - def _find_longest_path(self, image_node, checker, index): found_path = [] @@ -79,7 +77,6 @@ class ImageTree(object): return [image_node.image] + found_path - def tag_containing_image(self, image): """ Returns the name of the closest tag containing the given image. """ if not image: @@ -99,4 +96,4 @@ class ImageTree(object): if found is not None: return found - return None \ No newline at end of file + return None diff --git a/util/migrate/backfill_aggregate_sizes.py b/util/migrate/backfill_aggregate_sizes.py index db6f195f2..b19314e1a 100644 --- a/util/migrate/backfill_aggregate_sizes.py +++ b/util/migrate/backfill_aggregate_sizes.py @@ -35,7 +35,7 @@ def backfill_aggregate_sizes(): aggregate_size = image.storage.image_size - image_ids = image.ancestors.split('/')[1:-1] + image_ids = image.ancestor_id_list() for image_id in image_ids: to_add = db_for_update(Image .select(Image, ImageStorage)