+
+ # Return array of Collection objects
+ def self.find_all_for_docker_image(search_term, search_tag=nil, readers=nil)
+ readers ||= [Thread.current[:user]]
+ base_search = Link.
+ readable_by(*readers).
+ readable_by(*readers, table_name: "collections").
+ joins("JOIN collections ON links.head_uuid = collections.uuid").
+ order("links.created_at DESC")
+
+ # If the search term is a Collection locator that contains one file
+ # that looks like a Docker image, return it.
+ if loc = Keep::Locator.parse(search_term)
+ loc.strip_hints!
+ coll_match = readable_by(*readers).where(portable_data_hash: loc.to_s).limit(1).first
+ if coll_match
+ # Check if the Collection contains exactly one file whose name
+ # looks like a saved Docker image.
+ manifest = Keep::Manifest.new(coll_match.manifest_text)
+ if manifest.exact_file_count?(1) and
+ (manifest.files[0][1] =~ /^[0-9A-Fa-f]{64}\.tar$/)
+ return [coll_match]
+ end
+ end
+ end
+
+ if search_tag.nil? and (n = search_term.index(":"))
+ search_tag = search_term[n+1..-1]
+ search_term = search_term[0..n-1]
+ end
+
+ # Find Collections with matching Docker image repository+tag pairs.
+ matches = base_search.
+ where(link_class: "docker_image_repo+tag",
+ name: "#{search_term}:#{search_tag || 'latest'}")
+
+ # If that didn't work, find Collections with matching Docker image hashes.
+ if matches.empty?
+ matches = base_search.
+ where("link_class = ? and links.name LIKE ?",
+ "docker_image_hash", "#{search_term}%")
+ end
+
+ # Generate an order key for each result. We want to order the results
+ # so that anything with an image timestamp is considered more recent than
+ # anything without; then we use the link's created_at as a tiebreaker.
+ uuid_timestamps = {}
+ matches.all.map do |link|
+ uuid_timestamps[link.head_uuid] = [(-link.properties["image_timestamp"].to_datetime.to_i rescue 0),
+ -link.created_at.to_i]
+ end
+ Collection.where('uuid in (?)', uuid_timestamps.keys).sort_by { |c| uuid_timestamps[c.uuid] }
+ end
+
+ def self.for_latest_docker_image(search_term, search_tag=nil, readers=nil)
+ find_all_for_docker_image(search_term, search_tag, readers).first
+ end
+
+ def self.searchable_columns operator
+ super - ["manifest_text"]
+ end
+
+ protected
+ def portable_manifest_text
+ portable_manifest = self[:manifest_text].dup
+ self.class.munge_manifest_locators!(portable_manifest) do |loc|
+ loc.hash + '+' + loc.size.to_s
+ end
+ portable_manifest
+ end
+
+ def compute_pdh
+ portable_manifest = portable_manifest_text
+ (Digest::MD5.hexdigest(portable_manifest) +
+ '+' +
+ portable_manifest.bytesize.to_s)
+ end