Merge pull request #243 from abf/rosa-build:83-lock-files-for-synchronisation-and-publishing

abf/abf-ideas#83: lock files for synchronisation and publishing.
This commit is contained in:
avm 2013-07-29 19:41:20 +04:00
commit 327277c2e1
13 changed files with 212 additions and 25 deletions

View File

@ -33,6 +33,16 @@ class Api::V1::RepositoriesController < Api::V1::BaseController
def key_pair def key_pair
end end
def add_repo_lock_file
@repository.add_repo_lock_file
render_json_response @repository, "'.repo.lock' file has been added to repository successfully"
end
def remove_repo_lock_file
@repository.remove_repo_lock_file
render_json_response @repository, "'.repo.lock' file has been removed from repository successfully"
end
def add_project def add_project
project = Project.where(:id => params[:project_id]).first project = Project.where(:id => params[:project_id]).first
if project if project

View File

@ -149,6 +149,17 @@ class Platforms::RepositoriesController < Platforms::BaseController
redirect_to platform_repository_path(@platform, @repository) redirect_to platform_repository_path(@platform, @repository)
end end
def sync_lock_file
if params[:remove]
@repository.remove_sync_lock_file
flash[:notice] = t('flash.repository.sync_lock_file_removed')
else
flash[:notice] = t('flash.repository.sync_lock_file_added')
@repository.add_sync_lock_file
end
redirect_to edit_platform_repository_path(@platform, @repository)
end
protected protected
def set_members def set_members

View File

@ -119,7 +119,7 @@ class Ability
can [:read, :projects_list, :projects], Repository, :platform => {:owner_type => 'Group', :owner_id => user.group_ids} can [:read, :projects_list, :projects], Repository, :platform => {:owner_type => 'Group', :owner_id => user.group_ids}
can([:read, :projects_list, :projects], Repository, read_relations_for('repositories')) {|repository| can? :show, repository.platform} can([:read, :projects_list, :projects], Repository, read_relations_for('repositories')) {|repository| can? :show, repository.platform}
can([:read, :projects_list, :projects], Repository, read_relations_for('repositories', 'platforms')) {|repository| local_reader? repository.platform} can([:read, :projects_list, :projects], Repository, read_relations_for('repositories', 'platforms')) {|repository| local_reader? repository.platform}
can([:create, :edit, :update, :destroy, :projects_list, :projects, :add_project, :remove_project, :regenerate_metadata], Repository) {|repository| local_admin? repository.platform} can([:create, :edit, :update, :destroy, :projects_list, :projects, :add_project, :remove_project, :regenerate_metadata, :sync_lock_file, :add_repo_lock_file, :remove_repo_lock_file], Repository) {|repository| local_admin? repository.platform}
can([:remove_members, :remove_member, :add_member, :signatures], Repository) {|repository| owner?(repository.platform) || local_admin?(repository.platform)} can([:remove_members, :remove_member, :add_member, :signatures], Repository) {|repository| owner?(repository.platform) || local_admin?(repository.platform)}
can([:add_project, :remove_project], Repository) {|repository| repository.members.exists?(:id => user.id)} can([:add_project, :remove_project], Repository) {|repository| repository.members.exists?(:id => user.id)}
can(:clear, Platform) {|platform| owner?(platform) && platform.personal?} can(:clear, Platform) {|platform| owner?(platform) && platform.personal?}
@ -164,7 +164,8 @@ class Ability
# Shared cannot rights for all users (registered, admin) # Shared cannot rights for all users (registered, admin)
cannot :destroy, Platform, :platform_type => 'personal' cannot :destroy, Platform, :platform_type => 'personal'
cannot [:create, :destroy], Repository, :platform => {:platform_type => 'personal'}, :name => 'main' cannot [:create, :destroy], Repository, :platform => {:platform_type => 'personal'}, :name => 'main'
cannot [:remove_members, :remove_member, :add_member], Repository, :platform => {:platform_type => 'personal'} cannot [:remove_members, :remove_member, :add_member, :sync_lock_file, :add_repo_lock_file, :remove_repo_lock_file], Repository, :platform => {:platform_type => 'personal'}
cannot :clear, Platform, :platform_type => 'main' cannot :clear, Platform, :platform_type => 'main'
cannot :destroy, Issue cannot :destroy, Issue

View File

@ -1,5 +1,6 @@
# -*- encoding : utf-8 -*- # -*- encoding : utf-8 -*-
class Repository < ActiveRecord::Base class Repository < ActiveRecord::Base
LOCK_FILE_NAMES = {:sync => '.sync.lock', :repo => '.repo.lock'}
SORT = {'base' => 1, 'main' => 2, 'contrib' => 3, 'non-free' => 4, 'restricted' => 5} SORT = {'base' => 1, 'main' => 2, 'contrib' => 3, 'non-free' => 4, 'restricted' => 5}
belongs_to :platform belongs_to :platform
@ -45,6 +46,40 @@ class Repository < ActiveRecord::Base
end end
end end
# Checks locking of sync
def sync_lock_file_exists?
lock_file_actions :check, :sync
end
# Uses for locking sync
# Calls from UI
def add_sync_lock_file
lock_file_actions :add, :sync
end
# Uses for unlocking sync
# Calls from UI
def remove_sync_lock_file
lock_file_actions :remove, :sync
end
# Uses for locking publishing
# Calls from API
def add_repo_lock_file
lock_file_actions :add, :repo
end
# Uses for unlocking publishing
# Calls from API
def remove_repo_lock_file
lock_file_actions :remove, :repo
end
# Presence of `.repo.lock` file means that mirror is currently synchronising the repository state.
def repo_lock_file_exists?
lock_file_actions :check, :repo
end
def add_member(member, role = 'admin') def add_member(member, role = 'admin')
Relation.add_member(member, self, role) Relation.add_member(member, self, role)
end end
@ -72,6 +107,22 @@ class Repository < ActiveRecord::Base
protected protected
def lock_file_actions(action, lock_file)
result = false
(['SRPMS'] << Arch.pluck(:name)).each do |arch|
path = "#{platform.path}/repository/#{arch}/#{name}/#{LOCK_FILE_NAMES[lock_file]}"
case action
when :add
result ||= FileUtils.touch(path) rescue nil
when :remove
result ||= FileUtils.rm_f(path)
when :check
return true if File.exist?(path)
end
end
return result
end
def detele_directory def detele_directory
return unless platform return unless platform
repository_path = platform.path << '/repository' repository_path = platform.path << '/repository'

View File

@ -1,8 +0,0 @@
.hr
= form_for @repository, :url => regenerate_metadata_platform_repository_path(@platform, @repository), :html => { :class => :form, :method => :put } do |f|
.leftside= t('layout.repositories.regenerate_metadata')
.rightside
= select_tag :build_for_platform_id, options_from_collection_for_select(Platform.main, :id, :name) if @platform.personal?
= f.submit t('layout.repositories.regenerate_metadata'), :confirm => t('layout.confirm')
.hr{:style => 'padding-bottom:20px;'}
.both

View File

@ -8,7 +8,29 @@
= render "form", :f => f = render "form", :f => f
%br %br
= render 'regenerate_metadata' if can?(:regenerate_metadata, @repository) - if can?(:update, @repository)
.hr
%h3= t('layout.repositories.extra_actions')
.leftlist= t('layout.repositories.regenerate_metadata')
.rightlist= link_to t('layout.repositories.regenerate_metadata'),
regenerate_metadata_platform_repository_path(@platform, @repository),
:method => :put, :confirm => t('layout.confirm'), :class => 'button'
- if @platform.main?
.leftlist= t('layout.repositories.sync_lock_file_info')
- if @repository.sync_lock_file_exists?
- label = t('layout.repositories.remove_sync_lock_file')
- path = sync_lock_file_platform_repository_path(@platform, @repository, :remove => true)
- else
- label = t('layout.repositories.add_sync_lock_file')
- path = sync_lock_file_platform_repository_path(@platform, @repository)
.rightlist= link_to label, path, :method => :put, :confirm => t('layout.confirm'), :class => 'button'
.both
.hr
- if @platform.main? - if @platform.main?
= render "shared/members_table", = render "shared/members_table",

View File

@ -1,6 +1,10 @@
en: en:
layout: layout:
repositories: repositories:
extra_actions: Extra actions
add_sync_lock_file: "Add '.sync.lock' file"
remove_sync_lock_file: "Remove '.sync.lock' file"
sync_lock_file_info: "Presence of '.sync.lock' file means that repository is not ready for sync with mirror"
add_project_to: Add project to repository add_project_to: Add project to repository
edit: Settings edit: Settings
list: List list: List
@ -28,6 +32,10 @@ en:
flash: flash:
repository: repository:
sync_lock_file_added: "'.sync.lock' file has been added to repository"
sync_lock_file_removed: "'.sync.lock' file has been removed from repository"
saved: Repository added saved: Repository added
updated: Repository updated updated: Repository updated
save_error: Unable to add repository save_error: Unable to add repository

View File

@ -1,6 +1,10 @@
ru: ru:
layout: layout:
repositories: repositories:
extra_actions: Дополнительные действия
add_sync_lock_file: "Добавить '.sync.lock' файл"
remove_sync_lock_file: "Удалить '.sync.lock' файл"
sync_lock_file_info: "Наличие '.sync.lock' файла означает, что репозиторий не готов для синхронизации с зеркалом"
add_project_to: Добавить проект к репозиторию add_project_to: Добавить проект к репозиторию
edit: Настройки edit: Настройки
list: Список list: Список
@ -28,6 +32,9 @@ ru:
flash: flash:
repository: repository:
sync_lock_file_added: "'.sync.lock' файл добавлен в репозиторий"
sync_lock_file_removed: "'.sync.lock' файл удален из репозитория"
saved: Репозиторий успешно добавлен saved: Репозиторий успешно добавлен
updated: Репозиторий успешно обновлен updated: Репозиторий успешно обновлен
save_error: Не удалось добавить репозиторий save_error: Не удалось добавить репозиторий

View File

@ -39,13 +39,15 @@ Rosa::Application.routes.draw do
end end
resources :repositories, :only => [:show, :update, :destroy] do resources :repositories, :only => [:show, :update, :destroy] do
member { member {
get :projects get :projects
get :key_pair get :key_pair
put :add_member put :add_member
delete :remove_member delete :remove_member
put :add_project put :add_project
delete :remove_project delete :remove_project
put :signatures put :signatures
put :add_repo_lock_file
delete :remove_repo_lock_file
} }
end end
resources :projects, :only => [:index, :show, :update, :create, :destroy] do resources :projects, :only => [:index, :show, :update, :create, :destroy] do
@ -174,13 +176,14 @@ Rosa::Application.routes.draw do
resources :repositories do resources :repositories do
member do member do
get :add_project get :add_project
delete :remove_project delete :remove_project
get :projects_list get :projects_list
post :remove_members # fixme: change post to delete post :remove_members # fixme: change post to delete
delete :remove_member delete :remove_member
post :add_member post :add_member
put :regenerate_metadata put :regenerate_metadata
put :sync_lock_file
end end
end end
resources :key_pairs, :only => [:create, :index, :destroy] resources :key_pairs, :only => [:create, :index, :destroy]

View File

@ -173,6 +173,8 @@ module AbfWorker
resign_repos = @redis.lrange RESIGN_REPOSITORIES, 0, -1 resign_repos = @redis.lrange RESIGN_REPOSITORIES, 0, -1
Repository.where(:id => (resign_repos - locked_repositories)).each do |r| Repository.where(:id => (resign_repos - locked_repositories)).each do |r|
# Checks mirror sync status
next if r.repo_lock_file_exists?
@redis.lrem RESIGN_REPOSITORIES, 0, r.id @redis.lrem RESIGN_REPOSITORIES, 0, r.id
@redis.lpush LOCKED_REPOSITORIES, r.id @redis.lpush LOCKED_REPOSITORIES, r.id
@ -274,6 +276,9 @@ module AbfWorker
return false if !bl && old_packages[:sources].empty? return false if !bl && old_packages[:sources].empty?
save_to_repository = Repository.find save_to_repository_id save_to_repository = Repository.find save_to_repository_id
# Checks mirror sync status
return false if save_to_repository.repo_lock_file_exists?
save_to_platform = save_to_repository.platform save_to_platform = save_to_repository.platform
build_for_platform = Platform.find build_for_platform_id build_for_platform = Platform.find build_for_platform_id
platform_path = "#{save_to_platform.path}/repository" platform_path = "#{save_to_platform.path}/repository"
@ -346,6 +351,8 @@ module AbfWorker
regen_repos = regen_repos_and_pl.map{ |r| r.gsub(/\-[\d]*$/, '') } regen_repos = regen_repos_and_pl.map{ |r| r.gsub(/\-[\d]*$/, '') }
Repository.where(:id => regen_repos).each do |rep| Repository.where(:id => regen_repos).each do |rep|
# Checks mirror sync status
next if rep.repo_lock_file_exists?
regen_repos_and_pl.select{ |kind| kind =~ /^#{rep.id}\-/ }.each do |lock_str| regen_repos_and_pl.select{ |kind| kind =~ /^#{rep.id}\-/ }.each do |lock_str|
next if locked_rep_and_pl.include?(lock_str) next if locked_rep_and_pl.include?(lock_str)
@redis.lrem REGENERATE_METADATA, 0, lock_str @redis.lrem REGENERATE_METADATA, 0, lock_str

View File

@ -62,6 +62,15 @@ shared_examples_for 'api repository user with writer rights' do
end end
end end
context 'api repository user with add/remove repo_lock_file rights' do
[:add_repo_lock_file, :remove_repo_lock_file].each do |action|
it "should be able to perform #{action} action" do
put action, :id => @repository.id, :format => :json
response.should be_success
end
end
end
context 'api repository user with add_member rights' do context 'api repository user with add_member rights' do
let(:member) { FactoryGirl.create(:user) } let(:member) { FactoryGirl.create(:user) }
before do before do
@ -179,6 +188,15 @@ shared_examples_for 'api repository user without writer rights' do
end end
end end
context 'api repository user without add/remove repo_lock_file rights' do
[:add_repo_lock_file, :remove_repo_lock_file].each do |action|
it "should not be able to perform #{action} action" do
put action, :id => @repository.id, :format => :json
response.should_not be_success
end
end
end
context 'api repository user without add_member rights' do context 'api repository user without add_member rights' do
let(:member) { FactoryGirl.create(:user) } let(:member) { FactoryGirl.create(:user) }
before do before do

View File

@ -22,6 +22,20 @@ shared_examples_for 'user with change projects in repository rights' do
end end
shared_examples_for 'user with rights of add/remove sync_lock_file to repository' do
it 'should be able to perform sync_lock_file action' do
put :sync_lock_file, :id => @repository.id, :platform_id => @platform.id
response.should redirect_to(edit_platform_repository_path(@platform, @repository))
end
end
shared_examples_for 'user without rights of add/remove sync_lock_file to repository' do
it 'should not be able to perform #{action} action' do
put :sync_lock_file, :id => @repository.id, :platform_id => @platform.id
response.should redirect_to(redirect_path)
end
end
shared_examples_for 'user without change projects in repository rights' do shared_examples_for 'user without change projects in repository rights' do
it 'should not be able to add project to repository' do it 'should not be able to add project to repository' do
get :add_project, :id => @repository.id, :platform_id => @platform.id, :project_id => @project.id get :add_project, :id => @repository.id, :platform_id => @platform.id, :project_id => @project.id
@ -133,6 +147,7 @@ end
shared_examples_for 'platform admin user' do shared_examples_for 'platform admin user' do
it_should_behave_like 'registered user' it_should_behave_like 'registered user'
it_should_behave_like 'user with rights of add/remove sync_lock_file to repository'
it 'should be able to perform new action' do it 'should be able to perform new action' do
get :new, :platform_id => @platform.id get :new, :platform_id => @platform.id
@ -240,6 +255,7 @@ describe Platforms::RepositoriesController do
let(:redirect_path) { new_user_session_path } let(:redirect_path) { new_user_session_path }
it_should_behave_like 'registered user or guest' it_should_behave_like 'registered user or guest'
it_should_behave_like 'user without change projects in repository rights' it_should_behave_like 'user without change projects in repository rights'
it_should_behave_like 'user without rights of add/remove sync_lock_file to repository'
it "should not be able to perform show action", :anonymous_access => false do it "should not be able to perform show action", :anonymous_access => false do
get :show, :id => @repository get :show, :id => @repository
@ -264,6 +280,7 @@ describe Platforms::RepositoriesController do
let(:redirect_path) { forbidden_path } let(:redirect_path) { forbidden_path }
it_should_behave_like 'registered user or guest' it_should_behave_like 'registered user or guest'
it_should_behave_like 'user without change projects in repository rights' it_should_behave_like 'user without change projects in repository rights'
it_should_behave_like 'user without rights of add/remove sync_lock_file to repository'
end end
context 'for admin' do context 'for admin' do
@ -311,6 +328,7 @@ describe Platforms::RepositoriesController do
let(:redirect_path) { forbidden_path } let(:redirect_path) { forbidden_path }
it_should_behave_like 'registered user or guest' it_should_behave_like 'registered user or guest'
it_should_behave_like 'user with change projects in repository rights' it_should_behave_like 'user with change projects in repository rights'
it_should_behave_like 'user without rights of add/remove sync_lock_file to repository'
context 'for hidden platform' do context 'for hidden platform' do
before do before do
@ -322,6 +340,7 @@ describe Platforms::RepositoriesController do
let(:redirect_path) { forbidden_path } let(:redirect_path) { forbidden_path }
it_should_behave_like 'registered user or guest' it_should_behave_like 'registered user or guest'
it_should_behave_like 'user with change projects in repository rights' it_should_behave_like 'user with change projects in repository rights'
it_should_behave_like 'user without rights of add/remove sync_lock_file to repository'
end end
end end

View File

@ -30,6 +30,44 @@ describe Repository do
end end
context '#sync_lock_file_exists?, #add_sync_lock_file, #remove_sync_lock_file, #add_repo_lock_file, #remove_repo_lock_file' do
let(:repository) { FactoryGirl.create(:repository) }
let(:path) { "#{repository.platform.path}/repository/SRPMS/#{repository.name}" }
before { FileUtils.mkdir_p path }
it 'ensures that #sync_lock_file_exists? returns false if .sync.lock file does not exist' do
repository.sync_lock_file_exists?.should be_false
end
it 'ensures that #sync_lock_file_exists? returns true if .sync.lock file does exist' do
FileUtils.touch "#{path}/.sync.lock"
repository.sync_lock_file_exists?.should be_true
end
it 'ensures that #add_sync_lock_file creates .sync.lock file' do
repository.add_sync_lock_file
File.exist?("#{path}/.sync.lock").should be_true
end
it 'ensures that #remove_sync_lock_file removes .sync.lock file' do
FileUtils.touch "#{path}/.sync.lock"
repository.remove_sync_lock_file
File.exist?("#{path}/.sync.lock").should be_false
end
it 'ensures that #add_repo_lock_file creates .repo.lock file' do
repository.add_repo_lock_file
File.exist?("#{path}/.repo.lock").should be_true
end
it 'ensures that #remove_repo_lock_file removes .repo.lock file' do
FileUtils.touch "#{path}/.repo.lock"
repository.remove_repo_lock_file
File.exist?("#{path}/.repo.lock").should be_false
end
end
context 'when create with same owner that platform' do context 'when create with same owner that platform' do
before do before do
@platform = FactoryGirl.create(:platform) @platform = FactoryGirl.create(:platform)