diff --git a/app/assets/stylesheets/fontawesome_custom.scss b/app/assets/stylesheets/fontawesome_custom.scss
index c2ce2368705..005390e60b6 100644
--- a/app/assets/stylesheets/fontawesome_custom.scss
+++ b/app/assets/stylesheets/fontawesome_custom.scss
@@ -247,10 +247,6 @@
content: '\f187';
}
-.fa-lock::before {
- content: '\f023';
-}
-
.fa-sign-out::before {
content: '\f08b';
}
@@ -315,10 +311,6 @@
content: '\f1b3';
}
-.fa-edit::before {
- content: '\f044';
-}
-
.fa-times-circle::before {
content: '\f057';
}
diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb
index 0fbede1806c..10c95da394f 100644
--- a/app/helpers/snippets_helper.rb
+++ b/app/helpers/snippets_helper.rb
@@ -60,9 +60,9 @@ module SnippetsHelper
def snippet_badge(snippet)
return unless attrs = snippet_badge_attributes(snippet)
- css_class, text = attrs
+ icon_name, text = attrs
tag.span(class: %w[badge badge-gray]) do
- concat(tag.i(class: ['fa', css_class]))
+ concat(sprite_icon(icon_name, size: 14, css_class: 'gl-vertical-align-middle'))
concat(' ')
concat(text)
end
@@ -70,7 +70,7 @@ module SnippetsHelper
def snippet_badge_attributes(snippet)
if snippet.private?
- ['fa-lock', _('private')]
+ ['lock', _('private')]
end
end
diff --git a/app/models/clusters/applications/ingress.rb b/app/models/clusters/applications/ingress.rb
index a44450ec7a9..f71c3e40cad 100644
--- a/app/models/clusters/applications/ingress.rb
+++ b/app/models/clusters/applications/ingress.rb
@@ -3,7 +3,7 @@
module Clusters
module Applications
class Ingress < ApplicationRecord
- VERSION = '1.29.7'
+ VERSION = '1.40.2'
INGRESS_CONTAINER_NAME = 'nginx-ingress-controller'
MODSECURITY_LOG_CONTAINER_NAME = 'modsecurity-log'
MODSECURITY_MODE_LOGGING = "DetectionOnly"
diff --git a/app/views/projects/runners/_runner.html.haml b/app/views/projects/runners/_runner.html.haml
index 8773c239ce6..6480569b499 100644
--- a/app/views/projects/runners/_runner.html.haml
+++ b/app/views/projects/runners/_runner.html.haml
@@ -6,11 +6,12 @@
= link_to _("%{token}...") % { token: runner.short_sha }, project_runner_path(@project, runner), class: 'commit-sha has-tooltip', title: _("Partial token for reference only")
- if runner.locked?
- = icon('lock', class: 'has-tooltip', title: _('Locked to current projects'))
+ %span.has-tooltip{ title: _('Locked to current projects') }
+ = sprite_icon('lock', size: 16)
%small.edit-runner
- = link_to edit_project_runner_path(@project, runner) do
- %i.fa.fa-edit.btn
+ = link_to edit_project_runner_path(@project, runner), class: 'btn btn-edit' do
+ = sprite_icon('pencil', size: 16)
- else
%span.commit-sha
= runner.short_sha
diff --git a/changelogs/unreleased/jcunha-update-ingress-version.yml b/changelogs/unreleased/jcunha-update-ingress-version.yml
new file mode 100644
index 00000000000..d71c2e1f97b
--- /dev/null
+++ b/changelogs/unreleased/jcunha-update-ingress-version.yml
@@ -0,0 +1,5 @@
+---
+title: Updates GitLab managed app Ingress version to 1.40.2
+merge_request: 35924
+author:
+type: changed
diff --git a/changelogs/unreleased/sh-better-diff-stats-versioning.yml b/changelogs/unreleased/sh-better-diff-stats-versioning.yml
new file mode 100644
index 00000000000..130dd536962
--- /dev/null
+++ b/changelogs/unreleased/sh-better-diff-stats-versioning.yml
@@ -0,0 +1,5 @@
+---
+title: Use Gitaly protobuf version as DiffStats cache key
+merge_request: 38414
+author:
+type: fixed
diff --git a/doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md b/doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md
index 77d820e1686..2cf2bb5b1d0 100644
--- a/doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md
+++ b/doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md
@@ -48,3 +48,89 @@ only to prevent it from running in the pipelines for live environments such as S
If Jenkins Docker container exits without providing any information in the logs, try increasing the memory used by
the Docker Engine.
+
+## Gitaly Cluster tests
+
+The tests tagged `:gitaly_ha` are orchestrated tests that can only be run against a set of Docker containers as configured and started by [the `Test::Integration::GitalyCluster` GitLab QA scenario](https://gitlab.com/gitlab-org/gitlab-qa/-/blob/master/docs/what_tests_can_be_run.md#testintegrationgitalycluster-ceeefull-image-address).
+
+As described in the documentation about the scenario noted above, the following command will run the tests:
+
+```shell
+gitlab-qa Test::Integration::GitalyCluster EE
+```
+
+However, that will remove the containers after it finishes running the tests. If you would like to do further testing, for example, if you would like to run a single test via a debugger, you can use [the `--no-tests` option](https://gitlab.com/gitlab-org/gitlab-qa#command-line-options) to make `gitlab-qa` skip running the tests, and to leave the containers running so that you can continue to use them.
+
+```shell
+gitlab-qa Test::Integration::GitalyCluster EE --no-tests
+```
+
+When all the containers are running you will see the output of the `docker ps` command, showing on which ports the GitLab container can be accessed. For example:
+
+```plaintext
+CONTAINER ID ... PORTS NAMES
+d15d3386a0a8 ... 22/tcp, 443/tcp, 0.0.0.0:32772->80/tcp gitlab-gitaly-ha
+```
+
+That shows that the GitLab instance running in the `gitlab-gitaly-ha` container can be reached via `http://localhost:32772`. However, Git operations like cloning and pushing are performed against the URL revealed via the UI as the clone URL. It uses the hostname configured for the GitLab instance, which in this case matches the Docker container name and network, `gitlab-gitaly-ha.test`. Before you can run the tests you need to configure your computer to access the container via that address. One option is to [use caddyserver as described for running tests against GDK](https://gitlab.com/gitlab-org/gitlab-qa/-/blob/master/docs/run_qa_against_gdk.md#workarounds).
+
+Another option is to use NGINX.
+
+In both cases you will need to configure your machine to translate `gitlab-gitlab-ha.test` into an appropriate IP address:
+
+```shell
+echo '127.0.0.1 gitlab-gitaly-ha.test' | sudo tee -a /etc/hosts
+```
+
+Then install NGINX:
+
+```shell
+# on macOS
+brew install nginx
+
+# on Debian/Ubuntu
+apt install nginx
+
+# on Fedora
+yum install nginx
+```
+
+Finally, configure NGINX to pass requests for `gitlab-gitaly-ha.test` to the GitLab instance:
+
+```plaintext
+# On Debian/Ubuntu, in /etc/nginx/sites-enabled/gitlab-cluster
+# On macOS, in /usr/local/etc/nginx/nginx.conf
+
+server {
+ server_name gitlab-gitaly-ha.test;
+ client_max_body_size 500m;
+
+ location / {
+ proxy_pass http://127.0.0.1:32772;
+ proxy_set_header Host gitlab-gitaly-ha.test;
+ }
+}
+```
+
+Restart NGINX for the configuration to take effect. For example:
+
+```shell
+# On Debian/Ubuntu
+sudo systemctl restart nginx
+
+# on macOS
+sudo nginx -s reload
+```
+
+You could then run the tests from the `/qa` directory:
+
+```shell
+CHROME_HEADLESS=false bin/qa Test::Instance::All http://gitlab-gitaly-ha.test -- --tag gitaly_ha
+```
+
+Once you have finished testing you can stop and remove the Docker containers:
+
+```shell
+docker stop gitlab-gitaly-ha praefect postgres gitaly3 gitaly2 gitaly1
+docker rm gitlab-gitaly-ha praefect postgres gitaly3 gitaly2 gitaly1
+```
diff --git a/lib/gitlab/diff/stats_cache.rb b/lib/gitlab/diff/stats_cache.rb
index 7915d146738..a918fc08201 100644
--- a/lib/gitlab/diff/stats_cache.rb
+++ b/lib/gitlab/diff/stats_cache.rb
@@ -6,7 +6,8 @@ module Gitlab
include Gitlab::Utils::StrongMemoize
EXPIRATION = 1.week
- VERSION = 1
+ # The DiffStats#as_json representation is tied to the Gitaly protobuf version
+ VERSION = Gem.loaded_specs['gitaly'].version.to_s
def initialize(cachable_key:)
@cachable_key = cachable_key
@@ -29,6 +30,7 @@ module Gitlab
return unless stats
cache.write(key, stats.as_json, expires_in: EXPIRATION)
+ clear_memoization(:cached_values)
end
def clear
diff --git a/qa/qa/resource/repository/commit.rb b/qa/qa/resource/repository/commit.rb
index 3243eacdb28..d15210aa736 100644
--- a/qa/qa/resource/repository/commit.rb
+++ b/qa/qa/resource/repository/commit.rb
@@ -59,14 +59,19 @@ module QA
@update_files = files
end
- def resource_web_url(resource)
+ # If `actions` are specified, it performs the actions to create,
+ # update, or delete commits. If no actions are specified it
+ # gets existing commits.
+ def fabricate_via_api!
+ return api_get if actions.empty?
+
+ super
+ rescue ResourceNotFoundError
super
- rescue ResourceURLMissingError
- # this particular resource does not expose a web_url property
end
def api_get_path
- "#{api_post_path}/#{@sha}"
+ api_post_path
end
def api_post_path
diff --git a/qa/qa/service/praefect_manager.rb b/qa/qa/service/praefect_manager.rb
index ce57cd9e074..53fd3036be5 100644
--- a/qa/qa/service/praefect_manager.rb
+++ b/qa/qa/service/praefect_manager.rb
@@ -7,6 +7,8 @@ module QA
attr_accessor :gitlab
+ PrometheusQueryError = Class.new(StandardError)
+
def initialize
@gitlab = 'gitlab-gitaly-ha'
@praefect = 'praefect'
@@ -106,6 +108,18 @@ module QA
)
end
+ def query_read_distribution
+ output = shell "docker exec gitlab-gitaly-ha bash -c 'curl -s http://localhost:9090/api/v1/query?query=gitaly_praefect_read_distribution'" do |line|
+ QA::Runtime::Logger.debug(line)
+ break line
+ end
+ result = JSON.parse(output)
+
+ raise PrometheusQueryError, "Unable to query read distribution metrics" unless result['status'] == 'success'
+
+ result['data']['result'].map { |result| { node: result['metric']['storage'], value: result['value'][1].to_i } }
+ end
+
def replication_queue_lock_count
result = []
shell sql_to_docker_exec_cmd("select count(*) from replication_queue_lock where acquired = 't';") do |line|
@@ -285,6 +299,18 @@ module QA
)
end
+ # Waits until there is an increase in the number of reads for
+ # any node compared to the number of reads provided
+ def wait_for_read_count_change(pre_read_data)
+ diff_found = false
+ Support::Waiter.wait_until(sleep_interval: 5) do
+ query_read_distribution.each_with_index do |data, index|
+ diff_found = true if data[:value] > pre_read_data[index][:value]
+ end
+ diff_found
+ end
+ end
+
def wait_for_reliable_connection
QA::Runtime::Logger.info('Wait until GitLab and Praefect can communicate reliably')
wait_for_praefect
diff --git a/qa/qa/specs/features/api/3_create/repository/distributed_reads_spec.rb b/qa/qa/specs/features/api/3_create/repository/distributed_reads_spec.rb
new file mode 100644
index 00000000000..cff4b06b1ec
--- /dev/null
+++ b/qa/qa/specs/features/api/3_create/repository/distributed_reads_spec.rb
@@ -0,0 +1,51 @@
+# frozen_string_literal: true
+
+require 'parallel'
+
+module QA
+ RSpec.describe 'Create' do
+ context 'Gitaly' do
+ # Issue to track removal of feature flag: https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/602
+ describe 'Distributed reads', :orchestrated, :gitaly_ha, :skip_live_env, :requires_admin do
+ let(:number_of_reads) { 100 }
+ let(:praefect_manager) { Service::PraefectManager.new }
+ let(:project) do
+ Resource::Project.fabricate! do |project|
+ project.name = "gitaly_cluster"
+ project.initialize_with_readme = true
+ end
+ end
+
+ before do
+ Runtime::Feature.enable_and_verify('gitaly_distributed_reads')
+ praefect_manager.wait_for_replication(project.id)
+ end
+
+ after do
+ Runtime::Feature.disable_and_verify('gitaly_distributed_reads')
+ end
+
+ it 'reads from each node' do
+ pre_read_data = praefect_manager.query_read_distribution
+
+ QA::Runtime::Logger.info('Fetching commits from the repository')
+ Parallel.each((1..number_of_reads)) do |index|
+ Resource::Repository::Commit.fabricate_via_api! do |commits|
+ commits.project = project
+ end
+ end
+
+ praefect_manager.wait_for_read_count_change(pre_read_data)
+
+ aggregate_failures "each gitaly node" do
+ praefect_manager.query_read_distribution.each_with_index do |data, index|
+ expect(data[:value])
+ .to be > pre_read_data[index][:value],
+ "Read counts did not differ for node #{pre_read_data[index][:node]}"
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/helpers/snippets_helper_spec.rb b/spec/helpers/snippets_helper_spec.rb
index 1215286daed..302122c3990 100644
--- a/spec/helpers/snippets_helper_spec.rb
+++ b/spec/helpers/snippets_helper_spec.rb
@@ -122,7 +122,7 @@ RSpec.describe SnippetsHelper do
let(:visibility) { :private }
it 'returns the snippet badge' do
- expect(subject).to eq " private"
+ expect(subject).to eq "#{sprite_icon('lock', size: 14, css_class: 'gl-vertical-align-middle')} private"
end
end
diff --git a/spec/lib/gitlab/diff/stats_cache_spec.rb b/spec/lib/gitlab/diff/stats_cache_spec.rb
index 8bf510c0bdd..451fd52c084 100644
--- a/spec/lib/gitlab/diff/stats_cache_spec.rb
+++ b/spec/lib/gitlab/diff/stats_cache_spec.rb
@@ -81,4 +81,28 @@ RSpec.describe Gitlab::Diff::StatsCache, :use_clean_rails_memory_store_caching d
stats_cache.clear
end
end
+
+ it 'VERSION is set' do
+ expect(described_class::VERSION).to be_present
+ end
+
+ context 'with multiple cache versions' do
+ before do
+ stats_cache.write_if_empty(stats)
+ end
+
+ it 'does not read from a stale cache' do
+ expect(stats_cache.read.to_json).to eq(stats.to_json)
+
+ stub_const('Gitlab::Diff::StatsCache::VERSION', '1.0.new-new-thing')
+
+ stats_cache = described_class.new(cachable_key: cachable_key)
+
+ expect(stats_cache.read).to be_nil
+
+ stats_cache.write_if_empty(stats)
+
+ expect(stats_cache.read.to_json).to eq(stats.to_json)
+ end
+ end
end
diff --git a/spec/models/clusters/applications/ingress_spec.rb b/spec/models/clusters/applications/ingress_spec.rb
index d1138f5fa2d..e029283326f 100644
--- a/spec/models/clusters/applications/ingress_spec.rb
+++ b/spec/models/clusters/applications/ingress_spec.rb
@@ -136,7 +136,7 @@ RSpec.describe Clusters::Applications::Ingress do
it 'is initialized with ingress arguments' do
expect(subject.name).to eq('ingress')
expect(subject.chart).to eq('stable/nginx-ingress')
- expect(subject.version).to eq('1.29.7')
+ expect(subject.version).to eq('1.40.2')
expect(subject).to be_rbac
expect(subject.files).to eq(ingress.files)
end
@@ -153,7 +153,7 @@ RSpec.describe Clusters::Applications::Ingress do
let(:ingress) { create(:clusters_applications_ingress, :errored, version: 'nginx') }
it 'is initialized with the locked version' do
- expect(subject.version).to eq('1.29.7')
+ expect(subject.version).to eq('1.40.2')
end
end
end