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

Support Docker Distribution 2.5 #1068

Merged
merged 1 commit into from
Sep 29, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions app/models/registry_event.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# == Schema Information
#
# Table name: registry_events
#
# id :integer not null, primary key
# event_id :string(255) default(""), not null
# repository :string(255) default("")
# tag :string(255) default("")
# created_at :datetime not null
# updated_at :datetime not null
#

# RegistryEvent represents an event coming from the Registry. This model stores
# events that are being handled by Portus or that have been handled before. This
# way we avoid duplication of registry events
class RegistryEvent < ActiveRecord::Base
end
11 changes: 11 additions & 0 deletions db/migrate/20160927141850_create_registry_events.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class CreateRegistryEvents < ActiveRecord::Migration
def change
create_table :registry_events do |t|
t.string :event_id, default: ""
t.string :repository, default: ""
t.string :tag, default: ""

t.timestamps null: false
end
end
end
10 changes: 9 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: 20160825105515) do
ActiveRecord::Schema.define(version: 20160927141850) do

create_table "activities", force: :cascade do |t|
t.integer "trackable_id", limit: 4
Expand Down Expand Up @@ -89,6 +89,14 @@
add_index "registries", ["hostname"], name: "index_registries_on_hostname", unique: true, using: :btree
add_index "registries", ["name"], name: "index_registries_on_name", unique: true, using: :btree

create_table "registry_events", force: :cascade do |t|
t.string "event_id", limit: 255, default: ""
t.string "repository", limit: 255, default: ""
t.string "tag", limit: 255, default: ""
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end

create_table "repositories", force: :cascade do |t|
t.string "name", limit: 255, default: "", null: false
t.integer "namespace_id", limit: 4
Expand Down
52 changes: 47 additions & 5 deletions lib/portus/registry_notification.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,64 @@ class RegistryNotification
def self.process!(data, *handlers)
data["events"].each do |event|
Rails.logger.debug "Incoming event:\n#{JSON.pretty_generate(event)}"
next unless relevant?(event)

next unless should_handle?(event)
action = event["action"]
next unless HANDLED_EVENTS.include?(action)
Rails.logger.info "Handling '#{action}' event:\n#{JSON.pretty_generate(event)}"

# Only register pushes, since they are the conflicting actions since 2.5
if action == "push"
RegistryEvent.create!(
event_id: event["id"],
repository: event["target"]["repository"],
tag: event["target"]["tag"]
)
end

# Now it's time to delegate the handling to the proper handler.
Rails.logger.info "Handling '#{action}' event:\n#{JSON.pretty_generate(event)}"
handlers.each { |handler| handler.send("handle_#{action}_event".to_sym, event) }
end
end

# Returns true if the event should be handled by the according
# handler. Otherwise, it logs why it shouldn't be handled and returns false.
def self.should_handle?(event)
unless relevant?(event)
Rails.logger.debug "Event discarded because it's not relevant"
return false
end

action = event["action"]
unless HANDLED_EVENTS.include?(action)
Rails.logger.debug "Unsupported '#{action}' event (supported: #{HANDLED_EVENTS})"
return false
end

if RegistryEvent.exists?(event_id: event["id"])
Rails.logger.debug "Event is already being processed. Ignoring..."
false
else
true
end
end

# A relevant event is one that contains the "push" action, and that
# contains a Manifest v1 object in the target.
def self.relevant?(event)
return false unless event["target"].is_a?(Hash)
unless event["target"].is_a?(Hash)
Rails.logger.debug "Wrong format for event"
return false
end

return true if event["action"] == "delete"
event["target"]["mediaType"].starts_with? "application/vnd.docker.distribution.manifest"

mt = event["target"]["mediaType"]
if mt.starts_with? "application/vnd.docker.distribution.manifest"
true
else
Rails.logger.debug "Unsupported mediaType '#{mt}'"
false
end
end
end
end
16 changes: 16 additions & 0 deletions spec/lib/portus/registry_notification_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,20 @@
expect(Repository).to receive(:handle_push_event).with(version23)
Portus::RegistryNotification.process!(body, Repository)
end

it "does not process the same event multiple times" do
body["events"] = [version23]
expect(Repository).to receive(:handle_push_event).with(version23)
Portus::RegistryNotification.process!(body, Repository)

expect(RegistryEvent.count).to eq 1
event = RegistryEvent.find_by(event_id: version23["id"])
expect(event.event_id).to eq version23["id"]
expect(event.repository).to eq version23["target"]["repository"]

expect(Repository).to_not receive(:handle_push_event).with(version23)
Portus::RegistryNotification.process!(body, Repository)

expect(RegistryEvent.count).to eq 1
end
end