diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-03-17 06:08:05 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-03-17 06:08:05 +0300 |
commit | e6ac8e40c2c0fa317c319469d5102eec8be7becd (patch) | |
tree | 409145db12c32c6d3e342aeada2e3015d631dc07 | |
parent | 149436d2a55408accbf67f9301c0bfa3c6706fe6 (diff) |
Add latest changes from gitlab-org/gitlab@master
24 files changed, 338 insertions, 139 deletions
diff --git a/app/assets/javascripts/pages/dashboard/todos/index/todos.js b/app/assets/javascripts/pages/dashboard/todos/index/todos.js index cabb1b24ae6..c4bbbdcd8ec 100644 --- a/app/assets/javascripts/pages/dashboard/todos/index/todos.js +++ b/app/assets/javascripts/pages/dashboard/todos/index/todos.js @@ -96,6 +96,8 @@ export default class Todos { target.setAttribute('disabled', true); target.classList.add('disabled'); + target.querySelector('.gl-spinner-container').classList.add('gl-mr-2'); + axios[target.dataset.method](target.dataset.href) .then(({ data }) => { this.updateRowState(target); @@ -118,6 +120,8 @@ export default class Todos { target.removeAttribute('disabled'); target.classList.remove('disabled'); + target.querySelector('.gl-spinner-container').classList.remove('gl-mr-2'); + if (isInactive === true) { restoreBtn.classList.add('hidden'); doneBtn.classList.remove('hidden'); @@ -140,6 +144,8 @@ export default class Todos { target.setAttribute('disabled', true); target.classList.add('disabled'); + target.querySelector('.gl-spinner-container').classList.add('gl-mr-2'); + axios[target.dataset.method](target.dataset.href, { ids: this.todo_ids, }) @@ -163,6 +169,8 @@ export default class Todos { target.removeAttribute('disabled'); target.classList.remove('disabled'); + target.querySelector('.gl-spinner-container').classList.remove('gl-mr-2'); + this.todo_ids = target === markAllDoneBtn ? data.updated_ids : []; undoAllBtn.classList.toggle('hidden'); markAllDoneBtn.classList.toggle('hidden'); diff --git a/app/models/analytics/cycle_analytics/aggregation.rb b/app/models/analytics/cycle_analytics/aggregation.rb index 279938511fd..44d2dc369f7 100644 --- a/app/models/analytics/cycle_analytics/aggregation.rb +++ b/app/models/analytics/cycle_analytics/aggregation.rb @@ -7,7 +7,7 @@ class Analytics::CycleAnalytics::Aggregation < ApplicationRecord validates :incremental_runtimes_in_seconds, :incremental_processed_records, :last_full_run_runtimes_in_seconds, :last_full_run_processed_records, presence: true, length: { maximum: 10 }, allow_blank: true - scope :priority_order, -> { order('last_incremental_run_at ASC NULLS FIRST') } + scope :priority_order, -> (column_to_sort = :last_incremental_run_at) { order(arel_table[column_to_sort].asc.nulls_first) } scope :enabled, -> { where('enabled IS TRUE') } def estimated_next_run_at @@ -55,17 +55,17 @@ class Analytics::CycleAnalytics::Aggregation < ApplicationRecord connection.select_value("(#{max})") end - def self.load_batch(last_run_at, batch_size = 100) + def self.load_batch(last_run_at, column_to_query = :last_incremental_run_at, batch_size = 100) last_run_at_not_set = Analytics::CycleAnalytics::Aggregation .enabled - .where(last_incremental_run_at: nil) - .priority_order + .where(column_to_query => nil) + .priority_order(column_to_query) .limit(batch_size) last_run_at_before = Analytics::CycleAnalytics::Aggregation .enabled - .where('last_incremental_run_at < ?', last_run_at) - .priority_order + .where(arel_table[column_to_query].lt(last_run_at)) + .priority_order(column_to_query) .limit(batch_size) Analytics::CycleAnalytics::Aggregation diff --git a/app/views/dashboard/todos/_todo.html.haml b/app/views/dashboard/todos/_todo.html.haml index 766900b7a99..c932b416b66 100644 --- a/app/views/dashboard/todos/_todo.html.haml +++ b/app/views/dashboard/todos/_todo.html.haml @@ -50,12 +50,12 @@ .todo-actions.gl-ml-3 - if todo.pending? = link_to dashboard_todo_path(todo), method: :delete, class: 'gl-button btn btn-default btn-loading d-flex align-items-center js-done-todo', data: { href: dashboard_todo_path(todo) } do - = gl_loading_icon(inline: true, css_class: 'gl-mr-2') + = gl_loading_icon(inline: true) Done = link_to restore_dashboard_todo_path(todo), method: :patch, class: 'gl-button btn btn-default btn-loading d-flex align-items-center js-undo-todo hidden', data: { href: restore_dashboard_todo_path(todo) } do - = gl_loading_icon(inline: true, css_class: 'gl-mr-2') + = gl_loading_icon(inline: true) Undo - else = link_to restore_dashboard_todo_path(todo), method: :patch, class: 'gl-button btn btn-default btn-loading d-flex align-items-center js-add-todo', data: { href: restore_dashboard_todo_path(todo) } do - = gl_loading_icon(inline: true, css_class: 'gl-mr-2') + = gl_loading_icon(inline: true) Add a to do diff --git a/app/views/dashboard/todos/index.html.haml b/app/views/dashboard/todos/index.html.haml index 7a329fb34a9..cd177db3ed0 100644 --- a/app/views/dashboard/todos/index.html.haml +++ b/app/views/dashboard/todos/index.html.haml @@ -22,10 +22,10 @@ - if @allowed_todos.any?(&:pending?) .gl-mr-3 = link_to destroy_all_dashboard_todos_path(todos_filter_params), class: 'gl-button btn btn-default btn-loading align-items-center js-todos-mark-all', method: :delete, data: { href: destroy_all_dashboard_todos_path(todos_filter_params) } do - = gl_loading_icon(inline: true, css_class: 'gl-mr-2') + = gl_loading_icon(inline: true) = s_("Todos|Mark all as done") = link_to bulk_restore_dashboard_todos_path, class: 'gl-button btn btn-default btn-loading align-items-center js-todos-undo-all hidden', method: :patch , data: { href: bulk_restore_dashboard_todos_path(todos_filter_params) } do - = gl_loading_icon(inline: true, css_class: 'gl-mr-2') + = gl_loading_icon(inline: true) = s_("Todos|Undo mark all as done") .todos-filters diff --git a/config/feature_flags/development/vsa_consistency_worker.yml b/config/feature_flags/development/vsa_consistency_worker.yml new file mode 100644 index 00000000000..d880f38af69 --- /dev/null +++ b/config/feature_flags/development/vsa_consistency_worker.yml @@ -0,0 +1,8 @@ +--- +name: vsa_consistency_worker +introduced_by_url: +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/355709 +milestone: '14.9' +type: development +group: group::optimize +default_enabled: false diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index 844aff1b060..fe5bf2c8717 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -628,6 +628,9 @@ Gitlab.ee do Settings.cron_jobs['analytics_cycle_analytics_incremental_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['analytics_cycle_analytics_incremental_worker']['cron'] ||= '*/10 * * * *' Settings.cron_jobs['analytics_cycle_analytics_incremental_worker']['job_class'] = 'Analytics::CycleAnalytics::IncrementalWorker' + Settings.cron_jobs['analytics_cycle_analytics_consistency_worker'] ||= Settingslogic.new({}) + Settings.cron_jobs['analytics_cycle_analytics_consistency_worker']['cron'] ||= '*/30 * * * *' + Settings.cron_jobs['analytics_cycle_analytics_consistency_worker']['job_class'] = 'Analytics::CycleAnalytics::ConsistencyWorker' Settings.cron_jobs['active_user_count_threshold_worker'] ||= Settingslogic.new({}) Settings.cron_jobs['active_user_count_threshold_worker']['cron'] ||= '0 12 * * *' Settings.cron_jobs['active_user_count_threshold_worker']['job_class'] = 'ActiveUserCountThresholdWorker' diff --git a/doc/administration/audit_events.md b/doc/administration/audit_events.md index de2cd9c1f77..955f7a6a830 100644 --- a/doc/administration/audit_events.md +++ b/doc/administration/audit_events.md @@ -44,6 +44,9 @@ There are two kinds of events logged: - Instance events scoped to the whole GitLab instance, used by your Compliance team to perform formal audits. +NOTE: +Some events are recorded and available only as [streaming audit events](audit_event_streaming.md). + ### Impersonation data > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/536) in GitLab 13.0. diff --git a/doc/administration/monitoring/performance/performance_bar.md b/doc/administration/monitoring/performance/performance_bar.md index 93098e43544..bc311f73dfe 100644 --- a/doc/administration/monitoring/performance/performance_bar.md +++ b/doc/administration/monitoring/performance/performance_bar.md @@ -30,8 +30,7 @@ From left to right, the performance bar displays: is enabled. It shows which server role was used for the query. "Primary" means that the query was sent to the read/write primary server. "Replica" means it was sent to a read-only replica. - - **Configuration name**: shows up only when the - `GITLAB_MULTIPLE_DATABASE_METRICS` environment variable is set. This is + - **Configuration name**: this is used to distinguish between different databases configured for different GitLab features. The name shown is the same name used to configure database connections in GitLab. diff --git a/doc/administration/pages/index.md b/doc/administration/pages/index.md index be1ec290c95..56ccf70d169 100644 --- a/doc/administration/pages/index.md +++ b/doc/administration/pages/index.md @@ -1388,15 +1388,20 @@ This can happen if your `gitlab-secrets.json` file is out of date between GitLab Pages. Follow steps 8-10 of [Running GitLab Pages on a separate server](#running-gitlab-pages-on-a-separate-server), in all of your GitLab Pages instances. -### Intermittent 502 errors when using an AWS Network Load Balancer and GitLab Pages is running on multiple application servers +### Intermittent 502 errors when using an AWS Network Load Balancer and GitLab Pages Connections will time out when using a Network Load Balancer with client IP preservation enabled and [the request is looped back to the source server](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-troubleshooting.html#loopback-timeout). This can happen to GitLab instances with multiple servers -running both the core GitLab application and GitLab Pages. +running both the core GitLab application and GitLab Pages. This can also happen when a single +container is running both the core GitLab application and GitLab Pages. AWS [recommends using an IP target type](https://aws.amazon.com/premiumsupport/knowledge-center/target-connection-fails-load-balancer/) to resolve this issue. +Turning off [client IP preservation](https://docs.aws.amazon.com/elasticloadbalancing/latest/network/load-balancer-target-groups.html#client-ip-preservation) +may resolve this issue when the core GitLab application and GitLab Pages run on the same host or +container. + ### 500 error with `securecookie: failed to generate random iv` and `Failed to save the session` This problem most likely results from an [out-dated operating system](../package_information/supported_os.md#os-versions-that-are-no-longer-supported). diff --git a/doc/development/documentation/testing.md b/doc/development/documentation/testing.md index 2c97b5e2aff..49fe0aff3c6 100644 --- a/doc/development/documentation/testing.md +++ b/doc/development/documentation/testing.md @@ -270,12 +270,10 @@ build pipelines: [used (see `variables:` section)](https://gitlab.com/gitlab-org/gitlab-docs/-/blob/main/.gitlab-ci.yml) when building the `image:docs-lint-markdown`. -1. Install [`vale`](https://github.com/errata-ai/vale/releases). For example, to install using - `brew` for macOS, run: +1. Install [`vale`](https://github.com/errata-ai/vale/releases). To install for: - ```shell - brew install vale - ``` + - macOS using `brew`, run: `brew install vale`. + - Linux, use your distribution's package manager or a [released binary](https://github.com/errata-ai/vale/releases). These tools can be [integrated with your code editor](#configure-editors). diff --git a/doc/development/emails.md b/doc/development/emails.md index 5361282334f..b8e390988bd 100644 --- a/doc/development/emails.md +++ b/doc/development/emails.md @@ -13,6 +13,9 @@ If a mailer argument needs to be added or removed, it is important to ensure both backward and forward compatibility. Adhere to the Sidekiq steps for [changing the arguments for a worker](sidekiq/compatibility_across_updates.md#changing-the-arguments-for-a-worker). +The same applies to a new mailer method, or a new mailer. If you introduce either, +follow the steps for [adding new workers](sidekiq/compatibility_across_updates.md#adding-new-workers). + In the following example from [`NotificationService`](https://gitlab.com/gitlab-org/gitlab/-/blob/33ccb22e4fc271dbaac94b003a7a1a2915a13441/app/services/notification_service.rb#L74) adding or removing an argument in this mailer's definition may cause problems during deployment before all Rails and Sidekiq nodes have the updated code. diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md index 881c1fb8c78..a4a7e6703ab 100644 --- a/doc/user/application_security/dependency_scanning/index.md +++ b/doc/user/application_security/dependency_scanning/index.md @@ -69,7 +69,8 @@ stages in the `.gitlab-ci.yml` file, the `test` stage is required. To run dependency scanning jobs, by default, you need GitLab Runner with the [`docker`](https://docs.gitlab.com/runner/executors/docker.html) or [`kubernetes`](https://docs.gitlab.com/runner/install/kubernetes.html) executor. -If you're using the shared runners on GitLab.com, this is enabled by default. +If you're using the shared runners on GitLab.com, this is enabled by default. The analyzer images +provided are for the Linux/amd64 architecture. WARNING: If you use your own runners, make sure your installed version of Docker diff --git a/doc/user/compliance/compliance_report/index.md b/doc/user/compliance/compliance_report/index.md index bd4bdb21f35..27783a063da 100644 --- a/doc/user/compliance/compliance_report/index.md +++ b/doc/user/compliance/compliance_report/index.md @@ -108,7 +108,8 @@ The remaining records are truncated when this limit is reached. ## Merge request violations -> Introduced in GitLab 14.6. [Deployed behind the `compliance_violations_report` flag](../../../administration/feature_flags.md). Disabled by default. +> - Introduced in GitLab 14.6. [Deployed behind the `compliance_violations_report` flag](../../../administration/feature_flags.md). Disabled by default. +> - GraphQL API [introduced](https://gitlab.com/groups/gitlab-org/-/epics/7222) in GitLab 14.9. FLAG: On self-managed GitLab, by default this feature is not available. To make it available, @@ -123,10 +124,10 @@ that exist in projects in a specific group. For each separation of duties compli - Reason for the compliance violation. - A link to the merge request that caused the compliance violation. -Merge request violations can only be access in the GitLab UI, but issues are tracking adding: +Merge request violations can be accessed: -- [A GraphQL type to allow retrieval of compliance violations](https://gitlab.com/gitlab-org/gitlab/-/issues/347325). -- [Consuming the merge request violations GraphQL type in the user interface](https://gitlab.com/gitlab-org/gitlab/-/issues/342897). +- In the GitLab UI. +- Using the [GraphQL API](../../../api/graphql/reference/index.md#complianceviolation) (GitLab 14.9 and later). ### View merge request violations @@ -134,3 +135,34 @@ To view merge request violations: 1. On the top bar, select **Menu > Groups** and find your group. 1. On the left sidebar, select **Security & Compliance > Compliance report**. + +### Severity levels scale + +The following is a list of available violation severity levels, ranked from most to least severe: + +| Icon | Severity level | +|:----------------------------------------------|:---------------| +| **{severity-critical, 18, gl-fill-red-800}** | Critical | +| **{severity-high, 18, gl-fill-red-600}** | High | +| **{severity-medium, 18, gl-fill-orange-400}** | Medium | +| **{severity-low, 18, gl-fill-orange-300}** | Low | +| **{severity-info, 18, gl-fill-blue-400}** | Info | + +### Violation types + +The following is a list of violations that are either: + +- Already available. +- Aren't available, but which we are tracking in issues. + +| Violation | Severity level | Category | Description | Availability | +|:-------------------------------------|:----------------|:----------------------------------------------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------------------------------------------------------------| +| Author approved merge request | High | [Separation of duties](#approval-status-and-separation-of-duties) | The author of the merge request approved their own merge request. [Learn more](../../project/merge_requests/approvals/settings.md#prevent-approval-by-author). | [Unavailable](https://gitlab.com/groups/gitlab-org/-/epics/6870) | +| Committers approved merge request | High | [Separation of duties](#approval-status-and-separation-of-duties) | The committers of the merge request approved the merge request they contributed to. [Learn more](../../project/merge_requests/approvals/settings.md#prevent-approvals-by-users-who-add-commits). | [Unavailable](https://gitlab.com/groups/gitlab-org/-/epics/6870) | +| Fewer than two approvals | High | [Separation of duties](#approval-status-and-separation-of-duties) | The merge request was merged with fewer than two approvals. [Learn more](../../project/merge_requests/approvals/rules.md). | [Unavailable](https://gitlab.com/groups/gitlab-org/-/epics/6870) | +| Pipeline failed | Medium | [Pipeline results](../../../ci/pipelines/index.md) | The merge requests pipeline failed and was merged. | [Unavailable](https://gitlab.com/gitlab-org/gitlab/-/issues/346011) | +| Pipeline passed with warnings | Info | [Pipeline results](../../../ci/pipelines/index.md) | The merge request pipeline passed with warnings and was merged. | [Unavailable](https://gitlab.com/gitlab-org/gitlab/-/issues/346011) | +| Code coverage down more than 10% | High | [Code coverage](../../../ci/pipelines/settings.md#merge-request-test-coverage-results) | The code coverage report for the merge request indicates a reduction in coverage of more than 10%. | [Unavailable](https://gitlab.com/gitlab-org/gitlab/-/issues/346011) | +| Code coverage down between 5% to 10% | Medium | [Code coverage](../../../ci/pipelines/settings.md#merge-request-test-coverage-results) | The code coverage report for the merge request indicates a reduction in coverage of between 5% to 10%. | [Unavailable](https://gitlab.com/gitlab-org/gitlab/-/issues/346011) | +| Code coverage down between 1% to 5% | Low | [Code coverage](../../../ci/pipelines/settings.md#merge-request-test-coverage-results) | The code coverage report for the merge request indicates a reduction in coverage of between 1% to 5%. | [Unavailable](https://gitlab.com/gitlab-org/gitlab/-/issues/346011) | +| Code coverage down less than 1% | Info | [Code coverage](../../../ci/pipelines/settings.md#merge-request-test-coverage-results) | The code coverage report for the merge request indicates a reduction in coverage of less than 1%. | [Unavailable](https://gitlab.com/gitlab-org/gitlab/-/issues/346011) | diff --git a/lib/gitlab/http_connection_adapter.rb b/lib/gitlab/http_connection_adapter.rb index 6e3a83f08c3..002708beb3c 100644 --- a/lib/gitlab/http_connection_adapter.rb +++ b/lib/gitlab/http_connection_adapter.rb @@ -47,6 +47,7 @@ module Gitlab def validate_url!(url) Gitlab::UrlBlocker.validate!(url, allow_local_network: allow_local_requests?, allow_localhost: allow_local_requests?, + allow_object_storage: allow_object_storage?, dns_rebind_protection: dns_rebind_protection?) rescue Gitlab::UrlBlocker::BlockedUrlError => e raise Gitlab::HTTP::BlockedUrlError, "URL '#{url}' is blocked: #{e.message}" @@ -56,6 +57,10 @@ module Gitlab options.fetch(:allow_local_requests, allow_settings_local_requests?) end + def allow_object_storage? + options.fetch(:allow_object_storage, false) + end + def dns_rebind_protection? return false if Gitlab.http_proxy_env? diff --git a/lib/gitlab/import_export/command_line_util.rb b/lib/gitlab/import_export/command_line_util.rb index 68d0494fbe2..2b0467d8779 100644 --- a/lib/gitlab/import_export/command_line_util.rb +++ b/lib/gitlab/import_export/command_line_util.rb @@ -62,23 +62,27 @@ module Gitlab end def download(url, upload_path, size_limit: nil) - File.open(upload_path, 'w') do |file| - # Download (stream) file from the uploader's location - IO.copy_stream( - URI.parse(url).open(progress_proc: file_size_limiter(size_limit)), - file - ) + File.open(upload_path, 'wb') do |file| + current_size = 0 + + Gitlab::HTTP.get(url, stream_body: true, allow_object_storage: true) do |fragment| + if [301, 302, 307].include?(fragment.code) + Gitlab::Import::Logger.warn(message: "received redirect fragment", fragment_code: fragment.code) + elsif fragment.code == 200 + current_size += fragment.bytesize + + raise FileOversizedError if size_limit.present? && current_size > size_limit + + file.write(fragment) + else + raise Gitlab::ImportExport::Error, "unsupported response downloading fragment #{fragment.code}" + end + end end rescue FileOversizedError nil end - def file_size_limiter(limit) - return if limit.blank? - - -> (current_size) { raise FileOversizedError if current_size > limit } - end - def tar_with_options(archive:, dir:, options:) execute_cmd(%W(tar -#{options} #{archive} -C #{dir} .)) end diff --git a/lib/gitlab/metrics/subscribers/active_record.rb b/lib/gitlab/metrics/subscribers/active_record.rb index 715dd86d93c..12576cabb19 100644 --- a/lib/gitlab/metrics/subscribers/active_record.rb +++ b/lib/gitlab/metrics/subscribers/active_record.rb @@ -134,11 +134,7 @@ module Gitlab :"gitlab_transaction_db_#{counter}_total" end - if ENV['GITLAB_MULTIPLE_DATABASE_METRICS'] - current_transaction&.increment(prometheus_key, 1, { db_config_name: db_config_name }) - else - current_transaction&.increment(prometheus_key, 1) - end + current_transaction&.increment(prometheus_key, 1, { db_config_name: db_config_name }) Gitlab::SafeRequestStore[log_key] = Gitlab::SafeRequestStore[log_key].to_i + 1 @@ -154,11 +150,7 @@ module Gitlab def observe(histogram, event, &block) db_config_name = db_config_name(event.payload) - if ENV['GITLAB_MULTIPLE_DATABASE_METRICS'] - current_transaction&.observe(histogram, event.duration / 1000.0, { db_config_name: db_config_name }, &block) - else - current_transaction&.observe(histogram, event.duration / 1000.0, &block) - end + current_transaction&.observe(histogram, event.duration / 1000.0, { db_config_name: db_config_name }, &block) end def current_transaction @@ -193,11 +185,9 @@ module Gitlab counters << compose_metric_key(metric, role) end - if ENV['GITLAB_MULTIPLE_DATABASE_METRICS'] - ::Gitlab::Database.db_config_names.each do |config_name| - counters << compose_metric_key(metric, nil, config_name) # main - counters << compose_metric_key(metric, nil, config_name + ::Gitlab::Database::LoadBalancing::LoadBalancer::REPLICA_SUFFIX) # main_replica - end + ::Gitlab::Database.db_config_names.each do |config_name| + counters << compose_metric_key(metric, nil, config_name) # main + counters << compose_metric_key(metric, nil, config_name + ::Gitlab::Database::LoadBalancing::LoadBalancer::REPLICA_SUFFIX) # main_replica end end diff --git a/lib/gitlab/url_blocker.rb b/lib/gitlab/url_blocker.rb index 48228ede684..60c002853b1 100644 --- a/lib/gitlab/url_blocker.rb +++ b/lib/gitlab/url_blocker.rb @@ -13,6 +13,7 @@ module Gitlab # ports - Raises error if the given URL port does is not between given ports. # allow_localhost - Raises error if URL resolves to a localhost IP address and argument is false. # allow_local_network - Raises error if URL resolves to a link-local address and argument is false. + # allow_object_storage - Avoid raising an error if URL resolves to an object storage endpoint and argument is true. # ascii_only - Raises error if URL has unicode characters and argument is true. # enforce_user - Raises error if URL user doesn't start with alphanumeric characters and argument is true. # enforce_sanitization - Raises error if URL includes any HTML/CSS/JS tags and argument is true. @@ -25,6 +26,7 @@ module Gitlab schemes: [], allow_localhost: false, allow_local_network: true, + allow_object_storage: false, ascii_only: false, enforce_user: false, enforce_sanitization: false, @@ -58,6 +60,8 @@ module Gitlab # Allow url from the GitLab instance itself but only for the configured hostname and ports return protected_uri_with_hostname if internal?(uri) + return protected_uri_with_hostname if allow_object_storage && object_storage_endpoint?(uri) + validate_local_request( address_info: address_info, allow_localhost: allow_localhost, @@ -269,6 +273,30 @@ module Gitlab get_port(uri) == config.gitlab_shell.ssh_port end + def enabled_object_storage_endpoints + ObjectStoreSettings::SUPPORTED_TYPES.collect do |type| + section_setting = config.try(type) + + next unless section_setting + + object_store_setting = section_setting['object_store'] + + next unless object_store_setting && object_store_setting['enabled'] + + object_store_setting.dig('connection', 'endpoint') + end.compact.uniq + end + + def object_storage_endpoint?(uri) + enabled_object_storage_endpoints.any? do |endpoint| + endpoint_uri = URI(endpoint) + + uri.scheme == endpoint_uri.scheme && + uri.hostname == endpoint_uri.hostname && + get_port(uri) == get_port(endpoint_uri) + end + end + def domain_allowed?(uri) Gitlab::UrlBlockers::UrlAllowlist.domain_allowed?(uri.normalized_host, port: get_port(uri)) end diff --git a/lib/peek/views/active_record.rb b/lib/peek/views/active_record.rb index 6a41de8f0b0..75346626255 100644 --- a/lib/peek/views/active_record.rb +++ b/lib/peek/views/active_record.rb @@ -75,14 +75,6 @@ module Peek "Role: #{role.to_s.capitalize}" end - - def format_call_details(call) - if ENV['GITLAB_MULTIPLE_DATABASE_METRICS'] - super - else - super.except(:db_config_name) - end - end end end end diff --git a/spec/lib/gitlab/import_export/command_line_util_spec.rb b/spec/lib/gitlab/import_export/command_line_util_spec.rb index 5f1d598e3ab..f5913da08ba 100644 --- a/spec/lib/gitlab/import_export/command_line_util_spec.rb +++ b/spec/lib/gitlab/import_export/command_line_util_spec.rb @@ -68,31 +68,126 @@ RSpec.describe Gitlab::ImportExport::CommandLineUtil do end describe '#download' do - before do - stub_request(:get, 'http://localhost:3000/file') - .to_return( - status: 200, - body: File.open(archive), - headers: { - 'Content-Type' => 'application/x-tar' - } - ) - end + let(:content) { File.open('spec/fixtures/rails_sample.tif') } + + context 'a non-localhost uri' do + before do + stub_request(:get, url) + .to_return( + status: status, + body: content + ) + end + + let(:url) { 'https://gitlab.com/file' } + + context 'with ok status code' do + let(:status) { HTTP::Status::OK } + + it 'gets the contents' do + Tempfile.create('test') do |file| + subject.download(url, file.path) + expect(file.read).to eq(File.open('spec/fixtures/rails_sample.tif').read) + end + end + + it 'streams the contents via Gitlab::HTTP' do + expect(Gitlab::HTTP).to receive(:get).with(url, hash_including(stream_body: true)) + + Tempfile.create('test') do |file| + subject.download(url, file.path) + end + end + + it 'does not get the content over the size_limit' do + Tempfile.create('test') do |file| + subject.download(url, file.path, size_limit: 300.kilobytes) + expect(file.read).to eq('') + end + end + + it 'gets the content within the size_limit' do + Tempfile.create('test') do |file| + subject.download(url, file.path, size_limit: 400.kilobytes) + expect(file.read).to eq(File.open('spec/fixtures/rails_sample.tif').read) + end + end + end - let(:tempfile) { Tempfile.new('test', path) } + %w[MOVED_PERMANENTLY FOUND TEMPORARY_REDIRECT].each do |code| + context "with a redirect status code #{code}" do + let(:status) { HTTP::Status.const_get(code, false) } - it 'downloads the file in the given path' do - subject.download('http://localhost:3000/file', tempfile) + it 'logs the redirect' do + expect(Gitlab::Import::Logger).to receive(:warn) - expect(File.exist?(tempfile)).to eq(true) - expect(tempfile.size).to eq(File.size(archive)) + Tempfile.create('test') do |file| + subject.download(url, file.path) + end + end + end + end + + %w[ACCEPTED UNAUTHORIZED BAD_REQUEST].each do |code| + context "with an invalid status code #{code}" do + let(:status) { HTTP::Status.const_get(code, false) } + + it 'throws an error' do + Tempfile.create('test') do |file| + expect { subject.download(url, file.path) }.to raise_error(Gitlab::ImportExport::Error) + end + end + end + end end - it 'limit the size of the downloaded file' do - subject.download('http://localhost:3000/file', tempfile, size_limit: 1.byte) + context 'a localhost uri' do + include StubRequests + + let(:status) { HTTP::Status::OK } + let(:url) { "#{host}/foo/bar" } + let(:host) { 'http://localhost:8081' } - expect(File.exist?(tempfile)).to eq(true) - expect(tempfile.size).to eq(0) + before do + # Note: the hostname gets changed to an ip address due to dns_rebind_protection + stub_dns(url, ip_address: '127.0.0.1') + stub_request(:get, 'http://127.0.0.1:8081/foo/bar') + .to_return( + status: status, + body: content + ) + end + + it 'throws a blocked url error' do + Tempfile.create('test') do |file| + expect { subject.download(url, file.path) }.to raise_error((Gitlab::HTTP::BlockedUrlError)) + end + end + + context 'for object_storage uri' do + let(:enabled_object_storage_setting) do + { + 'object_store' => + { + 'enabled' => true, + 'connection' => { + 'endpoint' => host + } + } + } + end + + before do + allow(Settings).to receive(:external_diffs).and_return(enabled_object_storage_setting) + end + + it 'gets the content' do + Tempfile.create('test') do |file| + subject.download(url, file.path) + expect(file.read).to eq(File.open('spec/fixtures/rails_sample.tif').read) + end + end + end end end diff --git a/spec/lib/gitlab/url_blocker_spec.rb b/spec/lib/gitlab/url_blocker_spec.rb index 5b77290ce2e..3c51f3b4bcf 100644 --- a/spec/lib/gitlab/url_blocker_spec.rb +++ b/spec/lib/gitlab/url_blocker_spec.rb @@ -39,6 +39,73 @@ RSpec.describe Gitlab::UrlBlocker, :stub_invalid_dns_only do end end + context 'when URI is for a local object storage' do + let(:import_url) { "#{host}/external-diffs/merge_request_diffs/mr-1/diff-1" } + let(:enabled_object_storage_setting) do + { + 'object_store' => + { + 'enabled' => true, + 'connection' => { + 'endpoint' => host + } + } + } + end + + before do + allow(Settings).to receive(:external_diffs).and_return(enabled_object_storage_setting) + end + + context 'when allow_object_storage is true' do + subject { described_class.validate!(import_url, allow_object_storage: true) } + + context 'with a local domain name' do + let(:host) { 'http://review-minio-svc.svc:9000' } + + before do + stub_dns(host, ip_address: '127.0.0.1') + end + + it_behaves_like 'validates URI and hostname' do + let(:expected_uri) { 'http://127.0.0.1:9000/external-diffs/merge_request_diffs/mr-1/diff-1' } + let(:expected_hostname) { 'review-minio-svc.svc' } + end + end + + context 'with an IP address' do + let(:host) { 'http://127.0.0.1:9000' } + + it_behaves_like 'validates URI and hostname' do + let(:expected_uri) { 'http://127.0.0.1:9000/external-diffs/merge_request_diffs/mr-1/diff-1' } + let(:expected_hostname) { nil } + end + end + end + + context 'when allow_object_storage is false' do + context 'with a local domain name' do + let(:host) { 'http://review-minio-svc.svc:9000' } + + before do + stub_dns(host, ip_address: '127.0.0.1') + end + + it 'raises an error' do + expect { subject }.to raise_error(described_class::BlockedUrlError) + end + end + + context 'with an IP address' do + let(:host) { 'http://127.0.0.1:9000' } + + it 'raises an error' do + expect { subject }.to raise_error(described_class::BlockedUrlError) + end + end + end + end + context 'when the URL hostname is a domain' do context 'when domain can be resolved' do let(:import_url) { 'https://example.org' } diff --git a/spec/lib/peek/views/active_record_spec.rb b/spec/lib/peek/views/active_record_spec.rb index c89f6a21b35..7bc15f40065 100644 --- a/spec/lib/peek/views/active_record_spec.rb +++ b/spec/lib/peek/views/active_record_spec.rb @@ -119,16 +119,4 @@ RSpec.describe Peek::Views::ActiveRecord, :request_store do ) ) end - - context 'when the GITLAB_MULTIPLE_DATABASE_METRICS env var is disabled' do - before do - stub_env('GITLAB_MULTIPLE_DATABASE_METRICS', nil) - end - - it 'does not include db_config_name field' do - ActiveSupport::Notifications.publish('sql.active_record', Time.current, Time.current + 1.second, '1', event_1) - - expect(subject.results[:details][0][:db_config_name]).to be_nil - end - end end diff --git a/spec/models/analytics/cycle_analytics/aggregation_spec.rb b/spec/models/analytics/cycle_analytics/aggregation_spec.rb index 7df1bf60e9c..4bf737df56a 100644 --- a/spec/models/analytics/cycle_analytics/aggregation_spec.rb +++ b/spec/models/analytics/cycle_analytics/aggregation_spec.rb @@ -51,14 +51,14 @@ RSpec.describe Analytics::CycleAnalytics::Aggregation, type: :model do end describe '#load_batch' do - let!(:aggregation1) { create(:cycle_analytics_aggregation, last_incremental_run_at: nil) } + let!(:aggregation1) { create(:cycle_analytics_aggregation, last_incremental_run_at: nil, last_consistency_check_updated_at: 3.days.ago).reload } let!(:aggregation2) { create(:cycle_analytics_aggregation, last_incremental_run_at: 5.days.ago).reload } - let!(:aggregation3) { create(:cycle_analytics_aggregation, last_incremental_run_at: nil) } - let!(:aggregation5) { create(:cycle_analytics_aggregation, last_incremental_run_at: 10.days.ago).reload } + let!(:aggregation3) { create(:cycle_analytics_aggregation, last_incremental_run_at: nil, last_consistency_check_updated_at: 2.days.ago).reload } + let!(:aggregation4) { create(:cycle_analytics_aggregation, last_incremental_run_at: 10.days.ago).reload } before do create(:cycle_analytics_aggregation, :disabled) # disabled rows are skipped - create(:cycle_analytics_aggregation, last_incremental_run_at: 1.day.ago) # "early" rows are filtered out + create(:cycle_analytics_aggregation, last_incremental_run_at: 1.day.ago, last_consistency_check_updated_at: 1.hour.ago) # "early" rows are filtered out end it 'loads records in priority order' do @@ -70,7 +70,20 @@ RSpec.describe Analytics::CycleAnalytics::Aggregation, type: :model do # Using match_array because the order can be undeterministic for nil values. expect(first_two).to match_array([aggregation1, aggregation3]) - expect(last_two).to eq([aggregation5, aggregation2]) + expect(last_two).to eq([aggregation4, aggregation2]) + end + + context 'when loading batch for last_consistency_check_updated_at' do + it 'loads records in priority order' do + batch = described_class.load_batch(1.day.ago, :last_consistency_check_updated_at).to_a + + expect(batch.size).to eq(4) + first_two = batch.first(2) + last_two = batch.last(2) + + expect(first_two).to match_array([aggregation2, aggregation4]) + expect(last_two).to eq([aggregation1, aggregation3]) + end end end diff --git a/spec/support/enable_multiple_database_metrics_by_default.rb b/spec/support/enable_multiple_database_metrics_by_default.rb deleted file mode 100644 index 6eeb4acd3d6..00000000000 --- a/spec/support/enable_multiple_database_metrics_by_default.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true - -RSpec.configure do |config| - config.before do - # Enable this by default in all tests so it behaves like a FF - stub_env('GITLAB_MULTIPLE_DATABASE_METRICS', '1') - end -end diff --git a/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb b/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb index 6e8c340582a..3f187a7e9e4 100644 --- a/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb +++ b/spec/support/shared_examples/metrics/active_record_subscriber_shared_examples.rb @@ -91,21 +91,6 @@ RSpec.shared_examples 'store ActiveRecord info in RequestStore' do |db_role| end end end - - context 'when the GITLAB_MULTIPLE_DATABASE_METRICS env var is disabled' do - before do - stub_env('GITLAB_MULTIPLE_DATABASE_METRICS', nil) - end - - it 'does not include per database metrics' do - Gitlab::WithRequestStore.with_request_store do - subscriber.sql(event) - - expect(described_class.db_counter_payload).not_to include(:"db_replica_#{db_config_name}_duration_s") - expect(described_class.db_counter_payload).not_to include(:"db_replica_#{db_config_name}_count") - end - end - end end RSpec.shared_examples 'record ActiveRecord metrics in a metrics transaction' do |db_role| @@ -160,26 +145,6 @@ RSpec.shared_examples 'record ActiveRecord metrics in a metrics transaction' do subscriber.sql(event) end - - context 'when the GITLAB_MULTIPLE_DATABASE_METRICS env var is disabled' do - before do - stub_env('GITLAB_MULTIPLE_DATABASE_METRICS', nil) - end - - it 'does not include db_config_name label' do - allow(transaction).to receive(:increment) do |*args| - labels = args[2] || {} - expect(labels).not_to include(:db_config_name) - end - - allow(transaction).to receive(:observe) do |*args| - labels = args[2] || {} - expect(labels).not_to include(:db_config_name) - end - - subscriber.sql(event) - end - end end RSpec.shared_examples 'record ActiveRecord metrics' do |db_role| |