From d0032935a1d720c8e3cfd2597667c62a0c4015a1 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 4 May 2016 14:34:11 +0200 Subject: Add runner db field for ability to run untagged jobs --- db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb | 11 +++++++++++ db/schema.rb | 1 + 2 files changed, 12 insertions(+) create mode 100644 db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb diff --git a/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb b/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb new file mode 100644 index 00000000000..38fee17f904 --- /dev/null +++ b/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb @@ -0,0 +1,11 @@ +class AddRunUntaggedToCiRunner < ActiveRecord::Migration + ## + # Downtime expected! + # + # This migration will cause downtime due to exclusive lock + # caused by the default value. + # + def change + add_column :ci_runners, :run_untagged, :boolean, default: true + end +end diff --git a/db/schema.rb b/db/schema.rb index af4f4c609e7..5b9fbe79fa2 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -269,6 +269,7 @@ ActiveRecord::Schema.define(version: 20160509201028) do t.string "revision" t.string "platform" t.string "architecture" + t.boolean "run_untagged", default: true end add_index "ci_runners", ["description"], name: "index_ci_runners_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"} -- cgit v1.2.1 From a8a205cd5c3f2840e7e8eab16bceff608c288f33 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 4 May 2016 14:40:03 +0200 Subject: Add form for runner config to run untagged jobs --- app/views/projects/runners/_form.html.haml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/views/projects/runners/_form.html.haml b/app/views/projects/runners/_form.html.haml index 2d6c964ae94..147f1a2ebc8 100644 --- a/app/views/projects/runners/_form.html.haml +++ b/app/views/projects/runners/_form.html.haml @@ -5,6 +5,12 @@ .checkbox = f.check_box :active %span.light Paused runners don't accept new builds + .form-group + = label :run_untagged, 'Run untagged jobs', class: 'control-label' + .col-sm-10 + .checkbox + = f.check_box :run_untagged + %span.light This runner can pick jobs without tags .form-group = label_tag :token, class: 'control-label' do Token -- cgit v1.2.1 From 2aa57071947e562f302b3858a249339024ef7119 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 5 May 2016 12:20:33 +0200 Subject: Extend runner config options for untagged jobs --- app/models/ci/runner.rb | 2 +- app/views/admin/runners/show.html.haml | 2 -- app/views/projects/runners/_runner.html.haml | 2 +- app/views/projects/runners/show.html.haml | 51 +++++++++++----------------- spec/features/runners_spec.rb | 29 ++++++++++++++++ 5 files changed, 51 insertions(+), 35 deletions(-) diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 819064f99bb..23b1556712c 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -4,7 +4,7 @@ module Ci LAST_CONTACT_TIME = 5.minutes.ago AVAILABLE_SCOPES = %w[specific shared active paused online] - FORM_EDITABLE = %i[description tag_list active] + FORM_EDITABLE = %i[description tag_list active run_untagged] has_many :builds, class_name: 'Ci::Build' has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject' diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml index 4dfb3ed05bb..c3784bf7192 100644 --- a/app/views/admin/runners/show.html.haml +++ b/app/views/admin/runners/show.html.haml @@ -9,8 +9,6 @@ %span.runner-state.runner-state-specific Specific - - - if @runner.shared? .bs-callout.bs-callout-success %h4 This runner will process builds from ALL UNASSIGNED projects diff --git a/app/views/projects/runners/_runner.html.haml b/app/views/projects/runners/_runner.html.haml index 47ec420189d..96e2aac451f 100644 --- a/app/views/projects/runners/_runner.html.haml +++ b/app/views/projects/runners/_runner.html.haml @@ -5,7 +5,7 @@ - if @runners.include?(runner) = link_to runner.short_sha, runner_path(runner) %small - =link_to edit_namespace_project_runner_path(@project.namespace, @project, runner) do + = link_to edit_namespace_project_runner_path(@project.namespace, @project, runner) do %i.fa.fa-edit.btn - else = runner.short_sha diff --git a/app/views/projects/runners/show.html.haml b/app/views/projects/runners/show.html.haml index 5bf4c09ca25..f24e1b9144e 100644 --- a/app/views/projects/runners/show.html.haml +++ b/app/views/projects/runners/show.html.haml @@ -17,50 +17,39 @@ %th Property Name %th Value %tr - %td - Tags + %td Active + %td= @runner.active? ? 'Yes' : 'No' + %tr + %td Can run untagged jobs + %td= @runner.run_untagged? ? 'Yes' : 'No' + %tr + %td Tags %td - @runner.tag_list.each do |tag| %span.label.label-primary = tag %tr - %td - Name - %td - = @runner.name + %td Name + %td= @runner.name %tr - %td - Version - %td - = @runner.version + %td Version + %td= @runner.version %tr - %td - Revision - %td - = @runner.revision + %td Revision + %td= @runner.revision %tr - %td - Platform - %td - = @runner.platform + %td Platform + %td= @runner.platform %tr - %td - Architecture - %td - = @runner.architecture + %td Architecture + %td= @runner.architecture %tr - %td - Description - %td - = @runner.description + %td Description + %td= @runner.description %tr - %td - Last contact + %td Last contact %td - if @runner.contacted_at #{time_ago_in_words(@runner.contacted_at)} ago - else Never - - - diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index 8edeb8d18af..4f942c3ab1c 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -110,4 +110,33 @@ describe "Runners" do expect(page).to have_content(@specific_runner.platform) end end + + feature 'configuring runners ability to picking untagged jobs' do + given(:project) { create(:empty_project) } + given(:runner) { create(:ci_runner) } + + background do + project.team << [user, :master] + project.runners << runner + end + + scenario 'user checks default configuration' do + visit namespace_project_runner_path(project.namespace, project, runner) + + expect(page).to have_content 'Can run untagged jobs Yes' + end + + scenario 'user want to prevent runner from running untagged job' do + visit runners_path(project) + page.within('.activated-specific-runners') do + first('small > a').click + end + + uncheck 'runner_run_untagged' + click_button 'Save changes' + + expect(page).to have_content 'Can run untagged jobs No' + expect(runner.reload.run_untagged?).to eq false + end + end end -- cgit v1.2.1 From 83df6384558c27d3ff7282e6d66b06fa7e9c0c60 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 5 May 2016 13:08:17 +0200 Subject: Disallow runner to pick untagged build if configured --- app/models/ci/build.rb | 4 ++++ spec/models/build_spec.rb | 38 +++++++++++++++++++++++++++----------- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 92327bdb08d..77a2dec4f72 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -290,6 +290,10 @@ module Ci end def can_be_served?(runner) + if tag_list.empty? && !runner.run_untagged? + return false + end + (tag_list - runner.tag_list).empty? end diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index b5d356aa066..5da54e07de8 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -259,11 +259,11 @@ describe Ci::Build, models: true do end describe '#can_be_served?' do - let(:runner) { FactoryGirl.create :ci_runner } + let(:runner) { create(:ci_runner) } before { build.project.runners << runner } - context 'runner without tags' do + context 'when runner does not have tags' do it 'can handle builds without tags' do expect(build.can_be_served?(runner)).to be_truthy end @@ -274,21 +274,37 @@ describe Ci::Build, models: true do end end - context 'runner with tags' do + context 'when runner has tags' do before { runner.tag_list = ['bb', 'cc'] } - it 'can handle builds without tags' do - expect(build.can_be_served?(runner)).to be_truthy + shared_examples 'tagged build picker' do + it 'can handle build with matching tags' do + build.tag_list = ['bb'] + expect(build.can_be_served?(runner)).to be_truthy + end + + it 'cannot handle build without matching tags' do + build.tag_list = ['aa'] + expect(build.can_be_served?(runner)).to be_falsey + end end - it 'can handle build with matching tags' do - build.tag_list = ['bb'] - expect(build.can_be_served?(runner)).to be_truthy + context 'when runner can pick untagged jobs' do + it 'can handle builds without tags' do + expect(build.can_be_served?(runner)).to be_truthy + end + + it_behaves_like 'tagged build picker' end - it 'cannot handle build with not matching tags' do - build.tag_list = ['aa'] - expect(build.can_be_served?(runner)).to be_falsey + context 'when runner can not pick untagged jobs' do + before { runner.run_untagged = false } + + it 'can not handle builds without tags' do + expect(build.can_be_served?(runner)).to be_falsey + end + + it_behaves_like 'tagged build picker' end end end -- cgit v1.2.1 From 9129c37c5bd6e648e23ebe6847b909dd151c7e8a Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 5 May 2016 13:28:44 +0200 Subject: Add CI API tests for runner config and untagged jobs --- spec/requests/ci/api/builds_spec.rb | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index cae4656010f..7ebf8e41f3b 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -128,6 +128,38 @@ describe Ci::API::API do end end end + + context 'when build has no tags' do + before do + commit = create(:ci_commit, project: project) + create(:ci_build, commit: commit, tags: []) + end + + context 'when runner is allowed to pick untagged builds' do + before { runner.update_column(:run_untagged, true) } + + it 'picks build' do + register_builds + + expect(response).to have_http_status 201 + end + end + + context 'when runner is not allowed to pick untagged builds' do + before { runner.update_column(:run_untagged, false) } + + it 'does not pick build' do + register_builds + + expect(response).to have_http_status 404 + end + end + + def register_builds + post ci_api("/builds/register"), token: runner.token, + info: { platform: :darwin } + end + end end describe "PUT /builds/:id" do -- cgit v1.2.1 From dd8e9e2d7acf05b837a3bacd1258e9f3df4254a6 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 5 May 2016 14:27:06 +0200 Subject: Add custom validator to runner model --- app/controllers/projects/runners_controller.rb | 3 ++- app/models/ci/runner.rb | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index 3a9d67aff64..12172b47520 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -20,7 +20,8 @@ class Projects::RunnersController < Projects::ApplicationController if @runner.update_attributes(runner_params) redirect_to runner_path(@runner), notice: 'Runner was successfully updated.' else - redirect_to runner_path(@runner), alert: 'Runner was not updated.' + flash[:alert] = @runner.errors.full_messages.to_sentence + render 'edit' end end diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 23b1556712c..b1227d72405 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -26,6 +26,13 @@ module Ci .where("ci_runner_projects.gl_project_id = :project_id OR ci_runners.is_shared = true", project_id: project_id) end + validate do |runner| + if runner.tag_list.empty? && !runner.run_untagged? + errors.add(:tags_errors, + 'Runner without tags must be able to pick untagged jobs!') + end + end + acts_as_taggable # Searches for runners matching the given query. -- cgit v1.2.1 From 2ee24bd9ece394269c006f9762d3fa5e16876949 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 6 May 2016 08:41:51 +0200 Subject: Update specs to be valid only for tagged runner --- spec/features/runners_spec.rb | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index 4f942c3ab1c..2c9c12b4dfa 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -126,17 +126,21 @@ describe "Runners" do expect(page).to have_content 'Can run untagged jobs Yes' end - scenario 'user want to prevent runner from running untagged job' do - visit runners_path(project) - page.within('.activated-specific-runners') do - first('small > a').click - end + context 'when runner has tags' do + before { runner.update_attribute(:tag_list, ['tag']) } - uncheck 'runner_run_untagged' - click_button 'Save changes' + scenario 'user want to prevent runner from running untagged job' do + visit runners_path(project) + page.within('.activated-specific-runners') do + first('small > a').click + end - expect(page).to have_content 'Can run untagged jobs No' - expect(runner.reload.run_untagged?).to eq false + uncheck 'runner_run_untagged' + click_button 'Save changes' + + expect(page).to have_content 'Can run untagged jobs No' + expect(runner.reload.run_untagged?).to eq false + end end end end -- cgit v1.2.1 From 9fd6f1b6009410cee0d0d7db8703ad64701db62b Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 6 May 2016 08:47:19 +0200 Subject: Improve displaying validation messages for runner --- app/controllers/projects/runners_controller.rb | 1 - app/models/ci/runner.rb | 10 +++++++--- app/views/projects/runners/edit.html.haml | 6 ++++++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index 12172b47520..0b4fa572501 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -20,7 +20,6 @@ class Projects::RunnersController < Projects::ApplicationController if @runner.update_attributes(runner_params) redirect_to runner_path(@runner), notice: 'Runner was successfully updated.' else - flash[:alert] = @runner.errors.full_messages.to_sentence render 'edit' end end diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index b1227d72405..f5813589492 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -27,9 +27,9 @@ module Ci end validate do |runner| - if runner.tag_list.empty? && !runner.run_untagged? - errors.add(:tags_errors, - 'Runner without tags must be able to pick untagged jobs!') + unless runner.has_tags? || runner.run_untagged? + errors.add(:tags_list, + 'can not be empty when runner is not allowed to pick untagged jobs') end end @@ -103,5 +103,9 @@ module Ci def short_sha token[0...8] if token end + + def has_tags? + tag_list.any? + end end end diff --git a/app/views/projects/runners/edit.html.haml b/app/views/projects/runners/edit.html.haml index 771947d7908..a5cfa9a8da9 100644 --- a/app/views/projects/runners/edit.html.haml +++ b/app/views/projects/runners/edit.html.haml @@ -1,5 +1,11 @@ - page_title "Edit", "#{@runner.description} ##{@runner.id}", "Runners" %h4 Runner ##{@runner.id} + +- if @runner.errors.any? + .error-message.js-errors + - @runner.errors.full_messages.each do |error| + %div= error + %hr = render 'form', runner: @runner, runner_form_url: runner_path(@runner) -- cgit v1.2.1 From 8252333183dcff74fd229ca81e7317641ad30791 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 6 May 2016 09:04:09 +0200 Subject: Extend CI runners specs --- spec/models/ci/runner_spec.rb | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index eaa94228922..7c6e39419ed 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -1,6 +1,24 @@ require 'spec_helper' describe Ci::Runner, models: true do + describe 'validation' do + context 'when runner is not allowed to pick untagged jobs' do + context 'when runner does not have tags' do + it 'is not valid' do + runner = build(:ci_runner, tag_list: [], run_untagged: false) + expect(runner).to be_invalid + end + end + + context 'when runner has tags' do + it 'is valid' do + runner = build(:ci_runner, tag_list: ['tag'], run_untagged: false) + expect(runner).to be_valid + end + end + end + end + describe '#display_name' do it 'should return the description if it has a value' do runner = FactoryGirl.build(:ci_runner, description: 'Linux/Ruby-1.9.3-p448') @@ -114,7 +132,19 @@ describe Ci::Runner, models: true do end end - describe '#search' do + describe '#has_tags?' do + context 'when runner has tags' do + subject { create(:ci_runner, tag_list: ['tag']) } + it { is_expected.to have_tags } + end + + context 'when runner does not have tags' do + subject { create(:ci_runner, tag_list: []) } + it { is_expected.to_not have_tags } + end + end + + describe '.search' do let(:runner) { create(:ci_runner, token: '123abc') } it 'returns runners with a matching token' do -- cgit v1.2.1 From 8001eed06e5871f1cb108ceb70213c8b3f9192d2 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 6 May 2016 09:17:27 +0200 Subject: Add method that check if build has tags --- app/models/ci/build.rb | 8 +++++--- spec/models/build_spec.rb | 12 ++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 77a2dec4f72..6c9ce0dc481 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -290,13 +290,15 @@ module Ci end def can_be_served?(runner) - if tag_list.empty? && !runner.run_untagged? - return false - end + return false unless has_tags? || runner.run_untagged? (tag_list - runner.tag_list).empty? end + def has_tags? + tag_list.any? + end + def any_runners_online? project.any_runners? { |runner| runner.active? && runner.online? && can_be_served?(runner) } end diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index 5da54e07de8..abae3271a5c 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -309,6 +309,18 @@ describe Ci::Build, models: true do end end + describe '#has_tags?' do + context 'when build has tags' do + subject { create(:ci_build, tag_list: ['tag']) } + it { is_expected.to have_tags } + end + + context 'when build does not have tags' do + subject { create(:ci_build, tag_list: []) } + it { is_expected.to_not have_tags } + end + end + describe '#any_runners_online?' do subject { build.any_runners_online? } -- cgit v1.2.1 From 7b607cf4f0edda45e864c07b468b0b0819f974df Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 6 May 2016 09:58:17 +0200 Subject: Add Changelog entry for new runner configuration --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index 422718dec9a..e8987000f6f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -6,6 +6,7 @@ v 8.8.0 (unreleased) - Assign labels and milestone to target project when moving issue. !3934 (Long Nguyen) - Use a case-insensitive comparison in sanitizing URI schemes - Toggle sign-up confirmation emails in application settings + - Make it possible to prevent tagged runner from picking untagged jobs - Project#open_branches has been cleaned up and no longer loads entire records into memory. - Escape HTML in commit titles in system note messages - Improve multiple branch push performance by memoizing permission checking -- cgit v1.2.1 From da8b72d45300db3339925cf34800b6d17828582f Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 6 May 2016 12:32:17 +0200 Subject: Extend runner options that are configurable via API --- lib/api/entities.rb | 1 + lib/api/runners.rb | 2 +- spec/requests/api/runners_spec.rb | 15 +++++++++++---- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/api/entities.rb b/lib/api/entities.rb index dbd03ea74fa..8298e3ad34c 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -408,6 +408,7 @@ module API class RunnerDetails < Runner expose :tag_list + expose :run_untagged expose :version, :revision, :platform, :architecture expose :contacted_at expose :token, if: lambda { |runner, options| options[:current_user].is_admin? || !runner.is_shared? } diff --git a/lib/api/runners.rb b/lib/api/runners.rb index 8ec91485b26..4faba9dc87b 100644 --- a/lib/api/runners.rb +++ b/lib/api/runners.rb @@ -49,7 +49,7 @@ module API runner = get_runner(params[:id]) authenticate_update_runner!(runner) - attrs = attributes_for_keys [:description, :active, :tag_list] + attrs = attributes_for_keys [:description, :active, :tag_list, :run_untagged] if runner.update(attrs) present runner, with: Entities::RunnerDetails, current_user: current_user else diff --git a/spec/requests/api/runners_spec.rb b/spec/requests/api/runners_spec.rb index 3af61d4b335..73ae8ef631c 100644 --- a/spec/requests/api/runners_spec.rb +++ b/spec/requests/api/runners_spec.rb @@ -184,21 +184,24 @@ describe API::Runners, api: true do description = shared_runner.description active = shared_runner.active - put api("/runners/#{shared_runner.id}", admin), description: "#{description}_updated", active: !active, - tag_list: ['ruby2.1', 'pgsql', 'mysql'] + update_runner(shared_runner.id, admin, description: "#{description}_updated", + active: !active, + tag_list: ['ruby2.1', 'pgsql', 'mysql'], + run_untagged: 'false') shared_runner.reload expect(response.status).to eq(200) expect(shared_runner.description).to eq("#{description}_updated") expect(shared_runner.active).to eq(!active) expect(shared_runner.tag_list).to include('ruby2.1', 'pgsql', 'mysql') + expect(shared_runner.run_untagged?).to be false end end context 'when runner is not shared' do it 'should update runner' do description = specific_runner.description - put api("/runners/#{specific_runner.id}", admin), description: 'test' + update_runner(specific_runner.id, admin, description: 'test') specific_runner.reload expect(response.status).to eq(200) @@ -208,10 +211,14 @@ describe API::Runners, api: true do end it 'should return 404 if runner does not exists' do - put api('/runners/9999', admin), description: 'test' + update_runner(9999, admin, description: 'test') expect(response.status).to eq(404) end + + def update_runner(id, user, args) + put api("/runners/#{id}", user), args + end end context 'authorized user' do -- cgit v1.2.1 From b253aa32c75cb5b0796ad6a7cd85cba78516ba26 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 6 May 2016 12:44:20 +0200 Subject: Add docs for a new configuration setting for runner --- doc/ci/runners/README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/ci/runners/README.md b/doc/ci/runners/README.md index a06650b3387..e449d3dc61e 100644 --- a/doc/ci/runners/README.md +++ b/doc/ci/runners/README.md @@ -125,7 +125,13 @@ shared runners will only run the jobs they are equipped to run. For instance, at GitLab we have runners tagged with "rails" if they contain the appropriate dependencies to run Rails test suites. -### Be Careful with Sensitive Information +### Prevent runner with tags from picking jobs without tags + +You can configure runner to prevent it from picking jobs with tags when +runnner does not have tags assigned. This configuration setting is available +in GitLab interface when editting runner details. + +### Be careful with sensitive information If you can run a build on a runner, you can get access to any code it runs and get the token of the runner. With shared runners, this means that anyone -- cgit v1.2.1 From 9ba72378fc006ecd353e1447a50d2231df09c851 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 6 May 2016 13:56:22 +0200 Subject: Refactor CI API specs for creating runner --- spec/requests/ci/api/runners_spec.rb | 46 +++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/spec/requests/ci/api/runners_spec.rb b/spec/requests/ci/api/runners_spec.rb index db8189ffb79..5d6f5f774ea 100644 --- a/spec/requests/ci/api/runners_spec.rb +++ b/spec/requests/ci/api/runners_spec.rb @@ -12,44 +12,56 @@ describe Ci::API::API do end describe "POST /runners/register" do - describe "should create a runner if token provided" do + context 'when runner token is provided' do before { post ci_api("/runners/register"), token: registration_token } - it { expect(response.status).to eq(201) } + it 'creates runner' do + expect(response.status).to eq(201) + end end - describe "should create a runner with description" do + context 'when runner description is provided' do before { post ci_api("/runners/register"), token: registration_token, description: "server.hostname" } - it { expect(response.status).to eq(201) } - it { expect(Ci::Runner.first.description).to eq("server.hostname") } + it 'creates runner' do + expect(response.status).to eq(201) + expect(Ci::Runner.first.description).to eq("server.hostname") + end end - describe "should create a runner with tags" do + context 'when runner tags are provided' do before { post ci_api("/runners/register"), token: registration_token, tag_list: "tag1, tag2" } - it { expect(response.status).to eq(201) } - it { expect(Ci::Runner.first.tag_list.sort).to eq(["tag1", "tag2"]) } + it 'creates runner' do + expect(response.status).to eq(201) + expect(Ci::Runner.first.tag_list.sort).to eq(["tag1", "tag2"]) + end end - describe "should create a runner if project token provided" do + context 'when project token is provided' do let(:project) { FactoryGirl.create(:empty_project) } before { post ci_api("/runners/register"), token: project.runners_token } - it { expect(response.status).to eq(201) } - it { expect(project.runners.size).to eq(1) } + it 'creates runner' do + expect(response.status).to eq(201) + expect(project.runners.size).to eq(1) + end end - it "should return 403 error if token is invalid" do - post ci_api("/runners/register"), token: 'invalid' + context 'when token is invalid' do + it 'returns 403 error' do + post ci_api("/runners/register"), token: 'invalid' - expect(response.status).to eq(403) + expect(response.status).to eq(403) + end end - it "should return 400 error if no token" do - post ci_api("/runners/register") + context 'when no token provided' do + it 'returns 400 error' do + post ci_api("/runners/register") - expect(response.status).to eq(400) + expect(response.status).to eq(400) + end end %w(name version revision platform architecture).each do |param| -- cgit v1.2.1 From b8cf2a340b3c56eb7e226473034ead2c4e5d609a Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Fri, 6 May 2016 14:06:57 +0200 Subject: Set run untagged option when registering a runner --- lib/ci/api/runners.rb | 15 ++++++--------- spec/requests/ci/api/runners_spec.rb | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/lib/ci/api/runners.rb b/lib/ci/api/runners.rb index 192b1d18a51..ea35d0f6dd0 100644 --- a/lib/ci/api/runners.rb +++ b/lib/ci/api/runners.rb @@ -28,20 +28,17 @@ module Ci post "register" do required_attributes! [:token] + attributes = { description: params[:description], + tag_list: params[:tag_list], + run_untagged: params[:run_untagged] || true } + runner = if runner_registration_token_valid? # Create shared runner. Requires admin access - Ci::Runner.create( - description: params[:description], - tag_list: params[:tag_list], - is_shared: true - ) + Ci::Runner.create(attributes.merge(is_shared: true)) elsif project = Project.find_by(runners_token: params[:token]) # Create a specific runner for project. - project.runners.create( - description: params[:description], - tag_list: params[:tag_list] - ) + project.runners.create(attributes) end return forbidden! unless runner diff --git a/spec/requests/ci/api/runners_spec.rb b/spec/requests/ci/api/runners_spec.rb index 5d6f5f774ea..eb11258cbbb 100644 --- a/spec/requests/ci/api/runners_spec.rb +++ b/spec/requests/ci/api/runners_spec.rb @@ -15,13 +15,17 @@ describe Ci::API::API do context 'when runner token is provided' do before { post ci_api("/runners/register"), token: registration_token } - it 'creates runner' do + it 'creates runner with default values' do expect(response.status).to eq(201) + expect(Ci::Runner.first.run_untagged).to be true end end context 'when runner description is provided' do - before { post ci_api("/runners/register"), token: registration_token, description: "server.hostname" } + before do + post ci_api("/runners/register"), token: registration_token, + description: "server.hostname" + end it 'creates runner' do expect(response.status).to eq(201) @@ -30,7 +34,10 @@ describe Ci::API::API do end context 'when runner tags are provided' do - before { post ci_api("/runners/register"), token: registration_token, tag_list: "tag1, tag2" } + before do + post ci_api("/runners/register"), token: registration_token, + tag_list: "tag1, tag2" + end it 'creates runner' do expect(response.status).to eq(201) @@ -38,6 +45,28 @@ describe Ci::API::API do end end + context 'when option for running untagged jobs is provided' do + context 'when tags are provided' do + it 'creates runner' do + post ci_api("/runners/register"), token: registration_token, + run_untagged: false, + tag_list: ['tag'] + + expect(response.status).to eq(201) + expect(Ci::Runner.first.run_untagged).to be false + end + end + + context 'when tags are not provided' do + it 'does not create runner' do + post ci_api("/runners/register"), token: registration_token, + run_untagged: false + + expect(response.status).to eq(404) + end + end + end + context 'when project token is provided' do let(:project) { FactoryGirl.create(:empty_project) } before { post ci_api("/runners/register"), token: project.runners_token } -- cgit v1.2.1 From 3b0eeccc0324e2d6c024fd94067933314e45b862 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 7 May 2016 20:23:36 +0200 Subject: Add not null constraint to run untagged runner option --- db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb | 2 +- db/schema.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb b/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb index 38fee17f904..c6753f2b398 100644 --- a/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb +++ b/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb @@ -6,6 +6,6 @@ class AddRunUntaggedToCiRunner < ActiveRecord::Migration # caused by the default value. # def change - add_column :ci_runners, :run_untagged, :boolean, default: true + add_column :ci_runners, :run_untagged, :boolean, default: true, null: false end end diff --git a/db/schema.rb b/db/schema.rb index 5b9fbe79fa2..2e154b98b34 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -269,7 +269,7 @@ ActiveRecord::Schema.define(version: 20160509201028) do t.string "revision" t.string "platform" t.string "architecture" - t.boolean "run_untagged", default: true + t.boolean "run_untagged", default: true, null: false end add_index "ci_runners", ["description"], name: "index_ci_runners_on_description_trigram", using: :gin, opclasses: {"description"=>"gin_trgm_ops"} -- cgit v1.2.1 From 8ab4a67ca6a9166168b744bc940da98bf9651efd Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 7 May 2016 20:35:08 +0200 Subject: Use form errors helper in CI runner edit form --- app/views/projects/runners/edit.html.haml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/views/projects/runners/edit.html.haml b/app/views/projects/runners/edit.html.haml index a5cfa9a8da9..e4aa98080e5 100644 --- a/app/views/projects/runners/edit.html.haml +++ b/app/views/projects/runners/edit.html.haml @@ -2,10 +2,7 @@ %h4 Runner ##{@runner.id} -- if @runner.errors.any? - .error-message.js-errors - - @runner.errors.full_messages.each do |error| - %div= error += form_errors(@runner) %hr = render 'form', runner: @runner, runner_form_url: runner_path(@runner) -- cgit v1.2.1 From f6dd8a5257bf71f87ea9a2fa2f3d16012e61b6e4 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 7 May 2016 20:36:03 +0200 Subject: Move runner validator to separate private method --- app/models/ci/runner.rb | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index f5813589492..cf4d3236519 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -26,12 +26,7 @@ module Ci .where("ci_runner_projects.gl_project_id = :project_id OR ci_runners.is_shared = true", project_id: project_id) end - validate do |runner| - unless runner.has_tags? || runner.run_untagged? - errors.add(:tags_list, - 'can not be empty when runner is not allowed to pick untagged jobs') - end - end + validate :verify_tags_constraints acts_as_taggable @@ -107,5 +102,14 @@ module Ci def has_tags? tag_list.any? end + + private + + def verify_tags_constraints + unless has_tags? || run_untagged? + errors.add(:tags_list, + 'can not be empty when runner is not allowed to pick untagged jobs') + end + end end end -- cgit v1.2.1 From 0fd100d28d3748de90aabc3dbbb789e37399f224 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Sat, 7 May 2016 20:42:36 +0200 Subject: Improve setting default runner attrs when using API --- lib/ci/api/runners.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/ci/api/runners.rb b/lib/ci/api/runners.rb index ea35d0f6dd0..1dc4c8c2cd1 100644 --- a/lib/ci/api/runners.rb +++ b/lib/ci/api/runners.rb @@ -29,8 +29,11 @@ module Ci required_attributes! [:token] attributes = { description: params[:description], - tag_list: params[:tag_list], - run_untagged: params[:run_untagged] || true } + tag_list: params[:tag_list] } + + unless params[:run_untagged].nil? + attributes.merge!(run_untagged: params[:run_untagged]) + end runner = if runner_registration_token_valid? -- cgit v1.2.1 From bf9cc351c28a349ca4c573978c869d2b90209d52 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Tue, 10 May 2016 13:19:25 +0200 Subject: Add minor corrections related to config of runner --- app/views/projects/runners/_form.html.haml | 3 ++- app/views/projects/runners/edit.html.haml | 2 -- doc/ci/runners/README.md | 6 +++--- lib/ci/api/runners.rb | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/views/projects/runners/_form.html.haml b/app/views/projects/runners/_form.html.haml index 147f1a2ebc8..6be406fa186 100644 --- a/app/views/projects/runners/_form.html.haml +++ b/app/views/projects/runners/_form.html.haml @@ -1,4 +1,5 @@ = form_for runner, url: runner_form_url, html: { class: 'form-horizontal' } do |f| + = form_errors(@runner) .form-group = label :active, "Active", class: 'control-label' .col-sm-10 @@ -10,7 +11,7 @@ .col-sm-10 .checkbox = f.check_box :run_untagged - %span.light This runner can pick jobs without tags + %span.light Indicates whether runner can pick jobs without tags .form-group = label_tag :token, class: 'control-label' do Token diff --git a/app/views/projects/runners/edit.html.haml b/app/views/projects/runners/edit.html.haml index e4aa98080e5..95706888655 100644 --- a/app/views/projects/runners/edit.html.haml +++ b/app/views/projects/runners/edit.html.haml @@ -2,7 +2,5 @@ %h4 Runner ##{@runner.id} -= form_errors(@runner) - %hr = render 'form', runner: @runner, runner_form_url: runner_path(@runner) diff --git a/doc/ci/runners/README.md b/doc/ci/runners/README.md index e449d3dc61e..b42d7a62ebc 100644 --- a/doc/ci/runners/README.md +++ b/doc/ci/runners/README.md @@ -127,9 +127,9 @@ the appropriate dependencies to run Rails test suites. ### Prevent runner with tags from picking jobs without tags -You can configure runner to prevent it from picking jobs with tags when -runnner does not have tags assigned. This configuration setting is available -in GitLab interface when editting runner details. +You can configure a runner to prevent it from picking jobs with tags when +the runnner does not have tags assigned. This setting is available on each +runner in *Project Settings* > *Runners*. ### Be careful with sensitive information diff --git a/lib/ci/api/runners.rb b/lib/ci/api/runners.rb index 1dc4c8c2cd1..0c41f22c7c5 100644 --- a/lib/ci/api/runners.rb +++ b/lib/ci/api/runners.rb @@ -32,7 +32,7 @@ module Ci tag_list: params[:tag_list] } unless params[:run_untagged].nil? - attributes.merge!(run_untagged: params[:run_untagged]) + attributes[:run_untagged] = params[:run_untagged] end runner = -- cgit v1.2.1 From 52ba3a2d05ab93caa5ddbc6207359e99301dda91 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 18 May 2016 17:23:26 +0200 Subject: Display validation errors when admin edits a runner --- app/controllers/admin/runners_controller.rb | 26 ++++++++++++++++---------- app/views/projects/runners/_form.html.haml | 2 +- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb index 8b8a7320072..a164209455b 100644 --- a/app/controllers/admin/runners_controller.rb +++ b/app/controllers/admin/runners_controller.rb @@ -9,19 +9,13 @@ class Admin::RunnersController < Admin::ApplicationController end def show - @builds = @runner.builds.order('id DESC').first(30) - @projects = - if params[:search].present? - ::Project.search(params[:search]) - else - Project.all - end - @projects = @projects.where.not(id: @runner.projects.select(:id)) if @runner.projects.any? - @projects = @projects.page(params[:page]).per(30) + set_builds_and_projects end def update - @runner.update_attributes(runner_params) + unless @runner.update_attributes(runner_params) + set_builds_and_projects and return render 'show' + end respond_to do |format| format.js @@ -60,4 +54,16 @@ class Admin::RunnersController < Admin::ApplicationController def runner_params params.require(:runner).permit(Ci::Runner::FORM_EDITABLE) end + + def set_builds_and_projects + @builds = runner.builds.order('id DESC').first(30) + @projects = + if params[:search].present? + ::Project.search(params[:search]) + else + Project.all + end + @projects = @projects.where.not(id: runner.projects.select(:id)) if runner.projects.any? + @projects = @projects.page(params[:page]).per(30) + end end diff --git a/app/views/projects/runners/_form.html.haml b/app/views/projects/runners/_form.html.haml index 6be406fa186..b8fe0a98107 100644 --- a/app/views/projects/runners/_form.html.haml +++ b/app/views/projects/runners/_form.html.haml @@ -1,5 +1,5 @@ = form_for runner, url: runner_form_url, html: { class: 'form-horizontal' } do |f| - = form_errors(@runner) + = form_errors(runner) .form-group = label :active, "Active", class: 'control-label' .col-sm-10 -- cgit v1.2.1 From c3c503d259bbf4691f0fb24fcd713ec5b4474e61 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Wed, 18 May 2016 18:38:21 +0200 Subject: Rename method that validates runner tag constrains --- app/models/ci/runner.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index cf4d3236519..6829dc91cb9 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -26,7 +26,7 @@ module Ci .where("ci_runner_projects.gl_project_id = :project_id OR ci_runners.is_shared = true", project_id: project_id) end - validate :verify_tags_constraints + validate :tag_constraints acts_as_taggable @@ -105,7 +105,7 @@ module Ci private - def verify_tags_constraints + def tag_constraints unless has_tags? || run_untagged? errors.add(:tags_list, 'can not be empty when runner is not allowed to pick untagged jobs') -- cgit v1.2.1 From 4cc77c3bf8ef72d1b08160da9f55eb1c5f95e832 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 19 May 2016 21:27:52 +0200 Subject: Minor runner-related code refactorings --- app/controllers/admin/runners_controller.rb | 19 ++++++++++--------- app/views/projects/runners/_form.html.haml | 2 +- spec/features/runners_spec.rb | 2 +- spec/requests/ci/api/runners_spec.rb | 20 ++++++++++---------- 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb index a164209455b..7345c91f67d 100644 --- a/app/controllers/admin/runners_controller.rb +++ b/app/controllers/admin/runners_controller.rb @@ -9,17 +9,18 @@ class Admin::RunnersController < Admin::ApplicationController end def show - set_builds_and_projects + assign_builds_and_projects end def update - unless @runner.update_attributes(runner_params) - set_builds_and_projects and return render 'show' - end - - respond_to do |format| - format.js - format.html { redirect_to admin_runner_path(@runner) } + if @runner.update_attributes(runner_params) + respond_to do |format| + format.js + format.html { redirect_to admin_runner_path(@runner) } + end + else + assign_builds_and_projects + render 'show' end end @@ -55,7 +56,7 @@ class Admin::RunnersController < Admin::ApplicationController params.require(:runner).permit(Ci::Runner::FORM_EDITABLE) end - def set_builds_and_projects + def assign_builds_and_projects @builds = runner.builds.order('id DESC').first(30) @projects = if params[:search].present? diff --git a/app/views/projects/runners/_form.html.haml b/app/views/projects/runners/_form.html.haml index b8fe0a98107..d62f5c8f131 100644 --- a/app/views/projects/runners/_form.html.haml +++ b/app/views/projects/runners/_form.html.haml @@ -11,7 +11,7 @@ .col-sm-10 .checkbox = f.check_box :run_untagged - %span.light Indicates whether runner can pick jobs without tags + %span.light Indicates whether this runner can pick jobs without tags .form-group = label_tag :token, class: 'control-label' do Token diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index 2c9c12b4dfa..49156130ad3 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -129,7 +129,7 @@ describe "Runners" do context 'when runner has tags' do before { runner.update_attribute(:tag_list, ['tag']) } - scenario 'user want to prevent runner from running untagged job' do + scenario 'user wants to prevent runner from running untagged job' do visit runners_path(project) page.within('.activated-specific-runners') do first('small > a').click diff --git a/spec/requests/ci/api/runners_spec.rb b/spec/requests/ci/api/runners_spec.rb index eb11258cbbb..43596f07cb5 100644 --- a/spec/requests/ci/api/runners_spec.rb +++ b/spec/requests/ci/api/runners_spec.rb @@ -16,7 +16,7 @@ describe Ci::API::API do before { post ci_api("/runners/register"), token: registration_token } it 'creates runner with default values' do - expect(response.status).to eq(201) + expect(response).to have_http_status 201 expect(Ci::Runner.first.run_untagged).to be true end end @@ -28,7 +28,7 @@ describe Ci::API::API do end it 'creates runner' do - expect(response.status).to eq(201) + expect(response).to have_http_status 201 expect(Ci::Runner.first.description).to eq("server.hostname") end end @@ -40,7 +40,7 @@ describe Ci::API::API do end it 'creates runner' do - expect(response.status).to eq(201) + expect(response).to have_http_status 201 expect(Ci::Runner.first.tag_list.sort).to eq(["tag1", "tag2"]) end end @@ -52,7 +52,7 @@ describe Ci::API::API do run_untagged: false, tag_list: ['tag'] - expect(response.status).to eq(201) + expect(response).to have_http_status 201 expect(Ci::Runner.first.run_untagged).to be false end end @@ -62,7 +62,7 @@ describe Ci::API::API do post ci_api("/runners/register"), token: registration_token, run_untagged: false - expect(response.status).to eq(404) + expect(response).to have_http_status 404 end end end @@ -72,7 +72,7 @@ describe Ci::API::API do before { post ci_api("/runners/register"), token: project.runners_token } it 'creates runner' do - expect(response.status).to eq(201) + expect(response).to have_http_status 201 expect(project.runners.size).to eq(1) end end @@ -81,7 +81,7 @@ describe Ci::API::API do it 'returns 403 error' do post ci_api("/runners/register"), token: 'invalid' - expect(response.status).to eq(403) + expect(response).to have_http_status 403 end end @@ -89,7 +89,7 @@ describe Ci::API::API do it 'returns 400 error' do post ci_api("/runners/register") - expect(response.status).to eq(400) + expect(response).to have_http_status 400 end end @@ -101,7 +101,7 @@ describe Ci::API::API do it do post ci_api("/runners/register"), token: registration_token, info: { param => value } - expect(response.status).to eq(201) + expect(response).to have_http_status 201 is_expected.to eq(value) end end @@ -112,7 +112,7 @@ describe Ci::API::API do let!(:runner) { FactoryGirl.create(:ci_runner) } before { delete ci_api("/runners/delete"), token: runner.token } - it { expect(response.status).to eq(200) } + it { expect(response).to have_http_status 200 } it { expect(Ci::Runner.count).to eq(0) } end end -- cgit v1.2.1 From 52c8b9da37451943fe97f3a687d43ae39105aaa0 Mon Sep 17 00:00:00 2001 From: Grzegorz Bizon Date: Thu, 19 May 2016 22:11:40 +0200 Subject: Use migration helper to prevent downtime migration --- .../20160504112519_add_run_untagged_to_ci_runner.rb | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb b/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb index c6753f2b398..84e5e4eabe2 100644 --- a/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb +++ b/db/migrate/20160504112519_add_run_untagged_to_ci_runner.rb @@ -1,11 +1,13 @@ class AddRunUntaggedToCiRunner < ActiveRecord::Migration - ## - # Downtime expected! - # - # This migration will cause downtime due to exclusive lock - # caused by the default value. - # - def change - add_column :ci_runners, :run_untagged, :boolean, default: true, null: false + include Gitlab::Database::MigrationHelpers + disable_ddl_transaction! + + def up + add_column_with_default(:ci_runners, :run_untagged, :boolean, + default: true, allow_null: false) + end + + def down + remove_column(:ci_runners, :run_untagged) end end -- cgit v1.2.1