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

Commit

Permalink
Fixed notifications for the latest docker when the repo did not exist
Browse files Browse the repository at this point in the history
Before this patch, for docker versions using the Manifest Version 2 schema 2,
Portus failed when the repository did not exist.

Fixes #729

Signed-off-by: Miquel Sabaté Solà <[email protected]>
  • Loading branch information
mssola committed Feb 15, 2016
1 parent 0cfcd65 commit 75d61c6
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 14 deletions.
16 changes: 9 additions & 7 deletions app/models/registry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ def get_namespace_from_event(event)
return
end

tag_name = get_tag_from_target(event["target"])
tag_name = get_tag_from_target(namespace, repo, event["target"])
return if tag_name.nil?

[namespace, repo, tag_name]
Expand Down Expand Up @@ -106,14 +106,14 @@ def reachable?
protected

# Fetch the tag being pushed through the given target object.
def get_tag_from_target(target)
def get_tag_from_target(namespace, repo, target)
case target["mediaType"]
when "application/vnd.docker.distribution.manifest.v1+json",
"application/vnd.docker.distribution.manifest.v1+prettyjws"
get_tag_from_manifest(target)
when "application/vnd.docker.distribution.manifest.v2+json",
"application/vnd.docker.distribution.manifest.list.v2+json"
get_tag_from_list(target["repository"])
get_tag_from_list(namespace, repo)
else
raise "unsupported media type \"#{target["mediaType"]}\""
end
Expand All @@ -127,12 +127,14 @@ def get_tag_from_target(target)
# Fetch the tag by making the difference of what we've go on the DB, and
# what's available on the registry. Returns a string with the tag on success,
# otherwise it returns nil.
def get_tag_from_list(repository)
tags = client.tags(repository)
def get_tag_from_list(namespace, repository)
full_repo_name = namespace.global? ? repository : "#{namespace.name}/#{repository}"
tags = client.tags(full_repo_name)
return if tags.nil?

available = Repository.find_by(name: repository).tags.pluck(:name)
resulting = tags - available
repo = Repository.find_by(name: repository, namespace: namespace)
return tags.first if repo.nil?
resulting = tags - repo.tags.pluck(:name)

# Note that it might happen that there are multiple tags not yet in sync
# with Portus' DB. This means that the registry might have been
Expand Down
38 changes: 31 additions & 7 deletions spec/models/registry_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,9 @@ def o.tags(*_)
o
end

def get_tag_from_target_test(repo, mtype, digest)
def get_tag_from_target_test(namespace, repo, mtype, digest)
target = { "mediaType" => mtype, "repository" => repo, "digest" => digest }
get_tag_from_target(target)
get_tag_from_target(namespace, repo, target)
end
end

Expand Down Expand Up @@ -72,6 +72,12 @@ def client
end
end

def create_empty_namespace
owner = create(:user)
team = create(:team, owners: [owner])
create(:namespace, team: team)
end

describe Registry, type: :model do
it { should have_many(:namespaces) }

Expand Down Expand Up @@ -136,7 +142,7 @@ def client
it "returns a tag on success" do
mock = RegistryMock.new(false)

ret = mock.get_tag_from_target_test("busybox",
ret = mock.get_tag_from_target_test(nil, "busybox",
"application/vnd.docker.distribution.manifest.v1+json",
"sha:1234")
expect(ret).to eq "latest"
Expand All @@ -150,10 +156,27 @@ def client
create(:tag, name: "latest", repository: repo)

mock = RegistryMock.new(false)
ret = mock.get_tag_from_target_test("busybox",
ret = mock.get_tag_from_target_test(namespace, "busybox",
"application/vnd.docker.distribution.manifest.v2+json",
"sha:1234")
expect(ret).to eq "0.1"

# Differentiate between global & local namespace

ret = mock.get_tag_from_target_test(create_empty_namespace,
"busybox",
"application/vnd.docker.distribution.manifest.v2+json",
"sha:1234")
expect(ret).to eq "latest"
end

it "returns the tags on an unknown repository" do
mock = RegistryMock.new(false)
ret = mock.get_tag_from_target_test(create_empty_namespace,
"busybox",
"application/vnd.docker.distribution.manifest.v2+json",
"sha:1234")
expect(ret).to eq "latest"
end

it "handles errors properly" do
Expand All @@ -162,7 +185,7 @@ def client
expect(Rails.logger).to receive(:info).with(/Could not fetch the tag/)
expect(Rails.logger).to receive(:info).with(/Reason: Some message/)

ret = m.get_tag_from_target_test("busybox",
ret = m.get_tag_from_target_test(nil, "busybox",
"application/vnd.docker.distribution.manifest.v1+prettyjws",
"sha:1234")
expect(ret).to be_nil
Expand All @@ -174,7 +197,8 @@ def client
expect(Rails.logger).to receive(:info).with(/Could not fetch the tag/)
expect(Rails.logger).to receive(:info).with(/Reason: Some message/)

ret = mock.get_tag_from_target_test("busybox",
ret = mock.get_tag_from_target_test(create_empty_namespace,
"busybox",
"application/vnd.docker.distribution.manifest.v2+json",
"sha:1234")
expect(ret).to be_nil
Expand All @@ -186,7 +210,7 @@ def client
expect(Rails.logger).to receive(:info).with(/Could not fetch the tag/)
expect(Rails.logger).to receive(:info).with(/Reason: unsupported media type "a"/)

mock.get_tag_from_target_test("busybox", "a", "sha:1234")
mock.get_tag_from_target_test(nil, "busybox", "a", "sha:1234")
end
end
end
65 changes: 65 additions & 0 deletions spec/models/repository_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,27 @@ def get_url(repo, tag)
expect(repository.tags.find_by(name: tag_name).author).to eq(user)
end

it "creates the repo also for version 2 schema 2" do
repository = nil
@event["target"]["mediaType"] = "application/vnd.docker.distribution.manifest.v2+json"

VCR.use_cassette("registry/get_tags_list_webhook", record: :none) do
expect do
repository = Repository.handle_push_event(@event)
end.to change(Namespace, :count).by(0)
end

expect(repository).not_to be_nil
expect(Repository.count).to eq 1
expect(Tag.count).to eq 1

expect(repository.namespace).to eq(registry.global_namespace)
expect(repository.name).to eq(repository_name)
expect(repository.tags.count).to eq 1
expect(repository.tags.first.name).to eq tag_name
expect(repository.tags.find_by(name: tag_name).author).to eq(user)
end

it "tracks the event" do
repository = nil
VCR.use_cassette("registry/get_image_manifest_webhook", record: :none) do
Expand Down Expand Up @@ -293,6 +314,50 @@ def get_url(repo, tag)
expect(repository.tags.map(&:name)).to include("1.0.0", tag_name)
expect(repository.tags.find_by(name: tag_name).author).to eq(user)
end

it "repo is unknown - manifest version 2, schema 2" do
@event["target"]["mediaType"] = "application/vnd.docker.distribution.manifest.v2+json"

repository = nil
VCR.use_cassette("registry/get_image_manifest_namespaced_webhook_v2", record: :none) do
expect do
repository = Repository.handle_push_event(@event)
end.to change(Repository, :count).by(1)
end

expect(repository).not_to be_nil
expect(Repository.count).to eq 1
expect(Repository.count).to eq 1
expect(Tag.count).to eq 1

expect(repository.namespace.name).to eq(namespace_name)
expect(repository.name).to eq(repository_name)
expect(repository.tags.count).to eq 1
expect(repository.tags.first.name).to eq tag_name
expect(repository.tags.first.digest).to eq digest
expect(repository.tags.find_by(name: tag_name).author).to eq(user)
end

it "repo exists - manifest version 2, schema 2" do
@event["target"]["mediaType"] = "application/vnd.docker.distribution.manifest.v2+json"
repository = create(:repository, name: repository_name, namespace: @namespace)
repository.tags << Tag.new(name: "1.0.0")

VCR.use_cassette("registry/get_image_manifest_namespaced_webhook_v2", record: :none) do
repository = Repository.handle_push_event(@event)
end

expect(repository).not_to be_nil
expect(Repository.count).to eq 1
expect(Repository.count).to eq 1
expect(Tag.count).to eq 2

expect(repository.namespace.name).to eq(namespace_name)
expect(repository.name).to eq(repository_name)
expect(repository.tags.count).to eq 2
expect(repository.tags.map(&:name)).to include("1.0.0", tag_name)
expect(repository.tags.find_by(name: tag_name).author).to eq(user)
end
end
end
end
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
http_interactions:
- request:
method: get
uri: http://registry.test.lan/v2/suse/busybox/tags/list?n=100
body:
encoding: US-ASCII
string: ''
headers:
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
User-Agent:
- Ruby
Host:
- registry.test.lan
response:
status:
code: 200
message: OK
headers:
Content-Type:
- application/json; charset=utf-8
Docker-Distribution-Api-Version:
- registry/2.0
Date:
- Mon, 20 Apr 2015 10:06:19 GMT
Content-Length:
- '170'
body:
encoding: UTF-8
string: |
{"name":"suse/busybox","tags":["latest"]}
http_version:
recorded_at: Mon, 20 Apr 2015 10:06:19 GMT

36 changes: 36 additions & 0 deletions spec/vcr_cassettes/registry/get_tags_list_webhook.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
---
http_interactions:
- request:
method: get
uri: http://registry.test.lan/v2/busybox/tags/list?n=100
body:
encoding: US-ASCII
string: ''
headers:
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
User-Agent:
- Ruby
Host:
- registry.test.lan
response:
status:
code: 200
message: OK
headers:
Content-Type:
- application/json; charset=utf-8
Docker-Distribution-Api-Version:
- registry/2.0
Date:
- Mon, 20 Apr 2015 10:06:19 GMT
Content-Length:
- '170'
body:
encoding: UTF-8
string: |
{"name":"busybox", "tags": ["latest"] }
http_version:
recorded_at: Mon, 20 Apr 2015 10:06:19 GMT

0 comments on commit 75d61c6

Please sign in to comment.