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
20758bc3
Commit
20758bc3
authored
Nov 27, 2019
by
GitLab Bot
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add latest changes from gitlab-org/gitlab@master
parent
a98649b7
Changes
19
Show whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
289 additions
and
116 deletions
+289
-116
app/assets/javascripts/monitoring/components/dashboard.vue
app/assets/javascripts/monitoring/components/dashboard.vue
+1
-5
app/assets/stylesheets/page_bundles/ide.scss
app/assets/stylesheets/page_bundles/ide.scss
+14
-0
app/assets/stylesheets/pages/issues.scss
app/assets/stylesheets/pages/issues.scss
+11
-37
app/helpers/projects_helper.rb
app/helpers/projects_helper.rb
+3
-1
app/models/active_session.rb
app/models/active_session.rb
+36
-14
changelogs/unreleased/31611-limit-the-number-of-stored-sessions-per-user.yml
...ed/31611-limit-the-number-of-stored-sessions-per-user.yml
+5
-0
changelogs/unreleased/34377-design-view-download-single-issue-design-image.yml
.../34377-design-view-download-single-issue-design-image.yml
+5
-0
changelogs/unreleased/cleanup-monitoring-dashboard-unused-methods.yml
...nreleased/cleanup-monitoring-dashboard-unused-methods.yml
+5
-0
changelogs/unreleased/fix-fork-link-display-bug.yml
changelogs/unreleased/fix-fork-link-display-bug.yml
+5
-0
doc/administration/high_availability/README.md
doc/administration/high_availability/README.md
+44
-15
doc/user/profile/active_sessions.md
doc/user/profile/active_sessions.md
+3
-0
lib/quality/kubernetes_client.rb
lib/quality/kubernetes_client.rb
+33
-3
lib/tasks/gitlab/cleanup.rake
lib/tasks/gitlab/cleanup.rake
+1
-1
scripts/review_apps/review-apps.sh
scripts/review_apps/review-apps.sh
+21
-1
scripts/trigger-build
scripts/trigger-build
+1
-1
spec/lib/gitlab/health_checks/probes/collection_spec.rb
spec/lib/gitlab/health_checks/probes/collection_spec.rb
+1
-1
spec/lib/quality/kubernetes_client_spec.rb
spec/lib/quality/kubernetes_client_spec.rb
+52
-22
spec/models/active_session_spec.rb
spec/models/active_session_spec.rb
+46
-14
spec/views/projects/edit.html.haml_spec.rb
spec/views/projects/edit.html.haml_spec.rb
+2
-1
No files found.
app/assets/javascripts/monitoring/components/dashboard.vue
View file @
20758bc3
...
@@ -22,8 +22,7 @@ import MonitorTimeSeriesChart from './charts/time_series.vue';
...
@@ -22,8 +22,7 @@ import MonitorTimeSeriesChart from './charts/time_series.vue';
import
MonitorSingleStatChart
from
'
./charts/single_stat.vue
'
;
import
MonitorSingleStatChart
from
'
./charts/single_stat.vue
'
;
import
GraphGroup
from
'
./graph_group.vue
'
;
import
GraphGroup
from
'
./graph_group.vue
'
;
import
EmptyState
from
'
./empty_state.vue
'
;
import
EmptyState
from
'
./empty_state.vue
'
;
import
TrackEventDirective
from
'
~/vue_shared/directives/track_event
'
;
import
{
getTimeDiff
,
isValidDate
}
from
'
../utils
'
;
import
{
getTimeDiff
,
isValidDate
,
downloadCSVOptions
,
generateLinkToChartOptions
}
from
'
../utils
'
;
export
default
{
export
default
{
components
:
{
components
:
{
...
@@ -44,7 +43,6 @@ export default {
...
@@ -44,7 +43,6 @@ export default {
directives
:
{
directives
:
{
GlModal
:
GlModalDirective
,
GlModal
:
GlModalDirective
,
GlTooltip
:
GlTooltipDirective
,
GlTooltip
:
GlTooltipDirective
,
TrackEvent
:
TrackEventDirective
,
},
},
props
:
{
props
:
{
externalDashboardUrl
:
{
externalDashboardUrl
:
{
...
@@ -300,8 +298,6 @@ export default {
...
@@ -300,8 +298,6 @@ export default {
onDateTimePickerApply
(
timeWindowUrlParams
)
{
onDateTimePickerApply
(
timeWindowUrlParams
)
{
return
redirectTo
(
mergeUrlParams
(
timeWindowUrlParams
,
window
.
location
.
href
));
return
redirectTo
(
mergeUrlParams
(
timeWindowUrlParams
,
window
.
location
.
href
));
},
},
downloadCSVOptions
,
generateLinkToChartOptions
,
},
},
addMetric
:
{
addMetric
:
{
title
:
s__
(
'
Metrics|Add metric
'
),
title
:
s__
(
'
Metrics|Add metric
'
),
...
...
app/assets/stylesheets/page_bundles/ide.scss
View file @
20758bc3
...
@@ -883,6 +883,15 @@ $ide-commit-header-height: 48px;
...
@@ -883,6 +883,15 @@ $ide-commit-header-height: 48px;
margin-right
:
$ide-tree-padding
;
margin-right
:
$ide-tree-padding
;
border-bottom
:
1px
solid
$white-dark
;
border-bottom
:
1px
solid
$white-dark
;
svg
{
color
:
$gray-700
;
&
:focus
,
&
:hover
{
color
:
$blue-600
;
}
}
.ide-new-btn
{
.ide-new-btn
{
margin-left
:
auto
;
margin-left
:
auto
;
}
}
...
@@ -899,6 +908,11 @@ $ide-commit-header-height: 48px;
...
@@ -899,6 +908,11 @@ $ide-commit-header-height: 48px;
.dropdown-menu-toggle
{
.dropdown-menu-toggle
{
svg
{
svg
{
vertical-align
:
middle
;
vertical-align
:
middle
;
color
:
$gray-700
;
&
:hover
{
color
:
$gray-700
;
}
}
}
&
:hover
{
&
:hover
{
...
...
app/assets/stylesheets/pages/issues.scss
View file @
20758bc3
...
@@ -21,15 +21,10 @@
...
@@ -21,15 +21,10 @@
margin-bottom
:
2px
;
margin-bottom
:
2px
;
}
}
.issue-labels
{
.issue-labels
,
display
:
inline-block
;
}
.issuable-meta
{
.author-link
{
.author-link
{
display
:
inline-block
;
display
:
inline-block
;
}
}
}
.icon-merge-request-unmerged
{
.icon-merge-request-unmerged
{
height
:
13px
;
height
:
13px
;
...
@@ -53,16 +48,6 @@
...
@@ -53,16 +48,6 @@
margin-right
:
15px
;
margin-right
:
15px
;
}
}
.issues_content
{
.title
{
height
:
40px
;
}
form
{
margin
:
0
;
}
}
form
.edit-issue
{
form
.edit-issue
{
margin
:
0
;
margin
:
0
;
}
}
...
@@ -79,10 +64,6 @@ ul.related-merge-requests > li {
...
@@ -79,10 +64,6 @@ ul.related-merge-requests > li {
margin-left
:
5px
;
margin-left
:
5px
;
}
}
.row_title
{
vertical-align
:
bottom
;
}
gl-emoji
{
gl-emoji
{
font-size
:
1em
;
font-size
:
1em
;
}
}
...
@@ -93,10 +74,6 @@ ul.related-merge-requests > li {
...
@@ -93,10 +74,6 @@ ul.related-merge-requests > li {
font-weight
:
$gl-font-weight-bold
;
font-weight
:
$gl-font-weight-bold
;
}
}
.merge-request-id
{
display
:
inline-block
;
}
.merge-request-status
{
.merge-request-status
{
&
.merged
{
&
.merged
{
color
:
$blue-500
;
color
:
$blue-500
;
...
@@ -118,11 +95,7 @@ ul.related-merge-requests > li {
...
@@ -118,11 +95,7 @@ ul.related-merge-requests > li {
border-color
:
$issues-today-border
;
border-color
:
$issues-today-border
;
}
}
&
.closed
{
&
.closed
,
background
:
$gray-light
;
border-color
:
$border-color
;
}
&
.merged
{
&
.merged
{
background
:
$gray-light
;
background
:
$gray-light
;
border-color
:
$border-color
;
border-color
:
$border-color
;
...
@@ -160,9 +133,12 @@ ul.related-merge-requests > li {
...
@@ -160,9 +133,12 @@ ul.related-merge-requests > li {
padding-bottom
:
37px
;
padding-bottom
:
37px
;
}
}
.issues-nav-controls
{
.issues-nav-controls
,
.new-branch-col
{
font-size
:
0
;
font-size
:
0
;
}
.issues-nav-controls
{
.btn-group
:empty
{
.btn-group
:empty
{
display
:
none
;
display
:
none
;
}
}
...
@@ -198,8 +174,6 @@ ul.related-merge-requests > li {
...
@@ -198,8 +174,6 @@ ul.related-merge-requests > li {
}
}
.new-branch-col
{
.new-branch-col
{
font-size
:
0
;
.discussion-filter-container
{
.discussion-filter-container
{
&
:not
(
:only-child
)
{
&
:not
(
:only-child
)
{
margin-right
:
$gl-padding-8
;
margin-right
:
$gl-padding-8
;
...
@@ -297,13 +271,13 @@ ul.related-merge-requests > li {
...
@@ -297,13 +271,13 @@ ul.related-merge-requests > li {
padding-top
:
0
;
padding-top
:
0
;
align-self
:
center
;
align-self
:
center
;
}
}
}
.create-mr-dropdown-wrap
{
.create-mr-dropdown-wrap
{
.btn-group
:not
(
.hidden
)
{
.btn-group
:not
(
.hidden
)
{
display
:
inline-flex
;
display
:
inline-flex
;
}
}
}
}
}
}
}
@include
media-breakpoint-up
(
lg
)
{
@include
media-breakpoint-up
(
lg
)
{
...
...
app/helpers/projects_helper.rb
View file @
20758bc3
...
@@ -114,8 +114,10 @@ module ProjectsHelper
...
@@ -114,8 +114,10 @@ module ProjectsHelper
source
=
visible_fork_source
(
project
)
source
=
visible_fork_source
(
project
)
if
source
if
source
_
(
'This will remove the fork relationship between this project and %{fork_source}.'
)
%
msg
=
_
(
'This will remove the fork relationship between this project and %{fork_source}.'
)
%
{
fork_source:
link_to
(
source
.
full_name
,
project_path
(
source
))
}
{
fork_source:
link_to
(
source
.
full_name
,
project_path
(
source
))
}
msg
.
html_safe
else
else
_
(
'This will remove the fork relationship between this project and other projects in the fork network.'
)
_
(
'This will remove the fork relationship between this project and other projects in the fork network.'
)
end
end
...
...
app/models/active_session.rb
View file @
20758bc3
...
@@ -4,6 +4,7 @@ class ActiveSession
...
@@ -4,6 +4,7 @@ class ActiveSession
include
ActiveModel
::
Model
include
ActiveModel
::
Model
SESSION_BATCH_SIZE
=
200
SESSION_BATCH_SIZE
=
200
ALLOWED_NUMBER_OF_ACTIVE_SESSIONS
=
100
attr_accessor
:created_at
,
:updated_at
,
attr_accessor
:created_at
,
:updated_at
,
:session_id
,
:ip_address
,
:session_id
,
:ip_address
,
...
@@ -65,21 +66,22 @@ class ActiveSession
...
@@ -65,21 +66,22 @@ class ActiveSession
def
self
.
destroy
(
user
,
session_id
)
def
self
.
destroy
(
user
,
session_id
)
Gitlab
::
Redis
::
SharedState
.
with
do
|
redis
|
Gitlab
::
Redis
::
SharedState
.
with
do
|
redis
|
redis
.
srem
(
lookup_key_name
(
user
.
id
),
session_id
)
destroy_sessions
(
redis
,
user
,
[
session_id
])
deleted_keys
=
redis
.
del
(
key_name
(
user
.
id
,
session_id
))
# only allow deleting the devise session if we could actually find a
# related active session. this prevents another user from deleting
# someone else's session.
if
deleted_keys
>
0
redis
.
del
(
"
#{
Gitlab
::
Redis
::
SharedState
::
SESSION_NAMESPACE
}
:
#{
session_id
}
"
)
end
end
end
end
def
self
.
destroy_sessions
(
redis
,
user
,
session_ids
)
key_names
=
session_ids
.
map
{
|
session_id
|
key_name
(
user
.
id
,
session_id
)
}
session_names
=
session_ids
.
map
{
|
session_id
|
"
#{
Gitlab
::
Redis
::
SharedState
::
SESSION_NAMESPACE
}
:
#{
session_id
}
"
}
redis
.
srem
(
lookup_key_name
(
user
.
id
),
session_ids
)
redis
.
del
(
key_names
)
redis
.
del
(
session_names
)
end
end
def
self
.
cleanup
(
user
)
def
self
.
cleanup
(
user
)
Gitlab
::
Redis
::
SharedState
.
with
do
|
redis
|
Gitlab
::
Redis
::
SharedState
.
with
do
|
redis
|
clean_up_old_sessions
(
redis
,
user
)
cleaned_up_lookup_entries
(
redis
,
user
)
cleaned_up_lookup_entries
(
redis
,
user
)
end
end
end
end
...
@@ -118,19 +120,39 @@ class ActiveSession
...
@@ -118,19 +120,39 @@ class ActiveSession
end
end
end
end
def
self
.
raw_active_session_entries
(
session_ids
,
user_id
)
def
self
.
raw_active_session_entries
(
redis
,
session_ids
,
user_id
)
return
[]
if
session_ids
.
empty?
return
[]
if
session_ids
.
empty?
Gitlab
::
Redis
::
SharedState
.
with
do
|
redis
|
entry_keys
=
session_ids
.
map
{
|
session_id
|
key_name
(
user_id
,
session_id
)
}
entry_keys
=
session_ids
.
map
{
|
session_id
|
key_name
(
user_id
,
session_id
)
}
redis
.
mget
(
entry_keys
)
redis
.
mget
(
entry_keys
)
end
end
def
self
.
active_session_entries
(
session_ids
,
user_id
,
redis
)
return
[]
if
session_ids
.
empty?
entry_keys
=
raw_active_session_entries
(
redis
,
session_ids
,
user_id
)
entry_keys
.
map
do
|
raw_session
|
Marshal
.
load
(
raw_session
)
# rubocop:disable Security/MarshalLoad
end
end
def
self
.
clean_up_old_sessions
(
redis
,
user
)
session_ids
=
session_ids_for_user
(
user
.
id
)
return
if
session_ids
.
count
<=
ALLOWED_NUMBER_OF_ACTIVE_SESSIONS
# remove sessions if there are more than ALLOWED_NUMBER_OF_ACTIVE_SESSIONS.
sessions
=
active_session_entries
(
session_ids
,
user
.
id
,
redis
)
sessions
.
sort_by!
{
|
session
|
session
.
updated_at
}.
reverse!
sessions
=
sessions
[
ALLOWED_NUMBER_OF_ACTIVE_SESSIONS
..-
1
].
map
{
|
session
|
session
.
session_id
}
destroy_sessions
(
redis
,
user
,
sessions
)
end
end
def
self
.
cleaned_up_lookup_entries
(
redis
,
user
)
def
self
.
cleaned_up_lookup_entries
(
redis
,
user
)
session_ids
=
session_ids_for_user
(
user
.
id
)
session_ids
=
session_ids_for_user
(
user
.
id
)
entries
=
raw_active_session_entries
(
session_ids
,
user
.
id
)
entries
=
raw_active_session_entries
(
redis
,
session_ids
,
user
.
id
)
# remove expired keys.
# remove expired keys.
# only the single key entries are automatically expired by redis, the
# only the single key entries are automatically expired by redis, the
...
...
changelogs/unreleased/31611-limit-the-number-of-stored-sessions-per-user.yml
0 → 100644
View file @
20758bc3
---
title
:
Resolve Limit the number of stored sessions per user
merge_request
:
19325
author
:
type
:
added
changelogs/unreleased/34377-design-view-download-single-issue-design-image.yml
0 → 100644
View file @
20758bc3
---
title
:
'
Resolve
Design
view:
Download
single
issue
design
image'
merge_request
:
20703
author
:
type
:
added
changelogs/unreleased/cleanup-monitoring-dashboard-unused-methods.yml
0 → 100644
View file @
20758bc3
---
title
:
Removed unused methods in monitoring dashboard
merge_request
:
20819
author
:
type
:
other
changelogs/unreleased/fix-fork-link-display-bug.yml
0 → 100644
View file @
20758bc3
---
title
:
Fix a display bug in the fork removal description message
merge_request
:
20843
author
:
type
:
fixed
doc/administration/high_availability/README.md
View file @
20758bc3
...
@@ -219,13 +219,43 @@ Note that your exact needs may be more, depending on your workload. Your
...
@@ -219,13 +219,43 @@ Note that your exact needs may be more, depending on your workload. Your
workload is influenced by factors such as - but not limited to - how active your
workload is influenced by factors such as - but not limited to - how active your
users are, how much automation you use, mirroring, and repo/change size.
users are, how much automation you use, mirroring, and repo/change size.
### 5,000 User Configuration
-
**Supported Users (approximate):**
50,000
-
**Test RPS Rates:**
API: 100 RPS, Web: 10 RPS, Git: 10 RPS
-
**Status:**
Work-in-progress
-
**Known Issues:**
For the latest list of known performance issues head
[
here
](
https://gitlab.com/gitlab-org/gitlab/issues?label_name%5B%5D=Quality%3Aperformance-issues
)
.
NOTE:
**Note:**
This architecture is a work-in-progress of the work so far. The
Quality team will be certifying this environment in late 2019 or early 2020. The specifications
may be adjusted prior to certification based on performance testing.
| Service | Nodes | Configuration | GCP type |
| ----------------------------|-------|-----------------------|---------------|
| GitLab Rails
<br>
- Puma workers on each node set to 90% of available CPUs with 16 threads | 3 | 16 vCPU, 14.4GB Memory | n1-highcpu-16 |
| PostgreSQL | 3 | 2 vCPU, 7.5GB Memory | n1-standard-2 |
| PgBouncer | 3 | 2 vCPU, 1.8GB Memory | n1-highcpu-2 |
| Gitaly
<br>
- Gitaly Ruby workers on each node set to 20% of available CPUs | X[^1] . | 8 vCPU, 30GB Memory | n1-standard-8 |
| Redis Cache + Sentinel
<br>
- Cache maxmemory set to 90% of available memory | 3 | 2 vCPU, 7.5GB Memory | n1-standard-2 |
| Redis Persistent + Sentinel | 3 | 2 vCPU, 7.5GB Memory | n1-standard-2 |
| Sidekiq | 4 | 2 vCPU, 7.5GB Memory | n1-standard-2 |
| Consul | 3 | 2 vCPU, 1.8GB Memory | n1-highcpu-2 |
| NFS Server[^4] . | 1 | 4 vCPU, 3.6GB Memory | n1-highcpu-4 |
| S3 Object Storage[^3] . | - | - | - |
| Monitoring node | 1 | 2 vCPU, 1.8GB Memory | n1-highcpu-2 |
| External load balancing node[^2] . | 1 | 2 vCPU, 1.8GB Memory | n1-highcpu-2 |
| Internal load balancing node[^2] . | 1 | 2 vCPU, 1.8GB Memory | n1-highcpu-2 |
NOTE:
**Note:**
Memory values are given directly by GCP machine sizes. On different cloud
vendors a best effort like for like can be used.
### 10,000 User Configuration
### 10,000 User Configuration
-
**Supported Users (approximate):**
10,000
-
**Supported Users (approximate):**
10,000
-
**Test RPS Rates:**
API: 200 RPS, Web: 20 RPS, Git: 20 RPS
-
**Test RPS Rates:**
API: 200 RPS, Web: 20 RPS, Git: 20 RPS
-
**Known Issues:**
While validating the reference architectures, slow API
-
**Known Issues:**
For the latest list of known performance issues head
endpoints were discovered. For details, see the related issues list in
[
here
](
https://gitlab.com/gitlab-org/gitlab/issues?label_name%5B%5D=Quality%3Aperformance-issues
)
.
[
this issue
](
https://gitlab.com/gitlab-org/quality/performance/issues/125
)
.
| Service | Nodes | Configuration | GCP type |
| Service | Nodes | Configuration | GCP type |
| ----------------------------|-------|-----------------------|---------------|
| ----------------------------|-------|-----------------------|---------------|
...
@@ -250,9 +280,8 @@ vendors a best effort like for like can be used.
...
@@ -250,9 +280,8 @@ vendors a best effort like for like can be used.
-
**Supported Users (approximate):**
25,000
-
**Supported Users (approximate):**
25,000
-
**Test RPS Rates:**
API: 500 RPS, Web: 50 RPS, Git: 50 RPS
-
**Test RPS Rates:**
API: 500 RPS, Web: 50 RPS, Git: 50 RPS
-
**Known Issues:**
While validating the reference architectures, slow API
-
**Known Issues:**
For the latest list of known performance issues head
endpoints were discovered. For details, see the related issues list in
[
here
](
https://gitlab.com/gitlab-org/gitlab/issues?label_name%5B%5D=Quality%3Aperformance-issues
)
.
[
this issue
](
https://gitlab.com/gitlab-org/quality/performance/issues/125
)
.
| Service | Nodes | Configuration | GCP type |
| Service | Nodes | Configuration | GCP type |
| ----------------------------|-------|-----------------------|---------------|
| ----------------------------|-------|-----------------------|---------------|
...
@@ -277,9 +306,8 @@ vendors a best effort like for like can be used.
...
@@ -277,9 +306,8 @@ vendors a best effort like for like can be used.
-
**Supported Users (approximate):**
50,000
-
**Supported Users (approximate):**
50,000
-
**Test RPS Rates:**
API: 1000 RPS, Web: 100 RPS, Git: 100 RPS
-
**Test RPS Rates:**
API: 1000 RPS, Web: 100 RPS, Git: 100 RPS
-
**Known Issues:**
While validating the reference architectures, slow API
-
**Known Issues:**
For the latest list of known performance issues head
endpoints were discovered. For details, see the related issues list in
[
here
](
https://gitlab.com/gitlab-org/gitlab/issues?label_name%5B%5D=Quality%3Aperformance-issues
)
.
[
this issue
](
https://gitlab.com/gitlab-org/quality/performance/issues/125
)
.
| Service | Nodes | Configuration | GCP type |
| Service | Nodes | Configuration | GCP type |
| ----------------------------|-------|-----------------------|---------------|
| ----------------------------|-------|-----------------------|---------------|
...
@@ -300,15 +328,16 @@ endpoints were discovered. For details, see the related issues list in
...
@@ -300,15 +328,16 @@ endpoints were discovered. For details, see the related issues list in
NOTE:
**Note:**
Memory values are given directly by GCP machine sizes. On different cloud
NOTE:
**Note:**
Memory values are given directly by GCP machine sizes. On different cloud
vendors a best effort like for like can be used.
vendors a best effort like for like can be used.
[
^1
]:
Gitaly
node requirements are dependent on customer data. We recommend 2
[
^1
]:
Gitaly
node requirements are dependent on customer data, specifically the number of
nodes as an absolute minimum for performance at the 10,000 and 25,000 user
projects and their sizes. We recommend 2 nodes as an absolute minimum for HA environments
scale and 4 nodes as an absolute minimum at the 50,000 user scale, but
and at least 4 nodes should be used when supporting 50,000 or more users.
additional nodes should be considered in conjunction with a review of
We recommend that each Gitaly node should store no more than 5TB of data.
project counts and sizes.
Additional nodes should be considered in conjunction with a review of expected
data size and spread based on the recommendations above.
[
^2
]:
Our
architectures have been tested and validated with
[
HAProxy
](
https://www.haproxy.org/
)
[
^2
]:
Our
architectures have been tested and validated with
[
HAProxy
](
https://www.haproxy.org/
)
as the load balancer. However other reputable load balancers with similar feature sets
as the load balancer. However other reputable load balancers with similar feature sets
should also work here
but be aware these aren't validated.
should also work instead
but be aware these aren't validated.
[
^3
]:
For
data objects such as LFS, Uploads, Artifacts, etc... We recommend a S3 Object Storage
[
^3
]:
For
data objects such as LFS, Uploads, Artifacts, etc... We recommend a S3 Object Storage
where possible over NFS due to better performance and availability. Several types of objects
where possible over NFS due to better performance and availability. Several types of objects
...
...
doc/user/profile/active_sessions.md
View file @
20758bc3
...
@@ -18,6 +18,9 @@ review the sessions, and revoke any you don't recognize.
...
@@ -18,6 +18,9 @@ review the sessions, and revoke any you don't recognize.
![
Active sessions list
](
img/active_sessions_list.png
)
![
Active sessions list
](
img/active_sessions_list.png
)
CAUTION:
**Caution:**
It is currently possible to have 100 active sessions at once. If the number of active sessions exceed 100, the oldest ones will be deleted.
<!-- ## Troubleshooting
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
...
...
lib/quality/kubernetes_client.rb
View file @
20758bc3
...
@@ -4,6 +4,7 @@ require_relative '../gitlab/popen' unless defined?(Gitlab::Popen)
...
@@ -4,6 +4,7 @@ require_relative '../gitlab/popen' unless defined?(Gitlab::Popen)
module
Quality
module
Quality
class
KubernetesClient
class
KubernetesClient
RESOURCE_LIST
=
'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa,crd'
CommandFailedError
=
Class
.
new
(
StandardError
)
CommandFailedError
=
Class
.
new
(
StandardError
)
attr_reader
:namespace
attr_reader
:namespace
...
@@ -13,6 +14,13 @@ module Quality
...
@@ -13,6 +14,13 @@ module Quality
end
end
def
cleanup
(
release_name
:,
wait:
true
)
def
cleanup
(
release_name
:,
wait:
true
)
delete_by_selector
(
release_name:
release_name
,
wait:
wait
)
delete_by_matching_name
(
release_name:
release_name
)
end
private
def
delete_by_selector
(
release_name
:,
wait
:)
selector
=
case
release_name
selector
=
case
release_name
when
String
when
String
%(-l release="#{release_name}")
%(-l release="#{release_name}")
...
@@ -23,9 +31,9 @@ module Quality
...
@@ -23,9 +31,9 @@ module Quality
end
end
command
=
[
command
=
[
%(--namespace "#{namespace}")
,
'delete'
,
'delete'
,
'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa'
,
RESOURCE_LIST
,
%(--namespace "#{namespace}")
,
'--now'
,
'--now'
,
'--ignore-not-found'
,
'--ignore-not-found'
,
'--include-uninitialized'
,
'--include-uninitialized'
,
...
@@ -36,7 +44,29 @@ module Quality
...
@@ -36,7 +44,29 @@ module Quality
run_command
(
command
)
run_command
(
command
)
end
end
private
def
delete_by_matching_name
(
release_name
:)
resource_names
=
raw_resource_names
command
=
[
'delete'
,
%(--namespace "#{namespace}")
]
Array
(
release_name
).
each
do
|
release
|
resource_names
.
select
{
|
resource_name
|
resource_name
.
include?
(
release
)
}
.
each
{
|
matching_resource
|
run_command
(
command
+
[
matching_resource
])
}
end
end
def
raw_resource_names
command
=
[
'get'
,
RESOURCE_LIST
,
%(--namespace "#{namespace}")
,
'-o custom-columns=NAME:.metadata.name'
]
run_command
(
command
).
lines
.
map
(
&
:strip
)
end
def
run_command
(
command
)
def
run_command
(
command
)
final_command
=
[
'kubectl'
,
*
command
].
join
(
' '
)
final_command
=
[
'kubectl'
,
*
command
].
join
(
' '
)
...
...
lib/tasks/gitlab/cleanup.rake
View file @
20758bc3
...
@@ -92,7 +92,7 @@ namespace :gitlab do
...
@@ -92,7 +92,7 @@ namespace :gitlab do
lookup_key_count
=
redis
.
scard
(
key
)
lookup_key_count
=
redis
.
scard
(
key
)
session_ids
=
ActiveSession
.
session_ids_for_user
(
user_id
)
session_ids
=
ActiveSession
.
session_ids_for_user
(
user_id
)
entries
=
ActiveSession
.
raw_active_session_entries
(
session_ids
,
user_id
)
entries
=
ActiveSession
.
raw_active_session_entries
(
redis
,
session_ids
,
user_id
)
session_ids_and_entries
=
session_ids
.
zip
(
entries
)
session_ids_and_entries
=
session_ids
.
zip
(
entries
)
inactive_session_ids
=
session_ids_and_entries
.
map
do
|
session_id
,
session
|
inactive_session_ids
=
session_ids_and_entries
.
map
do
|
session_id
,
session
|
...
...
scripts/review_apps/review-apps.sh
View file @
20758bc3
...
@@ -48,11 +48,31 @@ function delete_release() {
...
@@ -48,11 +48,31 @@ function delete_release() {
return
return
fi
fi
echoinfo
"Deleting release '
${
release
}
'..."
true
helm_delete_release
"
${
namespace
}
"
"
${
release
}
"
kubectl_cleanup_release
"
${
namespace
}
"
"
${
release
}
"
}
function
helm_delete_release
()
{
local
namespace
=
"
${
1
}
"
local
release
=
"
${
2
}
"
echoinfo
"Deleting Helm release '
${
release
}
'..."
true
helm delete
--tiller-namespace
"
${
namespace
}
"
--purge
"
${
release
}
"
helm delete
--tiller-namespace
"
${
namespace
}
"
--purge
"
${
release
}
"
}
}
function
kubectl_cleanup_release
()
{
local
namespace
=
"
${
1
}
"
local
release
=
"
${
2
}
"
echoinfo
"Deleting all K8s resources matching '
${
release
}
'..."
true
kubectl
--namespace
"
${
namespace
}
"
get ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa,crd 2>&1
\
|
grep
"
${
release
}
"
\
|
awk
'{print $1}'
\
| xargs kubectl
--namespace
"
${
namespace
}
"
delete
\
||
true
}
function
delete_failed_release
()
{
function
delete_failed_release
()
{
local
namespace
=
"
${
KUBE_NAMESPACE
}
"
local
namespace
=
"
${
KUBE_NAMESPACE
}
"
local
release
=
"
${
CI_ENVIRONMENT_SLUG
}
"
local
release
=
"
${
CI_ENVIRONMENT_SLUG
}
"
...
...
scripts/trigger-build
View file @
20758bc3
...
@@ -71,7 +71,7 @@ module Trigger
...
@@ -71,7 +71,7 @@ module Trigger
# Can be overridden
# Can be overridden
def
version_param_value
(
version_file
)
def
version_param_value
(
version_file
)
File
.
read
(
version_file
).
strip
ENV
[
version_file
]
&
.
strip
||
File
.
read
(
version_file
).
strip
end
end
def
variables
def
variables
...
...
spec/lib/gitlab/health_checks/probes/collection_spec.rb
View file @
20758bc3
...
@@ -5,7 +5,7 @@ require 'spec_helper'
...
@@ -5,7 +5,7 @@ require 'spec_helper'
describe
Gitlab
::
HealthChecks
::
Probes
::
Collection
do
describe
Gitlab
::
HealthChecks
::
Probes
::
Collection
do
let
(
:readiness
)
{
described_class
.
new
(
*
checks
)
}
let
(
:readiness
)
{
described_class
.
new
(
*
checks
)
}
describe
'#
call
'
do
describe
'#
execute
'
do
subject
{
readiness
.
execute
}
subject
{
readiness
.
execute
}
context
'with all checks'
do
context
'with all checks'
do
...
...
spec/lib/quality/kubernetes_client_spec.rb
View file @
20758bc3
...
@@ -5,15 +5,27 @@ require 'fast_spec_helper'
...
@@ -5,15 +5,27 @@ require 'fast_spec_helper'
RSpec
.
describe
Quality
::
KubernetesClient
do
RSpec
.
describe
Quality
::
KubernetesClient
do
let
(
:namespace
)
{
'review-apps-ee'
}
let
(
:namespace
)
{
'review-apps-ee'
}
let
(
:release_name
)
{
'my-release'
}
let
(
:release_name
)
{
'my-release'
}
let
(
:pod_for_release
)
{
"pod-my-release-abcd"
}
let
(
:raw_resource_names_str
)
{
"NAME
\n
foo
\n
#{
pod_for_release
}
\n
bar"
}
let
(
:raw_resource_names
)
{
raw_resource_names_str
.
lines
.
map
(
&
:strip
)
}
subject
{
described_class
.
new
(
namespace:
namespace
)
}
subject
{
described_class
.
new
(
namespace:
namespace
)
}
describe
'RESOURCE_LIST'
do
it
'returns the correct list of resources separated by commas'
do
expect
(
described_class
::
RESOURCE_LIST
).
to
eq
(
'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa,crd'
)
end
end
describe
'#cleanup'
do
describe
'#cleanup'
do
before
do
allow
(
subject
).
to
receive
(
:raw_resource_names
).
and_return
(
raw_resource_names
)
end
it
'raises an error if the Kubernetes command fails'
do
it
'raises an error if the Kubernetes command fails'
do
expect
(
Gitlab
::
Popen
).
to
receive
(
:popen_with_detail
)
expect
(
Gitlab
::
Popen
).
to
receive
(
:popen_with_detail
)
.
with
([
%(kubectl --namespace "#{namespace}" delete )
\
.
with
([
"kubectl delete
#{
described_class
::
RESOURCE_LIST
}
"
+
'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa '
\
%(--namespace "#{namespace}" --now --ignore-not-found --include-uninitialized --wait=true -l release="#{release_name}")
])
"--now --ignore-not-found --include-uninitialized --wait=true -l release=
\"
#{
release_name
}
\"
"
])
.
and_return
(
Gitlab
::
Popen
::
Result
.
new
([],
''
,
''
,
double
(
success?:
false
)))
.
and_return
(
Gitlab
::
Popen
::
Result
.
new
([],
''
,
''
,
double
(
success?:
false
)))
expect
{
subject
.
cleanup
(
release_name:
release_name
)
}.
to
raise_error
(
described_class
::
CommandFailedError
)
expect
{
subject
.
cleanup
(
release_name:
release_name
)
}.
to
raise_error
(
described_class
::
CommandFailedError
)
...
@@ -21,9 +33,12 @@ RSpec.describe Quality::KubernetesClient do
...
@@ -21,9 +33,12 @@ RSpec.describe Quality::KubernetesClient do
it
'calls kubectl with the correct arguments'
do
it
'calls kubectl with the correct arguments'
do
expect
(
Gitlab
::
Popen
).
to
receive
(
:popen_with_detail
)
expect
(
Gitlab
::
Popen
).
to
receive
(
:popen_with_detail
)
.
with
([
%(kubectl --namespace "#{namespace}" delete )
\
.
with
([
"kubectl delete
#{
described_class
::
RESOURCE_LIST
}
"
+
'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa '
\
%(--namespace "#{namespace}" --now --ignore-not-found --include-uninitialized --wait=true -l release="#{release_name}")
])
"--now --ignore-not-found --include-uninitialized --wait=true -l release=
\"
#{
release_name
}
\"
"
])
.
and_return
(
Gitlab
::
Popen
::
Result
.
new
([],
''
,
''
,
double
(
success?:
true
)))
expect
(
Gitlab
::
Popen
).
to
receive
(
:popen_with_detail
)
.
with
([
%(kubectl delete --namespace "#{namespace}" #{pod_for_release})
])
.
and_return
(
Gitlab
::
Popen
::
Result
.
new
([],
''
,
''
,
double
(
success?:
true
)))
.
and_return
(
Gitlab
::
Popen
::
Result
.
new
([],
''
,
''
,
double
(
success?:
true
)))
# We're not verifying the output here, just silencing it
# We're not verifying the output here, just silencing it
...
@@ -35,9 +50,8 @@ RSpec.describe Quality::KubernetesClient do
...
@@ -35,9 +50,8 @@ RSpec.describe Quality::KubernetesClient do
it
'raises an error if the Kubernetes command fails'
do
it
'raises an error if the Kubernetes command fails'
do
expect
(
Gitlab
::
Popen
).
to
receive
(
:popen_with_detail
)
expect
(
Gitlab
::
Popen
).
to
receive
(
:popen_with_detail
)
.
with
([
%(kubectl --namespace "#{namespace}" delete )
\
.
with
([
"kubectl delete
#{
described_class
::
RESOURCE_LIST
}
"
+
'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa '
\
%(--namespace "#{namespace}" --now --ignore-not-found --include-uninitialized --wait=true -l 'release in (#{release_name.join(', ')})')
])
"--now --ignore-not-found --include-uninitialized --wait=true -l 'release in (
#{
release_name
.
join
(
', '
)
}
)'"
])
.
and_return
(
Gitlab
::
Popen
::
Result
.
new
([],
''
,
''
,
double
(
success?:
false
)))
.
and_return
(
Gitlab
::
Popen
::
Result
.
new
([],
''
,
''
,
double
(
success?:
false
)))
expect
{
subject
.
cleanup
(
release_name:
release_name
)
}.
to
raise_error
(
described_class
::
CommandFailedError
)
expect
{
subject
.
cleanup
(
release_name:
release_name
)
}.
to
raise_error
(
described_class
::
CommandFailedError
)
...
@@ -45,9 +59,12 @@ RSpec.describe Quality::KubernetesClient do
...
@@ -45,9 +59,12 @@ RSpec.describe Quality::KubernetesClient do
it
'calls kubectl with the correct arguments'
do
it
'calls kubectl with the correct arguments'
do
expect
(
Gitlab
::
Popen
).
to
receive
(
:popen_with_detail
)
expect
(
Gitlab
::
Popen
).
to
receive
(
:popen_with_detail
)
.
with
([
%(kubectl --namespace "#{namespace}" delete )
\
.
with
([
"kubectl delete
#{
described_class
::
RESOURCE_LIST
}
"
+
'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa '
\
%(--namespace "#{namespace}" --now --ignore-not-found --include-uninitialized --wait=true -l 'release in (#{release_name.join(', ')})')
])
"--now --ignore-not-found --include-uninitialized --wait=true -l 'release in (
#{
release_name
.
join
(
', '
)
}
)'"
])
.
and_return
(
Gitlab
::
Popen
::
Result
.
new
([],
''
,
''
,
double
(
success?:
true
)))
expect
(
Gitlab
::
Popen
).
to
receive
(
:popen_with_detail
)
.
with
([
%(kubectl delete --namespace "#{namespace}" #{pod_for_release})
])
.
and_return
(
Gitlab
::
Popen
::
Result
.
new
([],
''
,
''
,
double
(
success?:
true
)))
.
and_return
(
Gitlab
::
Popen
::
Result
.
new
([],
''
,
''
,
double
(
success?:
true
)))
# We're not verifying the output here, just silencing it
# We're not verifying the output here, just silencing it
...
@@ -58,9 +75,8 @@ RSpec.describe Quality::KubernetesClient do
...
@@ -58,9 +75,8 @@ RSpec.describe Quality::KubernetesClient do
context
'with `wait: false`'
do
context
'with `wait: false`'
do
it
'raises an error if the Kubernetes command fails'
do
it
'raises an error if the Kubernetes command fails'
do
expect
(
Gitlab
::
Popen
).
to
receive
(
:popen_with_detail
)
expect
(
Gitlab
::
Popen
).
to
receive
(
:popen_with_detail
)
.
with
([
%(kubectl --namespace "#{namespace}" delete )
\
.
with
([
"kubectl delete
#{
described_class
::
RESOURCE_LIST
}
"
+
'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa '
\
%(--namespace "#{namespace}" --now --ignore-not-found --include-uninitialized --wait=false -l release="#{release_name}")
])
"--now --ignore-not-found --include-uninitialized --wait=false -l release=
\"
#{
release_name
}
\"
"
])
.
and_return
(
Gitlab
::
Popen
::
Result
.
new
([],
''
,
''
,
double
(
success?:
false
)))
.
and_return
(
Gitlab
::
Popen
::
Result
.
new
([],
''
,
''
,
double
(
success?:
false
)))
expect
{
subject
.
cleanup
(
release_name:
release_name
,
wait:
false
)
}.
to
raise_error
(
described_class
::
CommandFailedError
)
expect
{
subject
.
cleanup
(
release_name:
release_name
,
wait:
false
)
}.
to
raise_error
(
described_class
::
CommandFailedError
)
...
@@ -68,9 +84,12 @@ RSpec.describe Quality::KubernetesClient do
...
@@ -68,9 +84,12 @@ RSpec.describe Quality::KubernetesClient do
it
'calls kubectl with the correct arguments'
do
it
'calls kubectl with the correct arguments'
do
expect
(
Gitlab
::
Popen
).
to
receive
(
:popen_with_detail
)
expect
(
Gitlab
::
Popen
).
to
receive
(
:popen_with_detail
)
.
with
([
%(kubectl --namespace "#{namespace}" delete )
\
.
with
([
"kubectl delete
#{
described_class
::
RESOURCE_LIST
}
"
+
'ingress,svc,pdb,hpa,deploy,statefulset,job,pod,secret,configmap,pvc,secret,clusterrole,clusterrolebinding,role,rolebinding,sa '
\
%(--namespace "#{namespace}" --now --ignore-not-found --include-uninitialized --wait=false -l release="#{release_name}")
])
"--now --ignore-not-found --include-uninitialized --wait=false -l release=
\"
#{
release_name
}
\"
"
])
.
and_return
(
Gitlab
::
Popen
::
Result
.
new
([],
''
,
''
,
double
(
success?:
true
)))
expect
(
Gitlab
::
Popen
).
to
receive
(
:popen_with_detail
)
.
with
([
%(kubectl delete --namespace "#{namespace}" #{pod_for_release})
])
.
and_return
(
Gitlab
::
Popen
::
Result
.
new
([],
''
,
''
,
double
(
success?:
true
)))
.
and_return
(
Gitlab
::
Popen
::
Result
.
new
([],
''
,
''
,
double
(
success?:
true
)))
# We're not verifying the output here, just silencing it
# We're not verifying the output here, just silencing it
...
@@ -78,4 +97,15 @@ RSpec.describe Quality::KubernetesClient do
...
@@ -78,4 +97,15 @@ RSpec.describe Quality::KubernetesClient do
end
end
end
end
end
end
describe
'#raw_resource_names'
do
it
'calls kubectl to retrieve the resource names'
do
expect
(
Gitlab
::
Popen
).
to
receive
(
:popen_with_detail
)
.
with
([
"kubectl get
#{
described_class
::
RESOURCE_LIST
}
"
+
%(--namespace "#{namespace}" -o custom-columns=NAME:.metadata.name)
])
.
and_return
(
Gitlab
::
Popen
::
Result
.
new
([],
raw_resource_names_str
,
''
,
double
(
success?:
true
)))
expect
(
subject
.
__send__
(
:raw_resource_names
)).
to
eq
(
raw_resource_names
)
end
end
end
end
spec/models/active_session_spec.rb
View file @
20758bc3
...
@@ -242,23 +242,13 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
...
@@ -242,23 +242,13 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
expect
(
redis
.
scan_each
(
match:
"session:gitlab:*"
).
to_a
).
to
be_empty
expect
(
redis
.
scan_each
(
match:
"session:gitlab:*"
).
to_a
).
to
be_empty
end
end
end
end
it
'does not remove the devise session if the active session could not be found'
do
Gitlab
::
Redis
::
SharedState
.
with
do
|
redis
|
redis
.
set
(
"session:gitlab:6919a6f1bb119dd7396fadc38fd18d0d"
,
''
)
end
end
other_user
=
create
(
:user
)
describe
'.cleanup'
do
before
do
ActiveSession
.
destroy
(
other_user
,
request
.
session
.
id
)
stub_const
(
"ActiveSession::ALLOWED_NUMBER_OF_ACTIVE_SESSIONS"
,
5
)
Gitlab
::
Redis
::
SharedState
.
with
do
|
redis
|
expect
(
redis
.
scan_each
(
match:
"session:gitlab:*"
).
to_a
).
not_to
be_empty
end
end
end
end
describe
'.cleanup'
do
it
'removes obsolete lookup entries'
do
it
'removes obsolete lookup entries'
do
Gitlab
::
Redis
::
SharedState
.
with
do
|
redis
|
Gitlab
::
Redis
::
SharedState
.
with
do
|
redis
|
redis
.
set
(
"session:user:gitlab:
#{
user
.
id
}
:6919a6f1bb119dd7396fadc38fd18d0d"
,
''
)
redis
.
set
(
"session:user:gitlab:
#{
user
.
id
}
:6919a6f1bb119dd7396fadc38fd18d0d"
,
''
)
...
@@ -276,5 +266,47 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
...
@@ -276,5 +266,47 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
it
'does not bail if there are no lookup entries'
do
it
'does not bail if there are no lookup entries'
do
ActiveSession
.
cleanup
(
user
)
ActiveSession
.
cleanup
(
user
)
end
end
context
'cleaning up old sessions'
do
let
(
:max_number_of_sessions_plus_one
)
{
ActiveSession
::
ALLOWED_NUMBER_OF_ACTIVE_SESSIONS
+
1
}
let
(
:max_number_of_sessions_plus_two
)
{
ActiveSession
::
ALLOWED_NUMBER_OF_ACTIVE_SESSIONS
+
2
}
before
do
Gitlab
::
Redis
::
SharedState
.
with
do
|
redis
|
(
1
..
max_number_of_sessions_plus_two
).
each
do
|
number
|
redis
.
set
(
"session:user:gitlab:
#{
user
.
id
}
:
#{
number
}
"
,
Marshal
.
dump
(
ActiveSession
.
new
(
session_id:
"
#{
number
}
"
,
updated_at:
number
.
days
.
ago
))
)
redis
.
sadd
(
"session:lookup:user:gitlab:
#{
user
.
id
}
"
,
"
#{
number
}
"
)
end
end
end
it
'removes obsolete active sessions entries'
do
ActiveSession
.
cleanup
(
user
)
Gitlab
::
Redis
::
SharedState
.
with
do
|
redis
|
sessions
=
redis
.
scan_each
(
match:
"session:user:gitlab:
#{
user
.
id
}
:*"
).
to_a
expect
(
sessions
.
count
).
to
eq
(
ActiveSession
::
ALLOWED_NUMBER_OF_ACTIVE_SESSIONS
)
expect
(
sessions
).
not_to
include
(
"session:user:gitlab:
#{
user
.
id
}
:
#{
max_number_of_sessions_plus_one
}
"
,
"session:user:gitlab:
#{
user
.
id
}
:
#{
max_number_of_sessions_plus_two
}
"
)
end
end
it
'removes obsolete lookup entries'
do
ActiveSession
.
cleanup
(
user
)
Gitlab
::
Redis
::
SharedState
.
with
do
|
redis
|
lookup_entries
=
redis
.
smembers
(
"session:lookup:user:gitlab:
#{
user
.
id
}
"
)
expect
(
lookup_entries
.
count
).
to
eq
(
ActiveSession
::
ALLOWED_NUMBER_OF_ACTIVE_SESSIONS
)
expect
(
lookup_entries
).
not_to
include
(
max_number_of_sessions_plus_one
.
to_s
,
max_number_of_sessions_plus_two
.
to_s
)
end
end
end
end
end
end
end
spec/views/projects/edit.html.haml_spec.rb
View file @
20758bc3
...
@@ -53,6 +53,7 @@ describe 'projects/edit' do
...
@@ -53,6 +53,7 @@ describe 'projects/edit' do
render
render
expect
(
rendered
).
to
have_content
(
'Remove fork relationship'
)
expect
(
rendered
).
to
have_content
(
'Remove fork relationship'
)
expect
(
rendered
).
to
have_link
(
source_project
.
full_name
,
href:
project_path
(
source_project
))
end
end
it
'hides the fork relationship settings from an unauthorized user'
do
it
'hides the fork relationship settings from an unauthorized user'
do
...
@@ -78,7 +79,7 @@ describe 'projects/edit' do
...
@@ -78,7 +79,7 @@ describe 'projects/edit' do
render
render
expect
(
rendered
).
to
have_content
(
'Remove fork relationship'
)
expect
(
rendered
).
to
have_content
(
'Remove fork relationship'
)
expect
(
rendered
).
to
have_
content
(
source_project
.
full_name
)
expect
(
rendered
).
to
have_
link
(
source_project
.
full_name
,
href:
project_path
(
source_project
)
)
end
end
end
end
end
end
...
...
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