summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.eslintrc.yml1
-rw-r--r--.gitlab-ci.yml1
-rw-r--r--.gitlab/ci/dev-fixtures.gitlab-ci.yml30
-rw-r--r--.rubocop.yml73
-rw-r--r--Gemfile8
-rw-r--r--Gemfile.lock36
-rw-r--r--app/assets/javascripts/awards_handler.js2
-rw-r--r--app/assets/javascripts/clusters/components/application_row.vue5
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js5
-rw-r--r--app/assets/javascripts/monitoring/components/shared/prometheus_header.vue2
-rw-r--r--app/assets/javascripts/pages/admin/admin.js2
-rw-r--r--app/assets/stylesheets/framework/files.scss8
-rw-r--r--app/assets/stylesheets/pages/boards.scss1
-rw-r--r--app/controllers/admin/system_info_controller.rb8
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb2
-rw-r--r--app/controllers/projects/pages_controller.rb10
-rw-r--r--app/controllers/projects/starrers_controller.rb4
-rw-r--r--app/graphql/types/ci/pipeline_type.rb6
-rw-r--r--app/helpers/container_expiration_policies_helper.rb12
-rw-r--r--app/helpers/dashboard_helper.rb2
-rw-r--r--app/helpers/snippets_helper.rb2
-rw-r--r--app/models/application_setting.rb5
-rw-r--r--app/models/clusters/applications/prometheus.rb2
-rw-r--r--app/models/error_tracking/project_error_tracking_setting.rb3
-rw-r--r--app/models/namespace.rb2
-rw-r--r--app/models/project.rb1
-rw-r--r--app/presenters/clusters/cluster_presenter.rb8
-rw-r--r--app/services/projects/update_pages_service.rb13
-rw-r--r--app/views/clusters/clusters/_cluster.html.haml2
-rw-r--r--app/views/clusters/clusters/aws/_new.html.haml4
-rw-r--r--app/views/projects/blame/show.html.haml10
-rw-r--r--changelogs/unreleased/16610-gitlab-pages-storage-size-limitations-by-project-or-group-4.yml5
-rw-r--r--changelogs/unreleased/19132-comment-anchor-twice-firefox.yml5
-rw-r--r--changelogs/unreleased/19299-blame-previous-revision.yml5
-rw-r--r--changelogs/unreleased/I-118638.yml5
-rw-r--r--changelogs/unreleased/sh-add-pipeline-index.yml5
-rw-r--r--changelogs/unreleased/update-prometheus-chart.yml5
-rw-r--r--config/application.rb4
-rw-r--r--config/initializers/1_settings.rb42
-rw-r--r--config/routes/admin.rb2
-rw-r--r--config/routes/project.rb1
-rw-r--r--db/fixtures/development/10_merge_requests.rb2
-rw-r--r--db/fixtures/development/17_cycle_analytics.rb2
-rw-r--r--db/migrate/20191213120427_fix_max_pages_size.rb20
-rw-r--r--db/post_migrate/20191218225624_add_index_on_project_id_to_ci_pipelines.rb18
-rw-r--r--db/schema.rb3
-rw-r--r--doc/administration/gitaly/index.md7
-rw-r--r--doc/administration/monitoring/performance/gitlab_configuration.md2
-rw-r--r--doc/administration/monitoring/performance/grafana_configuration.md6
-rw-r--r--doc/administration/monitoring/performance/influxdb_configuration.md6
-rw-r--r--doc/administration/monitoring/performance/influxdb_schema.md2
-rw-r--r--doc/administration/troubleshooting/elasticsearch.md9
-rw-r--r--doc/api/graphql/reference/gitlab_schema.graphql56
-rw-r--r--doc/api/graphql/reference/gitlab_schema.json34
-rw-r--r--doc/api/graphql/reference/index.md26
-rw-r--r--doc/api/project_level_variables.md1
-rw-r--r--doc/development/elasticsearch.md14
-rw-r--r--doc/gitlab-basics/start-using-git.md2
-rw-r--r--doc/integration/elasticsearch.md12
-rw-r--r--doc/update/patch_versions.md6
-rw-r--r--doc/update/upgrading_from_ce_to_ee.md6
-rw-r--r--doc/user/admin_area/index.md1
-rw-r--r--doc/user/clusters/applications.md4
-rw-r--r--doc/user/group/clusters/index.md9
-rw-r--r--doc/user/instance/clusters/index.md14
-rw-r--r--doc/user/project/clusters/add_remove_clusters.md434
-rw-r--r--doc/user/project/clusters/eks_and_gitlab/index.md4
-rw-r--r--doc/user/project/clusters/img/add_cluster.pngbin59516 -> 0 bytes
-rw-r--r--doc/user/project/clusters/index.md20
-rw-r--r--doc/user/project/clusters/serverless/index.md2
-rw-r--r--doc/user/project/repository/git_blame.md5
-rw-r--r--doc/user/project/repository/img/file_blame_previous_commit_v12_7.pngbin0 -> 105806 bytes
-rw-r--r--lib/api/award_emoji.rb1
-rw-r--r--lib/gitlab/background_migration/archive_legacy_traces.rb1
-rw-r--r--lib/gitlab/import_export/project_tree_restorer.rb2
-rw-r--r--lib/gitlab/metrics/subscribers/action_view.rb2
-rw-r--r--lib/gitlab/pages.rb1
-rw-r--r--lib/gitlab/sherlock/file_sample.rb2
-rw-r--r--lib/gitlab/sherlock/line_profiler.rb2
-rw-r--r--lib/gitlab/usage_data.rb1
-rw-r--r--lib/tasks/gitlab/lfs/migrate.rake2
-rw-r--r--locale/gitlab.pot40
-rw-r--r--qa/Dockerfile6
-rw-r--r--qa/qa.rb1
-rw-r--r--qa/qa/page/project/operations/kubernetes/index.rb4
-rw-r--r--qa/qa/page/project/operations/kubernetes/show.rb22
-rw-r--r--qa/qa/runtime/application_settings.rb46
-rw-r--r--qa/qa/runtime/env.rb4
-rw-r--r--qa/qa/service/cluster_provider/k3d.rb3
-rw-r--r--qa/qa/service/kubernetes_cluster.rb4
-rw-r--r--qa/qa/specs/features/browser_ui/7_configure/kubernetes/kubernetes_integration_spec.rb53
-rw-r--r--qa/spec/runtime/application_settings_spec.rb43
-rw-r--r--qa/spec/runtime/env_spec.rb14
-rw-r--r--spec/controllers/concerns/confirm_email_warning_spec.rb2
-rw-r--r--spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb4
-rw-r--r--spec/controllers/concerns/group_tree_spec.rb2
-rw-r--r--spec/controllers/concerns/lfs_request_spec.rb2
-rw-r--r--spec/controllers/concerns/metrics_dashboard_spec.rb2
-rw-r--r--spec/controllers/concerns/renders_commits_spec.rb2
-rw-r--r--spec/controllers/concerns/routable_actions_spec.rb2
-rw-r--r--spec/controllers/concerns/sourcegraph_gon_spec.rb2
-rw-r--r--spec/controllers/concerns/static_object_external_storage_spec.rb2
-rw-r--r--spec/controllers/projects/branches_controller_spec.rb4
-rw-r--r--spec/controllers/projects/pages_controller_spec.rb11
-rw-r--r--spec/features/projects/files/user_browses_files_spec.rb10
-rw-r--r--spec/features/projects/pages_spec.rb4
-rw-r--r--spec/features/projects/snippets/create_snippet_spec.rb4
-rw-r--r--spec/finders/branches_finder_spec.rb2
-rw-r--r--spec/finders/todos_finder_spec.rb2
-rw-r--r--spec/frontend/monitoring/components/dashboard_spec.js573
-rw-r--r--spec/frontend/monitoring/components/dashboard_time_url_spec.js54
-rw-r--r--spec/frontend/monitoring/components/dashboard_time_window_spec.js75
-rw-r--r--spec/frontend/monitoring/components/graph_group_spec.js (renamed from spec/javascripts/monitoring/components/graph_group_spec.js)0
-rw-r--r--spec/frontend/monitoring/init_utils.js56
-rw-r--r--spec/frontend/monitoring/shared/prometheus_header_spec.js (renamed from spec/javascripts/monitoring/shared/prometheus_header_spec.js)2
-rw-r--r--spec/helpers/container_expiration_policies_helper_spec.rb6
-rw-r--r--spec/helpers/projects_helper_spec.rb4
-rw-r--r--spec/javascripts/lib/utils/common_utils_spec.js3
-rw-r--r--spec/javascripts/monitoring/components/dashboard_resize_spec.js140
-rw-r--r--spec/javascripts/monitoring/components/dashboard_spec.js729
-rw-r--r--spec/lib/expand_variables_spec.rb2
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb1
-rw-r--r--spec/lib/gitlab/ci/config/entry/cache_spec.rb6
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb4
-rw-r--r--spec/lib/gitlab/experimentation_spec.rb4
-rw-r--r--spec/lib/gitlab/gitaly_client/blob_service_spec.rb2
-rw-r--r--spec/lib/gitlab/gitaly_client/remote_service_spec.rb2
-rw-r--r--spec/lib/gitlab/phabricator_import/conduit/user_spec.rb6
-rw-r--r--spec/lib/gitlab/phabricator_import/user_finder_spec.rb2
-rw-r--r--spec/lib/gitlab/quick_actions/dsl_spec.rb2
-rw-r--r--spec/lib/quality/helm_client_spec.rb2
-rw-r--r--spec/lib/quality/kubernetes_client_spec.rb2
-rw-r--r--spec/migrations/fix_max_pages_size_spec.rb19
-rw-r--r--spec/models/active_session_spec.rb2
-rw-r--r--spec/models/application_setting_spec.rb7
-rw-r--r--spec/models/clusters/applications/prometheus_spec.rb6
-rw-r--r--spec/models/error_tracking/project_error_tracking_setting_spec.rb21
-rw-r--r--spec/models/merge_request_spec.rb2
-rw-r--r--spec/models/namespace_spec.rb1
-rw-r--r--spec/models/project_spec.rb1
-rw-r--r--spec/requests/api/wikis_spec.rb2
-rw-r--r--spec/services/notification_service_spec.rb2
-rw-r--r--spec/services/projects/update_pages_service_spec.rb48
-rw-r--r--spec/support/features/discussion_comments_shared_example.rb4
-rw-r--r--spec/support/helpers/test_env.rb1
-rw-r--r--spec/support/shared_examples/pages_size_limit_shared_examples.rb30
-rw-r--r--spec/views/projects/commit/branches.html.haml_spec.rb4
-rw-r--r--vendor/prometheus/values.yaml4
148 files changed, 1938 insertions, 1280 deletions
diff --git a/.eslintrc.yml b/.eslintrc.yml
index 94a8d017c28..07542c42891 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -45,7 +45,6 @@ rules:
vue/no-use-v-if-with-v-for: off
vue/no-v-html: off
vue/use-v-on-exact: off
- no-jquery/no-animate: off
# all offenses of no-jquery/no-animate-toggle are false positives ( $toast.show() )
no-jquery/no-animate-toggle: off
no-jquery/no-event-shorthand: off
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 388f3f4b3e3..f59e8759e6d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -43,6 +43,7 @@ include:
- local: .gitlab/ci/rails.gitlab-ci.yml
- local: .gitlab/ci/review.gitlab-ci.yml
- local: .gitlab/ci/setup.gitlab-ci.yml
+ - local: .gitlab/ci/dev-fixtures.gitlab-ci.yml
- local: .gitlab/ci/test-metadata.gitlab-ci.yml
- local: .gitlab/ci/yaml.gitlab-ci.yml
- local: .gitlab/ci/releases.gitlab-ci.yml
diff --git a/.gitlab/ci/dev-fixtures.gitlab-ci.yml b/.gitlab/ci/dev-fixtures.gitlab-ci.yml
new file mode 100644
index 00000000000..a5dab5d8708
--- /dev/null
+++ b/.gitlab/ci/dev-fixtures.gitlab-ci.yml
@@ -0,0 +1,30 @@
+.run-dev-fixtures:
+ extends:
+ - .only-code-rails-job-base
+ - .use-pg9
+ stage: test
+ needs: ["setup-test-env"]
+ dependencies: ["setup-test-env"]
+ variables:
+ FIXTURE_PATH: "db/fixtures/development"
+ SEED_CYCLE_ANALYTICS: "true"
+ SEED_PRODUCTIVITY_ANALYTICS: "true"
+ CYCLE_ANALYTICS_ISSUE_COUNT: 1
+ SIZE: 0 # number of external projects to fork, requires network connection
+ # SEED_NESTED_GROUPS: "false" # requires network connection
+
+run-dev-fixtures-foss:
+ extends: .run-dev-fixtures
+ script:
+ - scripts/gitaly-test-spawn
+ - RAILS_ENV=test bundle exec rake db:seed_fu
+
+run-dev-fixtures-ee:
+ extends:
+ - .only-ee
+ - .use-pg9-ee
+ - .run-dev-fixtures
+ script:
+ - scripts/gitaly-test-spawn
+ - cp ee/db/fixtures/development/* $FIXTURE_PATH
+ - RAILS_ENV=test bundle exec rake db:seed_fu
diff --git a/.rubocop.yml b/.rubocop.yml
index 27dce2239d8..da3707972d3 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -299,3 +299,76 @@ Graphql/Descriptions:
RSpec/AnyInstanceOf:
Enabled: false
+
+# Cops for upgrade to gitlab-styles 3.1.0
+FactoryBot/FactoryClassName:
+ Enabled: false
+
+FactoryBot/CreateList:
+ Enabled: false
+
+Rails/SafeNavigationWithBlank:
+ Enabled: false
+
+Rails/ApplicationController:
+ Enabled: false
+
+Rails/ApplicationMailer:
+ Enabled: false
+
+Rails/RakeEnvironment:
+ Enabled: false
+
+Rails/HelperInstanceVariable:
+ Enabled: false
+
+Rails/EnumHash:
+ Enabled: false
+
+RSpec/ReceiveCounts:
+ Enabled: false
+
+RSpec/ContextMethod:
+ Enabled: false
+
+RSpec/ImplicitSubject:
+ Enabled: false
+
+RSpec/LeakyConstantDeclaration:
+ Enabled: false
+
+RSpec/EmptyLineAfterHook:
+ Enabled: false
+
+RSpec/HooksBeforeExamples:
+ Enabled: false
+
+RSpec/EmptyLineAfterExample:
+ Enabled: false
+
+RSpec/Be:
+ Enabled: false
+
+RSpec/DescribedClass:
+ Enabled: false
+
+RSpec/SharedExamples:
+ Enabled: false
+
+RSpec/EmptyLineAfterExampleGroup:
+ Enabled: false
+
+RSpec/ReceiveNever:
+ Enabled: false
+
+RSpec/MissingExampleGroupArgument:
+ Enabled: false
+
+RSpec/UnspecifiedException:
+ Enabled: false
+
+Style/MultilineWhenThen:
+ Enabled: false
+
+Style/FloatDivision:
+ Enabled: false \ No newline at end of file
diff --git a/Gemfile b/Gemfile
index 212a6860480..9949ae7d681 100644
--- a/Gemfile
+++ b/Gemfile
@@ -366,11 +366,11 @@ group :development, :test do
gem 'spring', '~> 2.0.0'
gem 'spring-commands-rspec', '~> 1.0.4'
- gem 'gitlab-styles', '~> 2.7', require: false
+ gem 'gitlab-styles', '~> 3.1.0', require: false
# Pin these dependencies, otherwise a new rule could break the CI pipelines
- gem 'rubocop', '~> 0.69.0'
- gem 'rubocop-performance', '~> 1.1.0'
- gem 'rubocop-rspec', '~> 1.22.1'
+ gem 'rubocop', '~> 0.74.0'
+ gem 'rubocop-performance', '~> 1.4.1'
+ gem 'rubocop-rspec', '~> 1.37.0'
gem 'scss_lint', '~> 0.56.0', require: false
gem 'haml_lint', '~> 0.34.0', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index fb660e53765..0086f7004f5 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -379,11 +379,12 @@ GEM
gitlab-puma (>= 2.7, < 5)
gitlab-sidekiq-fetcher (0.5.2)
sidekiq (~> 5)
- gitlab-styles (2.8.0)
- rubocop (~> 0.69.0)
+ gitlab-styles (3.1.0)
+ rubocop (~> 0.74.0)
rubocop-gitlab-security (~> 0.1.0)
- rubocop-performance (~> 1.1.0)
- rubocop-rspec (~> 1.19)
+ rubocop-performance (~> 1.4.1)
+ rubocop-rails (~> 2.0)
+ rubocop-rspec (~> 1.36)
gitlab_chronic_duration (0.10.6.2)
numerizer (~> 0.2)
gitlab_omniauth-ldap (2.1.1)
@@ -519,7 +520,7 @@ GEM
jaeger-client (0.10.0)
opentracing (~> 0.3)
thrift
- jaro_winkler (1.5.3)
+ jaro_winkler (1.5.4)
jira-ruby (1.7.1)
activesupport
atlassian-jwt
@@ -726,8 +727,8 @@ GEM
rubypants (~> 0.2)
orm_adapter (0.5.0)
os (1.0.0)
- parallel (1.17.0)
- parser (2.6.3.0)
+ parallel (1.19.1)
+ parser (2.6.5.0)
ast (~> 2.4.0)
parslet (1.8.2)
peek (1.1.0)
@@ -904,7 +905,7 @@ GEM
pg
rails
sqlite3
- rubocop (0.69.0)
+ rubocop (0.74.0)
jaro_winkler (~> 1.5.1)
parallel (~> 1.10)
parser (>= 2.6)
@@ -913,10 +914,13 @@ GEM
unicode-display_width (>= 1.4.0, < 1.7)
rubocop-gitlab-security (0.1.1)
rubocop (>= 0.51)
- rubocop-performance (1.1.0)
- rubocop (>= 0.67.0)
- rubocop-rspec (1.22.2)
- rubocop (>= 0.52.1)
+ rubocop-performance (1.4.1)
+ rubocop (>= 0.71.0)
+ rubocop-rails (2.4.0)
+ rack (>= 1.1)
+ rubocop (>= 0.72.0)
+ rubocop-rspec (1.37.0)
+ rubocop (>= 0.68.1)
ruby-enum (0.7.2)
i18n
ruby-fogbugz (0.2.1)
@@ -1207,7 +1211,7 @@ DEPENDENCIES
gitlab-puma (~> 4.3.1.gitlab.2)
gitlab-puma_worker_killer (~> 0.1.1.gitlab.1)
gitlab-sidekiq-fetcher (= 0.5.2)
- gitlab-styles (~> 2.7)
+ gitlab-styles (~> 3.1.0)
gitlab_chronic_duration (~> 0.10.6.2)
gitlab_omniauth-ldap (~> 2.1.1)
gon (~> 6.2)
@@ -1318,9 +1322,9 @@ DEPENDENCIES
rspec-set (~> 0.1.3)
rspec_junit_formatter
rspec_profiling (~> 0.0.5)
- rubocop (~> 0.69.0)
- rubocop-performance (~> 1.1.0)
- rubocop-rspec (~> 1.22.1)
+ rubocop (~> 0.74.0)
+ rubocop-performance (~> 1.4.1)
+ rubocop-rspec (~> 1.37.0)
ruby-fogbugz (~> 0.2.1)
ruby-prof (~> 1.0.0)
ruby-progressbar
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index 2fc8b9efc1b..de10d5397df 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -506,6 +506,8 @@ export class AwardsHandler {
const options = {
scrollTop: $('.awards').offset().top - 110,
};
+
+ // eslint-disable-next-line no-jquery/no-animate
return $('body, html').animate(options, 200);
}
diff --git a/app/assets/javascripts/clusters/components/application_row.vue b/app/assets/javascripts/clusters/components/application_row.vue
index ab1e5f92698..7db9898396b 100644
--- a/app/assets/javascripts/clusters/components/application_row.vue
+++ b/app/assets/javascripts/clusters/components/application_row.vue
@@ -290,6 +290,7 @@ export default {
disabled && 'cluster-application-disabled',
]"
class="cluster-application-row gl-responsive-table-row gl-responsive-table-row-col-span"
+ :data-qa-selector="id"
>
<div class="gl-responsive-table-row-layout" role="row">
<div class="table-section append-right-8 section-align-top" role="gridcell">
@@ -381,12 +382,16 @@ export default {
:disabled="disabled || installButtonDisabled"
:label="installButtonLabel"
class="js-cluster-application-install-button"
+ data-qa-selector="install_button"
+ :data-qa-application="id"
@click="installClicked"
/>
<uninstall-application-button
v-if="displayUninstallButton"
v-gl-modal-directive="'uninstall-' + id"
:status="status"
+ data-qa-selector="uninstall_button"
+ :data-qa-application="id"
class="js-cluster-application-uninstall-button"
/>
<uninstall-application-confirmation-modal
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index e4001e94478..71f73cd5420 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -135,7 +135,9 @@ export const handleLocationHash = () => {
adjustment -= topPadding;
}
- window.scrollBy(0, adjustment);
+ setTimeout(() => {
+ window.scrollBy(0, adjustment);
+ });
};
// Check if element scrolled into viewport from above or below
@@ -247,6 +249,7 @@ export const scrollToElement = element => {
}
const { top } = $el.offset();
+ // eslint-disable-next-line no-jquery/no-animate
return $('body, html').animate(
{
scrollTop: top - contentTop(),
diff --git a/app/assets/javascripts/monitoring/components/shared/prometheus_header.vue b/app/assets/javascripts/monitoring/components/shared/prometheus_header.vue
index 153c8f389db..ceeec51ee65 100644
--- a/app/assets/javascripts/monitoring/components/shared/prometheus_header.vue
+++ b/app/assets/javascripts/monitoring/components/shared/prometheus_header.vue
@@ -10,6 +10,6 @@ export default {
</script>
<template>
<div class="prometheus-graph-header">
- <h5 class="prometheus-graph-title js-graph-title">{{ graphTitle }}</h5>
+ <h5 ref="title" class="prometheus-graph-title">{{ graphTitle }}</h5>
</div>
</template>
diff --git a/app/assets/javascripts/pages/admin/admin.js b/app/assets/javascripts/pages/admin/admin.js
index 4616a075729..88967d82b2f 100644
--- a/app/assets/javascripts/pages/admin/admin.js
+++ b/app/assets/javascripts/pages/admin/admin.js
@@ -36,6 +36,8 @@ export default function adminInit() {
$('.log-bottom').on('click', e => {
e.preventDefault();
const $visibleLog = $('.file-content:visible');
+
+ // eslint-disable-next-line no-jquery/no-animate
$visibleLog.animate(
{
scrollTop: $visibleLog.find('ol').height(),
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index 8e0314bc6da..1a017f03ebb 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -207,6 +207,14 @@
border-left-color: mix($blame-gray, $blame-cyan, $i / 4 * 100%);
}
}
+
+ .doc-versions {
+ color: $gray-600;
+
+ &:hover {
+ color: $gray-900;
+ }
+ }
}
&.logs {
diff --git a/app/assets/stylesheets/pages/boards.scss b/app/assets/stylesheets/pages/boards.scss
index 90c2e369ccd..c4c1bb0bcc1 100644
--- a/app/assets/stylesheets/pages/boards.scss
+++ b/app/assets/stylesheets/pages/boards.scss
@@ -133,6 +133,7 @@
.issue-count-badge {
border: 0;
white-space: nowrap;
+ padding: 0;
}
.board-title-text > span,
diff --git a/app/controllers/admin/system_info_controller.rb b/app/controllers/admin/system_info_controller.rb
index 244fc2b31bb..657aa177ecf 100644
--- a/app/controllers/admin/system_info_controller.rb
+++ b/app/controllers/admin/system_info_controller.rb
@@ -1,10 +1,10 @@
# frozen_string_literal: true
class Admin::SystemInfoController < Admin::ApplicationController
- EXCLUDED_MOUNT_OPTIONS = [
- 'nobrowse',
- 'read-only',
- 'ro'
+ EXCLUDED_MOUNT_OPTIONS = %w[
+ nobrowse
+ read-only
+ ro
].freeze
EXCLUDED_MOUNT_TYPES = [
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index 92f36c031f1..1f1c01c8fcc 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -31,7 +31,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
# Extend the standard message generation to accept our custom exception
def failure_message
exception = request.env["omniauth.error"]
- error = exception.error_reason if exception.respond_to?(:error_reason)
+ error = exception.error_reason if exception.respond_to?(:error_reason)
error ||= exception.error if exception.respond_to?(:error)
error ||= exception.message if exception.respond_to?(:message)
error ||= request.env["omniauth.error.type"].to_s
diff --git a/app/controllers/projects/pages_controller.rb b/app/controllers/projects/pages_controller.rb
index f1e591ea1ec..18a171700e9 100644
--- a/app/controllers/projects/pages_controller.rb
+++ b/app/controllers/projects/pages_controller.rb
@@ -34,7 +34,7 @@ class Projects::PagesController < Projects::ApplicationController
if result[:status] == :success
flash[:notice] = 'Your changes have been saved'
else
- flash[:alert] = 'Something went wrong on our end'
+ flash[:alert] = result[:message]
end
redirect_to project_pages_path(@project)
@@ -45,6 +45,12 @@ class Projects::PagesController < Projects::ApplicationController
private
def project_params
- params.require(:project).permit(:pages_https_only)
+ params.require(:project).permit(project_params_attributes)
+ end
+
+ def project_params_attributes
+ %i[pages_https_only]
end
end
+
+Projects::PagesController.prepend_if_ee('EE::Projects::PagesController')
diff --git a/app/controllers/projects/starrers_controller.rb b/app/controllers/projects/starrers_controller.rb
index 4efe956e973..d9654f4f72a 100644
--- a/app/controllers/projects/starrers_controller.rb
+++ b/app/controllers/projects/starrers_controller.rb
@@ -7,8 +7,8 @@ class Projects::StarrersController < Projects::ApplicationController
@starrers = UsersStarProjectsFinder.new(@project, params, current_user: @current_user).execute
@sort = params[:sort].presence || sort_value_name
@starrers = @starrers.preload_users.sort_by_attribute(@sort).page(params[:page])
- @public_count = @project.starrers.with_public_profile.size
- @total_count = @project.starrers.size
+ @public_count = @project.starrers.with_public_profile.size
+ @total_count = @project.starrers.size
@private_count = @total_count - @public_count
end
diff --git a/app/graphql/types/ci/pipeline_type.rb b/app/graphql/types/ci/pipeline_type.rb
index e786add6359..d77b2a2ba32 100644
--- a/app/graphql/types/ci/pipeline_type.rb
+++ b/app/graphql/types/ci/pipeline_type.rb
@@ -17,16 +17,16 @@ module Types
field :sha, GraphQL::STRING_TYPE, null: false,
description: "SHA of the pipeline's commit"
field :before_sha, GraphQL::STRING_TYPE, null: true,
- description: "Base SHA of the source branch"
+ description: 'Base SHA of the source branch'
field :status, PipelineStatusEnum, null: false,
description: "Status of the pipeline (#{::Ci::Pipeline.all_state_names.compact.join(', ').upcase})"
field :detailed_status, Types::Ci::DetailedStatusType, null: false,
description: 'Detailed status of the pipeline',
resolve: -> (obj, _args, ctx) { obj.detailed_status(ctx[:current_user]) }
field :duration, GraphQL::INT_TYPE, null: true,
- description: "Duration of the pipeline in seconds"
+ description: 'Duration of the pipeline in seconds'
field :coverage, GraphQL::FLOAT_TYPE, null: true,
- description: "Coverage percentage"
+ description: 'Coverage percentage'
field :created_at, Types::TimeType, null: false,
description: "Timestamp of the pipeline's creation"
field :updated_at, Types::TimeType, null: false,
diff --git a/app/helpers/container_expiration_policies_helper.rb b/app/helpers/container_expiration_policies_helper.rb
index 17791e7b0ff..5fb7b5afa6e 100644
--- a/app/helpers/container_expiration_policies_helper.rb
+++ b/app/helpers/container_expiration_policies_helper.rb
@@ -3,19 +3,25 @@
module ContainerExpirationPoliciesHelper
def cadence_options
ContainerExpirationPolicy.cadence_options.map do |key, val|
- { key: key.to_s, label: val }
+ { key: key.to_s, label: val }.tap do |base|
+ base[:default] = true if key.to_s == '1d'
+ end
end
end
def keep_n_options
ContainerExpirationPolicy.keep_n_options.map do |key, val|
- { key: key, label: val }
+ { key: key, label: val }.tap do |base|
+ base[:default] = true if key == 10
+ end
end
end
def older_than_options
ContainerExpirationPolicy.older_than_options.map do |key, val|
- { key: key.to_s, label: val }
+ { key: key.to_s, label: val }.tap do |base|
+ base[:default] = true if key.to_s == '30d'
+ end
end
end
end
diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb
index 99ede8c3557..b38feb0fb6c 100644
--- a/app/helpers/dashboard_helper.rb
+++ b/app/helpers/dashboard_helper.rb
@@ -35,7 +35,7 @@ module DashboardHelper
tag.p(aria: { label: label }) do
concat(link_or_title)
- concat(tag.span(class: ['light', 'float-right']) do
+ concat(tag.span(class: %w[light float-right]) do
boolean_to_icon(enabled)
end)
diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb
index 1c7690f30d2..fd7e58826b5 100644
--- a/app/helpers/snippets_helper.rb
+++ b/app/helpers/snippets_helper.rb
@@ -127,7 +127,7 @@ module SnippetsHelper
return unless attrs = snippet_badge_attributes(snippet)
css_class, text = attrs
- tag.span(class: ['badge', 'badge-gray']) do
+ tag.span(class: %w[badge badge-gray]) do
concat(tag.i(class: ['fa', css_class]))
concat(' ')
concat(text)
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 456b6430088..3858adfc71f 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -121,6 +121,11 @@ class ApplicationSetting < ApplicationRecord
presence: true,
numericality: { only_integer: true, greater_than: 0 }
+ validates :max_pages_size,
+ presence: true,
+ numericality: { only_integer: true, greater_than: 0,
+ less_than: ::Gitlab::Pages::MAX_SIZE / 1.megabyte }
+
validates :default_artifacts_expire_in, presence: true, duration: true
validates :container_registry_token_expire_delay,
diff --git a/app/models/clusters/applications/prometheus.rb b/app/models/clusters/applications/prometheus.rb
index 4ac33d4e3be..5716614b8ac 100644
--- a/app/models/clusters/applications/prometheus.rb
+++ b/app/models/clusters/applications/prometheus.rb
@@ -5,7 +5,7 @@ module Clusters
class Prometheus < ApplicationRecord
include PrometheusAdapter
- VERSION = '6.7.3'
+ VERSION = '9.5.2'
self.table_name = 'clusters_applications_prometheus'
diff --git a/app/models/error_tracking/project_error_tracking_setting.rb b/app/models/error_tracking/project_error_tracking_setting.rb
index 6a9986e806b..bbafcaed46e 100644
--- a/app/models/error_tracking/project_error_tracking_setting.rb
+++ b/app/models/error_tracking/project_error_tracking_setting.rb
@@ -122,6 +122,9 @@ module ErrorTracking
{ error: e.message, error_type: SENTRY_API_ERROR_INVALID_SIZE }
rescue Sentry::Client::BadRequestError => e
{ error: e.message, error_type: SENTRY_API_ERROR_TYPE_BAD_REQUEST }
+ rescue StandardError => e
+ Gitlab::ErrorTracking.track_exception(e)
+ { error: 'Unexpected Error' }
end
# http://HOST/api/0/projects/ORG/PROJECT
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index d5a7c172fec..74774a22a37 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -46,6 +46,8 @@ class Namespace < ApplicationRecord
length: { maximum: 255 },
namespace_path: true
+ validates :max_artifacts_size, numericality: { only_integer: true, greater_than: 0, allow_nil: true }
+
validate :nesting_level_allowed
validates_associated :runners
diff --git a/app/models/project.rb b/app/models/project.rb
index 1a84827b5ec..9062377943f 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -375,6 +375,7 @@ class Project < ApplicationRecord
inclusion: { in: ->(_object) { Gitlab.config.repositories.storages.keys } }
validates :variables, variable_duplicates: { scope: :environment_scope }
validates :bfg_object_map, file_size: { maximum: :max_attachment_size }
+ validates :max_artifacts_size, numericality: { only_integer: true, greater_than: 0, allow_nil: true }
# Scopes
scope :pending_delete, -> { where(pending_delete: true) }
diff --git a/app/presenters/clusters/cluster_presenter.rb b/app/presenters/clusters/cluster_presenter.rb
index 97771d84031..3ace27c72d5 100644
--- a/app/presenters/clusters/cluster_presenter.rb
+++ b/app/presenters/clusters/cluster_presenter.rb
@@ -10,11 +10,11 @@ module Clusters
# We do not want to show the group path for clusters belonging to the
# clusterable, only for the ancestor clusters.
- def item_link(clusterable_presenter)
+ def item_link(clusterable_presenter, *html_options)
if cluster.group_type? && clusterable != clusterable_presenter.subject
contracted_group_name(cluster.group) + ' / ' + link_to_cluster
else
- link_to_cluster
+ link_to_cluster(*html_options)
end
end
@@ -84,8 +84,8 @@ module Clusters
sprite_icon('ellipsis_h', size: 12, css_class: 'vertical-align-middle')
end
- def link_to_cluster
- link_to_if(can_read_cluster?, cluster.name, show_path)
+ def link_to_cluster(html_options: {})
+ link_to_if(can_read_cluster?, cluster.name, show_path, html_options)
end
end
end
diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb
index e8a87fc4320..8b23f610ad1 100644
--- a/app/services/projects/update_pages_service.rb
+++ b/app/services/projects/update_pages_service.rb
@@ -6,7 +6,6 @@ module Projects
FailedToExtractError = Class.new(StandardError)
BLOCK_SIZE = 32.kilobytes
- MAX_SIZE = 1.terabyte
PUBLIC_DIR = 'public'
# this has to be invalid group name,
@@ -130,12 +129,16 @@ module Projects
1 + max_size / BLOCK_SIZE
end
+ def max_size_from_settings
+ Gitlab::CurrentSettings.max_pages_size.megabytes
+ end
+
def max_size
- max_pages_size = Gitlab::CurrentSettings.max_pages_size.megabytes
+ max_pages_size = max_size_from_settings
- return MAX_SIZE if max_pages_size.zero?
+ return ::Gitlab::Pages::MAX_SIZE if max_pages_size.zero?
- [max_pages_size, MAX_SIZE].min
+ max_pages_size
end
def tmp_path
@@ -200,3 +203,5 @@ module Projects
end
end
end
+
+Projects::UpdatePagesService.prepend_if_ee('EE::Projects::UpdatePagesService')
diff --git a/app/views/clusters/clusters/_cluster.html.haml b/app/views/clusters/clusters/_cluster.html.haml
index b89789e9915..04afc38a056 100644
--- a/app/views/clusters/clusters/_cluster.html.haml
+++ b/app/views/clusters/clusters/_cluster.html.haml
@@ -3,7 +3,7 @@
.table-section.section-60
.table-mobile-header{ role: "rowheader" }= s_("ClusterIntegration|Kubernetes cluster")
.table-mobile-content
- = cluster.item_link(clusterable)
+ = cluster.item_link(clusterable, html_options: { data: { qa_selector: 'cluster', qa_cluster_name: cluster.name } })
- unless cluster.enabled?
%span.badge.badge-danger Connection disabled
.table-section.section-25
diff --git a/app/views/clusters/clusters/aws/_new.html.haml b/app/views/clusters/clusters/aws/_new.html.haml
index d89e6965dac..5bbdadf83f3 100644
--- a/app/views/clusters/clusters/aws/_new.html.haml
+++ b/app/views/clusters/clusters/aws/_new.html.haml
@@ -11,6 +11,6 @@
'role-arn' => @aws_role.role_arn,
'instance-types' => @instance_types,
'kubernetes-integration-help-path' => help_page_path('user/project/clusters/index'),
- 'account-and-external-ids-help-path' => help_page_path('user/project/clusters/add_remove_clusters.md', anchor: 'eks-cluster'),
- 'create-role-arn-help-path' => help_page_path('user/project/clusters/add_remove_clusters.md', anchor: 'eks-cluster'),
+ 'account-and-external-ids-help-path' => help_page_path('user/project/clusters/add_remove_clusters.md', anchor: 'new-eks-cluster'),
+ 'create-role-arn-help-path' => help_page_path('user/project/clusters/add_remove_clusters.md', anchor: 'new-eks-cluster'),
'external-link-icon' => icon('external-link') } }
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index 30fe5622ebd..b17207c0da6 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -20,8 +20,14 @@
.commit-row-title
%span.item-title.str-truncated-100
= link_to commit.title, project_commit_path(@project, commit.id), class: "cdark", title: commit.title
- .float-right
- = link_to commit.short_id, project_commit_path(@project, commit), class: "commit-sha"
+ %span
+ - previous_commit_id = commit.parent_id
+ - if previous_commit_id
+ = link_to project_blame_path(@project, tree_join(previous_commit_id, @path)),
+ title: _('View blame prior to this change'),
+ aria: { label: _('View blame prior to this change') },
+ data: { toggle: 'tooltip', placement: 'right', container: 'body' } do
+ = sprite_icon('doc-versions', size: 16, css_class: 'doc-versions align-text-bottom')
&nbsp;
.light
= commit_author_link(commit, avatar: false)
diff --git a/changelogs/unreleased/16610-gitlab-pages-storage-size-limitations-by-project-or-group-4.yml b/changelogs/unreleased/16610-gitlab-pages-storage-size-limitations-by-project-or-group-4.yml
new file mode 100644
index 00000000000..039656ce20a
--- /dev/null
+++ b/changelogs/unreleased/16610-gitlab-pages-storage-size-limitations-by-project-or-group-4.yml
@@ -0,0 +1,5 @@
+---
+title: Fix pages size limit setting in database if it is above the hard limit
+merge_request: 20154
+author:
+type: fixed
diff --git a/changelogs/unreleased/19132-comment-anchor-twice-firefox.yml b/changelogs/unreleased/19132-comment-anchor-twice-firefox.yml
new file mode 100644
index 00000000000..02a7e1007b3
--- /dev/null
+++ b/changelogs/unreleased/19132-comment-anchor-twice-firefox.yml
@@ -0,0 +1,5 @@
+---
+title: Fix bug when clicking on same note twice in Firefox
+merge_request: 21699
+author: Jan Beckmann
+type: fixed
diff --git a/changelogs/unreleased/19299-blame-previous-revision.yml b/changelogs/unreleased/19299-blame-previous-revision.yml
new file mode 100644
index 00000000000..2b99dfb76f8
--- /dev/null
+++ b/changelogs/unreleased/19299-blame-previous-revision.yml
@@ -0,0 +1,5 @@
+---
+title: Add previous revision link to blame
+merge_request: 17088
+author: Hiroyuki Sato
+type: added
diff --git a/changelogs/unreleased/I-118638.yml b/changelogs/unreleased/I-118638.yml
new file mode 100644
index 00000000000..6d8e0c1e17b
--- /dev/null
+++ b/changelogs/unreleased/I-118638.yml
@@ -0,0 +1,5 @@
+---
+title: fix CSS when board issue is collapsed
+merge_request: 21940
+author: allenlai18
+type: fixed
diff --git a/changelogs/unreleased/sh-add-pipeline-index.yml b/changelogs/unreleased/sh-add-pipeline-index.yml
new file mode 100644
index 00000000000..1d7b5edf29a
--- /dev/null
+++ b/changelogs/unreleased/sh-add-pipeline-index.yml
@@ -0,0 +1,5 @@
+---
+title: Add index to optimize loading pipeline charts
+merge_request: 22052
+author:
+type: performance
diff --git a/changelogs/unreleased/update-prometheus-chart.yml b/changelogs/unreleased/update-prometheus-chart.yml
new file mode 100644
index 00000000000..afce595f70e
--- /dev/null
+++ b/changelogs/unreleased/update-prometheus-chart.yml
@@ -0,0 +1,5 @@
+---
+title: Update prometheus chart version to 9.5.2
+merge_request: 21935
+author:
+type: changed
diff --git a/config/application.rb b/config/application.rb
index cad5c8bbe76..4cb623ac1c5 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -234,7 +234,7 @@ module Gitlab
credentials: true,
headers: :any,
methods: :any,
- expose: ['Link', 'X-Total', 'X-Total-Pages', 'X-Per-Page', 'X-Page', 'X-Next-Page', 'X-Prev-Page']
+ expose: %w[Link X-Total X-Total-Pages X-Per-Page X-Page X-Next-Page X-Prev-Page]
end
# Cross-origin requests must not have the session cookie available
@@ -244,7 +244,7 @@ module Gitlab
credentials: false,
headers: :any,
methods: :any,
- expose: ['Link', 'X-Total', 'X-Total-Pages', 'X-Per-Page', 'X-Page', 'X-Next-Page', 'X-Prev-Page']
+ expose: %w[Link X-Total X-Total-Pages X-Per-Page X-Page X-Next-Page X-Prev-Page]
end
end
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 691e4339bf0..2283000dc89 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -162,8 +162,8 @@ Settings.gitlab['default_projects_limit'] ||= 100000
Settings.gitlab['default_branch_protection'] ||= 2
Settings.gitlab['default_can_create_group'] = true if Settings.gitlab['default_can_create_group'].nil?
Settings.gitlab['default_theme'] = Gitlab::Themes::APPLICATION_DEFAULT if Settings.gitlab['default_theme'].nil?
-Settings.gitlab['host'] ||= ENV['GITLAB_HOST'] || 'localhost'
-Settings.gitlab['ssh_host'] ||= Settings.gitlab.host
+Settings.gitlab['host'] ||= ENV['GITLAB_HOST'] || 'localhost'
+Settings.gitlab['ssh_host'] ||= Settings.gitlab.host
Settings.gitlab['https'] = false if Settings.gitlab['https'].nil?
Settings.gitlab['port'] ||= ENV['GITLAB_PORT'] || (Settings.gitlab.https ? 443 : 80)
Settings.gitlab['relative_url_root'] ||= ENV['RAILS_RELATIVE_URL_ROOT'] || ''
@@ -176,10 +176,10 @@ Settings.gitlab['email_display_name'] ||= ENV['GITLAB_EMAIL_DISPLAY_NAME'] || 'G
Settings.gitlab['email_reply_to'] ||= ENV['GITLAB_EMAIL_REPLY_TO'] || "noreply@#{Settings.gitlab.host}"
Settings.gitlab['email_subject_suffix'] ||= ENV['GITLAB_EMAIL_SUBJECT_SUFFIX'] || ""
Settings.gitlab['email_smime'] = SmimeSignatureSettings.parse(Settings.gitlab['email_smime'])
-Settings.gitlab['base_url'] ||= Settings.__send__(:build_base_gitlab_url)
-Settings.gitlab['url'] ||= Settings.__send__(:build_gitlab_url)
-Settings.gitlab['user'] ||= 'git'
-Settings.gitlab['user_home'] ||= begin
+Settings.gitlab['base_url'] ||= Settings.__send__(:build_base_gitlab_url)
+Settings.gitlab['url'] ||= Settings.__send__(:build_gitlab_url)
+Settings.gitlab['user'] ||= 'git'
+Settings.gitlab['user_home'] ||= begin
Etc.getpwnam(Settings.gitlab['user']).dir
rescue ArgumentError # no user configured
'/home/' + Settings.gitlab['user']
@@ -257,13 +257,13 @@ Settings.artifacts['object_store'] = ObjectStoreSettings.parse(Settings.artifact
# Registry
#
Settings['registry'] ||= Settingslogic.new({})
-Settings.registry['enabled'] ||= false
-Settings.registry['host'] ||= "example.com"
-Settings.registry['port'] ||= nil
-Settings.registry['api_url'] ||= "http://localhost:5000/"
-Settings.registry['key'] ||= nil
-Settings.registry['issuer'] ||= nil
-Settings.registry['host_port'] ||= [Settings.registry['host'], Settings.registry['port']].compact.join(':')
+Settings.registry['enabled'] ||= false
+Settings.registry['host'] ||= "example.com"
+Settings.registry['port'] ||= nil
+Settings.registry['api_url'] ||= "http://localhost:5000/"
+Settings.registry['key'] ||= nil
+Settings.registry['issuer'] ||= nil
+Settings.registry['host_port'] ||= [Settings.registry['host'], Settings.registry['port']].compact.join(':')
Settings.registry['path'] = Settings.absolute(Settings.registry['path'] || File.join(Settings.shared['path'], 'registry'))
Settings.registry['notifications'] ||= []
@@ -284,13 +284,13 @@ Settings.pages['enabled'] = false if Settings.pages['enabled'].nil?
Settings.pages['access_control'] = false if Settings.pages['access_control'].nil?
Settings.pages['path'] = Settings.absolute(Settings.pages['path'] || File.join(Settings.shared['path'], "pages"))
Settings.pages['https'] = false if Settings.pages['https'].nil?
-Settings.pages['host'] ||= "example.com"
-Settings.pages['port'] ||= Settings.pages.https ? 443 : 80
-Settings.pages['protocol'] ||= Settings.pages.https ? "https" : "http"
-Settings.pages['url'] ||= Settings.__send__(:build_pages_url)
-Settings.pages['external_http'] ||= false unless Settings.pages['external_http'].present?
-Settings.pages['external_https'] ||= false unless Settings.pages['external_https'].present?
-Settings.pages['artifacts_server'] ||= Settings.pages['enabled'] if Settings.pages['artifacts_server'].nil?
+Settings.pages['host'] ||= "example.com"
+Settings.pages['port'] ||= Settings.pages.https ? 443 : 80
+Settings.pages['protocol'] ||= Settings.pages.https ? "https" : "http"
+Settings.pages['url'] ||= Settings.__send__(:build_pages_url)
+Settings.pages['external_http'] ||= false unless Settings.pages['external_http'].present?
+Settings.pages['external_https'] ||= false unless Settings.pages['external_https'].present?
+Settings.pages['artifacts_server'] ||= Settings.pages['enabled'] if Settings.pages['artifacts_server'].nil?
Settings.pages['secret_file'] ||= Rails.root.join('.gitlab_pages_secret')
#
@@ -590,7 +590,7 @@ end
# Backup
#
Settings['backup'] ||= Settingslogic.new({})
-Settings.backup['keep_time'] ||= 0
+Settings.backup['keep_time'] ||= 0
Settings.backup['pg_schema'] = nil
Settings.backup['path'] = Settings.absolute(Settings.backup['path'] || "tmp/backups/")
Settings.backup['archive_permissions'] ||= 0600
diff --git a/config/routes/admin.rb b/config/routes/admin.rb
index 9238eae3a8e..34660358234 100644
--- a/config/routes/admin.rb
+++ b/config/routes/admin.rb
@@ -45,7 +45,6 @@ namespace :admin do
scope(path: 'groups/*id',
controller: :groups,
constraints: { id: Gitlab::PathRegex.full_namespace_route_regex, format: /(html|json|atom)/ }) do
-
scope(as: :group) do
put :members_update
get :edit, action: :edit
@@ -90,7 +89,6 @@ namespace :admin do
path: '/',
constraints: { id: Gitlab::PathRegex.project_route_regex },
only: [:show, :destroy]) do
-
member do
put :transfer
post :repository_check
diff --git a/config/routes/project.rb b/config/routes/project.rb
index 9e40e891fd5..398041083ef 100644
--- a/config/routes/project.rb
+++ b/config/routes/project.rb
@@ -29,7 +29,6 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
constraints: { project_id: Gitlab::PathRegex.project_route_regex },
module: :projects,
as: :project) do
-
# Begin of the /-/ scope.
# Use this scope for all new project routes.
scope '-' do
diff --git a/db/fixtures/development/10_merge_requests.rb b/db/fixtures/development/10_merge_requests.rb
index 77650ebb1bc..9157045a7fd 100644
--- a/db/fixtures/development/10_merge_requests.rb
+++ b/db/fixtures/development/10_merge_requests.rb
@@ -47,7 +47,7 @@ Gitlab::Seeder.quiet do
project = Project.find_by_full_path('gitlab-org/gitlab-test')
- next if project.empty_repo? # We don't have repository on CI
+ next if !project || project.empty_repo? # We don't have repository on CI
params = {
source_branch: 'feature',
diff --git a/db/fixtures/development/17_cycle_analytics.rb b/db/fixtures/development/17_cycle_analytics.rb
index 2532b71ad26..3e017b810b6 100644
--- a/db/fixtures/development/17_cycle_analytics.rb
+++ b/db/fixtures/development/17_cycle_analytics.rb
@@ -5,7 +5,7 @@ class Gitlab::Seeder::CycleAnalytics
def initialize(project, perf: false)
@project = project
@user = User.admins.first
- @issue_count = perf ? 1000 : 5
+ @issue_count = perf ? 1000 : ENV.fetch('CYCLE_ANALYTICS_ISSUE_COUNT', 5).to_i
end
def seed_metrics!
diff --git a/db/migrate/20191213120427_fix_max_pages_size.rb b/db/migrate/20191213120427_fix_max_pages_size.rb
new file mode 100644
index 00000000000..498ea91b773
--- /dev/null
+++ b/db/migrate/20191213120427_fix_max_pages_size.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+class FixMaxPagesSize < ActiveRecord::Migration[5.2]
+ DOWNTIME = false
+ MAX_SIZE = 1.terabyte / 1.megabyte
+
+ class ApplicationSetting < ActiveRecord::Base
+ self.table_name = 'application_settings'
+ self.inheritance_column = :_type_disabled
+ end
+
+ def up
+ table = ApplicationSetting.arel_table
+ ApplicationSetting.where(table[:max_pages_size].gt(MAX_SIZE)).update_all(max_pages_size: MAX_SIZE)
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/post_migrate/20191218225624_add_index_on_project_id_to_ci_pipelines.rb b/db/post_migrate/20191218225624_add_index_on_project_id_to_ci_pipelines.rb
new file mode 100644
index 00000000000..ab6c3b0616a
--- /dev/null
+++ b/db/post_migrate/20191218225624_add_index_on_project_id_to_ci_pipelines.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddIndexOnProjectIdToCiPipelines < ActiveRecord::Migration[5.2]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+ INDEX_NAME = 'index_ci_pipelines_on_project_id_and_id_desc'
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :ci_pipelines, [:project_id, :id], name: INDEX_NAME, order: { id: :desc }
+ end
+
+ def down
+ remove_concurrent_index :ci_pipelines, [:project_id, :id], name: INDEX_NAME, order: { id: :desc }
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 91fbcd42f30..dee28ea4287 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2019_12_16_183532) do
+ActiveRecord::Schema.define(version: 2019_12_18_225624) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_trgm"
@@ -863,6 +863,7 @@ ActiveRecord::Schema.define(version: 2019_12_16_183532) do
t.index ["external_pull_request_id"], name: "index_ci_pipelines_on_external_pull_request_id", where: "(external_pull_request_id IS NOT NULL)"
t.index ["merge_request_id"], name: "index_ci_pipelines_on_merge_request_id", where: "(merge_request_id IS NOT NULL)"
t.index ["pipeline_schedule_id"], name: "index_ci_pipelines_on_pipeline_schedule_id"
+ t.index ["project_id", "id"], name: "index_ci_pipelines_on_project_id_and_id_desc", order: { id: :desc }
t.index ["project_id", "iid"], name: "index_ci_pipelines_on_project_id_and_iid", unique: true, where: "(iid IS NOT NULL)"
t.index ["project_id", "ref", "id"], name: "index_ci_pipelines_on_project_idandrefandiddesc", order: { id: :desc }
t.index ["project_id", "ref", "status", "id"], name: "index_ci_pipelines_on_project_id_and_ref_and_status_and_id"
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
index 9218ffa4006..47ecfaec36c 100644
--- a/doc/administration/gitaly/index.md
+++ b/doc/administration/gitaly/index.md
@@ -47,10 +47,11 @@ But since 11.8 the indexer uses Gitaly for data access as well. NFS can still
be leveraged for redudancy on block level of the Git data. But only has to
be mounted on the Gitaly server.
-Starting with GitLab 11.8, it is possible to use Elasticsearch in conjunction with
+From GitLab v11.8 to v12.2, it is possible to use Elasticsearch in conjunction with
a Gitaly setup that isn't utilising NFS. In order to use Elasticsearch in this
-scenario, the [new repository indexer](../../integration/elasticsearch.md#elasticsearch-repository-indexer-beta)
-needs to be enabled in your GitLab configuration.
+scenario, the [new repository indexer](../../integration/elasticsearch.md#elasticsearch-repository-indexer)
+needs to be enabled in your GitLab configuration. [Since GitLab v12.3](https://gitlab.com/gitlab-org/gitlab/issues/6481),
+the new indexer becomes the default and no configuration is required.
NOTE: **Note:** While Gitaly can be used as a replacement for NFS, it's not recommended
to use EFS as it may impact GitLab's performance. Review the [relevant documentation](../high_availability/nfs.md#avoid-using-awss-elastic-file-system-efs)
diff --git a/doc/administration/monitoring/performance/gitlab_configuration.md b/doc/administration/monitoring/performance/gitlab_configuration.md
index 528e032417a..1bff170768a 100644
--- a/doc/administration/monitoring/performance/gitlab_configuration.md
+++ b/doc/administration/monitoring/performance/gitlab_configuration.md
@@ -32,7 +32,7 @@ have been performed.
Read more on:
-- [Introduction to GitLab Performance Monitoring](introduction.md)
+- [Introduction to GitLab Performance Monitoring](index.md)
- [InfluxDB Configuration](influxdb_configuration.md)
- [InfluxDB Schema](influxdb_schema.md)
- [Grafana Install/Configuration](grafana_configuration.md)
diff --git a/doc/administration/monitoring/performance/grafana_configuration.md b/doc/administration/monitoring/performance/grafana_configuration.md
index ccba0a55479..5f3882918a8 100644
--- a/doc/administration/monitoring/performance/grafana_configuration.md
+++ b/doc/administration/monitoring/performance/grafana_configuration.md
@@ -1,5 +1,9 @@
# Grafana Configuration
+CAUTION: **InfluxDB is deprecated in favor of Prometheus:**
+InfluxDB support is scheduled to be removed in GitLab 13.0.
+You are advised to use [Prometheus](../prometheus/index.md) instead.
+
[Grafana](https://grafana.com/) is a tool that allows you to visualize time
series metrics through graphs and dashboards. It supports several backend
data stores, including InfluxDB. GitLab writes performance data to InfluxDB
@@ -152,7 +156,7 @@ For more information and further mitigation details, please refer to our [blog p
Read more on:
-- [Introduction to GitLab Performance Monitoring](introduction.md)
+- [Introduction to GitLab Performance Monitoring](index.md)
- [GitLab Configuration](gitlab_configuration.md)
- [InfluxDB Installation/Configuration](influxdb_configuration.md)
- [InfluxDB Schema](influxdb_schema.md)
diff --git a/doc/administration/monitoring/performance/influxdb_configuration.md b/doc/administration/monitoring/performance/influxdb_configuration.md
index aaea0a5968d..49076d3f47d 100644
--- a/doc/administration/monitoring/performance/influxdb_configuration.md
+++ b/doc/administration/monitoring/performance/influxdb_configuration.md
@@ -1,7 +1,7 @@
# InfluxDB Configuration
-CAUTION: **InfluxDB is being deprecated in favor of Prometheus:**
-InfluxDB support is scheduled to be dropped in GitLab 13.0.
+CAUTION: **InfluxDB is deprecated in favor of Prometheus:**
+InfluxDB support is scheduled to be removed in GitLab 13.0.
You are advised to use [Prometheus](../prometheus/index.md) instead.
The default settings provided by [InfluxDB] are not sufficient for a high traffic
@@ -182,7 +182,7 @@ That's it! Now your GitLab instance should send data to InfluxDB.
Read more on:
-- [Introduction to GitLab Performance Monitoring](introduction.md)
+- [Introduction to GitLab Performance Monitoring](index.md)
- [GitLab Configuration](gitlab_configuration.md)
- [InfluxDB Schema](influxdb_schema.md)
- [Grafana Install/Configuration](grafana_configuration.md)
diff --git a/doc/administration/monitoring/performance/influxdb_schema.md b/doc/administration/monitoring/performance/influxdb_schema.md
index 71814deb2bc..34d35492ccf 100644
--- a/doc/administration/monitoring/performance/influxdb_schema.md
+++ b/doc/administration/monitoring/performance/influxdb_schema.md
@@ -95,7 +95,7 @@ Depending on the event type additional tags may be available as well.
Read more on:
-- [Introduction to GitLab Performance Monitoring](introduction.md)
+- [Introduction to GitLab Performance Monitoring](index.md)
- [GitLab Configuration](gitlab_configuration.md)
- [InfluxDB Configuration](influxdb_configuration.md)
- [Grafana Install/Configuration](grafana_configuration.md)
diff --git a/doc/administration/troubleshooting/elasticsearch.md b/doc/administration/troubleshooting/elasticsearch.md
index 5846514c574..560c6b2f53a 100644
--- a/doc/administration/troubleshooting/elasticsearch.md
+++ b/doc/administration/troubleshooting/elasticsearch.md
@@ -106,7 +106,7 @@ graph TD;
D2 --> |Yes| D4
D4 --> |No| D5
D4 --> |Yes| D6
- D{Is the error concerning<br>the beta indexer?}
+ D{Is the error concerning<br>the Go indexer?}
D1[It would be best<br>to speak with an<br>Elasticsearch admin.]
D2{Is the ICU development<br>package installed?}
D3>This package is required.<br>Install the package<br>and retry.]
@@ -245,12 +245,13 @@ much to "integrate" here.
If the issue is:
-- Not concerning the beta indexer, it is almost always an
+- With the Go indexer, check if the ICU development package is installed.
+ This is a required package so make sure you install it.
+ Go indexer was a beta indexer which can be optionally turned on/off, but in 12.3 it reached stable status and is now the default.
+- Not concerning the Go indexer, it is almost always an
Elasticsearch-side issue. This means you should reach out to your Elasticsearch admin
regarding the error(s) you are seeing. If you are unsure here, it never hurts to reach
out to GitLab support.
-- With the beta indexer, check if the ICU development package is installed.
- This is a required package so make sure you install it.
Beyond that, you will want to review the error. If it is:
diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql
index bc3395920ab..1e25e977286 100644
--- a/doc/api/graphql/reference/gitlab_schema.graphql
+++ b/doc/api/graphql/reference/gitlab_schema.graphql
@@ -503,6 +503,9 @@ type CreateSnippetPayload {
}
type Design implements Noteable {
+ """
+ Diff refs of the design
+ """
diffRefs: DiffRefs!
"""
@@ -531,13 +534,34 @@ type Design implements Noteable {
): DiscussionConnection!
"""
- The change that happened to the design at this version
+ Type of change made to the design at the version specified by the `atVersion`
+ argument if supplied. Defaults to the latest version
"""
event: DesignVersionEvent!
+
+ """
+ Filename of the design file
+ """
filename: String!
+
+ """
+ Full path of the design file
+ """
fullPath: String!
+
+ """
+ ID of the design
+ """
id: ID!
+
+ """
+ Image of the design
+ """
image: String!
+
+ """
+ Issue associated with the design
+ """
issue: Issue!
"""
@@ -566,13 +590,17 @@ type Design implements Noteable {
): NoteConnection!
"""
- The total count of user-created notes for this design
+ Total count of user-created notes for the design
"""
notesCount: Int!
+
+ """
+ Project associated with the design
+ """
project: Project!
"""
- All versions related to this design ordered newest first
+ All versions related to the design, ordered newest first
"""
versions(
"""
@@ -599,7 +627,7 @@ type Design implements Noteable {
type DesignCollection {
"""
- All designs for this collection
+ All designs for the design collection
"""
designs(
"""
@@ -638,11 +666,19 @@ type DesignCollection {
"""
last: Int
): DesignConnection!
+
+ """
+ Issue associated with the design collection
+ """
issue: Issue!
+
+ """
+ Project associated with the design collection
+ """
project: Project!
"""
- All versions related to all designs ordered newest first
+ All versions related to all designs, ordered newest first
"""
versions(
"""
@@ -799,7 +835,7 @@ type DesignManagementUploadPayload {
type DesignVersion {
"""
- All designs that were changed in this version
+ All designs that were changed in the version
"""
designs(
"""
@@ -822,7 +858,15 @@ type DesignVersion {
"""
last: Int
): DesignConnection!
+
+ """
+ ID of the design version
+ """
id: ID!
+
+ """
+ SHA of the design version
+ """
sha: ID!
}
diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json
index 3c30d2482e8..e71fd3fc71b 100644
--- a/doc/api/graphql/reference/gitlab_schema.json
+++ b/doc/api/graphql/reference/gitlab_schema.json
@@ -9206,7 +9206,7 @@
"fields": [
{
"name": "designs",
- "description": "All designs for this collection",
+ "description": "All designs for the design collection",
"args": [
{
"name": "ids",
@@ -9309,7 +9309,7 @@
},
{
"name": "issue",
- "description": null,
+ "description": "Issue associated with the design collection",
"args": [
],
@@ -9327,7 +9327,7 @@
},
{
"name": "project",
- "description": null,
+ "description": "Project associated with the design collection",
"args": [
],
@@ -9345,7 +9345,7 @@
},
{
"name": "versions",
- "description": "All versions related to all designs ordered newest first",
+ "description": "All versions related to all designs, ordered newest first",
"args": [
{
"name": "after",
@@ -10322,7 +10322,7 @@
"fields": [
{
"name": "diffRefs",
- "description": null,
+ "description": "Diff refs of the design",
"args": [
],
@@ -10397,7 +10397,7 @@
},
{
"name": "event",
- "description": "The change that happened to the design at this version",
+ "description": "Type of change made to the design at the version specified by the `atVersion` argument if supplied. Defaults to the latest version",
"args": [
],
@@ -10415,7 +10415,7 @@
},
{
"name": "filename",
- "description": null,
+ "description": "Filename of the design file",
"args": [
],
@@ -10433,7 +10433,7 @@
},
{
"name": "fullPath",
- "description": null,
+ "description": "Full path of the design file",
"args": [
],
@@ -10451,7 +10451,7 @@
},
{
"name": "id",
- "description": null,
+ "description": "ID of the design",
"args": [
],
@@ -10469,7 +10469,7 @@
},
{
"name": "image",
- "description": null,
+ "description": "Image of the design",
"args": [
],
@@ -10487,7 +10487,7 @@
},
{
"name": "issue",
- "description": null,
+ "description": "Issue associated with the design",
"args": [
],
@@ -10562,7 +10562,7 @@
},
{
"name": "notesCount",
- "description": "The total count of user-created notes for this design",
+ "description": "Total count of user-created notes for the design",
"args": [
],
@@ -10580,7 +10580,7 @@
},
{
"name": "project",
- "description": null,
+ "description": "Project associated with the design",
"args": [
],
@@ -10598,7 +10598,7 @@
},
{
"name": "versions",
- "description": "All versions related to this design ordered newest first",
+ "description": "All versions related to the design, ordered newest first",
"args": [
{
"name": "after",
@@ -10819,7 +10819,7 @@
"fields": [
{
"name": "designs",
- "description": "All designs that were changed in this version",
+ "description": "All designs that were changed in the version",
"args": [
{
"name": "after",
@@ -10876,7 +10876,7 @@
},
{
"name": "id",
- "description": null,
+ "description": "ID of the design version",
"args": [
],
@@ -10894,7 +10894,7 @@
},
{
"name": "sha",
- "description": null,
+ "description": "SHA of the design version",
"args": [
],
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 1669e3d604c..f142cee8544 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -104,22 +104,22 @@ The API can be explored interactively using the [GraphiQL IDE](../index.md#graph
| Name | Type | Description |
| --- | ---- | ---------- |
-| `id` | ID! | |
-| `project` | Project! | |
-| `issue` | Issue! | |
-| `notesCount` | Int! | The total count of user-created notes for this design |
-| `filename` | String! | |
-| `fullPath` | String! | |
-| `event` | DesignVersionEvent! | The change that happened to the design at this version |
-| `image` | String! | |
-| `diffRefs` | DiffRefs! | |
+| `id` | ID! | ID of the design |
+| `project` | Project! | Project associated with the design |
+| `issue` | Issue! | Issue associated with the design |
+| `notesCount` | Int! | Total count of user-created notes for the design |
+| `filename` | String! | Filename of the design file |
+| `fullPath` | String! | Full path of the design file |
+| `event` | DesignVersionEvent! | Type of change made to the design at the version specified by the `atVersion` argument if supplied. Defaults to the latest version |
+| `image` | String! | Image of the design |
+| `diffRefs` | DiffRefs! | Diff refs of the design |
### DesignCollection
| Name | Type | Description |
| --- | ---- | ---------- |
-| `project` | Project! | |
-| `issue` | Issue! | |
+| `project` | Project! | Project associated with the design collection |
+| `issue` | Issue! | Issue associated with the design collection |
### DesignManagementDeletePayload
@@ -142,8 +142,8 @@ The API can be explored interactively using the [GraphiQL IDE](../index.md#graph
| Name | Type | Description |
| --- | ---- | ---------- |
-| `id` | ID! | |
-| `sha` | ID! | |
+| `id` | ID! | ID of the design version |
+| `sha` | ID! | SHA of the design version |
### DestroyNotePayload
diff --git a/doc/api/project_level_variables.md b/doc/api/project_level_variables.md
index 591911bb8ec..d4bda992f7c 100644
--- a/doc/api/project_level_variables.md
+++ b/doc/api/project_level_variables.md
@@ -86,7 +86,6 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" "https://gitla
"value": "new value",
"protected": false,
"variable_type": "env_var",
- "protected": false,
"masked": false,
"environment_scope": "*"
}
diff --git a/doc/development/elasticsearch.md b/doc/development/elasticsearch.md
index 1375bd6d56d..bd8a4e1c6d7 100644
--- a/doc/development/elasticsearch.md
+++ b/doc/development/elasticsearch.md
@@ -37,20 +37,6 @@ brew install elasticsearch@5.6
There is no need to install any plugins
-## New repo indexer (beta)
-
-If you're interested on working with the new beta repo indexer, all you need to do is:
-
-```sh
-git clone git@gitlab.com:gitlab-org/gitlab-elasticsearch-indexer.git
-make
-make install
-```
-
-this adds `gitlab-elasticsearch-indexer` to `$GOPATH/bin`, please make sure that is in your `$PATH`. After that GitLab will find it and you'll be able to enable it in the admin settings area.
-
-**note:** `make` will not recompile the executable unless you do `make clean` beforehand
-
## Helpful rake tasks
- `gitlab:elastic:test:index_size`: Tells you how much space the current index is using, as well as how many documents are in the index.
diff --git a/doc/gitlab-basics/start-using-git.md b/doc/gitlab-basics/start-using-git.md
index 097794d39a7..7fa84bf45bd 100644
--- a/doc/gitlab-basics/start-using-git.md
+++ b/doc/gitlab-basics/start-using-git.md
@@ -313,7 +313,7 @@ git merge master
### Synchronize changes in a forked repository with the upstream
-[Forking a repository](../user/project/repository/forking_workflow.md lets you create
+[Forking a repository](../user/project/repository/forking_workflow.md) lets you create
a copy of a repository in your namespace. Changes made to your copy of the repository
are not synchronized automatically with the original.
Your local fork (copy) contains changes made by you only, so to keep the project
diff --git a/doc/integration/elasticsearch.md b/doc/integration/elasticsearch.md
index 62b3de72a3a..7d07e8f6944 100644
--- a/doc/integration/elasticsearch.md
+++ b/doc/integration/elasticsearch.md
@@ -40,14 +40,13 @@ Once the data is added to the database or repository and [Elasticsearch is
enabled in the admin area](#enabling-elasticsearch) the search index will be
updated automatically.
-## Elasticsearch repository indexer (beta)
+## Elasticsearch repository indexer
-In order to improve Elasticsearch indexing performance, GitLab has made available a [new indexer written in Go](https://gitlab.com/gitlab-org/gitlab-elasticsearch-indexer).
-This will replace the included Ruby indexer in the future but should be considered beta software for now, so there may be some bugs.
+For indexing Git repository data, GitLab uses an [indexer written in Go](https://gitlab.com/gitlab-org/gitlab-elasticsearch-indexer).
-The Elasticsearch Go indexer is included in Omnibus for GitLab 11.8 and newer.
-
-To use the new Elasticsearch indexer included in Omnibus, check the box "Use the new repository indexer (beta)" when [enabling the Elasticsearch integration](#enabling-elasticsearch).
+The Go indexer was included in Omnibus GitLab 11.8 as an optional replacement to a
+Ruby-based indexer. [Since GitLab v12.3](https://gitlab.com/gitlab-org/gitlab/issues/6481),
+all indexing is done by the Go indexer, and the Ruby indexer is removed.
If you would like to use the Elasticsearch Go indexer with a source installation or an older version of GitLab, please follow the instructions below.
@@ -139,7 +138,6 @@ The following Elasticsearch settings are available:
| Parameter | Description |
| ----------------------------------------------------- | ----------- |
| `Elasticsearch indexing` | Enables/disables Elasticsearch indexing. You may want to enable indexing but disable search in order to give the index time to be fully completed, for example. Also, keep in mind that this option doesn't have any impact on existing data, this only enables/disables background indexer which tracks data changes. So by enabling this you will not get your existing data indexed, use special rake task for that as explained in [Adding GitLab's data to the Elasticsearch index](#adding-gitlabs-data-to-the-elasticsearch-index). |
-| `Use the new repository indexer (beta)` | Perform repository indexing using [GitLab Elasticsearch Indexer](https://gitlab.com/gitlab-org/gitlab-elasticsearch-indexer). |
| `Search with Elasticsearch enabled` | Enables/disables using Elasticsearch in search. |
| `URL` | The URL to use for connecting to Elasticsearch. Use a comma-separated list to support clustering (e.g., `http://host1, https://host2:9200`). If your Elasticsearch instance is password protected, pass the `username:password` in the URL (e.g., `http://<username>:<password>@<elastic_host>:9200/`). |
| `Number of Elasticsearch shards` | Elasticsearch indexes are split into multiple shards for performance reasons. In general, larger indexes need to have more shards. Changes to this value do not take effect until the index is recreated. You can read more about tradeoffs in the [Elasticsearch documentation](https://www.elastic.co/guide/en/elasticsearch/reference/current/indices-create-index.html#create-index-settings) |
diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md
index b00fc5d90cf..5aa97d82fd1 100644
--- a/doc/update/patch_versions.md
+++ b/doc/update/patch_versions.md
@@ -94,11 +94,9 @@ sudo -u git -H git checkout v$(</home/git/gitlab/GITLAB_PAGES_VERSION)
sudo -u git -H make
```
-### 8. Install/Update `gitlab-elasticsearch-indexer` (optional) **(STARTER ONLY)**
+### 8. Install/Update `gitlab-elasticsearch-indexer` **(STARTER ONLY)**
-If you're interested in using GitLab's new [Elasticsearch repository indexer](../integration/elasticsearch.md#elasticsearch-repository-indexer-beta) (currently in beta)
-please follow the instructions on the document linked above and enable the
-indexer usage in the GitLab admin settings.
+Please follow the [install instruction](../integration/elasticsearch.md#installation).
### 9. Start application
diff --git a/doc/update/upgrading_from_ce_to_ee.md b/doc/update/upgrading_from_ce_to_ee.md
index 52a65a89cbf..d1853466e30 100644
--- a/doc/update/upgrading_from_ce_to_ee.md
+++ b/doc/update/upgrading_from_ce_to_ee.md
@@ -77,11 +77,9 @@ sudo -u git -H bundle exec rake yarn:install gitlab:assets:clean gitlab:assets:c
sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production
```
-### 4. Install `gitlab-elasticsearch-indexer` (optional) **(STARTER ONLY)**
+### 4. Install `gitlab-elasticsearch-indexer` **(STARTER ONLY)**
-If you're interested in using GitLab's new [Elasticsearch repository indexer](../integration/elasticsearch.md)
-(currently in beta) please follow the instructions on the
-document linked above and enable the indexer usage in the GitLab admin settings.
+Please follow the [install instruction](../integration/elasticsearch.md#installation).
### 5. Start application
diff --git a/doc/user/admin_area/index.md b/doc/user/admin_area/index.md
index ccf20d797aa..7d710e3b2c1 100644
--- a/doc/user/admin_area/index.md
+++ b/doc/user/admin_area/index.md
@@ -27,6 +27,7 @@ The Admin Area is made up of the following sections:
| Applications | Create system [OAuth applications](../../integration/oauth_provider.md) for integrations with other services. |
| Abuse Reports | Manage [abuse reports](abuse_reports.md) submitted by your users. |
| License **(STARTER ONLY)** | Upload, display, and remove [licenses](license.md). |
+| Kubernetes | Create and manage instance-level [Kubernetes clusters](../instance/clusters/index.md). |
| Push Rules **(STARTER)** | Configure pre-defined Git [push rules](../../push_rules/push_rules.md) for projects. |
| Geo **(PREMIUM ONLY)** | Configure and maintain [Geo nodes](geo_nodes.md). |
| Deploy Keys | Create instance-wide [SSH deploy keys](../../ssh/README.md#deploy-keys). |
diff --git a/doc/user/clusters/applications.md b/doc/user/clusters/applications.md
index 95dbe7d3b51..a7753e3f3a4 100644
--- a/doc/user/clusters/applications.md
+++ b/doc/user/clusters/applications.md
@@ -454,6 +454,10 @@ Supported applications:
### Usage
+You can find and import all the files referenced below
+in the [example cluster applications
+project](https://gitlab.com/gitlab-org/cluster-integration/example-cluster-applications/).
+
To install applications using GitLab CI:
1. Connect the cluster to a [cluster management project](management_project.md).
diff --git a/doc/user/group/clusters/index.md b/doc/user/group/clusters/index.md
index 2b36c3bdf5b..83d1fc672df 100644
--- a/doc/user/group/clusters/index.md
+++ b/doc/user/group/clusters/index.md
@@ -53,8 +53,8 @@ differentiate the new cluster from the rest.
## GitLab-managed clusters
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/22011) in GitLab 11.5.
-> Became [optional](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/26565) in GitLab 11.11.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/22011) in GitLab 11.5.
+> - Became [optional](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/26565) in GitLab 11.11.
You can choose to allow GitLab to manage your cluster for you. If your cluster is
managed by GitLab, resources for your projects will be automatically created. See the
@@ -170,6 +170,11 @@ For important information about securely configuring GitLab Runners, see
Runners](../../project/clusters/add_remove_clusters.md#security-of-gitlab-runners)
documentation for project-level clusters.
+## More information
+
+For information on integrating GitLab and Kubernetes, see
+[Kubernetes clusters](../../project/clusters/index.md).
+
<!-- ## Troubleshooting
Include any troubleshooting steps that you can foresee. If you know beforehand what issues
diff --git a/doc/user/instance/clusters/index.md b/doc/user/instance/clusters/index.md
index 3d9a1eb219e..7b5ba14a5ae 100644
--- a/doc/user/instance/clusters/index.md
+++ b/doc/user/instance/clusters/index.md
@@ -12,11 +12,12 @@ projects.
## Cluster precedence
-GitLab will try match to clusters in the following order:
+GitLab will try [to match](../../../ci/environments.md#scoping-environments-with-specs) clusters in
+the following order:
-- Project-level clusters
-- Group-level clusters
-- Instance level
+- Project-level clusters.
+- Group-level clusters.
+- Instance-level clusters.
To be selected, the cluster must be enabled and
match the [environment selector](../../../ci/environments.md#scoping-environments-with-specs).
@@ -26,3 +27,8 @@ match the [environment selector](../../../ci/environments.md#scoping-environment
For a consolidated view of which CI [environments](../../../ci/environments.md)
are deployed to the Kubernetes cluster, see the documentation for
[cluster environments](../../clusters/environments.md).
+
+## More information
+
+For information on integrating GitLab and Kubernetes, see
+[Kubernetes clusters](../../project/clusters/index.md).
diff --git a/doc/user/project/clusters/add_remove_clusters.md b/doc/user/project/clusters/add_remove_clusters.md
index 25851fb7b9f..513c18c095f 100644
--- a/doc/user/project/clusters/add_remove_clusters.md
+++ b/doc/user/project/clusters/add_remove_clusters.md
@@ -10,6 +10,74 @@ Every new Google Cloud Platform (GCP) account receives [$300 in credit upon sign
and in partnership with Google, GitLab is able to offer an additional $200 for new GCP accounts to get started with GitLab's
Google Kubernetes Engine Integration. All you have to do is [follow this link](https://cloud.google.com/partners/partnercredit/?pcn_code=0014M00001h35gDQAQ#contact-form) and apply for credit.
+## Before you begin
+
+Before [adding a Kubernetes cluster](#add-new-cluster) using GitLab, you need:
+
+- GitLab itself. Either:
+ - A GitLab.com [account](https://about.gitlab.com/pricing/#gitlab-com).
+ - A [self-managed installation](https://about.gitlab.com/pricing/#self-managed) with GitLab version
+ 12.5 or later. This will ensure the GitLab UI can be used for cluster creation.
+- The following GitLab access:
+ - [Maintainer access to a project](../../permissions.md#project-members-permissions) for a
+ project-level cluster.
+ - [Maintainer access to a group](../../permissions.md#group-members-permissions) for a
+ group-level cluster.
+ - [Admin Area access](../../admin_area/index.md) for a self-managed instance-level
+ cluster. **(CORE ONLY)**
+
+### GKE requirements
+
+Before creating your first cluster on Google GKE with GitLab's integration, make sure the following
+requirements are met:
+
+- A [billing account](https://cloud.google.com/billing/docs/how-to/manage-billing-account)
+ set up with access.
+- The Kubernetes Engine API and related service are enabled. It should work immediately but may
+ take up to 10 minutes after you create a project. For more information see the
+ ["Before you begin" section of the Kubernetes Engine docs](https://cloud.google.com/kubernetes-engine/docs/quickstart#before-you-begin).
+
+### EKS requirements
+
+Before creating your first cluster on Amazon EKS with GitLab's integration, make sure the following
+requirements are met:
+
+- An [Amazon Web Services](https://aws.amazon.com/) account is set up and you are able to log in.
+- You have permissions to manage IAM resources.
+- If you want to use an [existing EKS cluster](#existing-eks-cluster):
+ - An Amazon EKS cluster with worker nodes properly configured.
+ - `kubectl` [installed and configured](https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html#get-started-kubectl)
+ for access to the EKS cluster.
+
+#### Additional requirements for self-managed instances **(CORE ONLY)**
+
+If you are using a self-managed GitLab instance, GitLab must first be configured with a set of
+Amazon credentials. These credentials will be used to assume an Amazon IAM role provided by the user
+creating the cluster. Create an IAM user and ensure it has permissions to assume the role(s) that
+your users will use to create EKS clusters.
+
+For example, the following policy document allows assuming a role whose name starts with
+`gitlab-eks-` in account `123456789012`:
+
+```json
+{
+ "Version": "2012-10-17",
+ "Statement": {
+ "Effect": "Allow",
+ "Action": "sts:AssumeRole",
+ "Resource": "arn:aws:iam::123456789012:role/gitlab-eks-*"
+ }
+}
+```
+
+Generate an access key for the IAM user, and configure GitLab with the credentials:
+
+1. Navigate to **Admin Area > Settings > Integrations** and expand the **Amazon EKS** section.
+1. Check **Enable Amazon EKS integration**.
+1. Enter the account ID and access key credentials into the respective
+ `Account ID`, `Access key ID` and `Secret access key` fields.
+1. Click **Save changes**.
+
## Access controls
When creating a cluster in GitLab, you will be asked if you would like to create either:
@@ -116,57 +184,39 @@ New clusters can be added using GitLab for:
- Google Kubernetes Engine.
- Amazon Elastic Kubernetes Service.
-### GKE cluster
-
-GitLab supports:
-
-- Creating a new GKE cluster using the GitLab UI.
-- Providing credentials to add an [existing Kubernetes cluster](#add-existing-cluster).
-
-Starting from [GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/issues/25925), all the GKE clusters provisioned by GitLab are [VPC-native](https://cloud.google.com/kubernetes-engine/docs/how-to/alias-ips).
+### New GKE cluster
-NOTE: **Note:**
-The [Google authentication integration](../../../integration/google.md) must
-be enabled in GitLab at the instance level. If that's not the case, ask your
-GitLab administrator to enable it. On GitLab.com, this is enabled.
-
-#### GKE Requirements
+Starting from [GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/issues/25925), all the GKE clusters
+provisioned by GitLab are [VPC-native](https://cloud.google.com/kubernetes-engine/docs/how-to/alias-ips).
-Before creating your first cluster on Google Kubernetes Engine with GitLab's
-integration, make sure the following requirements are met:
-
-- A [billing account](https://cloud.google.com/billing/docs/how-to/manage-billing-account)
- is set up and you have permissions to access it.
-- The Kubernetes Engine API and related service are enabled. It should work immediately but may take up to 10 minutes after you create a project. For more information see the
- ["Before you begin" section of the Kubernetes Engine docs](https://cloud.google.com/kubernetes-engine/docs/quickstart#before-you-begin).
+#### Important notes
-Also note the following:
+Note the following:
+- The [Google authentication integration](../../../integration/google.md) must be enabled in GitLab
+ at the instance level. If that's not the case, ask your GitLab administrator to enable it. On
+ GitLab.com, this is enabled.
- Starting from [GitLab 12.1](https://gitlab.com/gitlab-org/gitlab-foss/issues/55902), all GKE clusters
created by GitLab are RBAC-enabled. Take a look at the [RBAC section](#rbac-cluster-resources) for
more information.
- Starting from [GitLab 12.5](https://gitlab.com/gitlab-org/gitlab/merge_requests/18341), the
cluster's pod address IP range will be set to /16 instead of the regular /14. /16 is a CIDR
notation.
-
-NOTE: **Note:**
-GitLab requires basic authentication enabled and a client certificate issued for the cluster in
-order to setup an [initial service account](#access-controls). Starting from [GitLab
-11.10](https://gitlab.com/gitlab-org/gitlab-foss/issues/58208), the cluster creation process will
-explicitly request that basic authentication and client certificate is enabled.
+- GitLab requires basic authentication enabled and a client certificate issued for the cluster to
+ set up an [initial service account](#access-controls). Starting from [GitLab
+ 11.10](https://gitlab.com/gitlab-org/gitlab-foss/issues/58208), the cluster creation process will
+ explicitly request that basic authentication and client certificate is enabled.
#### Creating the cluster on GKE
-If all of the above requirements are met, you can proceed to create and add a
-new Kubernetes cluster to your project:
-
-1. Navigate to your project's **Operations > Kubernetes** page.
-
- NOTE: **Note:**
- You need Maintainer [permissions](../../permissions.md) and above to access the Kubernetes page.
+To create and add a new Kubernetes cluster to your project, group, or instance:
+1. Navigate to your:
+ - Project's **Operations > Kubernetes** page, for a project-level cluster.
+ - Group's **Kubernetes** page, for a group-level cluster.
+ - **Admin Area > Kubernetes** page, for an instance-level cluster.
1. Click **Add Kubernetes cluster**.
-1. Click **Create with Google Kubernetes Engine**.
+1. Under the **Create new cluster** tab, click **Google GKE**.
1. Connect your Google account if you haven't done already by clicking the
**Sign in with Google** button.
1. Choose your cluster's settings:
@@ -198,64 +248,19 @@ separately after the cluster has been created. This means that Cloud Run
(Knative), Istio, and HTTP Load Balancing will be enabled on the cluster at
create time and cannot be [installed or uninstalled](../../clusters/applications.md) separately.
-### EKS Cluster
-
-GitLab supports:
-
-- Creating a new EKS cluster using the GitLab UI
- ([Introduced](https://gitlab.com/gitlab-org/gitlab/issues/22392) in GitLab 12.5).
-- Providing credentials to add an [existing Kubernetes cluster](#add-existing-cluster).
-
-#### EKS Requirements
-
-Before creating your first cluster on Amazon EKS with GitLab's integration,
-make sure the following requirements are met:
-
-- An [Amazon Web Services](https://aws.amazon.com/) account is set up and you are able to log in.
-- You have permissions to manage IAM resources.
-
-##### Additional requirements for self-managed instances
+### New EKS cluster
-If you are using a self-managed GitLab instance, GitLab must first
-be configured with a set of Amazon credentials. These credentials
-will be used to assume an Amazon IAM role provided by the user
-creating the cluster. Create an IAM user and ensure it has permissions
-to assume the role(s) that your users will use to create EKS clusters.
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/22392) in GitLab 12.5.
-For example, the following policy document allows assuming a role whose name starts with
-`gitlab-eks-` in account `123456789012`:
-
-```json
-{
- "Version": "2012-10-17",
- "Statement": {
- "Effect": "Allow",
- "Action": "sts:AssumeRole",
- "Resource": "arn:aws:iam::123456789012:role/gitlab-eks-*"
- }
-}
-```
-
-Generate an access key for the IAM user, and configure GitLab with the credentials:
-
-1. Navigate to **Admin Area > Settings > Integrations** and expand the **Amazon EKS** section.
-1. Check **Enable Amazon EKS integration**.
-1. Enter the account ID and access key credentials into the respective
- `Account ID`, `Access key ID` and `Secret access key` fields.
-1. Click **Save changes**.
-
-#### Creating the cluster on EKS
-
-If all of the above requirements are met, you can proceed to create and add a
-new Kubernetes cluster to your project:
-
-1. Navigate to your project's **Operations > Kubernetes** page.
-
- NOTE: **Note:**
- You need Maintainer [permissions](../../permissions.md) and above to access the Kubernetes page.
+To create and add a new Kubernetes cluster to your project, group, or instance:
+1. Navigate to your:
+ - Project's **Operations > Kubernetes** page, for a project-level cluster.
+ - Group's **Kubernetes** page, for a group-level cluster.
+ - **Admin Area > Kubernetes** page, for an instance-level cluster.
1. Click **Add Kubernetes cluster**.
-1. Click **Amazon EKS**. You will be provided with an `Account ID` and `External ID` to use in the next step.
+1. Under the **Create new cluster** tab, click **Amazon EKS**. You will be provided with an
+ `Account ID` and `External ID` to use in the next step.
1. In the [IAM Management Console](https://console.aws.amazon.com/iam/home), create an IAM role:
1. From the left panel, select **Roles**.
1. Click **Create role**.
@@ -356,24 +361,23 @@ to install some [pre-defined applications](index.md#installing-applications).
If you have either of the following types of clusters already, you can add them to a project:
-- [Google Kubernetes Engine cluster](#add-existing-gke-cluster).
-- [Amazon Elastic Kubernetes Service](#add-existing-eks-cluster).
+- [Google Kubernetes Engine cluster](#existing-gke-cluster).
+- [Amazon Elastic Kubernetes Service](#existing-eks-cluster).
NOTE: **Note:**
Kubernetes integration is not supported for arm64 clusters. See the issue
[Helm Tiller fails to install on arm64 cluster](https://gitlab.com/gitlab-org/gitlab-foss/issues/64044) for details.
-### Add existing GKE cluster
-
-To add an existing Kubernetes cluster to your project:
-
-1. Navigate to your project's **Operations > Kubernetes** page.
+### Existing GKE cluster
- NOTE: **Note:**
- You need Maintainer [permissions](../../permissions.md) and above to access the Kubernetes page.
+To add an existing GKE cluster to your project, group, or instance:
+1. Navigate to your:
+ - Project's **Operations > Kubernetes** page, for a project-level cluster.
+ - Group's **Kubernetes** page, for a group-level cluster.
+ - **Admin Area > Kubernetes** page, for an instance-level cluster.
1. Click **Add Kubernetes cluster**.
-1. Click **Add an existing Kubernetes cluster** and fill in the details:
+1. Click the **Add existing cluster** tab and fill in the details:
- **Kubernetes cluster name** (required) - The name you wish to give the cluster.
- **Environment scope** (required) - The
[associated environment](index.md#setting-the-environment-scope-premium) to this cluster.
@@ -389,7 +393,7 @@ To add an existing Kubernetes cluster to your project:
```
- **CA certificate** (required) - A valid Kubernetes certificate is needed to authenticate to the cluster. We will use the certificate created by default.
- - List the secrets with `kubectl get secrets`, and one should named similar to
+ - List the secrets with `kubectl get secrets`, and one should be named similar to
`default-token-xxxxx`. Copy that token name for use below.
- Get the certificate by running this command:
@@ -508,136 +512,110 @@ To add an existing Kubernetes cluster to your project:
After a couple of minutes, your cluster will be ready to go. You can now proceed
to install some [pre-defined applications](index.md#installing-applications).
-### Add existing EKS cluster
-
-In this section, we will show how to integrate an [Amazon EKS](https://aws.amazon.com/eks/) cluster with GitLab and begin
-deploying applications.
-
-#### Requirements
-
-To integrate with with EKS, you will need:
-
-- An account on GitLab, like [GitLab.com](https://gitlab.com).
-- An Amazon EKS cluster (with worker nodes properly configured).
-- `kubectl` [installed and configured for access to the EKS cluster](https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html#get-started-kubectl).
-
-If you don't have an Amazon EKS cluster, one can be created by following the
-[EKS getting started guide](https://docs.aws.amazon.com/eks/latest/userguide/getting-started.html).
-
-#### Configuring and connecting the EKS cluster
-
-From the left side bar, hover over **Operations > Kubernetes > Add Kubernetes cluster**,
-then click **Add an existing Kubernetes cluster**.
-
-A few details from the EKS cluster will be required to connect it to GitLab:
-
-1. **Retrieve the certificate**: A valid Kubernetes certificate is needed to
- authenticate to the EKS cluster. We will use the certificate created by default.
- Open a shell and use `kubectl` to retrieve it:
-
- - List the secrets with `kubectl get secrets`, and one should named similar to
- `default-token-xxxxx`. Copy that token name for use below.
- - Get the certificate with:
-
- ```sh
- kubectl get secret <secret name> -o jsonpath="{['data']['ca\.crt']}" | base64 --decode
- ```
-
-1. **Create admin token**: A `cluster-admin` token is required to install and
- manage Helm Tiller. GitLab establishes mutual SSL auth with Helm Tiller
- and creates limited service accounts for each application. To create the
- token we will create an admin service account as follows:
-
- 1. Create a file called `eks-admin-service-account.yaml` with contents:
-
- ```yaml
- apiVersion: v1
- kind: ServiceAccount
- metadata:
- name: eks-admin
- namespace: kube-system
- ```
-
- 1. Apply the service account to your cluster:
-
- ```bash
- kubectl apply -f eks-admin-service-account.yaml
- ```
-
- Output:
-
- ```bash
- serviceaccount "eks-admin" created
- ```
-
- 1. Create a file called `eks-admin-cluster-role-binding.yaml` with contents:
-
- ```yaml
- apiVersion: rbac.authorization.k8s.io/v1beta1
- kind: ClusterRoleBinding
- metadata:
- name: eks-admin
- roleRef:
- apiGroup: rbac.authorization.k8s.io
- kind: ClusterRole
- name: cluster-admin
- subjects:
- - kind: ServiceAccount
- name: eks-admin
- namespace: kube-system
- ```
-
- 1. Apply the cluster role binding to your cluster:
-
- ```bash
- kubectl apply -f eks-admin-cluster-role-binding.yaml
- ```
-
- Output:
-
- ```bash
- clusterrolebinding "eks-admin" created
- ```
-
- 1. Retrieve the token for the `eks-admin` service account:
-
- ```bash
- kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep eks-admin | awk '{print $1}')
- ```
-
- Copy the `<authentication_token>` value from the output:
-
- ```yaml
- Name: eks-admin-token-b5zv4
- Namespace: kube-system
- Labels: <none>
- Annotations: kubernetes.io/service-account.name=eks-admin
+### Existing EKS cluster
+
+To add an existing EKS cluster to your project, group, or instance:
+
+1. Perform the following steps on the EKS cluster:
+ 1. Retrieve the certificate. A valid Kubernetes certificate is needed to authenticate to the
+ EKS cluster. We will use the certificate created by default.
+ Open a shell and use `kubectl` to retrieve it:
+
+ 1. List the secrets with `kubectl get secrets`, and one should named similar to
+ `default-token-xxxxx`. Copy that token name for use below.
+ 1. Get the certificate with:
+
+ ```sh
+ kubectl get secret <secret name> -o jsonpath="{['data']['ca\.crt']}" | base64 --decode
+ ```
+
+ 1. Create admin token. A `cluster-admin` token is required to install and manage Helm Tiller.
+ GitLab establishes mutual SSL authentication with Helm Tiller and creates limited service
+ accounts for each application. To create the token we will create an admin service account as
+ follows:
+
+ 1. Create a file called `eks-admin-service-account.yaml` with contents:
+
+ ```yaml
+ apiVersion: v1
+ kind: ServiceAccount
+ metadata:
+ name: eks-admin
+ namespace: kube-system
+ ```
+
+ 1. Apply the service account to your cluster:
+
+ ```shell
+ $ kubectl apply -f eks-admin-service-account.yaml
+ serviceaccount "eks-admin" created
+ ```
+
+ 1. Create a file called `eks-admin-cluster-role-binding.yaml` with contents:
+
+ ```yaml
+ apiVersion: rbac.authorization.k8s.io/v1beta1
+ kind: ClusterRoleBinding
+ metadata:
+ name: eks-admin
+ roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: cluster-admin
+ subjects:
+ - kind: ServiceAccount
+ name: eks-admin
+ namespace: kube-system
+ ```
+
+ 1. Apply the cluster role binding to your cluster:
+
+ ```shell
+ $ kubectl apply -f eks-admin-cluster-role-binding.yaml
+ clusterrolebinding "eks-admin" created
+ ```
+
+ 1. Retrieve the token for the `eks-admin` service account:
+
+ ```bash
+ kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep eks-admin | awk '{print $1}')
+ ```
+
+ Copy the `<authentication_token>` value from the output:
+
+ ```yaml
+ Name: eks-admin-token-b5zv4
+ Namespace: kube-system
+ Labels: <none>
+ Annotations: kubernetes.io/service-account.name=eks-admin
kubernetes.io/service-account.uid=bcfe66ac-39be-11e8-97e8-026dce96b6e8
- Type: kubernetes.io/service-account-token
-
- Data
- ====
- ca.crt: 1025 bytes
- namespace: 11 bytes
- token: <authentication_token>
- ```
-
-1. The API server endpoint is also required, so GitLab can connect to the cluster.
- This is displayed on the AWS EKS console, when viewing the EKS cluster details.
-
-You now have all the information needed to connect the EKS cluster:
-
-- Kubernetes cluster name: Provide a name for the cluster to identify it within GitLab.
-- Environment scope: Leave this as `*` for now, since we are only connecting a single cluster.
-- API URL: Paste in the API server endpoint retrieved above.
-- CA Certificate: Paste the certificate data from the earlier step, as-is.
-- Paste the admin token value.
-- Project namespace: This can be left blank to accept the default namespace, based on the project name.
-
-![Add Cluster](img/add_cluster.png)
+ Type: kubernetes.io/service-account-token
+
+ Data
+ ====
+ ca.crt: 1025 bytes
+ namespace: 11 bytes
+ token: <authentication_token>
+ ```
+
+ 1. Locate the the API server endpoint so GitLab can connect to the cluster. This is displayed on
+ the AWS EKS console, when viewing the EKS cluster details.
+1. Navigate to your:
+ - Project's **Operations > Kubernetes** page, for a project-level cluster.
+ - Group's **Kubernetes** page, for a group-level cluster.
+ - **Admin Area > Kubernetes** page, for an instance-level cluster.
+1. Click **Add Kubernetes cluster**.
+1. Click the **Add existing cluster** tab and fill in the details:
+ - **Kubernetes cluster name**: A name for the cluster to identify it within GitLab.
+ - **Environment scope**: Leave this as `*` for now, since we are only connecting a single cluster.
+ - **API URL**: The API server endpoint retrieved earlier.
+ - **CA Certificate**: The certificate data from the earlier step, as-is.
+ - **Service Token**: The admin token value.
+ - For project-level clusters, **Project namespace prefix**: This can be left blank to accept the
+ default namespace, based on the project name.
+1. Click on **Add Kubernetes cluster**. The cluster is now connected to GitLab.
-Click on **Add Kubernetes cluster**, the cluster is now connected to GitLab.
At this point, [Kubernetes deployment variables](index.md#deployment-variables) will
automatically be available during CI/CD jobs, making it easy to interact with the cluster.
diff --git a/doc/user/project/clusters/eks_and_gitlab/index.md b/doc/user/project/clusters/eks_and_gitlab/index.md
index fda8cd6340e..9bb8f6cb83c 100644
--- a/doc/user/project/clusters/eks_and_gitlab/index.md
+++ b/doc/user/project/clusters/eks_and_gitlab/index.md
@@ -1,5 +1,5 @@
---
-redirect_to: '../add_remove_clusters.md#add-existing-eks-cluster'
+redirect_to: '../add_remove_clusters.md#existing-eks-cluster'
---
-This document was moved to [another location](../add_remove_clusters.md#add-existing-eks-cluster).
+This document was moved to [another location](../add_remove_clusters.md#existing-eks-cluster).
diff --git a/doc/user/project/clusters/img/add_cluster.png b/doc/user/project/clusters/img/add_cluster.png
deleted file mode 100644
index 94ec83f1514..00000000000
--- a/doc/user/project/clusters/img/add_cluster.png
+++ /dev/null
Binary files differ
diff --git a/doc/user/project/clusters/index.md b/doc/user/project/clusters/index.md
index 6d863a8b888..895cc6c4b57 100644
--- a/doc/user/project/clusters/index.md
+++ b/doc/user/project/clusters/index.md
@@ -30,9 +30,6 @@ Using the GitLab project Kubernetes integration, you can:
- View [Pod logs](#pod-logs-ultimate). **(ULTIMATE)**
- Run serverless workloads on [Kubernetes with Knative](serverless/index.md).
-See [Adding and removing Kubernetes clusters](add_remove_clusters.md) for details on how to
-set up integrations.
-
### Deploy Boards **(PREMIUM)**
GitLab's Deploy Boards offer a consolidated view of the current health and
@@ -79,10 +76,7 @@ Kubernetes clusters can be used without Auto DevOps.
### Web terminals
-NOTE: **Note:**
-Introduced in GitLab 8.15. You must be the project owner or have `maintainer` permissions
-to use terminals. Support is limited to the first container in the
-first pod of your environment.
+> Introduced in GitLab 8.15.
When enabled, the Kubernetes service adds [web terminal](../../../ci/environments.md#web-terminals)
support to your [environments](../../../ci/environments.md). This is based on the `exec` functionality found in
@@ -97,6 +91,14 @@ pods are annotated with:
`$CI_ENVIRONMENT_SLUG` and `$CI_PROJECT_PATH_SLUG` are the values of
the CI variables.
+You must be the project owner or have `maintainer` permissions to use terminals. Support is limited
+to the first container in the first pod of your environment.
+
+## Adding and removing clusters
+
+See [Adding and removing Kubernetes clusters](add_remove_clusters.md) for details on how to
+set up integrations with Google Cloud Platform (GCP) and Amazon Elastic Kubernetes Service (EKS).
+
## Cluster configuration
After [adding a Kubernetes cluster](add_remove_clusters.md) to GitLab, read this section that covers
@@ -115,8 +117,8 @@ applications running on the cluster.
### GitLab-managed clusters
-> [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/22011) in GitLab 11.5.
-> Became [optional](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/26565) in GitLab 11.11.
+> - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/22011) in GitLab 11.5.
+> - Became [optional](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/26565) in GitLab 11.11.
You can choose to allow GitLab to manage your cluster for you. If your cluster is
managed by GitLab, resources for your projects will be automatically created. See the
diff --git a/doc/user/project/clusters/serverless/index.md b/doc/user/project/clusters/serverless/index.md
index ae04dbab1a0..77981e5bd87 100644
--- a/doc/user/project/clusters/serverless/index.md
+++ b/doc/user/project/clusters/serverless/index.md
@@ -43,7 +43,7 @@ To run Knative on GitLab, you will need:
clone the sample [Knative Ruby App](https://gitlab.com/knative-examples/knative-ruby-app) to get
started.
1. **Kubernetes Cluster:** An RBAC-enabled Kubernetes cluster is required to deploy Knative.
- The simplest way to get started is to add a cluster using [GitLab's GKE integration](../add_remove_clusters.md#gke-cluster).
+ The simplest way to get started is to add a cluster using GitLab's [GKE integration](../add_remove_clusters.md).
The set of minimum recommended cluster specifications to run Knative is 3 nodes, 6 vCPUs, and 22.50 GB memory.
1. **Helm Tiller:** Helm is a package manager for Kubernetes and is required to install
Knative.
diff --git a/doc/user/project/repository/git_blame.md b/doc/user/project/repository/git_blame.md
index 454b3f86df9..4b645e4c4bc 100644
--- a/doc/user/project/repository/git_blame.md
+++ b/doc/user/project/repository/git_blame.md
@@ -23,6 +23,11 @@ noted information:
If you hover over a commit in the UI, you'll see a precise date and time
for that commit.
+![Blame previous commit](img/file_blame_previous_commit_v12_7.png "Blame previous commit")
+
+To see earlier revisions of a specific line, click **View blame prior to this change**
+until you've found the changes you're interested in viewing.
+
## Associated `git` command
If you're running `git` from the command line, the equivalent command is
diff --git a/doc/user/project/repository/img/file_blame_previous_commit_v12_7.png b/doc/user/project/repository/img/file_blame_previous_commit_v12_7.png
new file mode 100644
index 00000000000..3029f42c3c4
--- /dev/null
+++ b/doc/user/project/repository/img/file_blame_previous_commit_v12_7.png
Binary files differ
diff --git a/lib/api/award_emoji.rb b/lib/api/award_emoji.rb
index 89b7e5c5e4b..7a815fa3dde 100644
--- a/lib/api/award_emoji.rb
+++ b/lib/api/award_emoji.rb
@@ -27,7 +27,6 @@ module API
":id/#{awardable_string}/:#{awardable_id_string}/award_emoji",
":id/#{awardable_string}/:#{awardable_id_string}/notes/:note_id/award_emoji"
].each do |endpoint|
-
desc 'Get a list of project +awardable+ award emoji' do
detail 'This feature was introduced in 8.9'
success Entities::AwardEmoji
diff --git a/lib/gitlab/background_migration/archive_legacy_traces.rb b/lib/gitlab/background_migration/archive_legacy_traces.rb
index 3c26982729d..79f38aed9f1 100644
--- a/lib/gitlab/background_migration/archive_legacy_traces.rb
+++ b/lib/gitlab/background_migration/archive_legacy_traces.rb
@@ -11,7 +11,6 @@ module Gitlab
# So we chose a way to use ::Ci::Build directly and we don't change the `archive!` method until 11.1
::Ci::Build.finished.without_archived_trace
.where(id: start_id..stop_id).find_each do |build|
-
build.trace.archive!
rescue => e
Rails.logger.error "Failed to archive live trace. id: #{build.id} message: #{e.message}" # rubocop:disable Gitlab/RailsLogger
diff --git a/lib/gitlab/import_export/project_tree_restorer.rb b/lib/gitlab/import_export/project_tree_restorer.rb
index e274b68a94f..f309f106ec9 100644
--- a/lib/gitlab/import_export/project_tree_restorer.rb
+++ b/lib/gitlab/import_export/project_tree_restorer.rb
@@ -10,7 +10,7 @@ module Gitlab
def initialize(user:, shared:, project:)
@path = File.join(shared.export_path, 'project.json')
@user = user
- @shared = shared
+ @shared = shared
@project = project
end
diff --git a/lib/gitlab/metrics/subscribers/action_view.rb b/lib/gitlab/metrics/subscribers/action_view.rb
index 2ed5878286a..5bd21b8e5d1 100644
--- a/lib/gitlab/metrics/subscribers/action_view.rb
+++ b/lib/gitlab/metrics/subscribers/action_view.rb
@@ -36,7 +36,7 @@ module Gitlab
end
def relative_path(path)
- path.gsub(%r{^#{Rails.root.to_s}/?}, '')
+ path.gsub(%r{^#{Rails.root}/?}, '')
end
def values_for(event)
diff --git a/lib/gitlab/pages.rb b/lib/gitlab/pages.rb
index 4899b1d3234..7703b086341 100644
--- a/lib/gitlab/pages.rb
+++ b/lib/gitlab/pages.rb
@@ -4,6 +4,7 @@ module Gitlab
class Pages
VERSION = File.read(Rails.root.join("GITLAB_PAGES_VERSION")).strip.freeze
INTERNAL_API_REQUEST_HEADER = 'Gitlab-Pages-Api-Request'.freeze
+ MAX_SIZE = 1.terabyte
include JwtAuthenticatable
diff --git a/lib/gitlab/sherlock/file_sample.rb b/lib/gitlab/sherlock/file_sample.rb
index 604b6df12cc..5d10d8c4877 100644
--- a/lib/gitlab/sherlock/file_sample.rb
+++ b/lib/gitlab/sherlock/file_sample.rb
@@ -18,7 +18,7 @@ module Gitlab
end
def relative_path
- @relative_path ||= @file.gsub(%r{^#{Rails.root.to_s}/?}, '')
+ @relative_path ||= @file.gsub(%r{^#{Rails.root}/?}, '')
end
def to_param
diff --git a/lib/gitlab/sherlock/line_profiler.rb b/lib/gitlab/sherlock/line_profiler.rb
index 209ba784f9c..52d88f074b7 100644
--- a/lib/gitlab/sherlock/line_profiler.rb
+++ b/lib/gitlab/sherlock/line_profiler.rb
@@ -45,7 +45,7 @@ module Gitlab
require 'rblineprof'
retval = nil
- samples = lineprof(/^#{Rails.root.to_s}/) { retval = yield }
+ samples = lineprof(/^#{Rails.root}/) { retval = yield }
file_samples = aggregate_rblineprof(samples)
diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb
index ec2243345e1..0753b2edb6f 100644
--- a/lib/gitlab/usage_data.rb
+++ b/lib/gitlab/usage_data.rb
@@ -206,7 +206,6 @@ module Gitlab
.by_type(:JiraService)
.includes(:jira_tracker_data)
.find_in_batches(batch_size: BATCH_SIZE) do |services|
-
counts = services.group_by do |service|
# TODO: Simplify as part of https://gitlab.com/gitlab-org/gitlab/issues/29404
service_url = service.data_fields&.url || (service.properties && service.properties['url'])
diff --git a/lib/tasks/gitlab/lfs/migrate.rake b/lib/tasks/gitlab/lfs/migrate.rake
index 4142903d9c3..6f11646c841 100644
--- a/lib/tasks/gitlab/lfs/migrate.rake
+++ b/lib/tasks/gitlab/lfs/migrate.rake
@@ -9,7 +9,6 @@ namespace :gitlab do
LfsObject.with_files_stored_locally
.find_each(batch_size: 10) do |lfs_object|
-
lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE)
logger.info("Transferred LFS object #{lfs_object.oid} of size #{lfs_object.size.to_i.bytes} to object storage")
@@ -24,7 +23,6 @@ namespace :gitlab do
LfsObject.with_files_stored_remotely
.find_each(batch_size: 10) do |lfs_object|
-
lfs_object.file.migrate!(LfsObjectUploader::Store::LOCAL)
logger.info("Transferred LFS object #{lfs_object.oid} of size #{lfs_object.size.to_i.bytes} to local storage")
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index b3672f4ca6d..cef7a6896dc 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -5045,12 +5045,18 @@ msgstr ""
msgid "Could not create project"
msgstr ""
+msgid "Could not delete %{design}. Please try again."
+msgstr ""
+
msgid "Could not delete chat nickname %{chat_name}."
msgstr ""
msgid "Could not fetch projects"
msgstr ""
+msgid "Could not find design"
+msgstr ""
+
msgid "Could not remove the trigger."
msgstr ""
@@ -6062,15 +6068,12 @@ msgstr ""
msgid "DesignManagement|Are you sure you want to delete the selected designs?"
msgstr ""
-msgid "DesignManagement|Could not add a new comment. Please try again"
+msgid "DesignManagement|Could not add a new comment. Please try again."
msgstr ""
msgid "DesignManagement|Could not create new discussion. Please try again."
msgstr ""
-msgid "DesignManagement|Could not find design, please try again."
-msgstr ""
-
msgid "DesignManagement|Delete"
msgstr ""
@@ -6083,7 +6086,7 @@ msgstr ""
msgid "DesignManagement|Deselect all"
msgstr ""
-msgid "DesignManagement|Error uploading a new design. Please try again"
+msgid "DesignManagement|Error uploading a new design. Please try again."
msgstr ""
msgid "DesignManagement|Go back to designs"
@@ -6095,9 +6098,6 @@ msgstr ""
msgid "DesignManagement|Go to previous design"
msgstr ""
-msgid "DesignManagement|Requested design version does not exist"
-msgstr ""
-
msgid "DesignManagement|Requested design version does not exist. Showing latest version instead"
msgstr ""
@@ -6113,9 +6113,6 @@ msgstr ""
msgid "DesignManagement|Upload and view the latest designs for this issue. Consistent and easy to find, so everyone is up to date."
msgstr ""
-msgid "DesignManagement|We could not delete %{design}. Please try again."
-msgstr ""
-
msgid "Designs"
msgstr ""
@@ -8212,13 +8209,13 @@ msgstr ""
msgid "GeoNodes|Out of sync"
msgstr ""
-msgid "GeoNodes|Pausing replication stops the sync process."
+msgid "GeoNodes|Pausing replication stops the sync process. Are you sure?"
msgstr ""
-msgid "GeoNodes|Removing a primary node stops the sync process for all nodes. Syncing cannot be resumed without losing some data on all secondaries. In this case we would recommend setting up all nodes from scratch. Are you sure?"
+msgid "GeoNodes|Removing a Geo primary node stops the synchronization to that node. Are you sure?"
msgstr ""
-msgid "GeoNodes|Removing a secondary node stops the sync process. It is not currently possible to add back the same node without losing some data. We only recommend setting up a new secondary node in this case. Are you sure?"
+msgid "GeoNodes|Removing a Geo secondary node stops the synchronization to that node. Are you sure?"
msgstr ""
msgid "GeoNodes|Replication slot WAL"
@@ -14964,15 +14961,24 @@ msgstr ""
msgid "Remove milestone"
msgstr ""
+msgid "Remove node"
+msgstr ""
+
msgid "Remove parent epic from an epic"
msgstr ""
+msgid "Remove primary node"
+msgstr ""
+
msgid "Remove priority"
msgstr ""
msgid "Remove project"
msgstr ""
+msgid "Remove secondary node"
+msgstr ""
+
msgid "Remove spent time"
msgstr ""
@@ -15237,6 +15243,9 @@ msgstr ""
msgid "Requested %{time_ago}"
msgstr ""
+msgid "Requested design version does not exist"
+msgstr ""
+
msgid "Requests Profiles"
msgstr ""
@@ -20048,6 +20057,9 @@ msgstr ""
msgid "View app"
msgstr ""
+msgid "View blame prior to this change"
+msgstr ""
+
msgid "View dependency details for your project"
msgstr ""
diff --git a/qa/Dockerfile b/qa/Dockerfile
index e4b860b08b2..126d9fbc591 100644
--- a/qa/Dockerfile
+++ b/qa/Dockerfile
@@ -39,6 +39,12 @@ RUN wget -q https://chromedriver.storage.googleapis.com/$(wget -q -O - https://c
RUN unzip chromedriver_linux64.zip -d /usr/local/bin
##
+# Install K3d local cluster support
+# https://github.com/rancher/k3d
+#
+RUN curl -s https://raw.githubusercontent.com/rancher/k3d/master/install.sh | TAG=v1.3.4 bash
+
+##
# Install gcloud and kubectl CLI used in Auto DevOps test to create K8s
# clusters
#
diff --git a/qa/qa.rb b/qa/qa.rb
index 509de4af79c..1dcaa7f568e 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -37,6 +37,7 @@ module QA
autoload :MailHog, 'qa/runtime/mail_hog'
autoload :IPAddress, 'qa/runtime/ip_address'
autoload :Search, 'qa/runtime/search'
+ autoload :ApplicationSettings, 'qa/runtime/application_settings'
module API
autoload :Client, 'qa/runtime/api/client'
diff --git a/qa/qa/page/project/operations/kubernetes/index.rb b/qa/qa/page/project/operations/kubernetes/index.rb
index de54319596d..84b58e9ea5b 100644
--- a/qa/qa/page/project/operations/kubernetes/index.rb
+++ b/qa/qa/page/project/operations/kubernetes/index.rb
@@ -13,6 +13,10 @@ module QA
def add_kubernetes_cluster
click_on 'Add Kubernetes cluster'
end
+
+ def has_cluster?(cluster)
+ has_element?(:cluster, cluster_name: cluster.to_s)
+ end
end
end
end
diff --git a/qa/qa/page/project/operations/kubernetes/show.rb b/qa/qa/page/project/operations/kubernetes/show.rb
index fa276f15b8a..3d3eebdbec9 100644
--- a/qa/qa/page/project/operations/kubernetes/show.rb
+++ b/qa/qa/page/project/operations/kubernetes/show.rb
@@ -6,12 +6,6 @@ module QA
module Operations
module Kubernetes
class Show < Page::Base
- view 'app/assets/javascripts/clusters/components/application_row.vue' do
- element :application_row, 'js-cluster-application-row-${this.id}' # rubocop:disable QA/ElementWithPattern
- element :install_button, "__('Install')" # rubocop:disable QA/ElementWithPattern
- element :installed_button, "__('Installed')" # rubocop:disable QA/ElementWithPattern
- end
-
view 'app/assets/javascripts/clusters/components/applications.vue' do
element :ingress_ip_address, 'id="ingress-endpoint"' # rubocop:disable QA/ElementWithPattern
end
@@ -22,15 +16,21 @@ module QA
end
def install!(application_name)
- within(".js-cluster-application-row-#{application_name}") do
- page.has_button?('Install', wait: 30)
- click_on 'Install'
+ within_element(application_name) do
+ has_element?(:install_button, application: application_name, wait: 30)
+ click_on 'Install' # TODO replace with click_element
end
end
def await_installed(application_name)
- within(".js-cluster-application-row-#{application_name}") do
- page.has_text?(/Installed|Uninstall/, wait: 300)
+ within_element(application_name) do
+ has_element?(:uninstall_button, application: application_name, wait: 300)
+ end
+ end
+
+ def has_application_installed?(application_name)
+ within_element(application_name) do
+ has_element?(:uninstall_button, application: application_name, wait: 300)
end
end
diff --git a/qa/qa/runtime/application_settings.rb b/qa/qa/runtime/application_settings.rb
new file mode 100644
index 00000000000..df6323f9a48
--- /dev/null
+++ b/qa/qa/runtime/application_settings.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+module QA
+ module Runtime
+ module ApplicationSettings
+ extend self
+ extend Support::Api
+
+ APPLICATION_SETTINGS_PATH = '/application/settings'
+
+ # Set a GitLab application setting
+ # Example:
+ # #set({ allow_local_requests_from_web_hooks_and_services: true })
+ # #set(allow_local_requests_from_web_hooks_and_services: true)
+ # https://docs.gitlab.com/ee/api/settings.html
+ def set_application_settings(**application_settings)
+ QA::Runtime::Logger.info("Setting application settings: #{application_settings}")
+ r = put(Runtime::API::Request.new(api_client, APPLICATION_SETTINGS_PATH).url, **application_settings)
+ raise "Couldn't set application settings #{application_settings.inspect}" unless r.code == QA::Support::Api::HTTP_STATUS_OK
+ end
+
+ def get_application_settings
+ parse_body(get(Runtime::API::Request.new(api_client, APPLICATION_SETTINGS_PATH).url))
+ end
+
+ private
+
+ def api_client
+ @api_client ||= begin
+ return Runtime::API::Client.new(:gitlab, personal_access_token: Runtime::Env.admin_personal_access_token) if Runtime::Env.admin_personal_access_token
+
+ user = Resource::User.fabricate_via_api! do |user|
+ user.username = Runtime::User.admin_username
+ user.password = Runtime::User.admin_password
+ end
+
+ unless user.admin?
+ raise "Administrator access is required to set application settings. User '#{user.username}' is not an administrator."
+ end
+
+ Runtime::API::Client.new(:gitlab, user: user)
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/runtime/env.rb b/qa/qa/runtime/env.rb
index 184ccd3ef07..6514e41e279 100644
--- a/qa/qa/runtime/env.rb
+++ b/qa/qa/runtime/env.rb
@@ -248,6 +248,10 @@ module QA
raise ArgumentError, "Please provide GITHUB_ACCESS_TOKEN"
end
+ def require_admin_access_token!
+ admin_personal_access_token || (raise ArgumentError, "GITLAB_QA_ADMIN_ACCESS_TOKEN is required!")
+ end
+
# Returns true if there is an environment variable that indicates that
# the feature is supported in the environment under test.
# All features are supported by default.
diff --git a/qa/qa/service/cluster_provider/k3d.rb b/qa/qa/service/cluster_provider/k3d.rb
index 8e117c2dbd5..fe02dde607c 100644
--- a/qa/qa/service/cluster_provider/k3d.rb
+++ b/qa/qa/service/cluster_provider/k3d.rb
@@ -6,6 +6,8 @@ module QA
class K3d < Base
def validate_dependencies
find_executable('k3d') || raise("You must first install `k3d` executable to run these tests.")
+ Runtime::Env.require_admin_access_token!
+ Runtime::ApplicationSettings.set_application_settings(allow_local_requests_from_web_hooks_and_services: true)
end
def set_credentials(admin_user)
@@ -24,6 +26,7 @@ module QA
def teardown
ENV['KUBECONFIG'] = @old_kubeconfig
shell "k3d delete --name #{cluster_name}"
+ Runtime::ApplicationSettings.set_application_settings(allow_local_requests_from_web_hooks_and_services: false)
end
# Fetch "real" certificate
diff --git a/qa/qa/service/kubernetes_cluster.rb b/qa/qa/service/kubernetes_cluster.rb
index 26b5f58d2d3..84196556547 100644
--- a/qa/qa/service/kubernetes_cluster.rb
+++ b/qa/qa/service/kubernetes_cluster.rb
@@ -39,6 +39,10 @@ module QA
@provider.cluster_name
end
+ def to_s
+ cluster_name
+ end
+
private
def fetch_api_url
diff --git a/qa/qa/specs/features/browser_ui/7_configure/kubernetes/kubernetes_integration_spec.rb b/qa/qa/specs/features/browser_ui/7_configure/kubernetes/kubernetes_integration_spec.rb
new file mode 100644
index 00000000000..73b5a579e08
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/7_configure/kubernetes/kubernetes_integration_spec.rb
@@ -0,0 +1,53 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Configure' do
+ # This test requires GITLAB_QA_ADMIN_ACCESS_TOKEN to be specified
+ describe 'Kubernetes Cluster Integration', :orchestrated, :kubernetes, :requires_admin, :skip do
+ context 'Project Clusters' do
+ let(:cluster) { Service::KubernetesCluster.new(provider_class: Service::ClusterProvider::K3d).create! }
+ let(:project) do
+ Resource::Project.fabricate_via_api! do |project|
+ project.name = 'project-with-k8s'
+ project.description = 'Project with Kubernetes cluster integration'
+ end
+ end
+
+ before do
+ Flow::Login.sign_in
+ end
+
+ after do
+ cluster.remove!
+ end
+
+ it 'can create and associate a project cluster', :smoke do
+ Resource::KubernetesCluster.fabricate_via_browser_ui! do |k8s_cluster|
+ k8s_cluster.project = project
+ k8s_cluster.cluster = cluster
+ end
+
+ project.visit!
+
+ Page::Project::Menu.perform(&:go_to_operations_kubernetes)
+
+ Page::Project::Operations::Kubernetes::Index.perform do |index|
+ expect(index).to have_cluster(cluster)
+ end
+ end
+
+ it 'installs helm and tiller on a gitlab managed app' do
+ Resource::KubernetesCluster.fabricate_via_browser_ui! do |k8s_cluster|
+ k8s_cluster.project = project
+ k8s_cluster.cluster = cluster
+ k8s_cluster.install_helm_tiller = true
+ end
+
+ Page::Project::Operations::Kubernetes::Show.perform do |show|
+ expect(show).to have_application_installed(:helm)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/spec/runtime/application_settings_spec.rb b/qa/spec/runtime/application_settings_spec.rb
new file mode 100644
index 00000000000..fce0361aee0
--- /dev/null
+++ b/qa/spec/runtime/application_settings_spec.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+describe QA::Runtime::ApplicationSettings do
+ let(:api_client) { double('QA::Runtime::API::Client') }
+ let(:request) { Struct.new(:url).new('http://api') }
+ let(:get_response) { Struct.new(:body).new("{}") }
+
+ before do
+ allow(described_class).to receive(:api_client).and_return(api_client)
+ end
+
+ describe '.set_application_settings' do
+ it 'sets application settings' do
+ expect(QA::Runtime::API::Request)
+ .to receive(:new)
+ .with(api_client, '/application/settings')
+ .and_return(request)
+
+ expect(described_class)
+ .to receive(:put)
+ .with(request.url, { allow_local_requests_from_web_hooks_and_services: true })
+ .and_return(Struct.new(:code).new(200))
+
+ subject.set_application_settings(allow_local_requests_from_web_hooks_and_services: true)
+ end
+ end
+
+ describe '.get_application_settings' do
+ it 'gets application settings' do
+ expect(QA::Runtime::API::Request)
+ .to receive(:new)
+ .with(api_client, '/application/settings')
+ .and_return(request)
+
+ expect(described_class)
+ .to receive(:get)
+ .with(request.url)
+ .and_return(get_response)
+
+ subject.get_application_settings
+ end
+ end
+end
diff --git a/qa/spec/runtime/env_spec.rb b/qa/spec/runtime/env_spec.rb
index 340831aa06d..0a0bf33a726 100644
--- a/qa/spec/runtime/env_spec.rb
+++ b/qa/spec/runtime/env_spec.rb
@@ -230,6 +230,20 @@ describe QA::Runtime::Env do
end
end
+ describe '.require_admin_access_token!' do
+ it 'raises ArgumentError if GITLAB_QA_ADMIN_ACCESS_TOKEN is not specified' do
+ stub_env('GITLAB_QA_ADMIN_ACCESS_TOKEN', nil)
+
+ expect { described_class.require_admin_access_token! }.to raise_error(ArgumentError)
+ end
+
+ it 'does not raise exception if GITLAB_QA_ADMIN_ACCESS_TOKEN is specified' do
+ stub_env('GITLAB_QA_ADMIN_ACCESS_TOKEN', 'foobar123')
+
+ expect { described_class.require_admin_access_token! }.not_to raise_error
+ end
+ end
+
describe '.log_destination' do
it 'returns $stdout if QA_LOG_PATH is not defined' do
stub_env('QA_LOG_PATH', nil)
diff --git a/spec/controllers/concerns/confirm_email_warning_spec.rb b/spec/controllers/concerns/confirm_email_warning_spec.rb
index 25429cdd149..56a6efab8ed 100644
--- a/spec/controllers/concerns/confirm_email_warning_spec.rb
+++ b/spec/controllers/concerns/confirm_email_warning_spec.rb
@@ -10,7 +10,7 @@ describe ConfirmEmailWarning do
controller(ApplicationController) do
# `described_class` is not available in this context
- include ConfirmEmailWarning # rubocop:disable RSpec/DescribedClass
+ include ConfirmEmailWarning
def index
head :ok
diff --git a/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb b/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb
index 7a56f7203b0..e47f1650b1f 100644
--- a/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb
+++ b/spec/controllers/concerns/controller_with_cross_project_access_check_spec.rb
@@ -22,7 +22,7 @@ describe ControllerWithCrossProjectAccessCheck do
describe '#requires_cross_project_access' do
controller(ApplicationController) do
# `described_class` is not available in this context
- include ControllerWithCrossProjectAccessCheck # rubocop:disable RSpec/DescribedClass
+ include ControllerWithCrossProjectAccessCheck
requires_cross_project_access :index, show: false,
unless: -> { unless_condition },
@@ -81,7 +81,7 @@ describe ControllerWithCrossProjectAccessCheck do
describe '#skip_cross_project_access_check' do
controller(ApplicationController) do
# `described_class` is not available in this context
- include ControllerWithCrossProjectAccessCheck # rubocop:disable RSpec/DescribedClass
+ include ControllerWithCrossProjectAccessCheck
requires_cross_project_access
diff --git a/spec/controllers/concerns/group_tree_spec.rb b/spec/controllers/concerns/group_tree_spec.rb
index 835c3d9b3af..543f0170be0 100644
--- a/spec/controllers/concerns/group_tree_spec.rb
+++ b/spec/controllers/concerns/group_tree_spec.rb
@@ -8,7 +8,7 @@ describe GroupTree do
controller(ApplicationController) do
# `described_class` is not available in this context
- include GroupTree # rubocop:disable RSpec/DescribedClass
+ include GroupTree
def index
render_group_tree GroupsFinder.new(current_user).execute
diff --git a/spec/controllers/concerns/lfs_request_spec.rb b/spec/controllers/concerns/lfs_request_spec.rb
index 823b9a50434..584448e68f9 100644
--- a/spec/controllers/concerns/lfs_request_spec.rb
+++ b/spec/controllers/concerns/lfs_request_spec.rb
@@ -7,7 +7,7 @@ describe LfsRequest do
controller(Projects::GitHttpClientController) do
# `described_class` is not available in this context
- include LfsRequest # rubocop:disable RSpec/DescribedClass
+ include LfsRequest
def show
storage_project
diff --git a/spec/controllers/concerns/metrics_dashboard_spec.rb b/spec/controllers/concerns/metrics_dashboard_spec.rb
index ff2b6fbb8ec..389d264bed3 100644
--- a/spec/controllers/concerns/metrics_dashboard_spec.rb
+++ b/spec/controllers/concerns/metrics_dashboard_spec.rb
@@ -16,7 +16,7 @@ describe MetricsDashboard do
end
controller(::ApplicationController) do
- include MetricsDashboard # rubocop:disable RSpec/DescribedClass
+ include MetricsDashboard
end
let(:json_response) do
diff --git a/spec/controllers/concerns/renders_commits_spec.rb b/spec/controllers/concerns/renders_commits_spec.rb
index 79350847383..c43ceb6b795 100644
--- a/spec/controllers/concerns/renders_commits_spec.rb
+++ b/spec/controllers/concerns/renders_commits_spec.rb
@@ -9,7 +9,7 @@ describe RendersCommits do
controller(ApplicationController) do
# `described_class` is not available in this context
- include RendersCommits # rubocop:disable RSpec/DescribedClass
+ include RendersCommits
def index
@merge_request = MergeRequest.find(params[:id])
diff --git a/spec/controllers/concerns/routable_actions_spec.rb b/spec/controllers/concerns/routable_actions_spec.rb
index 59d48c68b9c..a11f4d2a154 100644
--- a/spec/controllers/concerns/routable_actions_spec.rb
+++ b/spec/controllers/concerns/routable_actions_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
describe RoutableActions do
controller(::ApplicationController) do
- include RoutableActions # rubocop:disable RSpec/DescribedClass
+ include RoutableActions
before_action :routable
diff --git a/spec/controllers/concerns/sourcegraph_gon_spec.rb b/spec/controllers/concerns/sourcegraph_gon_spec.rb
index 4fb7e37d148..d9273987871 100644
--- a/spec/controllers/concerns/sourcegraph_gon_spec.rb
+++ b/spec/controllers/concerns/sourcegraph_gon_spec.rb
@@ -17,7 +17,7 @@ describe SourcegraphGon do
let(:project) { internal_project }
controller(ApplicationController) do
- include SourcegraphGon # rubocop:disable RSpec/DescribedClass
+ include SourcegraphGon
def index
head :ok
diff --git a/spec/controllers/concerns/static_object_external_storage_spec.rb b/spec/controllers/concerns/static_object_external_storage_spec.rb
index 3a0219ddaa1..ddd1a95427e 100644
--- a/spec/controllers/concerns/static_object_external_storage_spec.rb
+++ b/spec/controllers/concerns/static_object_external_storage_spec.rb
@@ -4,7 +4,7 @@ require 'spec_helper'
describe StaticObjectExternalStorage do
controller(Projects::ApplicationController) do
- include StaticObjectExternalStorage # rubocop:disable RSpec/DescribedClass
+ include StaticObjectExternalStorage
before_action :redirect_to_external_storage, if: :static_objects_external_storage_enabled?
diff --git a/spec/controllers/projects/branches_controller_spec.rb b/spec/controllers/projects/branches_controller_spec.rb
index 757d8704a6a..ac39ac626c7 100644
--- a/spec/controllers/projects/branches_controller_spec.rb
+++ b/spec/controllers/projects/branches_controller_spec.rb
@@ -591,7 +591,7 @@ describe Projects::BranchesController do
params: {
namespace_id: project.namespace,
project_id: project,
- names: ['fix', 'add-pdf-file', 'branch-merged']
+ names: %w[fix add-pdf-file branch-merged]
}
expect(response).to have_gitlab_http_status(200)
@@ -639,7 +639,7 @@ describe Projects::BranchesController do
params: {
namespace_id: project.namespace,
project_id: project,
- names: ['fix', 'add-pdf-file', 'branch-merged']
+ names: %w[fix add-pdf-file branch-merged]
}
expect(response).to have_gitlab_http_status(200)
diff --git a/spec/controllers/projects/pages_controller_spec.rb b/spec/controllers/projects/pages_controller_spec.rb
index f80bbf0d78f..c07619465bf 100644
--- a/spec/controllers/projects/pages_controller_spec.rb
+++ b/spec/controllers/projects/pages_controller_spec.rb
@@ -115,5 +115,16 @@ describe Projects::PagesController do
patch :update, params: request_params
end
+
+ context 'when update_service returns an error message' do
+ let(:update_service) { double(execute: { status: :error, message: 'some error happened' }) }
+
+ it 'adds an error message' do
+ patch :update, params: request_params
+
+ expect(response).to redirect_to(project_pages_path(project))
+ expect(flash[:alert]).to eq('some error happened')
+ end
+ end
end
end
diff --git a/spec/features/projects/files/user_browses_files_spec.rb b/spec/features/projects/files/user_browses_files_spec.rb
index 10672bbec68..657513431e5 100644
--- a/spec/features/projects/files/user_browses_files_spec.rb
+++ b/spec/features/projects/files/user_browses_files_spec.rb
@@ -229,6 +229,16 @@ describe "User browses files" do
expect(page).to have_content("*.rb")
.and have_content("Dmitriy Zaporozhets")
.and have_content("Initial commit")
+ .and have_content("Ignore DS files")
+
+ previous_commit_anchor = "//a[@title='Ignore DS files']/parent::span/following-sibling::span/a"
+ find(:xpath, previous_commit_anchor).click
+
+ expect(page).to have_content("*.rb")
+ .and have_content("Dmitriy Zaporozhets")
+ .and have_content("Initial commit")
+
+ expect(page).not_to have_content("Ignore DS files")
end
end
diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb
index 3c4b5b2c4ca..afd1178f7f2 100644
--- a/spec/features/projects/pages_spec.rb
+++ b/spec/features/projects/pages_spec.rb
@@ -322,7 +322,7 @@ shared_examples 'pages settings editing' do
before do
allow(Projects::UpdateService).to receive(:new).and_return(service)
- allow(service).to receive(:execute).and_return(status: :error)
+ allow(service).to receive(:execute).and_return(status: :error, message: 'Some error has occured')
end
it 'tries to change the setting' do
@@ -332,7 +332,7 @@ shared_examples 'pages settings editing' do
click_button 'Save'
- expect(page).to have_text('Something went wrong on our end')
+ expect(page).to have_text('Some error has occured')
end
end
diff --git a/spec/features/projects/snippets/create_snippet_spec.rb b/spec/features/projects/snippets/create_snippet_spec.rb
index ad65e04473c..94af023e804 100644
--- a/spec/features/projects/snippets/create_snippet_spec.rb
+++ b/spec/features/projects/snippets/create_snippet_spec.rb
@@ -50,7 +50,7 @@ describe 'Projects > Snippets > Create Snippet', :js do
wait_for_requests
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
- expect(link).to match(%r{/#{Regexp.escape(project.full_path) }/uploads/\h{32}/banana_sample\.gif\z})
+ expect(link).to match(%r{/#{Regexp.escape(project.full_path)}/uploads/\h{32}/banana_sample\.gif\z})
end
it 'creates a snippet when all required fields are filled in after validation failing' do
@@ -72,7 +72,7 @@ describe 'Projects > Snippets > Create Snippet', :js do
expect(page).to have_selector('strong')
end
link = find('a.no-attachment-icon img[alt="banana_sample"]')['src']
- expect(link).to match(%r{/#{Regexp.escape(project.full_path) }/uploads/\h{32}/banana_sample\.gif\z})
+ expect(link).to match(%r{/#{Regexp.escape(project.full_path)}/uploads/\h{32}/banana_sample\.gif\z})
end
end
diff --git a/spec/finders/branches_finder_spec.rb b/spec/finders/branches_finder_spec.rb
index 70b5da0cc3c..5f75ff8c6ff 100644
--- a/spec/finders/branches_finder_spec.rb
+++ b/spec/finders/branches_finder_spec.rb
@@ -66,7 +66,7 @@ describe BranchesFinder do
end
it 'filters branches by provided names' do
- branches_finder = described_class.new(repository, { names: ['fix', 'csv', 'lfs', 'does-not-exist'] })
+ branches_finder = described_class.new(repository, { names: %w[fix csv lfs does-not-exist] })
result = branches_finder.execute
diff --git a/spec/finders/todos_finder_spec.rb b/spec/finders/todos_finder_spec.rb
index a837e7af251..a35c3a954e7 100644
--- a/spec/finders/todos_finder_spec.rb
+++ b/spec/finders/todos_finder_spec.rb
@@ -219,7 +219,7 @@ describe TodosFinder do
end
it "sorts by priority" do
- project_2 = create(:project)
+ project_2 = create(:project)
label_1 = create(:label, title: 'label_1', project: project, priority: 1)
label_2 = create(:label, title: 'label_2', project: project, priority: 2)
diff --git a/spec/frontend/monitoring/components/dashboard_spec.js b/spec/frontend/monitoring/components/dashboard_spec.js
new file mode 100644
index 00000000000..8a10857d0ff
--- /dev/null
+++ b/spec/frontend/monitoring/components/dashboard_spec.js
@@ -0,0 +1,573 @@
+import { shallowMount, createLocalVue, mount } from '@vue/test-utils';
+import { GlDropdownItem, GlButton, GlToast } from '@gitlab/ui';
+import VueDraggable from 'vuedraggable';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import statusCodes from '~/lib/utils/http_status';
+import { metricStates } from '~/monitoring/constants';
+import Dashboard from '~/monitoring/components/dashboard.vue';
+import DateTimePicker from '~/monitoring/components/date_time_picker/date_time_picker.vue';
+import GroupEmptyState from '~/monitoring/components/group_empty_state.vue';
+import { createStore } from '~/monitoring/stores';
+import * as types from '~/monitoring/stores/mutation_types';
+import * as monitoringUtils from '~/monitoring/utils';
+import { setupComponentStore, propsData } from '../init_utils';
+import {
+ metricsGroupsAPIResponse,
+ mockedQueryResultPayload,
+ mockApiEndpoint,
+ environmentData,
+ dashboardGitResponse,
+} from '../mock_data';
+
+const localVue = createLocalVue();
+const expectedPanelCount = 2;
+
+describe('Dashboard', () => {
+ let DashboardComponent;
+ let store;
+ let wrapper;
+ let mock;
+
+ const createShallowWrapper = (props = {}, options = {}) => {
+ wrapper = shallowMount(localVue.extend(DashboardComponent), {
+ localVue,
+ sync: false,
+ propsData: { ...propsData, ...props },
+ store,
+ ...options,
+ });
+ };
+
+ const createMountedWrapper = (props = {}, options = {}) => {
+ wrapper = mount(localVue.extend(DashboardComponent), {
+ localVue,
+ sync: false,
+ propsData: { ...propsData, ...props },
+ store,
+ ...options,
+ });
+ };
+
+ beforeEach(() => {
+ store = createStore();
+ DashboardComponent = localVue.extend(Dashboard);
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ }
+ mock.restore();
+ });
+
+ describe('no metrics are available yet', () => {
+ beforeEach(() => {
+ mock.onGet(mockApiEndpoint).reply(statusCodes.OK, metricsGroupsAPIResponse);
+
+ createShallowWrapper({}, { attachToDocument: true });
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('shows the environment selector', () => {
+ expect(wrapper.vm.$el.querySelector('.js-environments-dropdown')).toBeTruthy();
+ });
+ });
+
+ describe('no data found', () => {
+ beforeEach(done => {
+ mock.onGet(mockApiEndpoint).reply(statusCodes.OK, metricsGroupsAPIResponse);
+
+ createShallowWrapper({}, { attachToDocument: true });
+
+ wrapper.vm.$nextTick(done);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('shows the environment selector dropdown', () => {
+ expect(wrapper.vm.$el.querySelector('.js-environments-dropdown')).toBeTruthy();
+ });
+ });
+
+ describe('request information to the server', () => {
+ beforeEach(() => {
+ mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
+ });
+
+ it('shows up a loading state', done => {
+ createShallowWrapper({ hasMetrics: true }, { attachToDocument: true });
+
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ expect(wrapper.vm.emptyState).toEqual('loading');
+
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('hides the group panels when showPanels is false', done => {
+ createMountedWrapper(
+ { hasMetrics: true, showPanels: false },
+ { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
+ );
+
+ setupComponentStore(wrapper);
+
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ expect(wrapper.vm.showEmptyState).toEqual(false);
+ expect(wrapper.vm.$el.querySelector('.prometheus-panel')).toEqual(null);
+ // TODO: The last expectation doesn't belong here, it belongs in a `group_group_spec.js` file
+ // Issue: https://gitlab.com/gitlab-org/gitlab/issues/118780
+ // expect(wrapper.vm.$el.querySelector('.prometheus-graph-group')).toBeTruthy();
+
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('fetches the metrics data with proper time window', done => {
+ const getTimeDiffSpy = jest.spyOn(monitoringUtils, 'getTimeDiff');
+ jest.spyOn(store, 'dispatch');
+
+ createMountedWrapper(
+ { hasMetrics: true },
+ { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
+ );
+
+ wrapper.vm.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
+ environmentData,
+ );
+
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ expect(store.dispatch).toHaveBeenCalled();
+ expect(getTimeDiffSpy).toHaveBeenCalled();
+
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('when all requests have been commited by the store', () => {
+ beforeEach(() => {
+ mock.onGet(mockApiEndpoint).reply(statusCodes.OK, metricsGroupsAPIResponse);
+
+ createMountedWrapper(
+ { hasMetrics: true },
+ { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
+ );
+
+ setupComponentStore(wrapper);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders the environments dropdown with a number of environments', done => {
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ const environmentDropdownItems = wrapper
+ .find('.js-environments-dropdown')
+ .findAll(GlDropdownItem);
+
+ expect(wrapper.vm.environments.length).toEqual(environmentData.length);
+ expect(environmentDropdownItems.length).toEqual(wrapper.vm.environments.length);
+
+ environmentDropdownItems.wrappers.forEach((itemWrapper, index) => {
+ const anchorEl = itemWrapper.find('a');
+ if (anchorEl.exists() && environmentData[index].metrics_path) {
+ const href = anchorEl.attributes('href');
+ expect(href).toBe(environmentData[index].metrics_path);
+ }
+ });
+
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('renders the environments dropdown with a single active element', done => {
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ const environmentDropdownItems = wrapper
+ .find('.js-environments-dropdown')
+ .findAll(GlDropdownItem);
+ const activeItem = environmentDropdownItems.wrappers.filter(itemWrapper =>
+ itemWrapper.find('.active').exists(),
+ );
+
+ expect(activeItem.length).toBe(1);
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ it('hides the environments dropdown list when there is no environments', done => {
+ createMountedWrapper(
+ { hasMetrics: true },
+ { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
+ );
+
+ wrapper.vm.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
+ metricsGroupsAPIResponse,
+ );
+ wrapper.vm.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
+ mockedQueryResultPayload,
+ );
+
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ const environmentDropdownItems = wrapper
+ .find('.js-environments-dropdown')
+ .findAll(GlDropdownItem);
+
+ expect(environmentDropdownItems.length).toEqual(0);
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ it('renders the datetimepicker dropdown', done => {
+ createMountedWrapper(
+ { hasMetrics: true },
+ { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
+ );
+
+ setupComponentStore(wrapper);
+
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ expect(wrapper.find(DateTimePicker).exists()).toBe(true);
+ done();
+ })
+ .catch(done.fail);
+ });
+
+ describe('when one of the metrics is missing', () => {
+ beforeEach(done => {
+ mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
+
+ createShallowWrapper({ hasMetrics: true }, { attachToDocument: true });
+ setupComponentStore(wrapper);
+
+ wrapper.vm.$nextTick(done);
+ });
+
+ it('shows a group empty area', () => {
+ const emptyGroup = wrapper.findAll({ ref: 'empty-group' });
+
+ expect(emptyGroup).toHaveLength(1);
+ expect(emptyGroup.is(GroupEmptyState)).toBe(true);
+ });
+
+ it('group empty area displays a NO_DATA state', () => {
+ expect(
+ wrapper
+ .findAll({ ref: 'empty-group' })
+ .at(0)
+ .props('selectedState'),
+ ).toEqual(metricStates.NO_DATA);
+ });
+ });
+
+ describe('drag and drop function', () => {
+ const findDraggables = () => wrapper.findAll(VueDraggable);
+ const findEnabledDraggables = () => findDraggables().filter(f => !f.attributes('disabled'));
+ const findDraggablePanels = () => wrapper.findAll('.js-draggable-panel');
+ const findRearrangeButton = () => wrapper.find('.js-rearrange-button');
+
+ beforeEach(() => {
+ mock.onGet(mockApiEndpoint).reply(statusCodes.OK, metricsGroupsAPIResponse);
+ });
+
+ beforeEach(done => {
+ createShallowWrapper({ hasMetrics: true }, { attachToDocument: true });
+
+ setupComponentStore(wrapper);
+
+ wrapper.vm.$nextTick(done);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('wraps vuedraggable', () => {
+ expect(findDraggablePanels().exists()).toBe(true);
+ expect(findDraggablePanels().length).toEqual(expectedPanelCount);
+ });
+
+ it('is disabled by default', () => {
+ expect(findRearrangeButton().exists()).toBe(false);
+ expect(findEnabledDraggables().length).toBe(0);
+ });
+
+ describe('when rearrange is enabled', () => {
+ beforeEach(done => {
+ wrapper.setProps({ rearrangePanelsAvailable: true });
+ wrapper.vm.$nextTick(done);
+ });
+
+ it('displays rearrange button', () => {
+ expect(findRearrangeButton().exists()).toBe(true);
+ });
+
+ describe('when rearrange button is clicked', () => {
+ const findFirstDraggableRemoveButton = () =>
+ findDraggablePanels()
+ .at(0)
+ .find('.js-draggable-remove');
+
+ beforeEach(done => {
+ findRearrangeButton().vm.$emit('click');
+ wrapper.vm.$nextTick(done);
+ });
+
+ it('it enables draggables', () => {
+ expect(findRearrangeButton().attributes('pressed')).toBeTruthy();
+ expect(findEnabledDraggables()).toEqual(findDraggables());
+ });
+
+ it('metrics can be swapped', done => {
+ const firstDraggable = findDraggables().at(0);
+ const mockMetrics = [...metricsGroupsAPIResponse[1].panels];
+
+ const firstTitle = mockMetrics[0].title;
+ const secondTitle = mockMetrics[1].title;
+
+ // swap two elements and `input` them
+ [mockMetrics[0], mockMetrics[1]] = [mockMetrics[1], mockMetrics[0]];
+ firstDraggable.vm.$emit('input', mockMetrics);
+
+ wrapper.vm.$nextTick(() => {
+ const { panels } = wrapper.vm.dashboard.panel_groups[1];
+
+ expect(panels[1].title).toEqual(firstTitle);
+ expect(panels[0].title).toEqual(secondTitle);
+ done();
+ });
+ });
+
+ it('shows a remove button, which removes a panel', done => {
+ expect(findFirstDraggableRemoveButton().isEmpty()).toBe(false);
+
+ expect(findDraggablePanels().length).toEqual(expectedPanelCount);
+ findFirstDraggableRemoveButton().trigger('click');
+
+ wrapper.vm.$nextTick(() => {
+ expect(findDraggablePanels().length).toEqual(expectedPanelCount - 1);
+ done();
+ });
+ });
+
+ it('it disables draggables when clicked again', done => {
+ findRearrangeButton().vm.$emit('click');
+ wrapper.vm.$nextTick(() => {
+ expect(findRearrangeButton().attributes('pressed')).toBeFalsy();
+ expect(findEnabledDraggables().length).toBe(0);
+ done();
+ });
+ });
+ });
+ });
+ });
+
+ describe('cluster health', () => {
+ beforeEach(done => {
+ mock.onGet(propsData.metricsEndpoint).reply(statusCodes.OK, JSON.stringify({}));
+ createShallowWrapper({ hasMetrics: true });
+
+ // all_dashboards is not defined in health dashboards
+ wrapper.vm.$store.commit(`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, undefined);
+ wrapper.vm.$nextTick(done);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('renders correctly', () => {
+ expect(wrapper.isVueInstance()).toBe(true);
+ expect(wrapper.exists()).toBe(true);
+ });
+ });
+
+ describe('dashboard edit link', () => {
+ const findEditLink = () => wrapper.find('.js-edit-link');
+
+ beforeEach(done => {
+ mock.onGet(mockApiEndpoint).reply(statusCodes.OK, metricsGroupsAPIResponse);
+
+ createShallowWrapper({ hasMetrics: true }, { attachToDocument: true });
+
+ wrapper.vm.$store.commit(
+ `monitoringDashboard/${types.SET_ALL_DASHBOARDS}`,
+ dashboardGitResponse,
+ );
+ wrapper.vm.$nextTick(done);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('is not present for the default dashboard', () => {
+ expect(findEditLink().exists()).toBe(false);
+ });
+
+ it('is present for a custom dashboard, and links to its edit_path', done => {
+ const dashboard = dashboardGitResponse[1]; // non-default dashboard
+ const currentDashboard = dashboard.path;
+
+ wrapper.setProps({ currentDashboard });
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ expect(findEditLink().exists()).toBe(true);
+ expect(findEditLink().attributes('href')).toBe(dashboard.project_blob_path);
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('Dashboard dropdown', () => {
+ beforeEach(() => {
+ mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
+
+ createMountedWrapper(
+ { hasMetrics: true },
+ { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
+ );
+
+ wrapper.vm.$store.commit(
+ `monitoringDashboard/${types.SET_ALL_DASHBOARDS}`,
+ dashboardGitResponse,
+ );
+ });
+
+ it('shows the dashboard dropdown', done => {
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ const dashboardDropdown = wrapper.find('.js-dashboards-dropdown');
+
+ expect(dashboardDropdown.exists()).toBe(true);
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ describe('external dashboard link', () => {
+ beforeEach(() => {
+ mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
+
+ createMountedWrapper(
+ {
+ hasMetrics: true,
+ showPanels: false,
+ showTimeWindowDropdown: false,
+ externalDashboardUrl: '/mockUrl',
+ },
+ { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
+ );
+ });
+
+ it('shows the link', done => {
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ const externalDashboardButton = wrapper.find('.js-external-dashboard-link');
+
+ expect(externalDashboardButton.exists()).toBe(true);
+ expect(externalDashboardButton.is(GlButton)).toBe(true);
+ expect(externalDashboardButton.text()).toContain('View full dashboard');
+ done();
+ })
+ .catch(done.fail);
+ });
+ });
+
+ // https://gitlab.com/gitlab-org/gitlab-ce/issues/66922
+ // eslint-disable-next-line jest/no-disabled-tests
+ describe.skip('link to chart', () => {
+ const currentDashboard = 'TEST_DASHBOARD';
+ localVue.use(GlToast);
+ const link = () => wrapper.find('.js-chart-link');
+ const clipboardText = () => link().element.dataset.clipboardText;
+
+ beforeEach(done => {
+ mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
+
+ createShallowWrapper({ hasMetrics: true, currentDashboard }, { attachToDocument: true });
+
+ setTimeout(done);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('adds a copy button to the dropdown', () => {
+ expect(link().text()).toContain('Generate link to chart');
+ });
+
+ it('contains a link to the dashboard', () => {
+ expect(clipboardText()).toContain(`dashboard=${currentDashboard}`);
+ expect(clipboardText()).toContain(`group=`);
+ expect(clipboardText()).toContain(`title=`);
+ expect(clipboardText()).toContain(`y_label=`);
+ });
+
+ it('undefined parameter is stripped', done => {
+ wrapper.setProps({ currentDashboard: undefined });
+
+ wrapper.vm.$nextTick(() => {
+ expect(clipboardText()).not.toContain(`dashboard=`);
+ expect(clipboardText()).toContain(`y_label=`);
+ done();
+ });
+ });
+
+ it('null parameter is stripped', done => {
+ wrapper.setProps({ currentDashboard: null });
+
+ wrapper.vm.$nextTick(() => {
+ expect(clipboardText()).not.toContain(`dashboard=`);
+ expect(clipboardText()).toContain(`y_label=`);
+ done();
+ });
+ });
+
+ it('creates a toast when clicked', () => {
+ jest.spyOn(wrapper.vm.$toast, 'show').and.stub();
+
+ link().vm.$emit('click');
+
+ expect(wrapper.vm.$toast.show).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/spec/frontend/monitoring/components/dashboard_time_url_spec.js b/spec/frontend/monitoring/components/dashboard_time_url_spec.js
new file mode 100644
index 00000000000..8dc450cf131
--- /dev/null
+++ b/spec/frontend/monitoring/components/dashboard_time_url_spec.js
@@ -0,0 +1,54 @@
+import { mount, createLocalVue } from '@vue/test-utils';
+import createFlash from '~/flash';
+import Dashboard from '~/monitoring/components/dashboard.vue';
+import { createStore } from '~/monitoring/stores';
+import { propsData } from '../init_utils';
+
+const localVue = createLocalVue();
+
+jest.mock('~/flash');
+
+jest.mock('~/lib/utils/url_utility', () => ({
+ getParameterValues: jest.fn().mockReturnValue('<script>alert("XSS")</script>'),
+}));
+
+describe('dashboard invalid url parameters', () => {
+ let store;
+ let wrapper;
+
+ const createMountedWrapper = (props = {}, options = {}) => {
+ wrapper = mount(localVue.extend(Dashboard), {
+ localVue,
+ sync: false,
+ propsData: { ...propsData, ...props },
+ store,
+ ...options,
+ });
+ };
+
+ beforeEach(() => {
+ store = createStore();
+ });
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ }
+ });
+
+ it('shows an error message if invalid url parameters are passed', done => {
+ createMountedWrapper(
+ { hasMetrics: true },
+ { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
+ );
+
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ expect(createFlash).toHaveBeenCalled();
+
+ done();
+ })
+ .catch(done.fail);
+ });
+});
diff --git a/spec/frontend/monitoring/components/dashboard_time_window_spec.js b/spec/frontend/monitoring/components/dashboard_time_window_spec.js
new file mode 100644
index 00000000000..d49af6f84cb
--- /dev/null
+++ b/spec/frontend/monitoring/components/dashboard_time_window_spec.js
@@ -0,0 +1,75 @@
+import { mount, createLocalVue } from '@vue/test-utils';
+import { GlDropdownItem } from '@gitlab/ui';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import statusCodes from '~/lib/utils/http_status';
+import Dashboard from '~/monitoring/components/dashboard.vue';
+import { createStore } from '~/monitoring/stores';
+import { propsData, setupComponentStore } from '../init_utils';
+import { metricsGroupsAPIResponse, mockApiEndpoint } from '../mock_data';
+
+const localVue = createLocalVue();
+
+jest.mock('~/lib/utils/url_utility', () => ({
+ getParameterValues: jest.fn().mockImplementation(param => {
+ if (param === 'start') return ['2019-10-01T18:27:47.000Z'];
+ if (param === 'end') return ['2019-10-01T18:57:47.000Z'];
+ return [];
+ }),
+ mergeUrlParams: jest.fn().mockReturnValue('#'),
+}));
+
+describe('dashboard time window', () => {
+ let store;
+ let wrapper;
+ let mock;
+
+ const createComponentWrapperMounted = (props = {}, options = {}) => {
+ wrapper = mount(localVue.extend(Dashboard), {
+ localVue,
+ sync: false,
+ propsData: { ...propsData, ...props },
+ store,
+ ...options,
+ });
+ };
+
+ beforeEach(() => {
+ store = createStore();
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ if (wrapper) {
+ wrapper.destroy();
+ }
+ mock.restore();
+ });
+
+ it('shows an error message if invalid url parameters are passed', done => {
+ mock.onGet(mockApiEndpoint).reply(statusCodes.OK, metricsGroupsAPIResponse);
+
+ createComponentWrapperMounted(
+ { hasMetrics: true },
+ { attachToDocument: true, stubs: ['graph-group', 'panel-type'] },
+ );
+
+ setupComponentStore(wrapper);
+
+ wrapper.vm
+ .$nextTick()
+ .then(() => {
+ const timeWindowDropdownItems = wrapper
+ .find('.js-time-window-dropdown')
+ .findAll(GlDropdownItem);
+ const activeItem = timeWindowDropdownItems.wrappers.filter(itemWrapper =>
+ itemWrapper.find('.active').exists(),
+ );
+
+ expect(activeItem.length).toBe(1);
+
+ done();
+ })
+ .catch(done.fail);
+ });
+});
diff --git a/spec/javascripts/monitoring/components/graph_group_spec.js b/spec/frontend/monitoring/components/graph_group_spec.js
index 43ca17c3cbc..43ca17c3cbc 100644
--- a/spec/javascripts/monitoring/components/graph_group_spec.js
+++ b/spec/frontend/monitoring/components/graph_group_spec.js
diff --git a/spec/frontend/monitoring/init_utils.js b/spec/frontend/monitoring/init_utils.js
new file mode 100644
index 00000000000..10db8b902b5
--- /dev/null
+++ b/spec/frontend/monitoring/init_utils.js
@@ -0,0 +1,56 @@
+import * as types from '~/monitoring/stores/mutation_types';
+import {
+ metricsGroupsAPIResponse,
+ mockedEmptyResult,
+ mockedQueryResultPayload,
+ mockedQueryResultPayloadCoresTotal,
+ mockApiEndpoint,
+ environmentData,
+} from './mock_data';
+
+export const propsData = {
+ hasMetrics: false,
+ documentationPath: '/path/to/docs',
+ settingsPath: '/path/to/settings',
+ clustersPath: '/path/to/clusters',
+ tagsPath: '/path/to/tags',
+ projectPath: '/path/to/project',
+ metricsEndpoint: mockApiEndpoint,
+ deploymentsEndpoint: null,
+ emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
+ emptyLoadingSvgPath: '/path/to/loading.svg',
+ emptyNoDataSvgPath: '/path/to/no-data.svg',
+ emptyNoDataSmallSvgPath: '/path/to/no-data-small.svg',
+ emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
+ environmentsEndpoint: '/root/hello-prometheus/environments/35',
+ currentEnvironmentName: 'production',
+ customMetricsAvailable: false,
+ customMetricsPath: '',
+ validateQueryPath: '',
+};
+
+export const setupComponentStore = wrapper => {
+ wrapper.vm.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
+ metricsGroupsAPIResponse,
+ );
+
+ // Load 3 panels to the dashboard, one with an empty result
+ wrapper.vm.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
+ mockedEmptyResult,
+ );
+ wrapper.vm.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
+ mockedQueryResultPayload,
+ );
+ wrapper.vm.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
+ mockedQueryResultPayloadCoresTotal,
+ );
+
+ wrapper.vm.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
+ environmentData,
+ );
+};
diff --git a/spec/javascripts/monitoring/shared/prometheus_header_spec.js b/spec/frontend/monitoring/shared/prometheus_header_spec.js
index 9f916a4dfbb..b216bfb72d8 100644
--- a/spec/javascripts/monitoring/shared/prometheus_header_spec.js
+++ b/spec/frontend/monitoring/shared/prometheus_header_spec.js
@@ -18,7 +18,7 @@ describe('Prometheus Header component', () => {
describe('Prometheus header component', () => {
it('should show a title', () => {
- const title = prometheusHeader.vm.$el.querySelector('.js-graph-title').textContent;
+ const title = prometheusHeader.find({ ref: 'title' }).text();
expect(title).toBe('graph header');
});
diff --git a/spec/helpers/container_expiration_policies_helper_spec.rb b/spec/helpers/container_expiration_policies_helper_spec.rb
index 3eb1234d82b..f7e851fb012 100644
--- a/spec/helpers/container_expiration_policies_helper_spec.rb
+++ b/spec/helpers/container_expiration_policies_helper_spec.rb
@@ -8,7 +8,7 @@ describe ContainerExpirationPoliciesHelper do
expected_result = [
{ key: 1, label: '1 tag per image name' },
{ key: 5, label: '5 tags per image name' },
- { key: 10, label: '10 tags per image name' },
+ { key: 10, label: '10 tags per image name', default: true },
{ key: 25, label: '25 tags per image name' },
{ key: 50, label: '50 tags per image name' },
{ key: 100, label: '100 tags per image name' }
@@ -21,7 +21,7 @@ describe ContainerExpirationPoliciesHelper do
describe '#cadence_options' do
it 'returns cadence options formatted for dropdown usage' do
expected_result = [
- { key: '1d', label: 'Every day' },
+ { key: '1d', label: 'Every day', default: true },
{ key: '7d', label: 'Every week' },
{ key: '14d', label: 'Every two weeks' },
{ key: '1month', label: 'Every month' },
@@ -37,7 +37,7 @@ describe ContainerExpirationPoliciesHelper do
expected_result = [
{ key: '7d', label: '7 days until tags are automatically removed' },
{ key: '14d', label: '14 days until tags are automatically removed' },
- { key: '30d', label: '30 days until tags are automatically removed' },
+ { key: '30d', label: '30 days until tags are automatically removed', default: true },
{ key: '90d', label: '90 days until tags are automatically removed' }
]
diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb
index 46228d0d1c2..c7e454771bb 100644
--- a/spec/helpers/projects_helper_spec.rb
+++ b/spec/helpers/projects_helper_spec.rb
@@ -332,13 +332,13 @@ describe ProjectsHelper do
end
it 'returns image tag for member avatar' do
- expect(helper).to receive(:image_tag).with(expected, { width: 16, class: ["avatar", "avatar-inline", "s16"], alt: "", "data-src" => anything })
+ expect(helper).to receive(:image_tag).with(expected, { width: 16, class: %w[avatar avatar-inline s16], alt: "", "data-src" => anything })
helper.link_to_member_avatar(user)
end
it 'returns image tag with avatar class' do
- expect(helper).to receive(:image_tag).with(expected, { width: 16, class: ["avatar", "avatar-inline", "s16", "any-avatar-class"], alt: "", "data-src" => anything })
+ expect(helper).to receive(:image_tag).with(expected, { width: 16, class: %w[avatar avatar-inline s16 any-avatar-class], alt: "", "data-src" => anything })
helper.link_to_member_avatar(user, avatar_class: "any-avatar-class")
end
diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js
index e471be608c8..1d91b508b71 100644
--- a/spec/javascripts/lib/utils/common_utils_spec.js
+++ b/spec/javascripts/lib/utils/common_utils_spec.js
@@ -88,10 +88,12 @@ describe('common_utils', () => {
describe('handleLocationHash', () => {
beforeEach(() => {
spyOn(window.document, 'getElementById').and.callThrough();
+ jasmine.clock().install();
});
afterEach(() => {
window.history.pushState({}, null, '');
+ jasmine.clock().uninstall();
});
function expectGetElementIdToHaveBeenCalledWith(elementId) {
@@ -171,6 +173,7 @@ describe('common_utils', () => {
window.history.pushState({}, null, '#test');
commonUtils.handleLocationHash();
+ jasmine.clock().tick(1);
expectGetElementIdToHaveBeenCalledWith('test');
expectGetElementIdToHaveBeenCalledWith('user-content-test');
diff --git a/spec/javascripts/monitoring/components/dashboard_resize_spec.js b/spec/javascripts/monitoring/components/dashboard_resize_spec.js
new file mode 100644
index 00000000000..4eab398e3ab
--- /dev/null
+++ b/spec/javascripts/monitoring/components/dashboard_resize_spec.js
@@ -0,0 +1,140 @@
+import Vue from 'vue';
+import { createLocalVue } from '@vue/test-utils';
+import MockAdapter from 'axios-mock-adapter';
+import Dashboard from '~/monitoring/components/dashboard.vue';
+import * as types from '~/monitoring/stores/mutation_types';
+import { createStore } from '~/monitoring/stores';
+import axios from '~/lib/utils/axios_utils';
+import {
+ metricsGroupsAPIResponse,
+ mockedEmptyResult,
+ mockedQueryResultPayload,
+ mockedQueryResultPayloadCoresTotal,
+ mockApiEndpoint,
+ environmentData,
+} from '../mock_data';
+
+const localVue = createLocalVue();
+const propsData = {
+ hasMetrics: false,
+ documentationPath: '/path/to/docs',
+ settingsPath: '/path/to/settings',
+ clustersPath: '/path/to/clusters',
+ tagsPath: '/path/to/tags',
+ projectPath: '/path/to/project',
+ metricsEndpoint: mockApiEndpoint,
+ deploymentsEndpoint: null,
+ emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
+ emptyLoadingSvgPath: '/path/to/loading.svg',
+ emptyNoDataSvgPath: '/path/to/no-data.svg',
+ emptyNoDataSmallSvgPath: '/path/to/no-data-small.svg',
+ emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
+ environmentsEndpoint: '/root/hello-prometheus/environments/35',
+ currentEnvironmentName: 'production',
+ customMetricsAvailable: false,
+ customMetricsPath: '',
+ validateQueryPath: '',
+};
+
+function setupComponentStore(component) {
+ // Load 2 panel groups
+ component.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
+ metricsGroupsAPIResponse,
+ );
+
+ // Load 3 panels to the dashboard, one with an empty result
+ component.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
+ mockedEmptyResult,
+ );
+ component.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
+ mockedQueryResultPayload,
+ );
+ component.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
+ mockedQueryResultPayloadCoresTotal,
+ );
+
+ component.$store.commit(
+ `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
+ environmentData,
+ );
+}
+
+describe('Dashboard', () => {
+ let DashboardComponent;
+ let mock;
+ let store;
+ let component;
+ let wrapper;
+
+ beforeEach(() => {
+ setFixtures(`
+ <div class="prometheus-graphs"></div>
+ <div class="layout-page"></div>
+ `);
+
+ store = createStore();
+ mock = new MockAdapter(axios);
+ DashboardComponent = localVue.extend(Dashboard);
+ });
+
+ afterEach(() => {
+ if (component) {
+ component.$destroy();
+ }
+ if (wrapper) {
+ wrapper.destroy();
+ }
+ mock.restore();
+ });
+
+ describe('responds to window resizes', () => {
+ let promPanel;
+ let promGroup;
+ let panelToggle;
+ let chart;
+ beforeEach(() => {
+ mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
+
+ component = new DashboardComponent({
+ el: document.querySelector('.prometheus-graphs'),
+ propsData: {
+ ...propsData,
+ hasMetrics: true,
+ showPanels: true,
+ },
+ store,
+ });
+
+ setupComponentStore(component);
+
+ return Vue.nextTick().then(() => {
+ [, promPanel] = component.$el.querySelectorAll('.prometheus-panel');
+ promGroup = promPanel.querySelector('.prometheus-graph-group');
+ panelToggle = promPanel.querySelector('.js-graph-group-toggle');
+ chart = promGroup.querySelector('.position-relative svg');
+ });
+ });
+
+ it('setting chart size to zero when panel group is hidden', () => {
+ expect(promGroup.style.display).toBe('');
+ expect(chart.clientWidth).toBeGreaterThan(0);
+
+ panelToggle.click();
+ return Vue.nextTick().then(() => {
+ expect(promGroup.style.display).toBe('none');
+ expect(chart.clientWidth).toBe(0);
+ promPanel.style.width = '500px';
+ });
+ });
+
+ it('expanding chart panel group after resize displays chart', () => {
+ panelToggle.click();
+
+ expect(chart.clientWidth).toBeGreaterThan(0);
+ });
+ });
+});
diff --git a/spec/javascripts/monitoring/components/dashboard_spec.js b/spec/javascripts/monitoring/components/dashboard_spec.js
deleted file mode 100644
index b29bac21820..00000000000
--- a/spec/javascripts/monitoring/components/dashboard_spec.js
+++ /dev/null
@@ -1,729 +0,0 @@
-import Vue from 'vue';
-import { shallowMount, createLocalVue } from '@vue/test-utils';
-import { GlToast } from '@gitlab/ui';
-import VueDraggable from 'vuedraggable';
-import MockAdapter from 'axios-mock-adapter';
-import Dashboard from '~/monitoring/components/dashboard.vue';
-import { metricStates } from '~/monitoring/constants';
-import GroupEmptyState from '~/monitoring/components/group_empty_state.vue';
-import * as types from '~/monitoring/stores/mutation_types';
-import { createStore } from '~/monitoring/stores';
-import axios from '~/lib/utils/axios_utils';
-import {
- metricsGroupsAPIResponse,
- mockedEmptyResult,
- mockedQueryResultPayload,
- mockedQueryResultPayloadCoresTotal,
- mockApiEndpoint,
- environmentData,
- dashboardGitResponse,
-} from '../mock_data';
-
-const localVue = createLocalVue();
-const propsData = {
- hasMetrics: false,
- documentationPath: '/path/to/docs',
- settingsPath: '/path/to/settings',
- clustersPath: '/path/to/clusters',
- tagsPath: '/path/to/tags',
- projectPath: '/path/to/project',
- metricsEndpoint: mockApiEndpoint,
- deploymentsEndpoint: null,
- emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
- emptyLoadingSvgPath: '/path/to/loading.svg',
- emptyNoDataSvgPath: '/path/to/no-data.svg',
- emptyNoDataSmallSvgPath: '/path/to/no-data-small.svg',
- emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
- environmentsEndpoint: '/root/hello-prometheus/environments/35',
- currentEnvironmentName: 'production',
- customMetricsAvailable: false,
- customMetricsPath: '',
- validateQueryPath: '',
-};
-
-const resetSpy = spy => {
- if (spy) {
- spy.calls.reset();
- }
-};
-
-let expectedPanelCount;
-
-function setupComponentStore(component) {
- // Load 2 panel groups
- component.$store.commit(
- `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
- metricsGroupsAPIResponse,
- );
-
- // Load 3 panels to the dashboard, one with an empty result
- component.$store.commit(
- `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
- mockedEmptyResult,
- );
- component.$store.commit(
- `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
- mockedQueryResultPayload,
- );
- component.$store.commit(
- `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
- mockedQueryResultPayloadCoresTotal,
- );
-
- expectedPanelCount = 2;
-
- component.$store.commit(
- `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
- environmentData,
- );
-}
-
-describe('Dashboard', () => {
- let DashboardComponent;
- let mock;
- let store;
- let component;
- let wrapper;
-
- const createComponentWrapper = (props = {}, options = {}) => {
- wrapper = shallowMount(localVue.extend(DashboardComponent), {
- localVue,
- sync: false,
- propsData: { ...propsData, ...props },
- store,
- ...options,
- });
- };
-
- beforeEach(() => {
- setFixtures(`
- <div class="prometheus-graphs"></div>
- <div class="layout-page"></div>
- `);
-
- store = createStore();
- mock = new MockAdapter(axios);
- DashboardComponent = localVue.extend(Dashboard);
- });
-
- afterEach(() => {
- if (component) {
- component.$destroy();
- }
- if (wrapper) {
- wrapper.destroy();
- }
- mock.restore();
- });
-
- describe('no metrics are available yet', () => {
- beforeEach(() => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: { ...propsData },
- store,
- });
- });
-
- it('shows a getting started empty state when no metrics are present', () => {
- expect(component.$el.querySelector('.prometheus-graphs')).toBe(null);
- expect(component.emptyState).toEqual('gettingStarted');
- });
-
- it('shows the environment selector', () => {
- expect(component.$el.querySelector('.js-environments-dropdown')).toBeTruthy();
- });
- });
-
- describe('no data found', () => {
- it('shows the environment selector dropdown', () => {
- createComponentWrapper();
-
- expect(wrapper.find('.js-environments-dropdown').exists()).toBeTruthy();
- });
- });
-
- describe('cluster health', () => {
- beforeEach(done => {
- createComponentWrapper({ hasMetrics: true });
-
- // all_dashboards is not defined in health dashboards
- wrapper.vm.$store.commit(`monitoringDashboard/${types.SET_ALL_DASHBOARDS}`, undefined);
- wrapper.vm.$nextTick(done);
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders correctly', () => {
- expect(wrapper.isVueInstance()).toBe(true);
- expect(wrapper.exists()).toBe(true);
- });
- });
-
- describe('requests information to the server', () => {
- let spy;
- beforeEach(() => {
- mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
- });
-
- afterEach(() => {
- resetSpy(spy);
- });
-
- it('shows up a loading state', done => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: { ...propsData, hasMetrics: true },
- store,
- });
-
- Vue.nextTick(() => {
- expect(component.emptyState).toEqual('loading');
- done();
- });
- });
-
- it('hides the group panels when showPanels is false', done => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: {
- ...propsData,
- hasMetrics: true,
- showPanels: false,
- },
- store,
- });
-
- setupComponentStore(component);
-
- Vue.nextTick()
- .then(() => {
- expect(component.showEmptyState).toEqual(false);
- expect(component.$el.querySelector('.prometheus-panel')).toEqual(null);
- expect(component.$el.querySelector('.prometheus-graph-group')).toBeTruthy();
-
- done();
- })
- .catch(done.fail);
- });
-
- describe('when all the requests have been commited by the store', () => {
- beforeEach(() => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: {
- ...propsData,
- hasMetrics: true,
- },
- store,
- });
-
- setupComponentStore(component);
- });
-
- it('renders the environments dropdown with a number of environments', done => {
- Vue.nextTick()
- .then(() => {
- const dropdownMenuEnvironments = component.$el.querySelectorAll(
- '.js-environments-dropdown .dropdown-item',
- );
-
- expect(component.environments.length).toEqual(environmentData.length);
- expect(dropdownMenuEnvironments.length).toEqual(component.environments.length);
-
- Array.from(dropdownMenuEnvironments).forEach((value, index) => {
- if (environmentData[index].metrics_path) {
- expect(value).toHaveAttr('href', environmentData[index].metrics_path);
- }
- });
-
- done();
- })
- .catch(done.fail);
- });
-
- it('renders the environments dropdown with a single active element', done => {
- Vue.nextTick()
- .then(() => {
- const dropdownItems = component.$el.querySelectorAll(
- '.js-environments-dropdown .dropdown-item.active',
- );
-
- expect(dropdownItems.length).toEqual(1);
- done();
- })
- .catch(done.fail);
- });
- });
-
- it('hides the environments dropdown list when there is no environments', done => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: {
- ...propsData,
- hasMetrics: true,
- },
- store,
- });
-
- component.$store.commit(
- `monitoringDashboard/${types.RECEIVE_METRICS_DATA_SUCCESS}`,
- metricsGroupsAPIResponse,
- );
- component.$store.commit(
- `monitoringDashboard/${types.RECEIVE_METRIC_RESULT_SUCCESS}`,
- mockedQueryResultPayload,
- );
-
- Vue.nextTick()
- .then(() => {
- const dropdownMenuEnvironments = component.$el.querySelectorAll(
- '.js-environments-dropdown .dropdown-item',
- );
-
- expect(dropdownMenuEnvironments.length).toEqual(0);
- done();
- })
- .catch(done.fail);
- });
-
- it('renders the datetimepicker dropdown', done => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: {
- ...propsData,
- hasMetrics: true,
- showPanels: false,
- },
- store,
- });
-
- setupComponentStore(component);
-
- Vue.nextTick()
- .then(() => {
- expect(component.$el.querySelector('.js-time-window-dropdown')).not.toBeNull();
- done();
- })
- .catch(done.fail);
- });
-
- it('fetches the metrics data with proper time window', done => {
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: {
- ...propsData,
- hasMetrics: true,
- showPanels: false,
- },
- store,
- });
-
- spyOn(component.$store, 'dispatch').and.stub();
- const getTimeDiffSpy = spyOnDependency(Dashboard, 'getTimeDiff').and.callThrough();
-
- component.$store.commit(
- `monitoringDashboard/${types.RECEIVE_ENVIRONMENTS_DATA_SUCCESS}`,
- environmentData,
- );
-
- component.$mount();
-
- Vue.nextTick()
- .then(() => {
- expect(component.$store.dispatch).toHaveBeenCalled();
- expect(getTimeDiffSpy).toHaveBeenCalled();
-
- done();
- })
- .catch(done.fail);
- });
-
- it('shows a specific time window selected from the url params', done => {
- const start = '2019-10-01T18:27:47.000Z';
- const end = '2019-10-01T18:57:47.000Z';
- spyOnDependency(Dashboard, 'getTimeDiff').and.returnValue({
- start,
- end,
- });
- spyOnDependency(Dashboard, 'getParameterValues').and.callFake(param => {
- if (param === 'start') return [start];
- if (param === 'end') return [end];
- return [];
- });
-
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: { ...propsData, hasMetrics: true },
- store,
- sync: false,
- });
-
- setupComponentStore(component);
-
- Vue.nextTick()
- .then(() => {
- const selectedTimeWindow = component.$el.querySelector(
- '.js-time-window-dropdown .active',
- );
-
- expect(selectedTimeWindow.textContent.trim()).toEqual('30 minutes');
- done();
- })
- .catch(done.fail);
- });
-
- it('shows an error message if invalid url parameters are passed', done => {
- spyOnDependency(Dashboard, 'getParameterValues').and.returnValue([
- '<script>alert("XSS")</script>',
- ]);
-
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: { ...propsData, hasMetrics: true },
- store,
- });
-
- spy = spyOn(component, 'showInvalidDateError');
- component.$mount();
-
- component.$nextTick(() => {
- expect(component.showInvalidDateError).toHaveBeenCalled();
- done();
- });
- });
- });
-
- describe('when one of the metrics is missing', () => {
- beforeEach(() => {
- mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
- });
-
- beforeEach(done => {
- createComponentWrapper({ hasMetrics: true });
- setupComponentStore(wrapper.vm);
-
- wrapper.vm.$nextTick(done);
- });
-
- it('shows a group empty area', () => {
- const emptyGroup = wrapper.findAll({ ref: 'empty-group' });
-
- expect(emptyGroup).toHaveLength(1);
- expect(emptyGroup.is(GroupEmptyState)).toBe(true);
- });
-
- it('group empty area displays a NO_DATA state', () => {
- expect(
- wrapper
- .findAll({ ref: 'empty-group' })
- .at(0)
- .props('selectedState'),
- ).toEqual(metricStates.NO_DATA);
- });
- });
-
- describe('drag and drop function', () => {
- const findDraggables = () => wrapper.findAll(VueDraggable);
- const findEnabledDraggables = () => findDraggables().filter(f => !f.attributes('disabled'));
- const findDraggablePanels = () => wrapper.findAll('.js-draggable-panel');
- const findRearrangeButton = () => wrapper.find('.js-rearrange-button');
-
- beforeEach(() => {
- mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
- });
-
- beforeEach(done => {
- createComponentWrapper({ hasMetrics: true }, { attachToDocument: true });
-
- setupComponentStore(wrapper.vm);
-
- wrapper.vm.$nextTick(done);
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('wraps vuedraggable', () => {
- expect(findDraggablePanels().exists()).toBe(true);
- expect(findDraggablePanels().length).toEqual(expectedPanelCount);
- });
-
- it('is disabled by default', () => {
- expect(findRearrangeButton().exists()).toBe(false);
- expect(findEnabledDraggables().length).toBe(0);
- });
-
- describe('when rearrange is enabled', () => {
- beforeEach(done => {
- wrapper.setProps({ rearrangePanelsAvailable: true });
- wrapper.vm.$nextTick(done);
- });
-
- it('displays rearrange button', () => {
- expect(findRearrangeButton().exists()).toBe(true);
- });
-
- describe('when rearrange button is clicked', () => {
- const findFirstDraggableRemoveButton = () =>
- findDraggablePanels()
- .at(0)
- .find('.js-draggable-remove');
-
- beforeEach(done => {
- findRearrangeButton().vm.$emit('click');
- wrapper.vm.$nextTick(done);
- });
-
- it('it enables draggables', () => {
- expect(findRearrangeButton().attributes('pressed')).toBeTruthy();
- expect(findEnabledDraggables()).toEqual(findDraggables());
- });
-
- it('metrics can be swapped', done => {
- const firstDraggable = findDraggables().at(0);
- const mockMetrics = [...metricsGroupsAPIResponse[1].panels];
-
- const firstTitle = mockMetrics[0].title;
- const secondTitle = mockMetrics[1].title;
-
- // swap two elements and `input` them
- [mockMetrics[0], mockMetrics[1]] = [mockMetrics[1], mockMetrics[0]];
- firstDraggable.vm.$emit('input', mockMetrics);
-
- wrapper.vm.$nextTick(() => {
- const { panels } = wrapper.vm.dashboard.panel_groups[1];
-
- expect(panels[1].title).toEqual(firstTitle);
- expect(panels[0].title).toEqual(secondTitle);
- done();
- });
- });
-
- it('shows a remove button, which removes a panel', done => {
- expect(findFirstDraggableRemoveButton().isEmpty()).toBe(false);
-
- expect(findDraggablePanels().length).toEqual(expectedPanelCount);
- findFirstDraggableRemoveButton().trigger('click');
-
- wrapper.vm.$nextTick(() => {
- expect(findDraggablePanels().length).toEqual(expectedPanelCount - 1);
- done();
- });
- });
-
- it('it disables draggables when clicked again', done => {
- findRearrangeButton().vm.$emit('click');
- wrapper.vm.$nextTick(() => {
- expect(findRearrangeButton().attributes('pressed')).toBeFalsy();
- expect(findEnabledDraggables().length).toBe(0);
- done();
- });
- });
- });
- });
- });
-
- // https://gitlab.com/gitlab-org/gitlab-ce/issues/66922
- // eslint-disable-next-line jasmine/no-disabled-tests
- xdescribe('link to chart', () => {
- const currentDashboard = 'TEST_DASHBOARD';
- localVue.use(GlToast);
- const link = () => wrapper.find('.js-chart-link');
- const clipboardText = () => link().element.dataset.clipboardText;
-
- beforeEach(done => {
- mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
-
- createComponentWrapper({ hasMetrics: true, currentDashboard }, { attachToDocument: true });
-
- setTimeout(done);
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('adds a copy button to the dropdown', () => {
- expect(link().text()).toContain('Generate link to chart');
- });
-
- it('contains a link to the dashboard', () => {
- expect(clipboardText()).toContain(`dashboard=${currentDashboard}`);
- expect(clipboardText()).toContain(`group=`);
- expect(clipboardText()).toContain(`title=`);
- expect(clipboardText()).toContain(`y_label=`);
- });
-
- it('undefined parameter is stripped', done => {
- wrapper.setProps({ currentDashboard: undefined });
-
- wrapper.vm.$nextTick(() => {
- expect(clipboardText()).not.toContain(`dashboard=`);
- expect(clipboardText()).toContain(`y_label=`);
- done();
- });
- });
-
- it('null parameter is stripped', done => {
- wrapper.setProps({ currentDashboard: null });
-
- wrapper.vm.$nextTick(() => {
- expect(clipboardText()).not.toContain(`dashboard=`);
- expect(clipboardText()).toContain(`y_label=`);
- done();
- });
- });
-
- it('creates a toast when clicked', () => {
- spyOn(wrapper.vm.$toast, 'show').and.stub();
-
- link().vm.$emit('click');
-
- expect(wrapper.vm.$toast.show).toHaveBeenCalled();
- });
- });
-
- describe('responds to window resizes', () => {
- let promPanel;
- let promGroup;
- let panelToggle;
- let chart;
- beforeEach(() => {
- mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
-
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: {
- ...propsData,
- hasMetrics: true,
- showPanels: true,
- },
- store,
- });
-
- setupComponentStore(component);
-
- return Vue.nextTick().then(() => {
- [, promPanel] = component.$el.querySelectorAll('.prometheus-panel');
- promGroup = promPanel.querySelector('.prometheus-graph-group');
- panelToggle = promPanel.querySelector('.js-graph-group-toggle');
- chart = promGroup.querySelector('.position-relative svg');
- });
- });
-
- it('setting chart size to zero when panel group is hidden', () => {
- expect(promGroup.style.display).toBe('');
- expect(chart.clientWidth).toBeGreaterThan(0);
-
- panelToggle.click();
- return Vue.nextTick().then(() => {
- expect(promGroup.style.display).toBe('none');
- expect(chart.clientWidth).toBe(0);
- promPanel.style.width = '500px';
- });
- });
-
- it('expanding chart panel group after resize displays chart', () => {
- panelToggle.click();
-
- expect(chart.clientWidth).toBeGreaterThan(0);
- });
- });
-
- describe('dashboard edit link', () => {
- const findEditLink = () => wrapper.find('.js-edit-link');
-
- beforeEach(done => {
- mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
-
- createComponentWrapper({ hasMetrics: true }, { attachToDocument: true });
-
- wrapper.vm.$store.commit(
- `monitoringDashboard/${types.SET_ALL_DASHBOARDS}`,
- dashboardGitResponse,
- );
- wrapper.vm.$nextTick(done);
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('is not present for the default dashboard', () => {
- expect(findEditLink().exists()).toBe(false);
- });
-
- it('is present for a custom dashboard, and links to its edit_path', done => {
- const dashboard = dashboardGitResponse[1]; // non-default dashboard
- const currentDashboard = dashboard.path;
-
- wrapper.setProps({ currentDashboard });
- wrapper.vm.$nextTick(() => {
- expect(findEditLink().exists()).toBe(true);
- expect(findEditLink().attributes('href')).toBe(dashboard.project_blob_path);
- done();
- });
- });
- });
-
- describe('external dashboard link', () => {
- beforeEach(() => {
- mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
-
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: {
- ...propsData,
- hasMetrics: true,
- showPanels: false,
- showTimeWindowDropdown: false,
- externalDashboardUrl: '/mockUrl',
- },
- store,
- });
- });
-
- it('shows the link', done => {
- setTimeout(() => {
- expect(component.$el.querySelector('.js-external-dashboard-link').innerText).toContain(
- 'View full dashboard',
- );
- done();
- });
- });
- });
-
- describe('Dashboard dropdown', () => {
- beforeEach(() => {
- mock.onGet(mockApiEndpoint).reply(200, metricsGroupsAPIResponse);
-
- component = new DashboardComponent({
- el: document.querySelector('.prometheus-graphs'),
- propsData: {
- ...propsData,
- hasMetrics: true,
- showPanels: false,
- },
- store,
- });
-
- component.$store.commit(
- `monitoringDashboard/${types.SET_ALL_DASHBOARDS}`,
- dashboardGitResponse,
- );
- });
-
- it('shows the dashboard dropdown', done => {
- setTimeout(() => {
- const dashboardDropdown = component.$el.querySelector('.js-dashboards-dropdown');
-
- expect(dashboardDropdown).not.toEqual(null);
- done();
- });
- });
- });
-});
diff --git a/spec/lib/expand_variables_spec.rb b/spec/lib/expand_variables_spec.rb
index 394efa85701..1b8ec2b1979 100644
--- a/spec/lib/expand_variables_spec.rb
+++ b/spec/lib/expand_variables_spec.rb
@@ -100,7 +100,7 @@ describe ExpandVariables do
end
with_them do
- subject { ExpandVariables.expand(value, variables) } # rubocop:disable RSpec/DescribedClass
+ subject { ExpandVariables.expand(value, variables) }
it { is_expected.to eq(result) }
end
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index 38ec04ebe81..ba5b70b44de 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -481,7 +481,6 @@ module Gitlab
['../sample.adoc', 'doc/sample.adoc', 'relative path to a file up one directory'],
['../../sample.adoc', 'sample.adoc', 'relative path for a file up multiple directories']
].each do |include_path_, file_path_, desc|
-
context "the file is specified by #{desc}" do
let(:include_path) { include_path_ }
let(:file_path) { file_path_ }
diff --git a/spec/lib/gitlab/ci/config/entry/cache_spec.rb b/spec/lib/gitlab/ci/config/entry/cache_spec.rb
index 4fa0a57dc82..f7b14360af3 100644
--- a/spec/lib/gitlab/ci/config/entry/cache_spec.rb
+++ b/spec/lib/gitlab/ci/config/entry/cache_spec.rb
@@ -31,13 +31,13 @@ describe Gitlab::Ci::Config::Entry::Cache do
it_behaves_like 'hash key value'
context 'with files' do
- let(:key) { { files: ['a-file', 'other-file'] } }
+ let(:key) { { files: %w[a-file other-file] } }
it_behaves_like 'hash key value'
end
context 'with files and prefix' do
- let(:key) { { files: ['a-file', 'other-file'], prefix: 'prefix-value' } }
+ let(:key) { { files: %w[a-file other-file], prefix: 'prefix-value' } }
it_behaves_like 'hash key value'
end
@@ -55,7 +55,7 @@ describe Gitlab::Ci::Config::Entry::Cache do
it { is_expected.to be_valid }
context 'with files' do
- let(:key) { { files: ['a-file', 'other-file'] } }
+ let(:key) { { files: %w[a-file other-file] } }
it { is_expected.to be_valid }
end
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb
index b09258ae227..56767c21ab7 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb
@@ -6,12 +6,12 @@ shared_examples 'renames child namespaces' do |type|
it 'renames namespaces' do
rename_namespaces = double
expect(described_class::RenameNamespaces)
- .to receive(:new).with(['first-path', 'second-path'], subject)
+ .to receive(:new).with(%w[first-path second-path], subject)
.and_return(rename_namespaces)
expect(rename_namespaces).to receive(:rename_namespaces)
.with(type: :child)
- subject.rename_wildcard_paths(['first-path', 'second-path'])
+ subject.rename_wildcard_paths(%w[first-path second-path])
end
end
diff --git a/spec/lib/gitlab/experimentation_spec.rb b/spec/lib/gitlab/experimentation_spec.rb
index b8be72cf8d7..e4624accd58 100644
--- a/spec/lib/gitlab/experimentation_spec.rb
+++ b/spec/lib/gitlab/experimentation_spec.rb
@@ -54,7 +54,7 @@ describe Gitlab::Experimentation do
describe '#experiment_enabled?' do
context 'cookie is not present' do
it 'calls Gitlab::Experimentation.enabled_for_user? with the name of the experiment and an experimentation_subject_index of nil' do
- expect(Gitlab::Experimentation).to receive(:enabled_for_user?).with(:test_experiment, nil) # rubocop:disable RSpec/DescribedClass
+ expect(Gitlab::Experimentation).to receive(:enabled_for_user?).with(:test_experiment, nil)
controller.experiment_enabled?(:test_experiment)
end
end
@@ -67,7 +67,7 @@ describe Gitlab::Experimentation do
it 'calls Gitlab::Experimentation.enabled_for_user? with the name of the experiment and an experimentation_subject_index of the modulo 100 of the hex value of the uuid' do
# 'abcd1234'.hex % 100 = 76
- expect(Gitlab::Experimentation).to receive(:enabled_for_user?).with(:test_experiment, 76) # rubocop:disable RSpec/DescribedClass
+ expect(Gitlab::Experimentation).to receive(:enabled_for_user?).with(:test_experiment, 76)
controller.experiment_enabled?(:test_experiment)
end
end
diff --git a/spec/lib/gitlab/gitaly_client/blob_service_spec.rb b/spec/lib/gitlab/gitaly_client/blob_service_spec.rb
index 887a6baf659..fc6ac491671 100644
--- a/spec/lib/gitlab/gitaly_client/blob_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/blob_service_spec.rb
@@ -12,7 +12,7 @@ describe Gitlab::GitalyClient::BlobService do
describe '#get_new_lfs_pointers' do
let(:revision) { 'master' }
let(:limit) { 5 }
- let(:not_in) { ['branch-a', 'branch-b'] }
+ let(:not_in) { %w[branch-a branch-b] }
let(:expected_params) do
{ revision: revision, limit: limit, not_in_refs: not_in, not_in_all: false }
end
diff --git a/spec/lib/gitlab/gitaly_client/remote_service_spec.rb b/spec/lib/gitlab/gitaly_client/remote_service_spec.rb
index 929ff5dee5d..73ae4cd95ce 100644
--- a/spec/lib/gitlab/gitaly_client/remote_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/remote_service_spec.rb
@@ -69,7 +69,7 @@ describe Gitlab::GitalyClient::RemoteService do
describe '#update_remote_mirror' do
let(:ref_name) { 'remote_mirror_1' }
- let(:only_branches_matching) { ['my-branch', 'master'] }
+ let(:only_branches_matching) { %w[my-branch master] }
let(:ssh_key) { 'KEY' }
let(:known_hosts) { 'KNOWN HOSTS' }
diff --git a/spec/lib/gitlab/phabricator_import/conduit/user_spec.rb b/spec/lib/gitlab/phabricator_import/conduit/user_spec.rb
index e88eec2c393..f3928f390bc 100644
--- a/spec/lib/gitlab/phabricator_import/conduit/user_spec.rb
+++ b/spec/lib/gitlab/phabricator_import/conduit/user_spec.rb
@@ -15,13 +15,13 @@ describe Gitlab::PhabricatorImport::Conduit::User do
it 'calls the api with the correct params' do
expected_params = {
- constraints: { phids: ['phid-1', 'phid-2'] }
+ constraints: { phids: %w[phid-1 phid-2] }
}
expect(fake_client).to receive(:get).with('user.search',
params: expected_params)
- user_client.users(['phid-1', 'phid-2'])
+ user_client.users(%w[phid-1 phid-2])
end
it 'returns an array of parsed responses' do
@@ -43,7 +43,7 @@ describe Gitlab::PhabricatorImport::Conduit::User do
expect(fake_client).to receive(:get).with('user.search',
params: second_params).once
- user_client.users(['phid-1', 'phid-2'])
+ user_client.users(%w[phid-1 phid-2])
end
end
end
diff --git a/spec/lib/gitlab/phabricator_import/user_finder_spec.rb b/spec/lib/gitlab/phabricator_import/user_finder_spec.rb
index 14a00deeb16..f260e38b7c8 100644
--- a/spec/lib/gitlab/phabricator_import/user_finder_spec.rb
+++ b/spec/lib/gitlab/phabricator_import/user_finder_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
describe Gitlab::PhabricatorImport::UserFinder, :clean_gitlab_redis_cache do
let(:project) { create(:project, namespace: create(:group)) }
- subject(:finder) { described_class.new(project, ['first-phid', 'second-phid']) }
+ subject(:finder) { described_class.new(project, %w[first-phid second-phid]) }
before do
project.namespace.add_developer(existing_user)
diff --git a/spec/lib/gitlab/quick_actions/dsl_spec.rb b/spec/lib/gitlab/quick_actions/dsl_spec.rb
index c98c36622f5..1145a7edc85 100644
--- a/spec/lib/gitlab/quick_actions/dsl_spec.rb
+++ b/spec/lib/gitlab/quick_actions/dsl_spec.rb
@@ -5,7 +5,7 @@ require 'spec_helper'
describe Gitlab::QuickActions::Dsl do
before :all do
DummyClass = Struct.new(:project) do
- include Gitlab::QuickActions::Dsl # rubocop:disable RSpec/DescribedClass
+ include Gitlab::QuickActions::Dsl
desc 'A command with no args'
command :no_args, :none do
diff --git a/spec/lib/quality/helm_client_spec.rb b/spec/lib/quality/helm_client_spec.rb
index 795aa43b849..8d199fe3531 100644
--- a/spec/lib/quality/helm_client_spec.rb
+++ b/spec/lib/quality/helm_client_spec.rb
@@ -110,7 +110,7 @@ RSpec.describe Quality::HelmClient do
end
context 'with multiple release names' do
- let(:release_name) { ['my-release', 'my-release-2'] }
+ let(:release_name) { %w[my-release my-release-2] }
it 'raises an error if the Helm command fails' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
diff --git a/spec/lib/quality/kubernetes_client_spec.rb b/spec/lib/quality/kubernetes_client_spec.rb
index 59d4a977d5e..6a62ef456c1 100644
--- a/spec/lib/quality/kubernetes_client_spec.rb
+++ b/spec/lib/quality/kubernetes_client_spec.rb
@@ -46,7 +46,7 @@ RSpec.describe Quality::KubernetesClient do
end
context 'with multiple releases' do
- let(:release_name) { ['my-release', 'my-release-2'] }
+ let(:release_name) { %w[my-release my-release-2] }
it 'raises an error if the Kubernetes command fails' do
expect(Gitlab::Popen).to receive(:popen_with_detail)
diff --git a/spec/migrations/fix_max_pages_size_spec.rb b/spec/migrations/fix_max_pages_size_spec.rb
new file mode 100644
index 00000000000..36b5445603e
--- /dev/null
+++ b/spec/migrations/fix_max_pages_size_spec.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20191213120427_fix_max_pages_size.rb')
+
+describe FixMaxPagesSize, :migration do
+ let(:application_settings) { table(:application_settings) }
+ let!(:default_setting) { application_settings.create! }
+ let!(:max_possible_setting) { application_settings.create!(max_pages_size: described_class::MAX_SIZE) }
+ let!(:higher_than_maximum_setting) { application_settings.create!(max_pages_size: described_class::MAX_SIZE + 1) }
+
+ it 'correctly updates settings only if needed' do
+ migrate!
+
+ expect(default_setting.reload.max_pages_size).to eq(100)
+ expect(max_possible_setting.reload.max_pages_size).to eq(described_class::MAX_SIZE)
+ expect(higher_than_maximum_setting.reload.max_pages_size).to eq(described_class::MAX_SIZE)
+ end
+end
diff --git a/spec/models/active_session_spec.rb b/spec/models/active_session_spec.rb
index 072d0fa86e5..6930f743c2f 100644
--- a/spec/models/active_session_spec.rb
+++ b/spec/models/active_session_spec.rb
@@ -139,7 +139,7 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
redis = double(:redis)
expect(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis)
- sessions = ['session-a', 'session-b']
+ sessions = %w[session-a session-b]
mget_responses = sessions.map { |session| [Marshal.dump(session)]}
expect(redis).to receive(:mget).twice.and_return(*mget_responses)
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index a403aa296d4..52e60a69a52 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -67,6 +67,13 @@ describe ApplicationSetting do
it { is_expected.not_to allow_value(nil).for(:push_event_activities_limit) }
it { is_expected.to validate_numericality_of(:snippet_size_limit).only_integer.is_greater_than(0) }
+ it { is_expected.to validate_presence_of(:max_artifacts_size) }
+ it do
+ is_expected.to validate_numericality_of(:max_pages_size).only_integer.is_greater_than(0)
+ .is_less_than(::Gitlab::Pages::MAX_SIZE / 1.megabyte)
+ end
+ it { is_expected.to validate_numericality_of(:max_artifacts_size).only_integer.is_greater_than(0) }
+ it { is_expected.to validate_numericality_of(:max_pages_size).only_integer.is_greater_than(0) }
it { is_expected.not_to allow_value(7).for(:minimum_password_length) }
it { is_expected.not_to allow_value(129).for(:minimum_password_length) }
diff --git a/spec/models/clusters/applications/prometheus_spec.rb b/spec/models/clusters/applications/prometheus_spec.rb
index 0f829e138d5..e7f5f493b82 100644
--- a/spec/models/clusters/applications/prometheus_spec.rb
+++ b/spec/models/clusters/applications/prometheus_spec.rb
@@ -130,7 +130,7 @@ describe Clusters::Applications::Prometheus do
it 'is initialized with 3 arguments' do
expect(subject.name).to eq('prometheus')
expect(subject.chart).to eq('stable/prometheus')
- expect(subject.version).to eq('6.7.3')
+ expect(subject.version).to eq('9.5.2')
expect(subject).to be_rbac
expect(subject.files).to eq(prometheus.files)
end
@@ -147,7 +147,7 @@ describe Clusters::Applications::Prometheus do
let(:prometheus) { create(:clusters_applications_prometheus, :errored, version: '2.0.0') }
it 'is initialized with the locked version' do
- expect(subject.version).to eq('6.7.3')
+ expect(subject.version).to eq('9.5.2')
end
end
@@ -218,7 +218,7 @@ describe Clusters::Applications::Prometheus do
it 'is initialized with 3 arguments' do
expect(patch_command.name).to eq('prometheus')
expect(patch_command.chart).to eq('stable/prometheus')
- expect(patch_command.version).to eq('6.7.3')
+ expect(patch_command.version).to eq('9.5.2')
expect(patch_command.files).to eq(prometheus.files)
end
end
diff --git a/spec/models/error_tracking/project_error_tracking_setting_spec.rb b/spec/models/error_tracking/project_error_tracking_setting_spec.rb
index ef426661066..5b8be7914d4 100644
--- a/spec/models/error_tracking/project_error_tracking_setting_spec.rb
+++ b/spec/models/error_tracking/project_error_tracking_setting_spec.rb
@@ -138,8 +138,6 @@ describe ErrorTracking::ProjectErrorTrackingSetting do
error: 'error message',
error_type: ErrorTracking::ProjectErrorTrackingSetting::SENTRY_API_ERROR_TYPE_NON_20X_RESPONSE
)
- expect(subject).to have_received(:sentry_client)
- expect(sentry_client).to have_received(:list_issues)
end
end
@@ -159,8 +157,6 @@ describe ErrorTracking::ProjectErrorTrackingSetting do
error: 'Sentry API response is missing keys. key not found: "id"',
error_type: ErrorTracking::ProjectErrorTrackingSetting::SENTRY_API_ERROR_TYPE_MISSING_KEYS
)
- expect(subject).to have_received(:sentry_client)
- expect(sentry_client).to have_received(:list_issues)
end
end
@@ -181,8 +177,21 @@ describe ErrorTracking::ProjectErrorTrackingSetting do
error: error_msg,
error_type: ErrorTracking::ProjectErrorTrackingSetting::SENTRY_API_ERROR_INVALID_SIZE
)
- expect(subject).to have_received(:sentry_client)
- expect(sentry_client).to have_received(:list_issues)
+ end
+ end
+
+ context 'when sentry client raises StandardError' do
+ let(:sentry_client) { spy(:sentry_client) }
+
+ before do
+ synchronous_reactive_cache(subject)
+
+ allow(subject).to receive(:sentry_client).and_return(sentry_client)
+ allow(sentry_client).to receive(:list_issues).with(opts).and_raise(StandardError)
+ end
+
+ it 'returns error' do
+ expect(result).to eq(error: 'Unexpected Error')
end
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index c98d123ff52..0e151475128 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -384,7 +384,7 @@ describe MergeRequest do
end
it 'returns target branches sort by updated at desc' do
- expect(described_class.recent_target_branches).to match_array(['feature', 'merge-test', 'fix'])
+ expect(described_class.recent_target_branches).to match_array(%w[feature merge-test fix])
end
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 2ba0d97792b..b732412c52c 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -26,6 +26,7 @@ describe Namespace do
it { is_expected.to validate_presence_of(:path) }
it { is_expected.to validate_length_of(:path).is_at_most(255) }
it { is_expected.to validate_presence_of(:owner) }
+ it { is_expected.to validate_numericality_of(:max_artifacts_size).only_integer.is_greater_than(0) }
it 'does not allow too deep nesting' do
ancestors = (1..21).to_a
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index e66f37f2eec..31dc0134410 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -211,6 +211,7 @@ describe Project do
it { is_expected.to validate_presence_of(:creator) }
it { is_expected.to validate_presence_of(:namespace) }
it { is_expected.to validate_presence_of(:repository_storage) }
+ it { is_expected.to validate_numericality_of(:max_artifacts_size).only_integer.is_greater_than(0) }
it 'validates build timeout constraints' do
is_expected.to validate_numericality_of(:build_timeout)
diff --git a/spec/requests/api/wikis_spec.rb b/spec/requests/api/wikis_spec.rb
index 310caa92eb9..2e0b7a30480 100644
--- a/spec/requests/api/wikis_spec.rb
+++ b/spec/requests/api/wikis_spec.rb
@@ -115,7 +115,7 @@ describe API::Wikis do
end
[:title, :content, :format].each do |part|
- it "it updates with wiki with missing #{part}" do
+ it "updates with wiki with missing #{part}" do
payload.delete(part)
put(api(url, user), params: payload)
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index b80f75c70e6..5440a42348e 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -2707,7 +2707,7 @@ describe NotificationService, :mailer do
# User to be participant by default
# This user does not contain any record in notification settings table
# It should be treated with a :participating notification_level
- @u_lazy_participant = create(:user, username: 'lazy-participant')
+ @u_lazy_participant = create(:user, username: 'lazy-participant')
@u_guest_watcher = create_user_with_notification(:watch, 'guest_watching')
@u_guest_custom = create_user_with_notification(:custom, 'guest_custom')
diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb
index fe92b53cd91..9aa8c7f85ca 100644
--- a/spec/services/projects/update_pages_service_spec.rb
+++ b/spec/services/projects/update_pages_service_spec.rb
@@ -185,60 +185,20 @@ describe Projects::UpdatePagesService do
.and_return(metadata)
end
- shared_examples 'pages size limit exceeded' do
- it 'limits the maximum size of gitlab pages' do
- subject.execute
-
- expect(deploy_status.description)
- .to match(/artifacts for pages are too large/)
- expect(deploy_status).to be_script_failure
- expect(project.pages_metadatum).not_to be_deployed
- end
- end
-
context 'when maximum pages size is set to zero' do
before do
stub_application_setting(max_pages_size: 0)
end
- context 'when page size does not exceed internal maximum' do
- before do
- allow(metadata).to receive(:total_size).and_return(200.megabytes)
- end
-
- it 'updates pages correctly' do
- subject.execute
-
- expect(deploy_status.description).not_to be_present
- expect(project.pages_metadatum).to be_deployed
- end
- end
-
- context 'when pages size does exceed internal maximum' do
- before do
- allow(metadata).to receive(:total_size).and_return(2.terabytes)
- end
-
- it_behaves_like 'pages size limit exceeded'
- end
- end
-
- context 'when pages size is greater than max size setting' do
- before do
- stub_application_setting(max_pages_size: 200)
- allow(metadata).to receive(:total_size).and_return(201.megabytes)
- end
-
- it_behaves_like 'pages size limit exceeded'
+ it_behaves_like 'pages size limit is', ::Gitlab::Pages::MAX_SIZE
end
- context 'when max size setting is greater than internal max size' do
+ context 'when size is limited on the instance level' do
before do
- stub_application_setting(max_pages_size: 3.terabytes / 1.megabyte)
- allow(metadata).to receive(:total_size).and_return(2.terabytes)
+ stub_application_setting(max_pages_size: 100)
end
- it_behaves_like 'pages size limit exceeded'
+ it_behaves_like 'pages size limit is', 100.megabytes
end
end
diff --git a/spec/support/features/discussion_comments_shared_example.rb b/spec/support/features/discussion_comments_shared_example.rb
index f070243f111..ea13e91860a 100644
--- a/spec/support/features/discussion_comments_shared_example.rb
+++ b/spec/support/features/discussion_comments_shared_example.rb
@@ -297,11 +297,11 @@ shared_examples 'thread comments' do |resource_name|
find("#{form_selector} .note-textarea").send_keys('a')
end
- it "should show a 'Comment & reopen #{resource_name}' button" do
+ it "shows a 'Comment & reopen #{resource_name}' button" do
expect(find("#{form_selector} .js-note-target-reopen")).to have_content "Comment & reopen #{resource_name}"
end
- it "should show a 'Start thread & reopen #{resource_name}' button when 'Start thread' is selected" do
+ it "shows a 'Start thread & reopen #{resource_name}' button when 'Start thread' is selected" do
find(toggle_selector).click
find("#{menu_selector} li", match: :first)
diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb
index 6a23875f103..f2fa6af6402 100644
--- a/spec/support/helpers/test_env.rb
+++ b/spec/support/helpers/test_env.rb
@@ -154,7 +154,6 @@ module TestEnv
install_dir: gitaly_dir,
version: Gitlab::GitalyClient.expected_server_version,
task: "gitlab:gitaly:install[#{install_gitaly_args}]") do
-
Gitlab::SetupHelper.create_gitaly_configuration(gitaly_dir, { 'default' => repos_path }, force: true)
start_gitaly(gitaly_dir)
end
diff --git a/spec/support/shared_examples/pages_size_limit_shared_examples.rb b/spec/support/shared_examples/pages_size_limit_shared_examples.rb
new file mode 100644
index 00000000000..c1e27194738
--- /dev/null
+++ b/spec/support/shared_examples/pages_size_limit_shared_examples.rb
@@ -0,0 +1,30 @@
+# frozen_string_literal: true
+
+shared_examples 'pages size limit is' do |size_limit|
+ context "when size is below the limit" do
+ before do
+ allow(metadata).to receive(:total_size).and_return(size_limit - 1.megabyte)
+ end
+
+ it 'updates pages correctly' do
+ subject.execute
+
+ expect(deploy_status.description).not_to be_present
+ expect(project.pages_metadatum).to be_deployed
+ end
+ end
+
+ context "when size is above the limit" do
+ before do
+ allow(metadata).to receive(:total_size).and_return(size_limit + 1.megabyte)
+ end
+
+ it 'limits the maximum size of gitlab pages' do
+ subject.execute
+
+ expect(deploy_status.description)
+ .to match(/artifacts for pages are too large/)
+ expect(deploy_status).to be_script_failure
+ end
+ end
+end
diff --git a/spec/views/projects/commit/branches.html.haml_spec.rb b/spec/views/projects/commit/branches.html.haml_spec.rb
index 36da489a84f..0fe7165a790 100644
--- a/spec/views/projects/commit/branches.html.haml_spec.rb
+++ b/spec/views/projects/commit/branches.html.haml_spec.rb
@@ -11,7 +11,7 @@ describe 'projects/commit/branches.html.haml' do
context 'when branches and tags are available' do
before do
- assign(:branches, ['master', 'test-branch'])
+ assign(:branches, %w[master test-branch])
assign(:branches_limit_exceeded, false)
assign(:tags, ['tag1'])
assign(:tags_limit_exceeded, false)
@@ -35,7 +35,7 @@ describe 'projects/commit/branches.html.haml' do
context 'when branches are available but no tags' do
before do
- assign(:branches, ['master', 'test-branch'])
+ assign(:branches, %w[master test-branch])
assign(:branches_limit_exceeded, false)
assign(:tags, [])
assign(:tags_limit_exceeded, true)
diff --git a/vendor/prometheus/values.yaml b/vendor/prometheus/values.yaml
index be79a0c5380..25384b088a2 100644
--- a/vendor/prometheus/values.yaml
+++ b/vendor/prometheus/values.yaml
@@ -1,7 +1,5 @@
alertmanager:
enabled: false
- image:
- tag: v0.15.2
kubeStateMetrics:
enabled: true
@@ -14,8 +12,6 @@ pushgateway:
server:
fullnameOverride: "prometheus-prometheus-server"
- image:
- tag: v2.4.3
serverFiles:
alerts: {}