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
b66233f9
Commit
b66233f9
authored
Feb 19, 2018
by
Tomasz Maczukin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Port of add-per-runner-job-timeout to EE
parent
bbede12a
Changes
35
Hide whitespace changes
Inline
Side-by-side
Showing
35 changed files
with
645 additions
and
30 deletions
+645
-30
app/assets/javascripts/jobs/components/sidebar_detail_row.vue
...assets/javascripts/jobs/components/sidebar_detail_row.vue
+24
-0
app/assets/javascripts/jobs/components/sidebar_details_block.vue
...ets/javascripts/jobs/components/sidebar_details_block.vue
+27
-0
app/assets/javascripts/jobs/job_details_bundle.js
app/assets/javascripts/jobs/job_details_bundle.js
+1
-0
app/models/ci/build.rb
app/models/ci/build.rb
+12
-4
app/models/ci/build_metadata.rb
app/models/ci/build_metadata.rb
+35
-0
app/models/ci/runner.rb
app/models/ci/runner.rb
+8
-1
app/models/concerns/chronic_duration_attribute.rb
app/models/concerns/chronic_duration_attribute.rb
+39
-0
app/presenters/ci/build_metadata_presenter.rb
app/presenters/ci/build_metadata_presenter.rb
+18
-0
app/serializers/build_details_entity.rb
app/serializers/build_details_entity.rb
+2
-0
app/serializers/build_metadata_entity.rb
app/serializers/build_metadata_entity.rb
+9
-0
app/views/projects/jobs/show.html.haml
app/views/projects/jobs/show.html.haml
+1
-1
app/views/projects/runners/_form.html.haml
app/views/projects/runners/_form.html.haml
+6
-0
app/views/projects/runners/show.html.haml
app/views/projects/runners/show.html.haml
+3
-0
changelogs/unreleased/add-per-runner-job-timeout.yml
changelogs/unreleased/add-per-runner-job-timeout.yml
+5
-0
db/migrate/20180219153455_add_maximum_timeout_to_ci_runners.rb
...grate/20180219153455_add_maximum_timeout_to_ci_runners.rb
+9
-0
db/migrate/20180301010859_create_ci_builds_metadata_table.rb
db/migrate/20180301010859_create_ci_builds_metadata_table.rb
+20
-0
db/schema.rb
db/schema.rb
+13
-0
doc/api/runners.md
doc/api/runners.md
+5
-2
doc/ci/runners/README.md
doc/ci/runners/README.md
+39
-6
doc/user/project/pipelines/settings.md
doc/user/project/pipelines/settings.md
+9
-0
lib/api/entities.rb
lib/api/entities.rb
+2
-1
lib/api/runner.rb
lib/api/runner.rb
+2
-1
lib/api/runners.rb
lib/api/runners.rb
+1
-0
lib/gitlab/ci/build/step.rb
lib/gitlab/ci/build/step.rb
+2
-2
spec/factories/ci/build_metadata.rb
spec/factories/ci/build_metadata.rb
+9
-0
spec/javascripts/jobs/mock_data.js
spec/javascripts/jobs/mock_data.js
+4
-0
spec/javascripts/jobs/sidebar_detail_row_spec.js
spec/javascripts/jobs/sidebar_detail_row_spec.js
+21
-0
spec/javascripts/jobs/sidebar_details_block_spec.js
spec/javascripts/jobs/sidebar_details_block_spec.js
+6
-0
spec/lib/gitlab/ci/build/step_spec.rb
spec/lib/gitlab/ci/build/step_spec.rb
+10
-2
spec/models/ci/build_metadata_spec.rb
spec/models/ci/build_metadata_spec.rb
+61
-0
spec/models/ci/build_spec.rb
spec/models/ci/build_spec.rb
+64
-6
spec/models/concerns/chronic_duration_attribute_spec.rb
spec/models/concerns/chronic_duration_attribute_spec.rb
+115
-0
spec/requests/api/runner_spec.rb
spec/requests/api/runner_spec.rb
+57
-2
spec/requests/api/runners_spec.rb
spec/requests/api/runners_spec.rb
+4
-1
spec/services/ci/retry_build_service_spec.rb
spec/services/ci/retry_build_service_spec.rb
+2
-1
No files found.
app/assets/javascripts/jobs/components/sidebar_detail_row.vue
View file @
b66233f9
...
...
@@ -11,11 +11,19 @@
type
:
String
,
required
:
true
,
},
helpUrl
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
},
computed
:
{
hasTitle
()
{
return
this
.
title
.
length
>
0
;
},
hasHelpURL
()
{
return
this
.
helpUrl
.
length
>
0
;
},
},
};
</
script
>
...
...
@@ -28,5 +36,21 @@
{{
title
}}
:
</span>
{{
value
}}
<span
v-if=
"hasHelpURL"
class=
"help-button pull-right"
>
<a
:href=
"helpUrl"
target=
"_blank"
rel=
"noopener noreferrer nofollow"
>
<i
class=
"fa fa-question-circle"
aria-hidden=
"true"
></i>
</a>
</span>
</p>
</
template
>
app/assets/javascripts/jobs/components/sidebar_details_block.vue
View file @
b66233f9
...
...
@@ -22,6 +22,11 @@
type
:
Boolean
,
required
:
true
,
},
runnerHelpUrl
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
},
computed
:
{
shouldRenderContent
()
{
...
...
@@ -39,6 +44,21 @@
runnerId
()
{
return
`#
${
this
.
job
.
runner
.
id
}
`
;
},
hasTimeout
()
{
return
this
.
job
.
metadata
!=
null
&&
this
.
job
.
metadata
.
timeout_human_readable
!==
''
;
},
timeout
()
{
if
(
this
.
job
.
metadata
==
null
)
{
return
''
;
}
let
t
=
this
.
job
.
metadata
.
timeout_human_readable
;
if
(
this
.
job
.
metadata
.
timeout_source
!==
''
)
{
t
+=
` (from
${
this
.
job
.
metadata
.
timeout_source
}
)`
;
}
return
t
;
},
renderBlock
()
{
return
this
.
job
.
merge_request
||
this
.
job
.
duration
||
...
...
@@ -114,6 +134,13 @@
title=
"Queued"
:value=
"queued"
/>
<detail-row
class=
"js-job-timeout"
v-if=
"hasTimeout"
title=
"Timeout"
:help-url=
"runnerHelpUrl"
:value=
"timeout"
/>
<detail-row
class=
"js-job-runner"
v-if=
"job.runner"
...
...
app/assets/javascripts/jobs/job_details_bundle.js
View file @
b66233f9
...
...
@@ -51,6 +51,7 @@ export default () => {
props
:
{
isLoading
:
this
.
mediator
.
state
.
isLoading
,
job
:
this
.
mediator
.
store
.
state
.
job
,
runnerHelpUrl
:
dataset
.
runnerHelpUrl
,
},
});
},
...
...
app/models/ci/build.rb
View file @
b66233f9
...
...
@@ -27,6 +27,10 @@ module Ci
has_one
:job_artifacts_metadata
,
->
{
where
(
file_type:
Ci
::
JobArtifact
.
file_types
[
:metadata
])
},
class_name:
'Ci::JobArtifact'
,
inverse_of: :job
,
foreign_key: :job_id
has_one
:job_artifacts_trace
,
->
{
where
(
file_type:
Ci
::
JobArtifact
.
file_types
[
:trace
])
},
class_name:
'Ci::JobArtifact'
,
inverse_of: :job
,
foreign_key: :job_id
has_one
:metadata
,
class_name:
'Ci::BuildMetadata'
delegate
:timeout
,
to: :metadata
,
prefix:
true
,
allow_nil:
true
# The "environment" field for builds is a String, and is the unexpanded name
def
persisted_environment
@persisted_environment
||=
Environment
.
find_by
(
...
...
@@ -156,6 +160,14 @@ module Ci
before_transition
any
=>
[
:running
]
do
|
build
|
build
.
validates_dependencies!
unless
Feature
.
enabled?
(
'ci_disable_validates_dependencies'
)
end
before_transition
pending: :running
do
|
build
|
build
.
ensure_metadata
.
update_timeout_state
end
end
def
ensure_metadata
metadata
||
build_metadata
(
project:
project
)
end
def
detailed_status
(
current_user
)
...
...
@@ -234,10 +246,6 @@ module Ci
latest_builds
.
where
(
'stage_idx < ?'
,
stage_idx
)
end
def
timeout
project
.
build_timeout
end
def
triggered_by?
(
current_user
)
user
==
current_user
end
...
...
app/models/ci/build_metadata.rb
0 → 100644
View file @
b66233f9
module
Ci
# The purpose of this class is to store Build related data that can be disposed.
# Data that should be persisted forever, should be stored with Ci::Build model.
class
BuildMetadata
<
ActiveRecord
::
Base
extend
Gitlab
::
Ci
::
Model
include
Presentable
include
ChronicDurationAttribute
self
.
table_name
=
'ci_builds_metadata'
belongs_to
:build
,
class_name:
'Ci::Build'
belongs_to
:project
validates
:build
,
presence:
true
validates
:project
,
presence:
true
chronic_duration_attr_reader
:timeout_human_readable
,
:timeout
enum
timeout_source:
{
unknown_timeout_source:
1
,
project_timeout_source:
2
,
runner_timeout_source:
3
}
def
update_timeout_state
return
unless
build
.
runner
.
present?
project_timeout
=
project
&
.
build_timeout
timeout
=
[
project_timeout
,
build
.
runner
.
maximum_timeout
].
compact
.
min
timeout_source
=
timeout
<
project_timeout
?
:runner_timeout_source
:
:project_timeout_source
update
(
timeout:
timeout
,
timeout_source:
timeout_source
)
end
end
end
app/models/ci/runner.rb
View file @
b66233f9
...
...
@@ -3,13 +3,14 @@ module Ci
extend
Gitlab
::
Ci
::
Model
include
Gitlab
::
SQL
::
Pattern
include
RedisCacheable
include
ChronicDurationAttribute
prepend
EE
::
Ci
::
Runner
RUNNER_QUEUE_EXPIRY_TIME
=
60
.
minutes
ONLINE_CONTACT_TIMEOUT
=
1
.
hour
UPDATE_DB_RUNNER_INFO_EVERY
=
40
.
minutes
AVAILABLE_SCOPES
=
%w[specific shared active paused online]
.
freeze
FORM_EDITABLE
=
%i[description tag_list active run_untagged locked access_level]
.
freeze
FORM_EDITABLE
=
%i[description tag_list active run_untagged locked access_level
maximum_timeout_human_readable
]
.
freeze
has_many
:builds
has_many
:runner_projects
,
dependent: :destroy
# rubocop:disable Cop/ActiveRecordDependent
...
...
@@ -52,6 +53,12 @@ module Ci
cached_attr_reader
:version
,
:revision
,
:platform
,
:architecture
,
:contacted_at
,
:ip_address
chronic_duration_attr
:maximum_timeout_human_readable
,
:maximum_timeout
validates
:maximum_timeout
,
allow_nil:
true
,
numericality:
{
greater_than_or_equal_to:
600
,
message:
'needs to be at least 10 minutes'
}
# Searches for runners matching the given query.
#
# This method uses ILIKE on PostgreSQL and LIKE on MySQL.
...
...
app/models/concerns/chronic_duration_attribute.rb
0 → 100644
View file @
b66233f9
module
ChronicDurationAttribute
extend
ActiveSupport
::
Concern
class_methods
do
def
chronic_duration_attr_reader
(
virtual_attribute
,
source_attribute
)
define_method
(
virtual_attribute
)
do
chronic_duration_attributes
[
virtual_attribute
]
||
output_chronic_duration_attribute
(
source_attribute
)
end
end
def
chronic_duration_attr_writer
(
virtual_attribute
,
source_attribute
)
chronic_duration_attr_reader
(
virtual_attribute
,
source_attribute
)
define_method
(
"
#{
virtual_attribute
}
="
)
do
|
value
|
chronic_duration_attributes
[
virtual_attribute
]
=
value
.
presence
||
''
begin
new_value
=
ChronicDuration
.
parse
(
value
).
to_i
if
value
.
present?
assign_attributes
(
source_attribute
=>
new_value
)
rescue
ChronicDuration
::
DurationParseError
# ignore error as it will be caught by validation
end
end
validates
virtual_attribute
,
allow_nil:
true
,
duration:
true
end
alias_method
:chronic_duration_attr
,
:chronic_duration_attr_writer
end
def
chronic_duration_attributes
@chronic_duration_attributes
||=
{}
end
def
output_chronic_duration_attribute
(
source_attribute
)
value
=
attributes
[
source_attribute
.
to_s
]
ChronicDuration
.
output
(
value
,
format: :short
)
if
value
end
end
app/presenters/ci/build_metadata_presenter.rb
0 → 100644
View file @
b66233f9
module
Ci
class
BuildMetadataPresenter
<
Gitlab
::
View
::
Presenter
::
Delegated
TIMEOUT_SOURCES
=
{
unknown_timeout_source:
nil
,
project_timeout_source:
'project'
,
runner_timeout_source:
'runner'
}.
freeze
presents
:metadata
def
timeout_source
return
unless
metadata
.
timeout_source?
TIMEOUT_SOURCES
[
metadata
.
timeout_source
.
to_sym
]
||
metadata
.
timeout_source
end
end
end
app/serializers/build_details_entity.rb
View file @
b66233f9
...
...
@@ -5,6 +5,8 @@ class BuildDetailsEntity < JobEntity
expose
:runner
,
using:
RunnerEntity
expose
:pipeline
,
using:
PipelineEntity
expose
:metadata
,
using:
BuildMetadataEntity
expose
:erased_by
,
if:
->
(
*
)
{
build
.
erased?
},
using:
UserEntity
expose
:erase_path
,
if:
->
(
*
)
{
build
.
erasable?
&&
can?
(
current_user
,
:erase_build
,
build
)
}
do
|
build
|
erase_project_job_path
(
project
,
build
)
...
...
app/serializers/build_metadata_entity.rb
0 → 100644
View file @
b66233f9
class
BuildMetadataEntity
<
Grape
::
Entity
expose
:timeout_human_readable
do
|
metadata
|
metadata
.
timeout_human_readable
unless
metadata
.
timeout
.
nil?
end
expose
:timeout_source
do
|
metadata
|
metadata
.
present
.
timeout_source
end
end
app/views/projects/jobs/show.html.haml
View file @
b66233f9
...
...
@@ -113,4 +113,4 @@
.js-build-options
{
data:
javascript_build_options
}
#js-job-details-vue
{
data:
{
endpoint:
project_job_path
(
@project
,
@build
,
format: :json
)
}
}
#js-job-details-vue
{
data:
{
endpoint:
project_job_path
(
@project
,
@build
,
format: :json
)
,
runner_help_url:
help_page_path
(
'ci/runners/README.html'
,
anchor:
'setting-maximum-job-timeout-for-a-runner'
)
}
}
app/views/projects/runners/_form.html.haml
View file @
b66233f9
...
...
@@ -39,6 +39,12 @@
Description
.col-sm-10
=
f
.
text_field
:description
,
class:
'form-control'
.form-group
=
label_tag
:maximum_timeout_human_readable
,
class:
'control-label'
do
Maximum job timeout
.col-sm-10
=
f
.
text_field
:maximum_timeout_human_readable
,
class:
'form-control'
.help-block
This timeout will take precedence when lower than Project-defined timeout
.form-group
=
label_tag
:tag_list
,
class:
'control-label'
do
Tags
...
...
app/views/projects/runners/show.html.haml
View file @
b66233f9
...
...
@@ -55,6 +55,9 @@
%tr
%td
Description
%td
=
@runner
.
description
%tr
%td
Maximum job timeout
%td
=
@runner
.
maximum_timeout_human_readable
%tr
%td
Last contact
%td
...
...
changelogs/unreleased/add-per-runner-job-timeout.yml
0 → 100644
View file @
b66233f9
---
title
:
Add per-runner configured job timeout
merge_request
:
17221
author
:
type
:
added
db/migrate/20180219153455_add_maximum_timeout_to_ci_runners.rb
0 → 100644
View file @
b66233f9
class
AddMaximumTimeoutToCiRunners
<
ActiveRecord
::
Migration
include
Gitlab
::
Database
::
MigrationHelpers
DOWNTIME
=
false
def
change
add_column
:ci_runners
,
:maximum_timeout
,
:integer
end
end
db/migrate/20180301010859_create_ci_builds_metadata_table.rb
0 → 100644
View file @
b66233f9
class
CreateCiBuildsMetadataTable
<
ActiveRecord
::
Migration
include
Gitlab
::
Database
::
MigrationHelpers
DOWNTIME
=
false
def
change
create_table
:ci_builds_metadata
do
|
t
|
t
.
integer
:build_id
,
null:
false
t
.
integer
:project_id
,
null:
false
t
.
integer
:timeout
t
.
integer
:timeout_source
,
null:
false
,
default:
1
t
.
foreign_key
:ci_builds
,
column: :build_id
,
on_delete: :cascade
t
.
foreign_key
:projects
,
column: :project_id
,
on_delete: :cascade
t
.
index
:build_id
,
unique:
true
t
.
index
:project_id
end
end
end
db/schema.rb
View file @
b66233f9
...
...
@@ -411,6 +411,16 @@ ActiveRecord::Schema.define(version: 20180327101207) do
add_index
"ci_builds"
,
[
"updated_at"
],
name:
"index_ci_builds_on_updated_at"
,
using: :btree
add_index
"ci_builds"
,
[
"user_id"
],
name:
"index_ci_builds_on_user_id"
,
using: :btree
create_table
"ci_builds_metadata"
,
force: :cascade
do
|
t
|
t
.
integer
"build_id"
,
null:
false
t
.
integer
"project_id"
,
null:
false
t
.
integer
"timeout"
t
.
integer
"timeout_source"
,
default:
1
,
null:
false
end
add_index
"ci_builds_metadata"
,
[
"build_id"
],
name:
"index_ci_builds_metadata_on_build_id"
,
unique:
true
,
using: :btree
add_index
"ci_builds_metadata"
,
[
"project_id"
],
name:
"index_ci_builds_metadata_on_project_id"
,
using: :btree
create_table
"ci_group_variables"
,
force: :cascade
do
|
t
|
t
.
string
"key"
,
null:
false
t
.
text
"value"
...
...
@@ -549,6 +559,7 @@ ActiveRecord::Schema.define(version: 20180327101207) do
t
.
boolean
"locked"
,
default:
false
,
null:
false
t
.
integer
"access_level"
,
default:
0
,
null:
false
t
.
string
"ip_address"
t
.
integer
"maximum_timeout"
end
add_index
"ci_runners"
,
[
"contacted_at"
],
name:
"index_ci_runners_on_contacted_at"
,
using: :btree
...
...
@@ -2605,6 +2616,8 @@ ActiveRecord::Schema.define(version: 20180327101207) do
add_foreign_key
"ci_builds"
,
"ci_pipelines"
,
column:
"auto_canceled_by_id"
,
name:
"fk_a2141b1522"
,
on_delete: :nullify
add_foreign_key
"ci_builds"
,
"ci_stages"
,
column:
"stage_id"
,
name:
"fk_3a9eaa254d"
,
on_delete: :cascade
add_foreign_key
"ci_builds"
,
"projects"
,
name:
"fk_befce0568a"
,
on_delete: :cascade
add_foreign_key
"ci_builds_metadata"
,
"ci_builds"
,
column:
"build_id"
,
on_delete: :cascade
add_foreign_key
"ci_builds_metadata"
,
"projects"
,
on_delete: :cascade
add_foreign_key
"ci_group_variables"
,
"namespaces"
,
column:
"group_id"
,
name:
"fk_33ae4d58d8"
,
on_delete: :cascade
add_foreign_key
"ci_job_artifacts"
,
"ci_builds"
,
column:
"job_id"
,
on_delete: :cascade
add_foreign_key
"ci_job_artifacts"
,
"projects"
,
on_delete: :cascade
...
...
doc/api/runners.md
View file @
b66233f9
...
...
@@ -153,7 +153,8 @@ Example response:
"mysql"
],
"version"
:
null
,
"access_level"
:
"ref_protected"
"access_level"
:
"ref_protected"
,
"maximum_timeout"
:
3600
}
```
...
...
@@ -174,6 +175,7 @@ PUT /runners/:id
|
`run_untagged`
| boolean | no | Flag indicating the runner can execute untagged jobs |
|
`locked`
| boolean | no | Flag indicating the runner is locked |
|
`access_level`
| string | no | The access_level of the runner;
`not_protected`
or
`ref_protected`
|
|
`maximum_timeout`
| integer | no | Maximum timeout set when this Runner will handle the job |
```
curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v4/runners/6" --form "description=test-1-20150125-test" --form "tag_list=ruby,mysql,tag1,tag2"
...
...
@@ -211,7 +213,8 @@ Example response:
"tag2"
],
"version"
:
null
,
"access_level"
:
"ref_protected"
"access_level"
:
"ref_protected"
,
"maximum_timeout"
:
null
}
```
...
...
doc/ci/runners/README.md
View file @
b66233f9
...
...
@@ -231,6 +231,38 @@ To make a Runner pick tagged/untagged jobs:
1.
Check the
**Run untagged jobs**
option
1.
Click
**Save changes**
for the changes to take effect
### Setting maximum job timeout for a Runner
For each Runner you can specify a _maximum job timeout_. Such timeout,
if smaller than [project defined timeout], will take the precedence. This
feature can be used to prevent Shared Runner from being appropriated
by a project by setting a ridiculous big timeout (e.g. one week).
When not configured, Runner will not override project timeout.
How this feature will work:
**Example 1 - Runner timeout bigger than project timeout**
1.
You set the _maximum job timeout_ for a Runner to 24 hours
1.
You set the _CI/CD Timeout_ for a project to
**2 hours**
1.
You start a job
1.
The job, if running longer, will be timeouted after
**2 hours**
**Example 2 - Runner timeout not configured**
1.
You remove the _maximum job timeout_ configuration from a Runner
1.
You set the _CI/CD Timeout_ for a project to
**2 hours**
1.
You start a job
1.
The job, if running longer, will be timeouted after
**2 hours**
**Example 3 - Runner timeout smaller than project timeout**
1.
You set the _maximum job timeout_ for a Runner to
**30 minutes**
1.
You set the _CI/CD Timeout_ for a project to 2 hours
1.
You start a job
1.
The job, if running longer, will be timeouted after
**30 minutes**
### Be careful with sensitive information
With some
[
Runner Executors
](
https://docs.gitlab.com/runner/executors/README.html
)
,
...
...
@@ -259,12 +291,6 @@ Mentioned briefly earlier, but the following things of Runners can be exploited.
We're always looking for contributions that can mitigate these
[
Security Considerations
](
https://docs.gitlab.com/runner/security/
)
.
[
install
]:
http://docs.gitlab.com/runner/install/
[
fifo
]:
https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics)
[
register
]:
http://docs.gitlab.com/runner/register/
[
protected branches
]:
../../user/project/protected_branches.md
[
protected tags
]:
../../user/project/protected_tags.md
## Determining the IP address of a Runner
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17286) in GitLab 10.6.
...
...
@@ -297,3 +323,10 @@ You can find the IP address of a Runner for a specific project by:
1.
On the details page you should see a row for "IP Address"
![
specific Runner IP address
](
img/specific_runner_ip_address.png
)
[
install
]:
http://docs.gitlab.com/runner/install/
[
fifo
]:
https://en.wikipedia.org/wiki/FIFO_(computing_and_electronics)
[
register
]:
http://docs.gitlab.com/runner/register/
[
protected branches
]:
../../user/project/protected_branches.md
[
protected tags
]:
../../user/project/protected_tags.md
[
project defined timeout
]:
../../user/project/pipelines/settings.html#timeout
doc/user/project/pipelines/settings.md
View file @
b66233f9
...
...
@@ -27,6 +27,13 @@ The default value is 60 minutes. Decrease the time limit if you want to impose
a hard limit on your jobs' running time or increase it otherwise. In any case,
if the job surpasses the threshold, it is marked as failed.
### Timeout overriding on Runner level
> - [Introduced][ce-17221] in GitLab 10.6.
Project defined timeout (either specific timeout set by user or the default
60 minutes timeout) may be
[
overridden on Runner level
][
timeout overriding
]
.
## Custom CI config path
> - [Introduced][ce-12509] in GitLab 9.4.
...
...
@@ -152,5 +159,7 @@ into your `README.md`:
[
var
]:
../../../ci/yaml/README.md#git-strategy
[
coverage report
]:
#test-coverage-parsing
[
timeout overriding
]:
../../../ci/runners/README.html#setting-maximum-job-timeout-for-a-runner
[
ce-9362
]:
https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/9362
[
ce-12509
]:
https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/12509
[
ce-17221
]:
https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/17221
lib/api/entities.rb
View file @
b66233f9
...
...
@@ -979,6 +979,7 @@ module API
expose
:tag_list
expose
:run_untagged
expose
:locked
expose
:maximum_timeout
expose
:access_level
expose
:version
,
:revision
,
:platform
,
:architecture
expose
:contacted_at
...
...
@@ -1147,7 +1148,7 @@ module API
end
class
RunnerInfo
<
Grape
::
Entity
expose
:timeout
expose
:
metadata_timeout
,
as: :
timeout
end
class
Step
<
Grape
::
Entity
...
...
lib/api/runner.rb
View file @
b66233f9
...
...
@@ -14,9 +14,10 @@ module API
optional
:locked
,
type:
Boolean
,
desc:
'Should Runner be locked for current project'
optional
:run_untagged
,
type:
Boolean
,
desc:
'Should Runner handle untagged jobs'
optional
:tag_list
,
type:
Array
[
String
],
desc:
%q(List of Runner's tags)
optional
:maximum_timeout
,
type:
Integer
,
desc:
'Maximum timeout set when this Runner will handle the job'
end
post
'/'
do
attributes
=
attributes_for_keys
([
:description
,
:locked
,
:run_untagged
,
:tag_list
])
attributes
=
attributes_for_keys
([
:description
,
:locked
,
:run_untagged
,
:tag_list
,
:maximum_timeout
])
.
merge
(
get_runner_details_from_request
)
runner
=
...
...
lib/api/runners.rb
View file @
b66233f9
...
...
@@ -57,6 +57,7 @@ module API
optional
:locked
,
type:
Boolean
,
desc:
'Flag indicating the runner is locked'
optional
:access_level
,
type:
String
,
values:
Ci
::
Runner
.
access_levels
.
keys
,
desc:
'The access_level of the runner'
optional
:maximum_timeout
,
type:
Integer
,
desc:
'Maximum timeout set when this Runner will handle the job'
at_least_one_of
:description
,
:active
,
:tag_list
,
:run_untagged
,
:locked
,
:access_level
end
put
':id'
do
...
...
lib/gitlab/ci/build/step.rb
View file @
b66233f9
...
...
@@ -14,7 +14,7 @@ module Gitlab
self
.
new
(
:script
).
tap
do
|
step
|
step
.
script
=
job
.
options
[
:before_script
].
to_a
+
job
.
options
[
:script
].
to_a
step
.
script
=
job
.
commands
.
split
(
"
\n
"
)
if
step
.
script
.
empty?
step
.
timeout
=
job
.
timeout
step
.
timeout
=
job
.
metadata_
timeout
step
.
when
=
WHEN_ON_SUCCESS
end
end
...
...
@@ -25,7 +25,7 @@ module Gitlab
self
.
new
(
:after_script
).
tap
do
|
step
|
step
.
script
=
after_script
step
.
timeout
=
job
.
timeout
step
.
timeout
=
job
.
metadata_
timeout
step
.
when
=
WHEN_ALWAYS
step
.
allow_failure
=
true
end
...
...
spec/factories/ci/build_metadata.rb
0 → 100644
View file @
b66233f9
FactoryBot
.
define
do
factory
:ci_build_metadata
,
class:
Ci
::
BuildMetadata
do
build
factory: :ci_build
after
(
:build
)
do
|
build_metadata
,
_
|
build_metadata
.
project
||=
build_metadata
.
build
.
project
end
end
end
spec/javascripts/jobs/mock_data.js
View file @
b66233f9
...
...
@@ -115,6 +115,10 @@ export default {
commit_path
:
'
/root/ci-mock/commit/c58647773a6b5faf066d4ad6ff2c9fbba5f180f6
'
,
},
},
metadata
:
{
timeout_human_readable
:
'
1m 40s
'
,
timeout_source
:
'
runner
'
,
},
merge_request
:
{
iid
:
2
,
path
:
'
/root/ci-mock/merge_requests/2
'
,
...
...
spec/javascripts/jobs/sidebar_detail_row_spec.js
View file @
b66233f9
...
...
@@ -37,4 +37,25 @@ describe('Sidebar detail row', () => {
vm
.
$el
.
textContent
.
replace
(
/
\s
+/g
,
'
'
).
trim
(),
).
toEqual
(
'
this is the title: this is the value
'
);
});
describe
(
'
when helpUrl not provided
'
,
()
=>
{
it
(
'
should not render help
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.help-button
'
)).
toBeNull
();
});
});
describe
(
'
when helpUrl provided
'
,
()
=>
{
beforeEach
(()
=>
{
vm
=
new
SidebarDetailRow
({
propsData
:
{
helpUrl
:
'
help url
'
,
value
:
'
foo
'
,
},
}).
$mount
();
});
it
(
'
should render help
'
,
()
=>
{
expect
(
vm
.
$el
.
querySelector
(
'
.help-button a
'
).
getAttribute
(
'
href
'
)).
toEqual
(
'
help url
'
);
});
});
});
spec/javascripts/jobs/sidebar_details_block_spec.js
View file @
b66233f9
...
...
@@ -96,6 +96,12 @@ describe('Sidebar details block', () => {
).
toEqual
(
'
Runner: #1
'
);
});
it
(
'
should render timeout information
'
,
()
=>
{
expect
(
trimWhitespace
(
vm
.
$el
.
querySelector
(
'
.js-job-timeout
'
)),
).
toEqual
(
'
Timeout: 1m 40s (from runner)
'
);
});
it
(
'
should render coverage
'
,
()
=>
{
expect
(
trimWhitespace
(
vm
.
$el
.
querySelector
(
'
.js-job-coverage
'
)),
...
...
spec/lib/gitlab/ci/build/step_spec.rb
View file @
b66233f9
...
...
@@ -5,10 +5,14 @@ describe Gitlab::Ci::Build::Step do
shared_examples
'has correct script'
do
subject
{
described_class
.
from_commands
(
job
)
}
before
do
job
.
run!
end
it
'fabricates an object'
do
expect
(
subject
.
name
).
to
eq
(
:script
)
expect
(
subject
.
script
).
to
eq
(
script
)
expect
(
subject
.
timeout
).
to
eq
(
job
.
timeout
)
expect
(
subject
.
timeout
).
to
eq
(
job
.
metadata_
timeout
)
expect
(
subject
.
when
).
to
eq
(
'on_success'
)
expect
(
subject
.
allow_failure
).
to
be_falsey
end
...
...
@@ -47,6 +51,10 @@ describe Gitlab::Ci::Build::Step do
subject
{
described_class
.
from_after_script
(
job
)
}
before
do
job
.
run!
end
context
'when after_script is empty'
do
it
'doesn not fabricate an object'
do
is_expected
.
to
be_nil
...
...
@@ -59,7 +67,7 @@ describe Gitlab::Ci::Build::Step do
it
'fabricates an object'
do
expect
(
subject
.
name
).
to
eq
(
:after_script
)
expect
(
subject
.
script
).
to
eq
([
'ls -la'
,
'date'
])
expect
(
subject
.
timeout
).
to
eq
(
job
.
timeout
)
expect
(
subject
.
timeout
).
to
eq
(
job
.
metadata_
timeout
)
expect
(
subject
.
when
).
to
eq
(
'always'
)
expect
(
subject
.
allow_failure
).
to
be_truthy
end
...
...
spec/models/ci/build_metadata_spec.rb
0 → 100644
View file @
b66233f9
require
'spec_helper'
describe
Ci
::
BuildMetadata
do
set
(
:user
)
{
create
(
:user
)
}
set
(
:group
)
{
create
(
:group
,
:access_requestable
)
}
set
(
:project
)
{
create
(
:project
,
:repository
,
group:
group
,
build_timeout:
2000
)
}
set
(
:pipeline
)
do
create
(
:ci_pipeline
,
project:
project
,
sha:
project
.
commit
.
id
,
ref:
project
.
default_branch
,
status:
'success'
)
end
let
(
:build
)
{
create
(
:ci_build
,
pipeline:
pipeline
)
}
let
(
:build_metadata
)
{
create
(
:ci_build_metadata
,
build:
build
)
}
describe
'#update_timeout_state'
do
subject
{
build_metadata
}
context
'when runner is not assigned to the job'
do
it
"doesn't change timeout value"
do
expect
{
subject
.
update_timeout_state
}.
not_to
change
{
subject
.
reload
.
timeout
}
end
it
"doesn't change timeout_source value"
do
expect
{
subject
.
update_timeout_state
}.
not_to
change
{
subject
.
reload
.
timeout_source
}
end
end
context
'when runner is assigned to the job'
do
before
do
build
.
update_attributes
(
runner:
runner
)
end
context
'when runner timeout is lower than project timeout'
do
let
(
:runner
)
{
create
(
:ci_runner
,
maximum_timeout:
1900
)
}
it
'sets runner timeout'
do
expect
{
subject
.
update_timeout_state
}.
to
change
{
subject
.
reload
.
timeout
}.
to
(
1900
)
end
it
'sets runner_timeout_source'
do
expect
{
subject
.
update_timeout_state
}.
to
change
{
subject
.
reload
.
timeout_source
}.
to
(
'runner_timeout_source'
)
end
end
context
'when runner timeout is higher than project timeout'
do
let
(
:runner
)
{
create
(
:ci_runner
,
maximum_timeout:
2100
)
}
it
'sets project timeout'
do
expect
{
subject
.
update_timeout_state
}.
to
change
{
subject
.
reload
.
timeout
}.
to
(
2000
)
end
it
'sets project_timeout_source'
do
expect
{
subject
.
update_timeout_state
}.
to
change
{
subject
.
reload
.
timeout_source
}.
to
(
'project_timeout_source'
)
end
end
end
end
end
spec/models/ci/build_spec.rb
View file @
b66233f9
...
...
@@ -1290,12 +1290,6 @@ describe Ci::Build do
end
describe
'project settings'
do
describe
'#timeout'
do
it
'returns project timeout configuration'
do
expect
(
build
.
timeout
).
to
eq
(
project
.
build_timeout
)
end
end
describe
'#allow_git_fetch'
do
it
'return project allow_git_fetch configuration'
do
expect
(
build
.
allow_git_fetch
).
to
eq
(
project
.
build_allow_git_fetch
)
...
...
@@ -2031,6 +2025,70 @@ describe Ci::Build do
end
end
describe
'state transition: pending: :running'
do
let
(
:runner
)
{
create
(
:ci_runner
)
}
let
(
:job
)
{
create
(
:ci_build
,
:pending
,
runner:
runner
)
}
before
do
job
.
project
.
update_attribute
(
:build_timeout
,
1800
)
end
def
run_job_without_exception
job
.
run!
rescue
StateMachines
::
InvalidTransition
end
shared_examples
'saves data on transition'
do
it
'saves timeout'
do
expect
{
job
.
run!
}.
to
change
{
job
.
reload
.
ensure_metadata
.
timeout
}.
from
(
nil
).
to
(
expected_timeout
)
end
it
'saves timeout_source'
do
expect
{
job
.
run!
}.
to
change
{
job
.
reload
.
ensure_metadata
.
timeout_source
}.
from
(
'unknown_timeout_source'
).
to
(
expected_timeout_source
)
end
context
'when Ci::BuildMetadata#update_timeout_state fails update'
do
before
do
allow_any_instance_of
(
Ci
::
BuildMetadata
).
to
receive
(
:update_timeout_state
).
and_return
(
false
)
end
it
"doesn't save timeout"
do
expect
{
run_job_without_exception
}.
not_to
change
{
job
.
reload
.
ensure_metadata
.
timeout_source
}
end
it
"doesn't save timeout_source"
do
expect
{
run_job_without_exception
}.
not_to
change
{
job
.
reload
.
ensure_metadata
.
timeout_source
}
end
it
'raises an exception'
do
expect
{
job
.
run!
}.
to
raise_error
(
StateMachines
::
InvalidTransition
)
end
end
end
context
'when runner timeout overrides project timeout'
do
let
(
:expected_timeout
)
{
900
}
let
(
:expected_timeout_source
)
{
'runner_timeout_source'
}
before
do
runner
.
update_attribute
(
:maximum_timeout
,
900
)
end
it_behaves_like
'saves data on transition'
end
context
"when runner timeout doesn't override project timeout"
do
let
(
:expected_timeout
)
{
1800
}
let
(
:expected_timeout_source
)
{
'project_timeout_source'
}
before
do
runner
.
update_attribute
(
:maximum_timeout
,
3600
)
end
it_behaves_like
'saves data on transition'
end
end
describe
'state transition: any => [:running]'
do
shared_examples
'validation is active'
do
context
'when depended job has not been completed yet'
do
...
...
spec/models/concerns/chronic_duration_attribute_spec.rb
0 → 100644
View file @
b66233f9
require
'spec_helper'
shared_examples
'ChronicDurationAttribute reader'
do
it
'contains dynamically created reader method'
do
expect
(
subject
.
class
).
to
be_public_method_defined
(
virtual_field
)
end
it
'outputs chronic duration formatted value'
do
subject
.
send
(
"
#{
source_field
}
="
,
120
)
expect
(
subject
.
send
(
virtual_field
)).
to
eq
(
'2m'
)
end
context
'when value is set to nil'
do
it
'outputs nil'
do
subject
.
send
(
"
#{
source_field
}
="
,
nil
)
expect
(
subject
.
send
(
virtual_field
)).
to
be_nil
end
end
end
shared_examples
'ChronicDurationAttribute writer'
do
it
'contains dynamically created writer method'
do
expect
(
subject
.
class
).
to
be_public_method_defined
(
"
#{
virtual_field
}
="
)
end
before
do
subject
.
send
(
"
#{
virtual_field
}
="
,
'10m'
)
end
it
'parses chronic duration input'
do
expect
(
subject
.
send
(
source_field
)).
to
eq
(
600
)
end
it
'passes validation'
do
expect
(
subject
.
valid?
).
to
be_truthy
end
context
'when negative input is used'
do
before
do
subject
.
send
(
"
#{
source_field
}
="
,
3600
)
end
it
"doesn't raise exception"
do
expect
{
subject
.
send
(
"
#{
virtual_field
}
="
,
'-10m'
)
}.
not_to
raise_error
(
ChronicDuration
::
DurationParseError
)
end
it
"doesn't change value"
do
expect
{
subject
.
send
(
"
#{
virtual_field
}
="
,
'-10m'
)
}.
not_to
change
{
subject
.
send
(
source_field
)
}
end
it
"doesn't pass validation"
do
subject
.
send
(
"
#{
virtual_field
}
="
,
'-10m'
)
expect
(
subject
.
valid?
).
to
be_falsey
expect
(
subject
.
errors
&
.
messages
).
to
include
(
virtual_field
=>
[
'is not a correct duration'
])
end
end
context
'when empty input is used'
do
before
do
subject
.
send
(
"
#{
virtual_field
}
="
,
''
)
end
it
'writes nil'
do
expect
(
subject
.
send
(
source_field
)).
to
be_nil
end
it
'passes validation'
do
expect
(
subject
.
valid?
).
to
be_truthy
end
end
context
'when nil input is used'
do
before
do
subject
.
send
(
"
#{
virtual_field
}
="
,
nil
)
end
it
'writes nil'
do
expect
(
subject
.
send
(
source_field
)).
to
be_nil
end
it
'passes validation'
do
expect
(
subject
.
valid?
).
to
be_truthy
end
it
"doesn't raise exception"
do
expect
{
subject
.
send
(
"
#{
virtual_field
}
="
,
nil
)
}.
not_to
raise_error
(
NoMethodError
)
end
end
end
describe
'ChronicDurationAttribute'
do
let
(
:source_field
)
{
:maximum_timeout
}
let
(
:virtual_field
)
{
:maximum_timeout_human_readable
}
subject
{
Ci
::
Runner
.
new
}
it_behaves_like
'ChronicDurationAttribute reader'
it_behaves_like
'ChronicDurationAttribute writer'
end
describe
'ChronicDurationAttribute - reader'
do
let
(
:source_field
)
{
:timeout
}
let
(
:virtual_field
)
{
:timeout_human_readable
}
subject
{
Ci
::
BuildMetadata
.
new
}
it
"doesn't contain dynamically created writer method"
do
expect
(
subject
.
class
).
not_to
be_public_method_defined
(
"
#{
virtual_field
}
="
)
end
it_behaves_like
'ChronicDurationAttribute reader'
end
spec/requests/api/runner_spec.rb
View file @
b66233f9
...
...
@@ -109,6 +109,26 @@ describe API::Runner do
end
end
context
'when maximum job timeout is specified'
do
it
'creates runner'
do
post
api
(
'/runners'
),
token:
registration_token
,
maximum_timeout:
9000
expect
(
response
).
to
have_gitlab_http_status
201
expect
(
Ci
::
Runner
.
first
.
maximum_timeout
).
to
eq
(
9000
)
end
context
'when maximum job timeout is empty'
do
it
'creates runner'
do
post
api
(
'/runners'
),
token:
registration_token
,
maximum_timeout:
''
expect
(
response
).
to
have_gitlab_http_status
201
expect
(
Ci
::
Runner
.
first
.
maximum_timeout
).
to
be_nil
end
end
end
%w(name version revision platform architecture)
.
each
do
|
param
|
context
"when info parameter '
#{
param
}
' info is present"
do
let
(
:value
)
{
"
#{
param
}
_value"
}
...
...
@@ -342,12 +362,12 @@ describe API::Runner do
let
(
:expected_steps
)
do
[{
'name'
=>
'script'
,
'script'
=>
%w(ls date)
,
'timeout'
=>
job
.
timeout
,
'timeout'
=>
job
.
metadata_
timeout
,
'when'
=>
'on_success'
,
'allow_failure'
=>
false
},
{
'name'
=>
'after_script'
,
'script'
=>
%w(ls date)
,
'timeout'
=>
job
.
timeout
,
'timeout'
=>
job
.
metadata_
timeout
,
'when'
=>
'always'
,
'allow_failure'
=>
true
}]
end
...
...
@@ -650,6 +670,41 @@ describe API::Runner do
end
end
end
describe
'timeout support'
do
context
'when project specifies job timeout'
do
let
(
:project
)
{
create
(
:project
,
shared_runners_enabled:
false
,
build_timeout:
1234
)
}
it
'contains info about timeout taken from project'
do
request_job
expect
(
response
).
to
have_gitlab_http_status
(
201
)
expect
(
json_response
[
'runner_info'
]).
to
include
({
'timeout'
=>
1234
})
end
context
'when runner specifies lower timeout'
do
let
(
:runner
)
{
create
(
:ci_runner
,
maximum_timeout:
1000
)
}
it
'contains info about timeout overridden by runner'
do
request_job
expect
(
response
).
to
have_gitlab_http_status
(
201
)
expect
(
json_response
[
'runner_info'
]).
to
include
({
'timeout'
=>
1000
})
end
end
context
'when runner specifies bigger timeout'
do
let
(
:runner
)
{
create
(
:ci_runner
,
maximum_timeout:
2000
)
}
it
'contains info about timeout not overridden by runner'
do
request_job
expect
(
response
).
to
have_gitlab_http_status
(
201
)
expect
(
json_response
[
'runner_info'
]).
to
include
({
'timeout'
=>
1234
})
end
end
end
end
end
def
request_job
(
token
=
runner
.
token
,
**
params
)
...
...
spec/requests/api/runners_spec.rb
View file @
b66233f9
...
...
@@ -123,6 +123,7 @@ describe API::Runners do
expect
(
response
).
to
have_gitlab_http_status
(
200
)
expect
(
json_response
[
'description'
]).
to
eq
(
shared_runner
.
description
)
expect
(
json_response
[
'maximum_timeout'
]).
to
be_nil
end
end
...
...
@@ -192,7 +193,8 @@ describe API::Runners do
tag_list:
[
'ruby2.1'
,
'pgsql'
,
'mysql'
],
run_untagged:
'false'
,
locked:
'true'
,
access_level:
'ref_protected'
)
access_level:
'ref_protected'
,
maximum_timeout:
1234
)
shared_runner
.
reload
expect
(
response
).
to
have_gitlab_http_status
(
200
)
...
...
@@ -204,6 +206,7 @@ describe API::Runners do
expect
(
shared_runner
.
ref_protected?
).
to
be_truthy
expect
(
shared_runner
.
ensure_runner_queue_value
)
.
not_to
eq
(
runner_queue_value
)
expect
(
shared_runner
.
maximum_timeout
).
to
eq
(
1234
)
end
end
...
...
spec/services/ci/retry_build_service_spec.rb
View file @
b66233f9
...
...
@@ -29,7 +29,8 @@ describe Ci::RetryBuildService do
commit_id deployments erased_by_id last_deployment project_id
runner_id tag_taggings taggings tags trigger_request_id
user_id auto_canceled_by_id retried failure_reason
sourced_pipelines artifacts_file_store artifacts_metadata_store]
.
freeze
# EE
sourced_pipelines artifacts_file_store artifacts_metadata_store
metadata]
.
freeze
# EE
shared_examples
'build duplication'
do
let
(
:another_pipeline
)
{
create
(
:ci_empty_pipeline
,
project:
project
)
}
...
...
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