Skip to content
This repository has been archived by the owner on Apr 17, 2023. It is now read-only.

Commit

Permalink
Merge pull request #852 from mssola/image-id
Browse files Browse the repository at this point in the history
Add the Image ID in the repositories#show page
  • Loading branch information
mssola committed May 2, 2016
2 parents 5ff0a85 + e57232b commit 6fa7a25
Show file tree
Hide file tree
Showing 14 changed files with 92 additions and 27 deletions.
3 changes: 2 additions & 1 deletion app/models/registry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,8 @@ def get_tag_from_list(namespace, repository)
#
# Returns the name of the tag if found, nil otherwise.
def get_tag_from_manifest(target)
client.manifest(target["repository"], target["digest"])["tag"]
_, _, manifest = client.manifest(target["repository"], target["digest"])
manifest["tag"]
end

# Create the global namespace for this registry and create the personal
Expand Down
26 changes: 22 additions & 4 deletions app/models/repository.rb
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,29 @@ def self.add_repo(event, namespace, repo, tag)
return
end

digest = event.try(:[], "target").try(:[], "digest")
tag = repository.tags.create(name: tag, author: actor, digest: digest)
# And store the tag and its activity.
id, digest = Repository.id_and_digest_from_event(event, repo)
tag = repository.tags.create(name: tag, author: actor, digest: digest, image_id: id)
repository.create_activity(:push, owner: actor, recipient: tag)
repository
end

# Fetch the image ID and the manifest digest from the given event.
def self.id_and_digest_from_event(event, repo)
digest = event.try(:[], "target").try(:[], "digest")
id = ""

unless digest.blank?
begin
id, = Registry.get.client.manifest(repo, digest)
rescue StandardError => e
logger.warn "Could not fetch manifest for '#{repo}' with digest '#{digest}': " + e.message
end
end

[id, digest]
end

# Create or update the given repository in JSON format. The given repository
# follows the same JSON format as in the one used by the Catalog API.
# Therefore, it's a hash with two keys:
Expand Down Expand Up @@ -141,12 +158,13 @@ def self.create_or_update!(repo)
to_be_created_tags.each do |tag|
# Try to fetch the manifest digest of the tag.
begin
digest = client.manifest(name, tag, true)
id, digest, = client.manifest(name, tag)
rescue
id = ""
digest = ""
end

Tag.create!(name: tag, repository: repository, author: portus, digest: digest)
Tag.create!(name: tag, repository: repository, author: portus, digest: digest, image_id: id)
logger.tagged("catalog") { logger.info "Created the tag '#{tag}'." }
end

Expand Down
1 change: 1 addition & 0 deletions app/models/tag.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# updated_at :datetime not null
# user_id :integer
# digest :string(255)
# image_id :string(255) default("")
#
# Indexes
#
Expand Down
16 changes: 15 additions & 1 deletion app/views/repositories/show.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@
.table-responsive.tags
table.table.table-stripped.table-hover
col.col-40
col.col-40
col.col-20
col.col-20
col.col-20
thead
tr
th Tag
th Author
th Image
th Pushed at
tbody
- @tags.each do |tag|
Expand All @@ -35,6 +37,12 @@
.label.label-success
= t.name
td= tag.first.author.username
td
- if tag.first.image_id.empty?
= "-"
- else
span[title="sha256:#{tag.first.image_id}"]
= tag.first.image_id[0, 12]
td= tag.first.created_at
- else
tr
Expand All @@ -43,6 +51,12 @@
.label.label-success
= t.name
td= tag.first.author.username
td
- if tag.first.image_id.empty?
= "-"
- else
span[title="sha256:#{tag.first.image_id}"]
= tag.first.image_id[0, 12]
td= tag.first.created_at

#write_comment_form.collapse
Expand Down
6 changes: 4 additions & 2 deletions bin/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@
name, tag = ARGV[1], "latest"
end

puts JSON.pretty_generate(registry.client.manifest(name, tag))
puts "Manifest digest: #{registry.client.manifest(name, tag, true)}"
id, digest, manifest = registry.client.manifest(name, tag)
puts "Image ID: #{id} (truncated as in Docker: #{id[0, 12]})"
puts "Manifest digest: #{digest}"
puts JSON.pretty_generate(manifest)
when "ping"
# No registry was found, trying to ping another one.
if registry.nil?
Expand Down
5 changes: 5 additions & 0 deletions db/migrate/20160422075603_add_image_id_to_tag.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddImageIdToTag < ActiveRecord::Migration
def change
add_column :tags, :image_id, :string, default: ""
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20151215152138) do
ActiveRecord::Schema.define(version: 20160422075603) do

create_table "activities", force: :cascade do |t|
t.integer "trackable_id", limit: 4
Expand Down Expand Up @@ -116,6 +116,7 @@
t.datetime "updated_at", null: false
t.integer "user_id", limit: 4
t.string "digest", limit: 255
t.string "image_id", limit: 255, default: ""
end

add_index "tags", ["name", "repository_id"], name: "index_tags_on_name_and_repository_id", unique: true, using: :btree
Expand Down
26 changes: 17 additions & 9 deletions lib/portus/registry_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,25 @@ def reachable?
!res.nil? && res.code.to_i == 401
end

# Calls the `/:repository/manifests/:tag` endpoint from the registry. With
# the `digest` parameter you can tell this method whether you want the full
# manifest for this tag, or just the digest. It defaults to false, meaning
# that the caller wants the full manifest. It will raise either a
# ManifestNotFoundError or a RuntimeError if something goes wrong.
def manifest(repository, tag = "latest", digest = false)
method = digest ? "head" : "get"
res = perform_request("#{repository}/manifests/#{tag}", method)
# Calls the `/:repository/manifests/:tag` endpoint from the registry. It
# returns a three-sized array:
#
# - The image ID (without the "sha256:" prefix): only available for v2
# manifests (nil if v1).
# - The manifest digest.
# - The manifest itself as a ruby hash.
#
# It will raise either a ManifestNotFoundError or a RuntimeError if
# something goes wrong.
def manifest(repository, tag = "latest")
res = perform_request("#{repository}/manifests/#{tag}", "get")

if res.code.to_i == 200
digest ? res["Docker-Content-Digest"] : JSON.parse(res.body)
mf = JSON.parse(res.body)
id = mf.try(:[], "config").try(:[], "digest")
id = id.split(":").last if id.is_a? String
digest = res["Docker-Content-Digest"]
[id, digest, mf]
elsif res.code.to_i == 404
handle_error res, repository: repository, tag: tag
else
Expand Down
15 changes: 12 additions & 3 deletions lib/tasks/portus.rake
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,11 @@ HERE

# Fetch the tags to be updated.
update = args[:update] == "true" || args[:update] == "t"
tags = update ? Tag.all : Tag.where(digest: "")
if update
tags = Tag.all
else
tags = Tag.where("tags.digest='' OR tags.image_id=''")
end

# Some information on the amount of tags to be updated.
if tags.empty?
Expand All @@ -106,8 +110,13 @@ HERE
tags.each_with_index do |t, index|
repo_name = t.repository.name
puts "[#{index + 1}/#{tags.size}] Updating #{repo_name}/#{t.name}"
digest = client.manifest(t.repository.name, t.name, true)
t.update_attributes(digest: digest)

begin
id, digest, = client.manifest(t.repository.name, t.name)
t.update_attributes(digest: digest, image_id: id)
rescue StandardError => e
puts "Could not get the manifest for #{repo_name}: #{e.message}"
end
end
puts
end
Expand Down
9 changes: 6 additions & 3 deletions spec/features/repositories_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,20 @@

scenario "Groupped tags are handled properly", js: true do
["", "", "same", "same", "another", "yet-another"].each_with_index do |digest, idx|
create(:tag, name: "tag#{idx}", author: user, repository: repository, digest: digest)
create(:tag, name: "tag#{idx}", author: user, repository: repository, digest: digest,
image_id: "Image")
end

expect = [["tag0"], ["tag1"], ["tag2", "tag3"], ["tag4"], ["tag5"]]
expectations = [["tag0"], ["tag1"], ["tag2", "tag3"], ["tag4"], ["tag5"]]

visit repository_path(repository)
page.all(".tags tr").each_with_index do |row, idx|
expect(row.text.include?("Image")).to be_truthy

# Skip the header.
next if idx == 0

expect[idx - 1].each { |tag| expect(row.text.include?(tag)).to be_truthy }
expectations[idx - 1].each { |tag| expect(row.text.include?(tag)).to be_truthy }
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion spec/lib/portus/registry_client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ def fetch_link_test(header)
username,
password)

manifest = registry.manifest(repository, tag)
_, _, manifest = registry.manifest(repository, tag)
expect(manifest["name"]).to eq(repository)
expect(manifest["tag"]).to eq(tag)
end
Expand Down
2 changes: 1 addition & 1 deletion spec/models/registry_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def o.tags(*_)
end
else
def o.manifest(*_)
{ "tag" => "latest" }
["id", "digest", { "tag" => "latest" }]
end

def o.tags(*_)
Expand Down
4 changes: 3 additions & 1 deletion spec/models/repository_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,9 @@ def get_url(repo, tag)
let!(:tag3) { create(:tag, name: "tag3", repository: repo2) }

before :each do
allow_any_instance_of(Portus::RegistryClient).to receive(:manifest).and_return("digest")
allow_any_instance_of(Portus::RegistryClient).to receive(:manifest).and_return(
["id", "digest", ""])
allow(Repository).to receive(:id_and_digest_from_event).and_return(["id", "digest"])
end

it "adds and deletes tags accordingly" do
Expand Down
1 change: 1 addition & 0 deletions spec/models/tag_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
# updated_at :datetime not null
# user_id :integer
# digest :string(255)
# image_id :string(255) default("")
#
# Indexes
#
Expand Down

0 comments on commit 6fa7a25

Please sign in to comment.