From ff2ae7398586ff134f7e187ea0d2c83918509d47 Mon Sep 17 00:00:00 2001 From: Vokhmin Alexey V Date: Thu, 6 Dec 2012 18:41:24 +0400 Subject: [PATCH] #759: add action for cancel build, some refactoring --- .../product_build_lists_controller.rb | 10 +- .../projects/build_lists_controller.rb | 12 +- app/models/ability.rb | 2 +- app/models/build_list.rb | 109 +++++++++--------- app/models/product_build_list.rb | 33 ++---- .../product_build_lists/show.html.haml | 9 +- config/locales/models/build_list.en.yml | 2 + config/locales/models/build_list.ru.yml | 2 + .../locales/models/product_build_list.en.yml | 4 +- .../locales/models/product_build_list.ru.yml | 4 +- config/routes.rb | 2 +- lib/abf_worker/model_helper.rb | 68 +++++++++++ .../product_build_lists_controller_spec.rb | 8 +- 13 files changed, 163 insertions(+), 102 deletions(-) create mode 100644 lib/abf_worker/model_helper.rb diff --git a/app/controllers/platforms/product_build_lists_controller.rb b/app/controllers/platforms/product_build_lists_controller.rb index 1d8a9b814..2bc8e4bbd 100644 --- a/app/controllers/platforms/product_build_lists_controller.rb +++ b/app/controllers/platforms/product_build_lists_controller.rb @@ -5,7 +5,7 @@ class Platforms::ProductBuildListsController < Platforms::BaseController load_and_authorize_resource :platform, :except => [:index, :status_build] load_and_authorize_resource :product, :through => :platform, :except => [:index, :status_build] load_and_authorize_resource :product_build_list, :through => :product, :except => [:index, :status_build] - load_and_authorize_resource :only => [:index, :show, :log, :stop] + load_and_authorize_resource :only => [:index, :show, :log, :cancel] before_filter :authenticate_product_builder!, :only => [:status_build] before_filter :find_product_build_list, :only => [:status_build] @@ -25,15 +25,15 @@ class Platforms::ProductBuildListsController < Platforms::BaseController def show end - def stop - @product_build_list.stop - flash[:notice] = t('flash.product_build_list.will_be_stopped') + def cancel + @product_build_list.cancel_job + flash[:notice] = t('layout.build_lists.will_be_canceled') redirect_to platform_product_product_build_list_path(@platform, @product, @product_build_list) end def log render :json => { - :log => @product_build_list.log, + :log => @product_build_list.abf_worker_log, :building => @product_build_list.build_started? } end diff --git a/app/controllers/projects/build_lists_controller.rb b/app/controllers/projects/build_lists_controller.rb index b9131fe95..c9d6a75d7 100644 --- a/app/controllers/projects/build_lists_controller.rb +++ b/app/controllers/projects/build_lists_controller.rb @@ -100,11 +100,17 @@ class Projects::BuildListsController < Projects::BaseController end def cancel - if @build_list.cancel - redirect_to :back, :notice => t('layout.build_lists.cancel_success') + if @build_list.new_core? && @build_list.can_cancel? + @build_list.cancel_job + notice = t('layout.build_lists.will_be_canceled') else - redirect_to :back, :notice => t('layout.build_lists.cancel_fail') + if @build_list.cancel + notice = t('layout.build_lists.cancel_success') + else + notice = t('layout.build_lists.cancel_fail') + end end + redirect_to :back, :notice => notice end def log diff --git a/app/models/ability.rb b/app/models/ability.rb index edba06d89..7331773d6 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -114,7 +114,7 @@ class Ability can(:read, Product, read_relations_for('products', 'platforms')) {|product| product.platform.main?} can([:create, :update, :destroy, :clone], Product) {|product| local_admin? product.platform and product.platform.main?} - can([:create, :stop], ProductBuildList) {|pbl| can?(:update, pbl.product)} + can([:create, :cancel], ProductBuildList) {|pbl| can?(:update, pbl.product)} can(:destroy, ProductBuildList) {|pbl| can?(:destroy, pbl.product)} can [:read, :create], PrivateUser, :platform => {:owner_type => 'User', :owner_id => user.id} diff --git a/app/models/build_list.rb b/app/models/build_list.rb index 6f9d6e69a..0120a86b2 100644 --- a/app/models/build_list.rb +++ b/app/models/build_list.rb @@ -1,6 +1,7 @@ # -*- encoding : utf-8 -*- class BuildList < ActiveRecord::Base include Modules::Models::CommitAndVersion + include AbfWorker::ModelHelper belongs_to :project belongs_to :arch @@ -49,11 +50,13 @@ class BuildList < ActiveRecord::Base BUILD_PUBLISH = 7000 FAILED_PUBLISH = 8000 REJECTED_PUBLISH = 9000 + BUILD_CANCELING = 10000 STATUSES = [ WAITING_FOR_RESPONSE, BUILD_CANCELED, BUILD_PENDING, BUILD_PUBLISHED, + BUILD_CANCELING, BUILD_PUBLISH, FAILED_PUBLISH, REJECTED_PUBLISH, @@ -70,6 +73,7 @@ class BuildList < ActiveRecord::Base HUMAN_STATUSES = { WAITING_FOR_RESPONSE => :waiting_for_response, BUILD_CANCELED => :build_canceled, + BUILD_CANCELING => :build_canceling, BUILD_PENDING => :build_pending, BUILD_PUBLISHED => :build_published, BUILD_PUBLISH => :build_publish, @@ -169,7 +173,7 @@ class BuildList < ActiveRecord::Base event :cancel do transition [:build_pending, :platform_pending] => :build_canceled, :if => lambda { |build_list| - build_list.can_cancel? && BuildServer.delete_build_list(build_list.bs_id) == BuildServer::SUCCESS + !build_list.new_core? && build_list.can_cancel? && BuildServer.delete_build_list(build_list.bs_id) == BuildServer::SUCCESS } end @@ -226,7 +230,11 @@ class BuildList < ActiveRecord::Base #TODO: Share this checking on product owner. def can_cancel? - [BUILD_PENDING, BuildServer::PLATFORM_PENDING].include?(status) && bs_id + if new_core? + build_started? || build_pending? + else + [BUILD_PENDING, BuildServer::PLATFORM_PENDING].include?(status) && bs_id + end end def can_publish? @@ -237,60 +245,10 @@ class BuildList < ActiveRecord::Base can_publish? and not save_to_repository.publish_without_qa end - def add_to_abf_worker_queue - include_repos_hash = {}.tap do |h| - include_repos.each do |r| - repo = Repository.find r - path = repo.platform.public_downloads_url(nil, arch.name, repo.name) - # path = path.gsub(/^http:\/\/0\.0\.0\.0\:3000/, 'https://abf.rosalinux.ru') - # Path looks like: - # http://abf.rosalinux.ru/downloads/rosa-server2012/repository/x86_64/base/ - # so, we should append: - # /release - # /updates - h["#{repo.name}_release"] = path + 'release' - h["#{repo.name}_updates"] = path + 'updates' - end - end - # mdv example: - # https://abf.rosalinux.ru/import/plasma-applet-stackfolder.git - # bfe6d68cc607238011a6108014bdcfe86c69456a - - # rhel example: - # https://abf.rosalinux.ru/server/gnome-settings-daemon.git - # fbb2549e44d97226fea6748a4f95d1d82ffb8726 - - options = { - :id => id, - :arch => arch.name, - :time_living => 2880, # 2 days - :distrib_type => build_for_platform.distrib_type, - # :git_project_address => 'https://abf.rosalinux.ru/server/gnome-settings-daemon.git', - :git_project_address => project.git_project_address, - # :commit_hash => 'fbb2549e44d97226fea6748a4f95d1d82ffb8726', - :commit_hash => commit_hash, - :build_requires => build_requires, - :include_repos => include_repos_hash, - :bplname => build_for_platform.name - # :project_version => project_version, - # :plname => save_to_platform.name, - # :bplname => (save_to_platform_id == build_for_platform_id ? '' : build_for_platform.name), - # :update_type => update_type, - # :priority => priority, - } - unless @status - Resque.push( - 'rpm_worker', - 'class' => 'AbfWorker::RpmWorker', - 'args' => [options] - ) - end - @status ||= BUILD_PENDING - end - def add_to_queue if new_core? - add_to_abf_worker_queue + add_job_to_abf_worker_queue unless @status + @status ||= BUILD_PENDING else # XML-RPC params: # - project_name @@ -390,16 +348,53 @@ class BuildList < ActiveRecord::Base def log(load_lines) if new_core? - log = Resque.redis.get("abfworker::rpm-worker-#{id}") + abf_worker_log else log = `tail -n #{load_lines.to_i} #{Rails.root + 'public' + fs_log_path}` - log = nil unless $?.success? + $?.success? ? log : I18n.t('layout.build_lists.log.not_available') end - log || I18n.t('layout.build_lists.log.not_available') end protected + def abf_worker_args + include_repos_hash = {}.tap do |h| + include_repos.each do |r| + repo = Repository.find r + path = repo.platform.public_downloads_url(nil, arch.name, repo.name) + # path = path.gsub(/^http:\/\/0\.0\.0\.0\:3000/, 'https://abf.rosalinux.ru') + # Path looks like: + # http://abf.rosalinux.ru/downloads/rosa-server2012/repository/x86_64/base/ + # so, we should append: + # /release + # /updates + h["#{repo.name}_release"] = path + 'release' + h["#{repo.name}_updates"] = path + 'updates' + end + end + # mdv example: + # https://abf.rosalinux.ru/import/plasma-applet-stackfolder.git + # bfe6d68cc607238011a6108014bdcfe86c69456a + + # rhel example: + # https://abf.rosalinux.ru/server/gnome-settings-daemon.git + # fbb2549e44d97226fea6748a4f95d1d82ffb8726 + + { + :id => id, + :arch => arch.name, + :time_living => 2880, # 2 days + :distrib_type => build_for_platform.distrib_type, + # :git_project_address => 'https://abf.rosalinux.ru/server/gnome-settings-daemon.git', + :git_project_address => project.git_project_address, + # :commit_hash => 'fbb2549e44d97226fea6748a4f95d1d82ffb8726', + :commit_hash => commit_hash, + :build_requires => build_requires, + :include_repos => include_repos_hash, + :bplname => build_for_platform.name + } + end + def notify_users unless mass_build_id users = [] diff --git a/app/models/product_build_list.rb b/app/models/product_build_list.rb index 5cfbed662..8548eb682 100644 --- a/app/models/product_build_list.rb +++ b/app/models/product_build_list.rb @@ -1,6 +1,7 @@ # -*- encoding : utf-8 -*- class ProductBuildList < ActiveRecord::Base include Modules::Models::CommitAndVersion + include AbfWorker::ModelHelper delegate :url_helpers, to: 'Rails.application.routes' BUILD_COMPLETED = 0 @@ -72,6 +73,10 @@ class ProductBuildList < ActiveRecord::Base status == BUILD_CANCELING end + def can_cancel? + [BUILD_STARTED, BUILD_PENDING].include? status + end + def container_path "/downloads/#{product.platform.name}/product/#{id}/" end @@ -92,22 +97,14 @@ class ProductBuildList < ActiveRecord::Base [BUILD_COMPLETED, BUILD_FAILED, BUILD_CANCELED].include? status end - def log - Resque.redis.get("abfworker::iso-worker-#{id}") || '' - end - - def stop - update_attributes({:status => BUILD_CANCELING}) - Resque.redis.setex( - "abfworker::iso-worker-#{id}::live-inspector", - 120, # Data will be removed from Redis after 120 sec. - 'USR1' # Immediately kill child but don't exit - ) - end - protected def xml_rpc_create + add_job_to_abf_worker_queue + return true + end + + def abf_worker_args file_name = "#{project.owner.uname}-#{project.name}-#{commit_hash}" srcpath = url_helpers.archive_url( project.owner, @@ -116,7 +113,7 @@ class ProductBuildList < ActiveRecord::Base 'tar.gz', :host => ActionMailer::Base.default_url_options[:host] ) - options = { + { :id => id, # TODO: remove comment # :srcpath => 'http://dl.dropbox.com/u/945501/avokhmin-test-iso-script-5d9b463d4e9c06ea8e7c89e1b7ff5cb37e99e27f.tar.gz', @@ -127,13 +124,7 @@ class ProductBuildList < ActiveRecord::Base :arch => arch.name, :distrib_type => product.platform.distrib_type } - Resque.push( - 'iso_worker', - 'class' => 'AbfWorker::IsoWorker', - 'args' => [options] - ) - return true - end + end def xml_delete_iso_container # TODO: write new worker for delete diff --git a/app/views/platforms/product_build_lists/show.html.haml b/app/views/platforms/product_build_lists/show.html.haml index 87aff3f1d..a39d22ecd 100644 --- a/app/views/platforms/product_build_lists/show.html.haml +++ b/app/views/platforms/product_build_lists/show.html.haml @@ -23,11 +23,12 @@ = render 'show_field', :key => :notified_at, :value => l(pbl.updated_at, :format => :long) +- if pbl.can_cancel? && can?(:cancel, pbl) + = link_to t("layout.build_lists.cancel"), cancel_platform_product_product_build_list_path(pbl.product.platform, pbl.product, pbl), + :method => :put, :confirm => t("layout.confirm"), :class => 'button' + .both + - if pbl.build_started? || pbl.build_canceling? - - if can? :stop, pbl - .leftlist= t("layout.product_build_lists.action") - .rightlist= link_to t("layout.product_build_lists.stop"), stop_platform_product_product_build_list_path(pbl.product.platform, pbl.product, pbl) - .both = render 'shared/log', { :build_started => true, :get_log_path => log_platform_product_product_build_list_path(pbl.product.platform, pbl.product, pbl) } = render 'results', :pbl => pbl diff --git a/config/locales/models/build_list.en.yml b/config/locales/models/build_list.en.yml index 8d381f75b..de90011ee 100644 --- a/config/locales/models/build_list.en.yml +++ b/config/locales/models/build_list.en.yml @@ -62,6 +62,7 @@ en: show: Show cancel: Cancel build cancel_success: 'Build canceled' + will_be_canceled: 'Build will be canceled...' publish_success: 'Build published' cancel_fail: 'Errors during build cancelation!' publish_fail: 'Errors during build publishing!' @@ -111,6 +112,7 @@ en: dependencies_fail: Dependences not found waiting_for_response: Waiting for response build_pending: Build pending + build_canceling: Build is canceling dependency_test_failed: Dependency test failed binary_test_failed: Binary test failed build_canceled: Build canceled diff --git a/config/locales/models/build_list.ru.yml b/config/locales/models/build_list.ru.yml index a04a52864..c7fb18275 100644 --- a/config/locales/models/build_list.ru.yml +++ b/config/locales/models/build_list.ru.yml @@ -60,6 +60,7 @@ ru: no_items_data: Данных нет show: Просмотр cancel: Отменить сборку + will_be_canceled: 'Сборка будет отменена...' cancel_success: 'Сборка отменена.' cancel_fail: 'При отмене сборки произошла ошибка!' publish_success: 'Сборка поставлена в очередь на публикацию.' @@ -111,6 +112,7 @@ ru: dependency_test_failed: тестирование зависимостей не пройдено binary_test_failed: тестирование бинарной совместимости не пройдено build_canceled: сборка отменена + build_canceling: сборка отменяется success: собран build_started: собирается platform_not_found: платформа не найдена diff --git a/config/locales/models/product_build_list.en.yml b/config/locales/models/product_build_list.en.yml index 9402287c2..945bdaaec 100644 --- a/config/locales/models/product_build_list.en.yml +++ b/config/locales/models/product_build_list.en.yml @@ -8,7 +8,6 @@ en: results_folder: all files from this folder will be uploaded into the file-store archives_folder: this folder will be archived and uploaded into the file-store delete: Delete - stop: Stop action: Action id_search: 'Id search' new: New build @@ -52,5 +51,4 @@ en: product_build_list: no_project: Project for build should be exist delete: Product build list deleted - delete_error: Unable to delete product build list - will_be_stopped: Build will be stopped \ No newline at end of file + delete_error: Unable to delete product build list \ No newline at end of file diff --git a/config/locales/models/product_build_list.ru.yml b/config/locales/models/product_build_list.ru.yml index 8411c9461..a6efbd419 100644 --- a/config/locales/models/product_build_list.ru.yml +++ b/config/locales/models/product_build_list.ru.yml @@ -8,7 +8,6 @@ ru: results_folder: все файлы из этой папки будут загружены на file-store archives_folder: эта папка будет заархивирована и загружена на file-store delete: Удалить - stop: Остановить action: Действие id_search: 'Поиск по Id' new: Новая сборка @@ -52,5 +51,4 @@ ru: product_build_list: no_project: Проект для сборки должен присутствовать delete: Сборочный лист продукта удален - delete_error: Не удалось удалить cборочный лист продукта - will_be_stopped: Сборка будет остановлена + delete_error: Не удалось удалить cборочный лист продукта \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 05d409a6d..2440bb70c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -151,7 +151,7 @@ Rosa::Application.routes.draw do resources :product_build_lists, :only => [:create, :destroy, :new, :show] do member { get :log - get :stop + put :cancel } end collection { get :autocomplete_project } diff --git a/lib/abf_worker/model_helper.rb b/lib/abf_worker/model_helper.rb new file mode 100644 index 000000000..fd58c17e5 --- /dev/null +++ b/lib/abf_worker/model_helper.rb @@ -0,0 +1,68 @@ +module AbfWorker + module ModelHelper + # In model which contains this helper should be: + # - BUILD_CANCELING + # - BUILD_CANCELED + # - #abf_worker_args + + def abf_worker_log + q = 'abfworker::' + q << worker_queue + q << '-' + q << id.to_s + Resque.redis.get(q) || I18n.t('layout.build_lists.log.not_available') + end + + def add_job_to_abf_worker_queue + Resque.push( + worker_queue, + 'class' => worker_queue_class, + 'args' => [abf_worker_args] + ) + end + + def cancel_job + update_attributes({:status => self.class::BUILD_CANCELING}) + + deleted = Resque::Job.destroy( + worker_queue, + worker_queue_class, + abf_worker_args + ) + if deleted + update_attributes({:status => self.class::BUILD_CANCELED}) + else + send_stop_signal + end + true + end + + private + + def send_stop_signal + Resque.redis.setex( + live_inspector_queue, + 90, # Data will be removed from Redis after 90 sec. + 'USR1' # Immediately kill child but don't exit + ) + end + + def live_inspector_queue + q = 'abfworker::' + q << worker_queue + q << '-' + q << id.to_s + q << '::live-inspector' + q + end + + def worker_queue + is_a?(BuildList) ? 'rpm_worker' : 'iso_worker' + end + + def worker_queue_class + is_a?(BuildList) ? 'AbfWorker::RpmWorker' : 'AbfWorker::IsoWorker' + end + + end +end \ No newline at end of file diff --git a/spec/controllers/platforms/product_build_lists_controller_spec.rb b/spec/controllers/platforms/product_build_lists_controller_spec.rb index 75b833a93..a5be42f7e 100644 --- a/spec/controllers/platforms/product_build_lists_controller_spec.rb +++ b/spec/controllers/platforms/product_build_lists_controller_spec.rb @@ -23,8 +23,8 @@ shared_examples_for 'product build list admin' do response.should render_template(:index) end - it 'should be able to perform stop action' do - get :stop, valid_attributes_for_show + it 'should be able to perform cancel action' do + put :cancel, valid_attributes_for_show response.should redirect_to(platform_product_product_build_list_path(@product.platform, @product, @pbl)) end @@ -56,8 +56,8 @@ shared_examples_for 'product build list user without admin rights' do response.should_not be_success end - it 'should not be able to perform stop action' do - get :stop, valid_attributes_for_show + it 'should not be able to perform cancel action' do + put :cancel, valid_attributes_for_show response.should_not redirect_to(platform_product_product_build_list_path(@product.platform, @product, @pbl)) end end