Commit 9ff0680d authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab-ce master

parents 1f4917f5 28ca155b
......@@ -170,12 +170,12 @@ gem 'acts-as-taggable-on', '~> 5.0'
# Background jobs
gem 'sidekiq', '~> 5.2.1'
gem 'sidekiq-cron', '~> 0.6.0'
gem 'sidekiq-cron', '~> 1.0'
gem 'redis-namespace', '~> 1.6.0'
gem 'gitlab-sidekiq-fetcher', '~> 0.4.0', require: 'sidekiq-reliable-fetch'
# Cron Parser
gem 'rufus-scheduler', '~> 3.4'
gem 'fugit', '~> 1.1'
# HTTP requests
gem 'httparty', '~> 0.13.3'
......
......@@ -206,7 +206,7 @@ GEM
erubi (1.7.1)
erubis (2.7.0)
escape_utils (1.2.1)
et-orbi (1.0.3)
et-orbi (1.1.7)
tzinfo
eventmachine (1.2.7)
excon (0.62.0)
......@@ -282,6 +282,9 @@ GEM
foreman (0.84.0)
thor (~> 0.19.1)
formatador (0.2.5)
fugit (1.1.7)
et-orbi (~> 1.1, >= 1.1.7)
raabro (~> 1.1)
fuubar (2.2.0)
rspec-core (~> 3.0)
ruby-progressbar (~> 1.4)
......@@ -636,6 +639,7 @@ GEM
get_process_mem (~> 0.2)
puma (>= 2.7, < 4)
pyu-ruby-sasl (0.0.3.3)
raabro (1.1.6)
rack (2.0.6)
rack-accept (0.4.5)
rack (>= 0.4)
......@@ -805,8 +809,6 @@ GEM
rubyntlm (0.6.2)
rubypants (0.2.0)
rubyzip (1.2.2)
rufus-scheduler (3.4.0)
et-orbi (~> 1.0)
rugged (0.27.5)
safe_yaml (1.0.4)
sanitize (4.6.6)
......@@ -850,8 +852,8 @@ GEM
connection_pool (~> 2.2, >= 2.2.2)
rack-protection (>= 1.5.0)
redis (>= 3.3.5, < 5)
sidekiq-cron (0.6.0)
rufus-scheduler (>= 3.3.0)
sidekiq-cron (1.0.4)
fugit (~> 1.1)
sidekiq (>= 4.2.1)
signet (0.11.0)
addressable (~> 2.3)
......@@ -1038,6 +1040,7 @@ DEPENDENCIES
fog-rackspace (~> 0.1.1)
font-awesome-rails (~> 4.7)
foreman (~> 0.84.0)
fugit (~> 1.1)
fuubar (~> 2.2.0)
gemojione (~> 3.3)
gettext (~> 3.2.2)
......@@ -1166,7 +1169,6 @@ DEPENDENCIES
ruby-prof (~> 0.17.0)
ruby-progressbar
ruby_parser (~> 3.8)
rufus-scheduler (~> 3.4)
rugged (~> 0.27)
sanitize (~> 4.6)
sass (~> 3.5)
......@@ -1180,7 +1182,7 @@ DEPENDENCIES
sham_rack (~> 1.3.6)
shoulda-matchers (~> 3.1.2)
sidekiq (~> 5.2.1)
sidekiq-cron (~> 0.6.0)
sidekiq-cron (~> 1.0)
simple_po_parser (~> 1.1.2)
simplecov (~> 0.14.0)
slack-notifier (~> 1.5.1)
......
---
title: Update sidekiq-cron to 1.0.4 and use fugit to replace rufus-scheduler to parse
cron syntax
merge_request: 24235
author:
type: other
---
title: Allow setting of feature gates per project
merge_request: 24184
author:
type: added
......@@ -60,9 +60,10 @@ POST /features/:name
| `value` | integer/string | yes | `true` or `false` to enable/disable, or an integer for percentage of time |
| `feature_group` | string | no | A Feature group name |
| `user` | string | no | A GitLab username |
| `project` | string | no | A projects path, for example 'gitlab-org/gitlab-ce' |
Note that you can enable or disable a feature for both a `feature_group` and a
`user` with a single API call.
Note that you can enable or disable a feature for a `feature_group`, a `user`,
and a `project` in a single API call.
```bash
curl --data "value=30" --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/features/new_library
......
......@@ -3,7 +3,7 @@
> **Notes**:
> - This feature was introduced in 9.1 as [Trigger Schedule][ce-10533].
> - In 9.2, the feature was [renamed to Pipeline Schedule][ce-10853].
> - Cron notation is parsed by [Rufus-Scheduler](https://github.com/jmettraux/rufus-scheduler).
> - Cron notation is parsed by [Fugit](https://github.com/floraison/fugit).
Pipeline schedules can be used to run a pipeline at specific intervals, for example every
month on the 22nd for a certain branch.
......
......@@ -16,15 +16,13 @@ module API
end
end
# rubocop: disable CodeReuse/ActiveRecord
def gate_targets(params)
targets = []
targets << Feature.group(params[:feature_group]) if params[:feature_group]
targets << UserFinder.new(params[:user]).find_by_username if params[:user]
Feature::Target.new(params).targets
end
targets
def gate_specified?(params)
Feature::Target.new(params).gate_specified?
end
# rubocop: enable CodeReuse/ActiveRecord
end
resource :features do
......@@ -44,6 +42,7 @@ module API
requires :value, type: String, desc: '`true` or `false` to enable/disable, an integer for percentage of time'
optional :feature_group, type: String, desc: 'A Feature group name'
optional :user, type: String, desc: 'A GitLab username'
optional :project, type: String, desc: 'A projects path, like gitlab-org/gitlab-ce'
end
post ':name' do
feature = Feature.get(params[:name])
......@@ -52,13 +51,13 @@ module API
case value
when true
if targets.present?
if gate_specified?(params)
targets.each { |target| feature.enable(target) }
else
feature.enable
end
when false
if targets.present?
if gate_specified?(params)
targets.each { |target| feature.disable(target) }
else
feature.disable
......
......@@ -104,4 +104,42 @@ class Feature
expires_in: 1.hour)
end
end
class Target
attr_reader :params
def initialize(params)
@params = params
end
def gate_specified?
%i(user project feature_group).any? { |key| params.key?(key) }
end
def targets
[feature_group, user, project].compact
end
private
# rubocop: disable CodeReuse/ActiveRecord
def feature_group
return unless params.key?(:feature_group)
Feature.group(params[:feature_group])
end
# rubocop: enable CodeReuse/ActiveRecord
def user
return unless params.key?(:user)
UserFinder.new(params[:user]).find_by_username!
end
def project
return unless params.key?(:project)
Project.find_by_full_path(params[:project])
end
end
end
......@@ -35,7 +35,7 @@ module Gitlab
# NOTE:
# cron_timezone can only accept timezones listed in TZInfo::Timezone.
# Aliases of Timezones from ActiveSupport::TimeZone are NOT accepted,
# because Rufus::Scheduler only supports TZInfo::Timezone.
# because Fugit::Cron only supports TZInfo::Timezone.
#
# For example, those codes have the same effect.
# Time.zone = 'Pacific Time (US & Canada)' (ActiveSupport::TimeZone)
......@@ -47,10 +47,7 @@ module Gitlab
# If you want to know more, please take a look
# https://github.com/rails/rails/blob/master/activesupport/lib/active_support/values/time_zone.rb
def try_parse_cron(cron, cron_timezone)
cron_line = Rufus::Scheduler.parse("#{cron} #{cron_timezone}")
cron_line if cron_line.is_a?(Rufus::Scheduler::CronLine)
rescue
# noop
Fugit::Cron.parse("#{cron} #{cron_timezone}")
end
end
end
......
......@@ -182,4 +182,18 @@ describe Feature do
expect(described_class.disabled?(:enabled_feature_flag)).to be_falsey
end
end
describe Feature::Target do
describe '#targets' do
let(:project) { create(:project) }
let(:user_name) { project.owner.username }
subject { described_class.new(user: user_name, project: project.full_path) }
it 'returns all found targets' do
expect(subject.targets).to be_an(Array)
expect(subject.targets).to eq([project.owner, project])
end
end
end
end
......@@ -129,6 +129,40 @@ describe API::Features do
end
end
context 'when enabling for a project by path' do
context 'when the project exists' do
let!(:project) { create(:project) }
it 'sets the feature gate' do
post api("/features/#{feature_name}", admin), params: { value: 'true', project: project.full_path }
expect(response).to have_gitlab_http_status(201)
expect(json_response).to eq(
'name' => 'my_feature',
'state' => 'conditional',
'gates' => [
{ 'key' => 'boolean', 'value' => false },
{ 'key' => 'actors', 'value' => ["Project:#{project.id}"] }
])
end
end
context 'when the project does not exist' do
it 'sets no new values' do
post api("/features/#{feature_name}", admin), params: { value: 'true', project: 'mep/to/the/mep/mep' }
expect(response).to have_gitlab_http_status(201)
expect(json_response).to eq(
"name" => "my_feature",
"state" => "off",
"gates" => [
{ "key" => "boolean", "value" => false }
]
)
end
end
end
it 'creates a feature with the given percentage if passed an integer' do
post api("/features/#{feature_name}", admin), params: { value: '50' }
......
......@@ -2,7 +2,7 @@ require 'spec_helper'
describe Sidekiq::Cron::Job do
describe 'cron jobs' do
context 'when rufus-scheduler depends on ZoTime or EoTime' do
context 'when Fugit depends on ZoTime or EoTime' do
before do
described_class
.create(name: 'TestCronWorker',
......@@ -10,7 +10,7 @@ describe Sidekiq::Cron::Job do
class: Settings.cron_jobs[:pipeline_schedule_worker]['job_class'])
end
it 'does not get "Rufus::Scheduler::ZoTime/EtOrbi::EoTime into an exact number"' do
it 'does not get any errors' do
expect { described_class.all.first.should_enque?(Time.now) }.not_to raise_error
end
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment