Commit e055f5cd authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch '27762-add-default-artifacts-expiration' into 'master'

Artifact Expiration Defaults

Closes #27762

See merge request !9219
parents c6ac7391 728b0a5f
...@@ -83,6 +83,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController ...@@ -83,6 +83,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:akismet_api_key, :akismet_api_key,
:akismet_enabled, :akismet_enabled,
:container_registry_token_expire_delay, :container_registry_token_expire_delay,
:default_artifacts_expire_in,
:default_branch_protection, :default_branch_protection,
:default_group_visibility, :default_group_visibility,
:default_project_visibility, :default_project_visibility,
......
...@@ -76,6 +76,12 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -76,6 +76,12 @@ class ApplicationSetting < ActiveRecord::Base
presence: true, presence: true,
numericality: { only_integer: true, greater_than: 0 } numericality: { only_integer: true, greater_than: 0 }
validates :max_artifacts_size,
presence: true,
numericality: { only_integer: true, greater_than: 0 }
validates :default_artifacts_expire_in, presence: true, duration: true
validates :container_registry_token_expire_delay, validates :container_registry_token_expire_delay,
presence: true, presence: true,
numericality: { only_integer: true, greater_than: 0 } numericality: { only_integer: true, greater_than: 0 }
...@@ -168,6 +174,7 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -168,6 +174,7 @@ class ApplicationSetting < ActiveRecord::Base
after_sign_up_text: nil, after_sign_up_text: nil,
akismet_enabled: false, akismet_enabled: false,
container_registry_token_expire_delay: 5, container_registry_token_expire_delay: 5,
default_artifacts_expire_in: '30 days',
default_branch_protection: Settings.gitlab['default_branch_protection'], default_branch_protection: Settings.gitlab['default_branch_protection'],
default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'], default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'],
default_projects_limit: Settings.gitlab['default_projects_limit'], default_projects_limit: Settings.gitlab['default_projects_limit'],
...@@ -201,9 +208,9 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -201,9 +208,9 @@ class ApplicationSetting < ActiveRecord::Base
sign_in_text: nil, sign_in_text: nil,
signin_enabled: Settings.gitlab['signin_enabled'], signin_enabled: Settings.gitlab['signin_enabled'],
signup_enabled: Settings.gitlab['signup_enabled'], signup_enabled: Settings.gitlab['signup_enabled'],
terminal_max_session_time: 0,
two_factor_grace_period: 48, two_factor_grace_period: 48,
user_default_external: false, user_default_external: false
terminal_max_session_time: 0
} }
end end
...@@ -215,6 +222,14 @@ class ApplicationSetting < ActiveRecord::Base ...@@ -215,6 +222,14 @@ class ApplicationSetting < ActiveRecord::Base
create(defaults) create(defaults)
end end
def self.human_attribute_name(attr, _options = {})
if attr == :default_artifacts_expire_in
'Default artifacts expiration'
else
super
end
end
def home_page_url_column_exist def home_page_url_column_exist
ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url) ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url)
end end
......
...@@ -484,7 +484,7 @@ module Ci ...@@ -484,7 +484,7 @@ module Ci
def artifacts_expire_in=(value) def artifacts_expire_in=(value)
self.artifacts_expire_at = self.artifacts_expire_at =
if value if value
Time.now + ChronicDuration.parse(value) ChronicDuration.parse(value)&.seconds&.from_now
end end
end end
......
# DurationValidator
#
# Validate the format conforms with ChronicDuration
#
# Example:
#
# class ApplicationSetting < ActiveRecord::Base
# validates :default_artifacts_expire_in, presence: true, duration: true
# end
#
class DurationValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
ChronicDuration.parse(value)
rescue ChronicDuration::DurationParseError
record.errors.add(attribute, "is not a correct duration")
end
end
...@@ -212,8 +212,16 @@ ...@@ -212,8 +212,16 @@
.col-sm-10 .col-sm-10
= f.number_field :max_artifacts_size, class: 'form-control' = f.number_field :max_artifacts_size, class: 'form-control'
.help-block .help-block
Set the maximum file size each jobs's artifacts can have Set the maximum file size for each job's artifacts
= link_to "(?)", help_page_path("user/admin_area/settings/continuous_integration", anchor: "maximum-artifacts-size") = link_to icon('question-circle'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'maximum-artifacts-size')
.form-group
= f.label :default_artifacts_expire_in, 'Default artifacts expiration', class: 'control-label col-sm-2'
.col-sm-10
= f.text_field :default_artifacts_expire_in, class: 'form-control'
.help-block
Set the default expiration time for each job's artifacts.
0 for unlimited.
= link_to icon('question-circle'), help_page_path('user/admin_area/settings/continuous_integration', anchor: 'default-artifacts-expiration')
- if Gitlab.config.registry.enabled - if Gitlab.config.registry.enabled
%fieldset %fieldset
......
---
title: Add admin setting for default artifacts expiration
merge_request: 9219
author:
class AddDefaultArtifactsExpirationToApplicationSettings < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
add_column :application_settings,
:default_artifacts_expire_in, :string,
null: false, default: '0'
end
end
...@@ -111,6 +111,7 @@ ActiveRecord::Schema.define(version: 20170216141440) do ...@@ -111,6 +111,7 @@ ActiveRecord::Schema.define(version: 20170216141440) do
t.boolean "plantuml_enabled" t.boolean "plantuml_enabled"
t.integer "max_pages_size", default: 100, null: false t.integer "max_pages_size", default: 100, null: false
t.integer "terminal_max_session_time", default: 0, null: false t.integer "terminal_max_session_time", default: 0, null: false
t.string "default_artifacts_expire_in", default: '0', null: false
end end
create_table "audit_events", force: :cascade do |t| create_table "audit_events", force: :cascade do |t|
......
...@@ -3,18 +3,38 @@ ...@@ -3,18 +3,38 @@
## Maximum artifacts size ## Maximum artifacts size
The maximum size of the [job artifacts][art-yml] can be set in the Admin area The maximum size of the [job artifacts][art-yml] can be set in the Admin area
of your GitLab instance. The value is in MB and the default is 100MB. Note that of your GitLab instance. The value is in *MB* and the default is 100MB. Note
this setting is set for each job. that this setting is set for each job.
1. Go to **Admin area > Settings** (`/admin/application_settings`). 1. Go to **Admin area > Settings** (`/admin/application_settings`).
![Admin area settings button](img/admin_area_settings_button.png) ![Admin area settings button](img/admin_area_settings_button.png)
1. Change the value of the maximum artifacts size (in MB): 1. Change the value of maximum artifacts size (in MB):
![Admin area maximum artifacts size](img/admin_area_maximum_artifacts_size.png) ![Admin area maximum artifacts size](img/admin_area_maximum_artifacts_size.png)
1. Hit **Save** for the changes to take effect. 1. Hit **Save** for the changes to take effect.
[art-yml]: ../../../administration/build_artifacts
[art-yml]: ../../../administration/job_artifacts.md ## Default artifacts expiration
The default expiration time of the [job artifacts][art-yml] can be set in
the Admin area of your GitLab instance. The syntax of duration is described
in [artifacts:expire_in][duration-syntax]. The default is `30 days`. Note that
this setting is set for each job. Set it to 0 if you don't want default
expiration.
1. Go to **Admin area > Settings** (`/admin/application_settings`).
![Admin area settings button](img/admin_area_settings_button.png)
1. Change the value of default expiration time ([syntax][duration-syntax]):
![Admin area default artifacts expiration](img/admin_area_default_artifacts_expiration.png)
1. Hit **Save** for the changes to take effect.
[art-yml]: ../../../administration/job_artifacts
[duration-syntax]: ../../../ci/yaml/README#artifactsexpire_in
...@@ -557,6 +557,7 @@ module API ...@@ -557,6 +557,7 @@ module API
expose :default_project_visibility expose :default_project_visibility
expose :default_snippet_visibility expose :default_snippet_visibility
expose :default_group_visibility expose :default_group_visibility
expose :default_artifacts_expire_in
expose :domain_whitelist expose :domain_whitelist
expose :domain_blacklist_enabled expose :domain_blacklist_enabled
expose :domain_blacklist expose :domain_blacklist
......
...@@ -56,7 +56,8 @@ module API ...@@ -56,7 +56,8 @@ module API
given shared_runners_enabled: ->(val) { val } do given shared_runners_enabled: ->(val) { val } do
requires :shared_runners_text, type: String, desc: 'Shared runners text ' requires :shared_runners_text, type: String, desc: 'Shared runners text '
end end
optional :max_artifacts_size, type: Integer, desc: "Set the maximum file size each build's artifacts can have" optional :max_artifacts_size, type: Integer, desc: "Set the maximum file size for each job's artifacts"
optional :default_artifacts_expire_in, type: String, desc: "Set the default expiration time for each job's artifacts"
optional :max_pages_size, type: Integer, desc: 'Maximum size of pages in MB' optional :max_pages_size, type: Integer, desc: 'Maximum size of pages in MB'
optional :container_registry_token_expire_delay, type: Integer, desc: 'Authorization token duration (minutes)' optional :container_registry_token_expire_delay, type: Integer, desc: 'Authorization token duration (minutes)'
optional :metrics_enabled, type: Boolean, desc: 'Enable the InfluxDB metrics' optional :metrics_enabled, type: Boolean, desc: 'Enable the InfluxDB metrics'
...@@ -117,7 +118,9 @@ module API ...@@ -117,7 +118,9 @@ module API
:send_user_confirmation_email, :domain_whitelist, :domain_blacklist_enabled, :send_user_confirmation_email, :domain_whitelist, :domain_blacklist_enabled,
:after_sign_up_text, :signin_enabled, :require_two_factor_authentication, :after_sign_up_text, :signin_enabled, :require_two_factor_authentication,
:home_page_url, :after_sign_out_path, :sign_in_text, :help_page_text, :home_page_url, :after_sign_out_path, :sign_in_text, :help_page_text,
:shared_runners_enabled, :max_artifacts_size, :max_pages_size, :container_registry_token_expire_delay, :shared_runners_enabled, :max_artifacts_size,
:default_artifacts_expire_in, :max_pages_size,
:container_registry_token_expire_delay,
:metrics_enabled, :sidekiq_throttling_enabled, :recaptcha_enabled, :metrics_enabled, :sidekiq_throttling_enabled, :recaptcha_enabled,
:akismet_enabled, :admin_notification_email, :sentry_enabled, :akismet_enabled, :admin_notification_email, :sentry_enabled,
:repository_storage, :repository_checks_enabled, :koding_enabled, :plantuml_enabled, :repository_storage, :repository_checks_enabled, :koding_enabled, :plantuml_enabled,
......
...@@ -167,7 +167,10 @@ module Ci ...@@ -167,7 +167,10 @@ module Ci
build.artifacts_file = artifacts build.artifacts_file = artifacts
build.artifacts_metadata = metadata build.artifacts_metadata = metadata
build.artifacts_expire_in = params['expire_in'] build.artifacts_expire_in =
params['expire_in'] ||
Gitlab::CurrentSettings.current_application_settings
.default_artifacts_expire_in
if build.save if build.save
present(build, with: Entities::BuildDetails) present(build, with: Entities::BuildDetails)
......
...@@ -29,6 +29,40 @@ describe ApplicationSetting, models: true do ...@@ -29,6 +29,40 @@ describe ApplicationSetting, models: true do
it { is_expected.not_to allow_value(['test']).for(:disabled_oauth_sign_in_sources) } it { is_expected.not_to allow_value(['test']).for(:disabled_oauth_sign_in_sources) }
end end
describe 'default_artifacts_expire_in' do
it 'sets an error if it cannot parse' do
setting.update(default_artifacts_expire_in: 'a')
expect_invalid
end
it 'sets an error if it is blank' do
setting.update(default_artifacts_expire_in: ' ')
expect_invalid
end
it 'sets the value if it is valid' do
setting.update(default_artifacts_expire_in: '30 days')
expect(setting).to be_valid
expect(setting.default_artifacts_expire_in).to eq('30 days')
end
it 'sets the value if it is 0' do
setting.update(default_artifacts_expire_in: '0')
expect(setting).to be_valid
expect(setting.default_artifacts_expire_in).to eq('0')
end
def expect_invalid
expect(setting).to be_invalid
expect(setting.errors.messages)
.to have_key(:default_artifacts_expire_in)
end
end
it { is_expected.to validate_presence_of(:max_attachment_size) } it { is_expected.to validate_presence_of(:max_attachment_size) }
it do it do
......
...@@ -162,11 +162,17 @@ describe Ci::Build, :models do ...@@ -162,11 +162,17 @@ describe Ci::Build, :models do
is_expected.to be_nil is_expected.to be_nil
end end
it 'when resseting value' do it 'when resetting value' do
build.artifacts_expire_in = nil build.artifacts_expire_in = nil
is_expected.to be_nil is_expected.to be_nil
end end
it 'when setting to 0' do
build.artifacts_expire_in = '0'
is_expected.to be_nil
end
end end
describe '#commit' do describe '#commit' do
......
...@@ -30,8 +30,14 @@ describe API::Settings, 'Settings', api: true do ...@@ -30,8 +30,14 @@ describe API::Settings, 'Settings', api: true do
it "updates application settings" do it "updates application settings" do
put api("/application/settings", admin), put api("/application/settings", admin),
default_projects_limit: 3, signin_enabled: false, repository_storage: 'custom', koding_enabled: true, koding_url: 'http://koding.example.com', default_projects_limit: 3,
plantuml_enabled: true, plantuml_url: 'http://plantuml.example.com' signin_enabled: false,
repository_storage: 'custom',
koding_enabled: true,
koding_url: 'http://koding.example.com',
plantuml_enabled: true,
plantuml_url: 'http://plantuml.example.com',
default_artifacts_expire_in: '2 days'
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
expect(json_response['default_projects_limit']).to eq(3) expect(json_response['default_projects_limit']).to eq(3)
expect(json_response['signin_enabled']).to be_falsey expect(json_response['signin_enabled']).to be_falsey
...@@ -41,6 +47,7 @@ describe API::Settings, 'Settings', api: true do ...@@ -41,6 +47,7 @@ describe API::Settings, 'Settings', api: true do
expect(json_response['koding_url']).to eq('http://koding.example.com') expect(json_response['koding_url']).to eq('http://koding.example.com')
expect(json_response['plantuml_enabled']).to be_truthy expect(json_response['plantuml_enabled']).to be_truthy
expect(json_response['plantuml_url']).to eq('http://plantuml.example.com') expect(json_response['plantuml_url']).to eq('http://plantuml.example.com')
expect(json_response['default_artifacts_expire_in']).to eq('2 days')
end end
end end
......
...@@ -630,6 +630,7 @@ describe Ci::API::Builds do ...@@ -630,6 +630,7 @@ describe Ci::API::Builds do
context 'with an expire date' do context 'with an expire date' do
let!(:artifacts) { file_upload } let!(:artifacts) { file_upload }
let(:default_artifacts_expire_in) {}
let(:post_data) do let(:post_data) do
{ 'file.path' => artifacts.path, { 'file.path' => artifacts.path,
...@@ -638,6 +639,9 @@ describe Ci::API::Builds do ...@@ -638,6 +639,9 @@ describe Ci::API::Builds do
end end
before do before do
stub_application_setting(
default_artifacts_expire_in: default_artifacts_expire_in)
post(post_url, post_data, headers_with_token) post(post_url, post_data, headers_with_token)
end end
...@@ -648,7 +652,8 @@ describe Ci::API::Builds do ...@@ -648,7 +652,8 @@ describe Ci::API::Builds do
build.reload build.reload
expect(response).to have_http_status(201) expect(response).to have_http_status(201)
expect(json_response['artifacts_expire_at']).not_to be_empty expect(json_response['artifacts_expire_at']).not_to be_empty
expect(build.artifacts_expire_at).to be_within(5.minutes).of(Time.now + 7.days) expect(build.artifacts_expire_at).
to be_within(5.minutes).of(7.days.from_now)
end end
end end
...@@ -661,6 +666,32 @@ describe Ci::API::Builds do ...@@ -661,6 +666,32 @@ describe Ci::API::Builds do
expect(json_response['artifacts_expire_at']).to be_nil expect(json_response['artifacts_expire_at']).to be_nil
expect(build.artifacts_expire_at).to be_nil expect(build.artifacts_expire_at).to be_nil
end end
context 'with application default' do
context 'default to 5 days' do
let(:default_artifacts_expire_in) { '5 days' }
it 'sets to application default' do
build.reload
expect(response).to have_http_status(201)
expect(json_response['artifacts_expire_at'])
.not_to be_empty
expect(build.artifacts_expire_at)
.to be_within(5.minutes).of(5.days.from_now)
end
end
context 'default to 0' do
let(:default_artifacts_expire_in) { '0' }
it 'does not set expire_in' do
build.reload
expect(response).to have_http_status(201)
expect(json_response['artifacts_expire_at']).to be_nil
expect(build.artifacts_expire_at).to be_nil
end
end
end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment