Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
23e06634
Commit
23e06634
authored
Jun 26, 2018
by
Kamil Trzciński
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add feature flags backend controls
parent
9aba3a90
Changes
17
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
240 additions
and
40 deletions
+240
-40
app/controllers/projects/feature_flags_controller.rb
app/controllers/projects/feature_flags_controller.rb
+57
-0
app/helpers/projects_helper.rb
app/helpers/projects_helper.rb
+2
-1
app/models/project_feature_flags_access_token.rb
app/models/project_feature_flags_access_token.rb
+3
-1
app/policies/project_policy.rb
app/policies/project_policy.rb
+6
-0
app/views/layouts/nav/sidebar/_project.html.haml
app/views/layouts/nav/sidebar/_project.html.haml
+10
-4
app/views/projects/feature_flags/_form.html.haml
app/views/projects/feature_flags/_form.html.haml
+25
-0
app/views/projects/feature_flags/_list.html.haml
app/views/projects/feature_flags/_list.html.haml
+17
-0
app/views/projects/feature_flags/_use.html.haml
app/views/projects/feature_flags/_use.html.haml
+23
-0
app/views/projects/feature_flags/edit.html.haml
app/views/projects/feature_flags/edit.html.haml
+11
-0
app/views/projects/feature_flags/index.html.haml
app/views/projects/feature_flags/index.html.haml
+16
-0
app/views/projects/feature_flags/new.html.haml
app/views/projects/feature_flags/new.html.haml
+12
-0
config/routes/project.rb
config/routes/project.rb
+2
-0
db/migrate/20180626171125_add_feature_flags_to_projects.rb
db/migrate/20180626171125_add_feature_flags_to_projects.rb
+1
-1
db/schema.rb
db/schema.rb
+20
-0
lib/api/entities.rb
lib/api/entities.rb
+6
-20
lib/api/unleash.rb
lib/api/unleash.rb
+27
-11
lib/gitlab/regex.rb
lib/gitlab/regex.rb
+2
-2
No files found.
app/controllers/projects/feature_flags_controller.rb
0 → 100644
View file @
23e06634
class
Projects::FeatureFlagsController
<
Projects
::
ApplicationController
respond_to
:html
before_action
:authorize_read_feature_flags!
before_action
:authorize_update_feature_flags!
,
only:
[
:edit
,
:update
]
before_action
:authorize_admin_feature_flags!
,
only:
[
:destroy
]
before_action
:feature_flag
,
only:
[
:edit
,
:update
,
:destroy
]
def
index
@feature_flags
=
project
.
project_feature_flags
@unleash_instanceid
=
project
.
project_feature_flags_access_tokens
.
first
&
.
token
||
project
.
project_feature_flags_access_tokens
.
create!
.
token
end
def
new
@feature_flag
=
project
.
project_feature_flags
.
new
end
def
create
@feature_flag
=
project
.
project_feature_flags
.
create
(
create_params
)
if
@feature_flag
.
persisted?
flash
[
:notice
]
=
'Feature flag was successfully created.'
redirect_to
project_feature_flags_path
(
@project
)
else
render
:new
end
end
def
edit
end
def
update
if
feature_flag
.
update
(
update_params
)
flash
[
:notice
]
=
'Feature flag was successfully updated.'
redirect_to
project_feature_flags_path
(
@project
)
else
render
:edit
end
end
protected
def
feature_flag
@feature_flag
||=
project
.
project_feature_flags
.
find
(
params
[
:id
])
end
def
create_params
params
.
require
(
:project_feature_flag
)
.
permit
(
:name
,
:description
,
:active
)
end
def
update_params
params
.
require
(
:project_feature_flag
)
.
permit
(
:description
,
:active
)
end
end
app/helpers/projects_helper.rb
View file @
23e06634
...
...
@@ -284,7 +284,7 @@ module ProjectsHelper
nav_tabs
<<
:pipelines
end
if
can?
(
current_user
,
:read_environment
,
project
)
||
can?
(
current_user
,
:read_cluster
,
project
)
if
can?
(
current_user
,
:read_environment
,
project
)
||
can?
(
current_user
,
:read_cluster
,
project
)
||
can?
(
current_user
,
:read_feature_flags
,
project
)
nav_tabs
<<
:operations
end
...
...
@@ -304,6 +304,7 @@ module ProjectsHelper
def
tab_ability_map
{
environments: :read_environment
,
feature_flags: :read_feature_flags
,
milestones: :read_milestone
,
snippets: :read_project_snippet
,
settings: :admin_project
,
...
...
app/models/project_feature_flag_access_token.rb
→
app/models/project_feature_flag
s
_access_token.rb
View file @
23e06634
class
ProjectFeatureFlagAccessToken
<
ActiveRecord
::
Base
class
ProjectFeatureFlag
s
AccessToken
<
ActiveRecord
::
Base
include
TokenAuthenticatable
belongs_to
:project
...
...
@@ -7,4 +7,6 @@ class ProjectFeatureFlagAccessToken < ActiveRecord::Base
validates
:token
,
presence:
true
add_authentication_token_field
:token
before_validation
:ensure_token!
end
app/policies/project_policy.rb
View file @
23e06634
...
...
@@ -15,6 +15,7 @@ class ProjectPolicy < BasePolicy
note
pipeline
pipeline_schedule
feature_flags
build
trigger
environment
...
...
@@ -234,6 +235,7 @@ class ProjectPolicy < BasePolicy
enable
:update_container_image
enable
:create_environment
enable
:create_deployment
enable
:read_feature_flags
end
rule
{
can?
(
:maintainer_access
)
}.
policy
do
...
...
@@ -258,6 +260,9 @@ class ProjectPolicy < BasePolicy
enable
:read_cluster
enable
:create_cluster
enable
:create_environment_terminal
enable
:create_feature_flags
enable
:update_feature_flags
enable
:admin_feature_flags
end
rule
{
(
mirror_available
&
can?
(
:admin_project
))
|
admin
}.
enable
:admin_remote_mirror
...
...
@@ -306,6 +311,7 @@ class ProjectPolicy < BasePolicy
prevent
(
*
create_read_update_admin_destroy
(
:build
))
prevent
(
*
create_read_update_admin_destroy
(
:pipeline_schedule
))
prevent
(
*
create_read_update_admin_destroy
(
:environment
))
prevent
(
*
create_read_update_admin_destroy
(
:feature_flags
))
prevent
(
*
create_read_update_admin_destroy
(
:cluster
))
prevent
(
*
create_read_update_admin_destroy
(
:deployment
))
end
...
...
app/views/layouts/nav/sidebar/_project.html.haml
View file @
23e06634
...
...
@@ -195,16 +195,16 @@
=
_
(
'Charts'
)
-
if
project_nav_tab?
:operations
=
nav_link
(
controller:
[
:environments
,
:clusters
,
:user
,
:gcp
])
do
=
link_to
metrics_
project_environments_path
(
@project
),
class:
'shortcuts-operations'
do
=
nav_link
(
controller:
[
:environments
,
:clusters
,
:user
,
:gcp
,
:feature_flags
])
do
=
link_to
project_environments_path
(
@project
),
class:
'shortcuts-operations'
do
.nav-icon-container
=
sprite_icon
(
'cloud-gear'
)
%span
.nav-item-name
=
_
(
'Operations'
)
%ul
.sidebar-sub-level-items
=
nav_link
(
controller:
[
:environments
,
:clusters
,
:user
,
:gcp
],
html_options:
{
class:
"fly-out-top-item"
}
)
do
=
link_to
metrics_
project_environments_path
(
@project
)
do
=
nav_link
(
controller:
[
:environments
,
:clusters
,
:user
,
:gcp
,
:feature_flags
],
html_options:
{
class:
"fly-out-top-item"
}
)
do
=
link_to
project_environments_path
(
@project
)
do
%strong
.fly-out-top-item-name
=
_
(
'Operations'
)
%li
.divider.fly-out-top-item
...
...
@@ -249,6 +249,12 @@
%span
=
_
(
"Got it!"
)
=
sprite_icon
(
'thumb-up'
)
-
if
project_nav_tab?
:feature_flags
=
nav_link
(
controller: :feature_flags
)
do
=
link_to
project_feature_flags_path
(
@project
),
title:
'Feature Flags'
,
class:
'shortcuts-feature-flags'
do
%span
=
_
(
'Feature Flags'
)
-
if
project_nav_tab?
:container_registry
=
nav_link
(
controller:
%w[projects/registry/repositories]
)
do
=
link_to
project_container_registry_index_path
(
@project
),
class:
'shortcuts-container-registry'
do
...
...
app/views/projects/feature_flags/_form.html.haml
0 → 100644
View file @
23e06634
-
if
@feature_flag
.
errors
.
any?
#error_explanation
.alert.alert-danger
-
@feature_flag
.
errors
.
full_messages
.
each
do
|
msg
|
%p
=
msg
-
unless
@feature_flag
.
persisted?
.form-group.row
=
f
.
label
:name
,
class:
'col-form-label col-sm-2'
do
Name
.col-sm-10
=
f
.
text_field
:name
,
required:
true
,
autocomplete:
'off'
,
class:
'form-control'
,
disabled:
@feature_flag
.
persisted?
.form-group.row
=
f
.
label
:active
,
class:
'col-form-label col-sm-2'
do
Active
.col-sm-10
=
f
.
check_box
:active
.form-group.row
=
f
.
label
:description
,
class:
'col-form-label col-sm-2'
do
Description
.col-sm-10
=
f
.
text_area
:description
,
rows:
5
,
class:
'form-control'
%span
.help-inline
Write a description of your feature flag
app/views/projects/feature_flags/_list.html.haml
0 → 100644
View file @
23e06634
.card
.card-header
Feature flags (
#{
@feature_flags
.
count
}
)
%ul
.content-list.pages-domain-list
-
@feature_flags
.
each
do
|
feature_flag
|
%li
.pages-domain-list-item.unstyled
=
feature_flag
.
name
%div
.controls.d-none.d-md-block
=
link_to
'Edit'
,
edit_project_feature_flag_path
(
@project
,
feature_flag
),
class:
"btn btn-sm btn-grouped"
=
link_to
'Remove'
,
project_feature_flag_path
(
@project
,
feature_flag
),
data:
{
confirm:
'Are you sure?'
},
method: :delete
,
class:
"btn btn-remove btn-sm btn-grouped"
%p
-
if
feature_flag
.
active?
%span
.badge.badge-success
Enabled
-
else
%span
.badge.badge-danger
Disabled
\ No newline at end of file
app/views/projects/feature_flags/_use.html.haml
0 → 100644
View file @
23e06634
.card.bg-info
.card-header
Configure feature flags
.card-body
%p
Learn how to enable feature flags for your application.
%ol
%li
=
_
(
"Install a compatible with client library"
)
=
(
_
(
"(checkout the %{link} for information on how to install it)."
)
%
{
link:
"here"
}).
html_safe
%li
=
_
(
"Specify the following URL during for the library configuration setup:"
)
%code
#coordinator_address
=
"
#{
root_url
(
only_path:
false
)
}
api/v4/unleash"
%li
=
_
(
"Use the following application name:"
)
%code
#registration_token
=
@project
.
id
%li
=
_
(
"Use the following application name:"
)
%code
#registration_token
=
@unleash_instanceid
%li
=
_
(
"You can also see all features online:"
)
%code
#registration_token
=
"
#{
root_url
(
only_path:
false
)
}
api/v4/unleash/features?appname=
#{
@project
.
id
}
&instanceid=
#{
@unleash_instanceid
}
"
\ No newline at end of file
app/views/projects/feature_flags/edit.html.haml
0 → 100644
View file @
23e06634
-
add_to_breadcrumbs
"Feature Flags"
,
project_feature_flags_path
(
@project
)
-
breadcrumb_title
@feature_flag
.
name
-
page_title
@feature_flag
.
name
%h3
.page-title
=
@feature_flag
.
name
%hr
.clearfix
%div
=
form_for
[
@project
,
@feature_flag
],
url:
project_feature_flag_path
(
@project
,
@feature_flag
),
html:
{
class:
'fieldset-form'
}
do
|
f
|
=
render
'form'
,
{
f:
f
}
.form-actions
=
f
.
submit
'Save Changes'
,
class:
"btn btn-save"
app/views/projects/feature_flags/index.html.haml
0 → 100644
View file @
23e06634
-
page_title
'Feature Flags'
%h3
.page-title.with-button
Feature Flags
-
if
can?
(
current_user
,
:create_feature_flags
,
@project
)
=
link_to
new_project_feature_flag_path
(
@project
),
class:
'btn btn-new float-right'
,
title:
'New Feature Flag'
do
New Feature Flag
%p
.light
With GitLab Feature Flags
%hr
.clearfix
=
render
'use'
=
render
'list'
app/views/projects/feature_flags/new.html.haml
0 → 100644
View file @
23e06634
-
add_to_breadcrumbs
"Feature Flags"
,
project_feature_flags_path
(
@project
)
-
page_title
'New Feature Flags'
%h3
.page-title
New Feature Flags
%hr
.clearfix
%div
=
form_for
[
@project
,
@feature_flag
],
url:
project_feature_flags_path
(
@project
),
html:
{
class:
'fieldset-form'
}
do
|
f
|
=
render
'form'
,
{
f:
f
}
.form-actions
=
f
.
submit
'Create New Feature Flag'
,
class:
"btn btn-save"
.float-right
=
link_to
_
(
'Cancel'
),
project_feature_flags_path
(
@project
),
class:
'btn btn-cancel'
config/routes/project.rb
View file @
23e06634
...
...
@@ -351,6 +351,8 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
namespace
:ci
do
resource
:lint
,
only:
[
:show
,
:create
]
end
resources
:feature_flags
end
draw
:legacy_builds
...
...
db/migrate/20180626171125_add_feature_flags_to_projects.rb
View file @
23e06634
...
...
@@ -19,7 +19,7 @@ class AddFeatureFlagsToProjects < ActiveRecord::Migration
t
.
index
[
:project_id
,
:name
],
unique:
true
end
create_table
:project_feature_flag_access_tokens
do
|
t
|
create_table
:project_feature_flag
s
_access_tokens
do
|
t
|
t
.
integer
:project_id
,
null:
false
t
.
string
:token
,
null:
false
...
...
db/schema.rb
View file @
23e06634
...
...
@@ -2090,6 +2090,24 @@ ActiveRecord::Schema.define(version: 20180926140319) do
add_index
"project_deploy_tokens"
,
[
"project_id"
,
"deploy_token_id"
],
name:
"index_project_deploy_tokens_on_project_id_and_deploy_token_id"
,
unique:
true
,
using: :btree
create_table
"project_feature_flags"
,
force: :cascade
do
|
t
|
t
.
integer
"project_id"
,
null:
false
t
.
datetime_with_timezone
"created_at"
,
null:
false
t
.
datetime_with_timezone
"updated_at"
,
null:
false
t
.
string
"name"
,
null:
false
t
.
text
"description"
t
.
boolean
"active"
,
null:
false
end
add_index
"project_feature_flags"
,
[
"project_id"
,
"name"
],
name:
"index_project_feature_flags_on_project_id_and_name"
,
unique:
true
,
using: :btree
create_table
"project_feature_flags_access_tokens"
,
force: :cascade
do
|
t
|
t
.
integer
"project_id"
,
null:
false
t
.
string
"token"
,
null:
false
end
add_index
"project_feature_flags_access_tokens"
,
[
"project_id"
,
"token"
],
name:
"project_feature_flag_access_token"
,
unique:
true
,
using: :btree
create_table
"project_features"
,
force: :cascade
do
|
t
|
t
.
integer
"project_id"
,
null:
false
t
.
integer
"merge_requests_access_level"
...
...
@@ -3247,6 +3265,8 @@ ActiveRecord::Schema.define(version: 20180926140319) do
add_foreign_key
"project_custom_attributes"
,
"projects"
,
on_delete: :cascade
add_foreign_key
"project_deploy_tokens"
,
"deploy_tokens"
,
on_delete: :cascade
add_foreign_key
"project_deploy_tokens"
,
"projects"
,
on_delete: :cascade
add_foreign_key
"project_feature_flags"
,
"projects"
,
on_delete: :cascade
add_foreign_key
"project_feature_flags_access_tokens"
,
"projects"
,
on_delete: :cascade
add_foreign_key
"project_features"
,
"projects"
,
name:
"fk_18513d9b92"
,
on_delete: :cascade
add_foreign_key
"project_group_links"
,
"projects"
,
name:
"fk_daa8cee94c"
,
on_delete: :cascade
add_foreign_key
"project_import_data"
,
"projects"
,
name:
"fk_ffb9ee3a10"
,
on_delete: :cascade
...
...
lib/api/entities.rb
View file @
23e06634
...
...
@@ -1515,31 +1515,17 @@ module API
end
expose
:label
,
using:
Entities
::
LabelBasic
expose
:action
end
class
Unleash
Issu
e
<
Grape
::
Entity
class
Unleash
Featur
e
<
Grape
::
Entity
expose
:name
expose
:title
,
as: :description
expose
:enabled
expose
:strategies
private
def
name
"
\#
#{
object
.
iid
}
"
end
def
enabled
true
end
def
strategies
[{
name:
'default'
}]
end
expose
:description
,
unless:
->
(
feature
)
{
feature
.
description
.
nil?
}
expose
:active
,
as: :enabled
end
class
UnleashFeature
Response
<
Grape
::
Entity
class
UnleashFeature
s
<
Grape
::
Entity
expose
:version
expose
:
features
,
with:
UnleashIssu
e
expose
:
project_feature_flags
,
as: :features
,
with:
UnleashFeatur
e
private
...
...
lib/api/unleash.rb
View file @
23e06634
...
...
@@ -2,23 +2,39 @@ module API
class
Unleash
<
Grape
::
API
include
PaginationParams
#before { authenticate! }
before
do
unauthorized!
unless
access_token
end
get
':unleash/features'
do
present
@project
,
with:
Entities
::
UnleashFeatures
end
post
'unleash/client/register'
do
status
:ok
end
p
arams
do
requires
:id
,
type:
String
,
desc:
'The ID of a project'
p
ost
'unleash/client/metrics'
do
status
:ok
end
resource
:projects
,
requirements:
API
::
PROJECT_ENDPOINT_REQUIREMENTS
do
get
':id/unleash/features'
do
issues
=
IssuesFinder
.
new
(
current_user
,
project_id:
user_project
.
id
,
label_name:
'rollout'
)
present
issues
,
with:
Entities
::
UnleashFeatureResponse
private
helpers
do
def
project
@project
||=
find_project
(
unleash_appname
)
end
def
access_token
@access_token
||=
ProjectFeatureFlagsAccessToken
.
find_by
(
token:
unleash_instanceid
,
project:
project
)
end
post
':id/unleash/client/register'
do
status
:ok
def
unleash_appname
params
[
:appname
]
||
env
[
:HTTP_UNLEASH_APPNAME
]
end
post
':id/unleash/client/metrics'
do
status
:ok
def
unleash_instanceid
params
[
:instanceid
]
||
env
[
:HTTP_UNLEASH_INSTANCEID
]
end
end
end
...
...
lib/gitlab/regex.rb
View file @
23e06634
...
...
@@ -72,12 +72,12 @@ module Gitlab
end
def
feature_flag_regex
/\A[a-z]([-a-z0-9]*[a-z0-9])?\z/
/\A[a-z]([-
_
a-z0-9]*[a-z0-9])?\z/
end
def
feature_flag_regex_message
"can contain only lowercase letters, digits, '_' and '-'. "
\
"Must start with a letter, and cannot end with '-'"
"Must start with a letter, and cannot end with '-'
or '_'
"
end
def
build_trace_section_regex
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment