summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-08-31 06:12:36 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-08-31 06:12:36 +0000
commit136651d7cb69357d2823adefac430df389e81e17 (patch)
tree9e501e488170ff6f8be0f7fec2405a54440391d4
parenta5bd90f43bbd7d7b3222cf84698daa2cbc6e2b3f (diff)
downloadgitlab-ce-136651d7cb69357d2823adefac430df389e81e17.tar.gz
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--app/controllers/projects_controller.rb18
-rw-r--r--config/feature_flags/development/use_gitaly_pagination_for_refs.yml8
-rw-r--r--db/post_migrate/20220825061250_drop_tmp_index_todos_attention_request_action_idx.rb18
-rw-r--r--db/schema_migrations/202208250612501
-rw-r--r--db/structure.sql2
-rw-r--r--doc/administration/index.md2
-rw-r--r--doc/user/admin_area/appearance.md4
-rw-r--r--doc/user/admin_area/settings/email.md2
-rw-r--r--package.json8
-rw-r--r--qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb9
-rw-r--r--qa/qa/specs/features/api/3_create/repository/files_spec.rb2
-rw-r--r--qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb6
-rw-r--r--spec/controllers/projects_controller_spec.rb34
-rw-r--r--workhorse/go.mod4
-rw-r--r--workhorse/go.sum14
-rw-r--r--workhorse/internal/redis/keywatcher.go116
-rw-r--r--workhorse/internal/redis/keywatcher_test.go62
-rw-r--r--workhorse/internal/upstream/metrics_test.go2
-rw-r--r--workhorse/internal/upstream/routes.go3
-rw-r--r--workhorse/internal/upstream/upstream.go9
-rw-r--r--workhorse/internal/upstream/upstream_test.go4
-rw-r--r--workhorse/main.go7
-rw-r--r--workhorse/main_test.go2
-rw-r--r--yarn.lock36
24 files changed, 225 insertions, 148 deletions
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 8a6bcb4b3fc..2165f66ccfc 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -12,6 +12,8 @@ class ProjectsController < Projects::ApplicationController
include SourcegraphDecorator
include PlanningHierarchy
+ REFS_LIMIT = 100
+
prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) }
around_action :allow_gitaly_ref_name_caching, only: [:index, :show]
@@ -309,6 +311,8 @@ class ProjectsController < Projects::ApplicationController
find_tags = true
find_commits = true
+ use_gitaly_pagination = Feature.enabled?(:use_gitaly_pagination_for_refs, @project)
+
unless find_refs.nil?
find_branches = find_refs.include?('branches')
find_tags = find_refs.include?('tags')
@@ -318,13 +322,21 @@ class ProjectsController < Projects::ApplicationController
options = {}
if find_branches
- branches = BranchesFinder.new(@repository, refs_params).execute.take(100).map(&:name)
+ branches = BranchesFinder.new(@repository, refs_params.merge(per_page: REFS_LIMIT))
+ .execute(gitaly_pagination: use_gitaly_pagination)
+ .take(REFS_LIMIT)
+ .map(&:name)
+
options['Branches'] = branches
end
if find_tags && @repository.tag_count.nonzero?
- tags = TagsFinder.new(@repository, refs_params).execute
- options['Tags'] = tags.take(100).map(&:name)
+ tags = TagsFinder.new(@repository, refs_params.merge(per_page: REFS_LIMIT))
+ .execute(gitaly_pagination: use_gitaly_pagination)
+ .take(REFS_LIMIT)
+ .map(&:name)
+
+ options['Tags'] = tags
end
# If reference is commit id - we should add it to branch/tag selectbox
diff --git a/config/feature_flags/development/use_gitaly_pagination_for_refs.yml b/config/feature_flags/development/use_gitaly_pagination_for_refs.yml
new file mode 100644
index 00000000000..40deacb1e20
--- /dev/null
+++ b/config/feature_flags/development/use_gitaly_pagination_for_refs.yml
@@ -0,0 +1,8 @@
+---
+name: use_gitaly_pagination_for_refs
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/96448
+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/372049
+milestone: '15.4'
+type: development
+group: group::source code
+default_enabled: false
diff --git a/db/post_migrate/20220825061250_drop_tmp_index_todos_attention_request_action_idx.rb b/db/post_migrate/20220825061250_drop_tmp_index_todos_attention_request_action_idx.rb
new file mode 100644
index 00000000000..091de49e1c9
--- /dev/null
+++ b/db/post_migrate/20220825061250_drop_tmp_index_todos_attention_request_action_idx.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class DropTmpIndexTodosAttentionRequestActionIdx < Gitlab::Database::Migration[2.0]
+ INDEX_NAME = "tmp_index_todos_attention_request_action"
+ ATTENTION_REQUESTED = 10
+
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index_by_name :todos, INDEX_NAME
+ end
+
+ def down
+ add_concurrent_index :todos, [:id],
+ where: "action = #{ATTENTION_REQUESTED}",
+ name: INDEX_NAME
+ end
+end
diff --git a/db/schema_migrations/20220825061250 b/db/schema_migrations/20220825061250
new file mode 100644
index 00000000000..62ce31a672a
--- /dev/null
+++ b/db/schema_migrations/20220825061250
@@ -0,0 +1 @@
+0338843ad56b423559e613f00df205122b4f6db194cf49712b2ff46b2ad030e0 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 9ab0f530448..7a88015661a 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -30668,8 +30668,6 @@ CREATE INDEX tmp_index_project_statistics_cont_registry_size ON project_statisti
CREATE INDEX tmp_index_system_note_metadata_on_id_where_task ON system_note_metadata USING btree (id, action) WHERE ((action)::text = 'task'::text);
-CREATE INDEX tmp_index_todos_attention_request_action ON todos USING btree (id) WHERE (action = 10);
-
CREATE INDEX tmp_index_vulnerability_occurrences_on_id_and_scanner_id ON vulnerability_occurrences USING btree (id, scanner_id) WHERE (report_type = ANY (ARRAY[7, 99]));
CREATE UNIQUE INDEX uniq_pkgs_deb_grp_architectures_on_distribution_id_and_name ON packages_debian_group_architectures USING btree (distribution_id, name);
diff --git a/doc/administration/index.md b/doc/administration/index.md
index 3f2ae3170ab..58284a74bf7 100644
--- a/doc/administration/index.md
+++ b/doc/administration/index.md
@@ -82,7 +82,7 @@ Learn how to install, configure, update, and maintain your GitLab instance.
#### Customizing GitLab appearance
-- [Header logo](../user/admin_area/appearance.md#navigation-bar): Change the logo on all pages and email headers.
+- [Header logo](../user/admin_area/appearance.md#top-bar): Change the logo on all pages and email headers.
- [Favicon](../user/admin_area/appearance.md#favicon): Change the default favicon to your own logo.
- [Branded login page](../user/admin_area/appearance.md#sign-in--sign-up-pages): Customize the login page with your own logo, title, and description.
- ["New Project" page](../user/admin_area/appearance.md#new-project-pages): Customize the text to be displayed on the page that opens whenever your users create a new project.
diff --git a/doc/user/admin_area/appearance.md b/doc/user/admin_area/appearance.md
index 4c3bdde223b..b68980c52f4 100644
--- a/doc/user/admin_area/appearance.md
+++ b/doc/user/admin_area/appearance.md
@@ -13,9 +13,9 @@ of GitLab. To access these settings:
1. On the top bar, select **Menu > Admin**.
1. On the left sidebar, select **Settings > Appearance**.
-## Navigation bar
+## Top bar
-By default, the navigation bar has the GitLab logo, but this can be customized with
+By default, the **top bar** has the GitLab logo, but this can be customized with
any image desired. It is optimized for images 28px high (any width), but any image can be
used (less than 1 MB) and it is automatically resized.
diff --git a/doc/user/admin_area/settings/email.md b/doc/user/admin_area/settings/email.md
index e4fc3b6e6d4..507a6899d13 100644
--- a/doc/user/admin_area/settings/email.md
+++ b/doc/user/admin_area/settings/email.md
@@ -11,7 +11,7 @@ You can customize some of the content in emails sent from your GitLab instance.
## Custom logo
-The logo in the header of some emails can be customized, see the [logo customization section](../appearance.md#navigation-bar).
+The logo in the header of some emails can be customized, see the [logo customization section](../appearance.md#top-bar).
## Include author name in email notification email body **(PREMIUM SELF)**
diff --git a/package.json b/package.json
index cf032dce3ef..b3b8f66e0d0 100644
--- a/package.json
+++ b/package.json
@@ -52,8 +52,8 @@
"@codesandbox/sandpack-client": "^1.2.2",
"@gitlab/at.js": "1.5.7",
"@gitlab/favicon-overlay": "2.0.0",
- "@gitlab/svgs": "3.2.0",
- "@gitlab/ui": "43.9.1",
+ "@gitlab/svgs": "3.3.0",
+ "@gitlab/ui": "43.9.3",
"@gitlab/visual-review-tools": "1.7.3",
"@gitlab/web-ide": "0.0.1-dev-20220815034418",
"@rails/actioncable": "6.1.4-7",
@@ -244,10 +244,10 @@
"sass": "^1.49.9",
"stylelint": "^14.9.1",
"timezone-mock": "^1.0.8",
- "webpack-dev-server": "4.10.0",
+ "webpack-dev-server": "4.10.1",
"xhr-mock": "^2.5.1",
"yarn-check-webpack-plugin": "^1.2.0",
- "yarn-deduplicate": "^5.0.2"
+ "yarn-deduplicate": "^6.0.0"
},
"blockedDependencies": {
"bootstrap-vue": "https://docs.gitlab.com/ee/development/fe_guide/dependencies.html#bootstrapvue"
diff --git a/qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb b/qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb
index de460a39ccf..e6b60a5b090 100644
--- a/qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb
+++ b/qa/qa/specs/features/api/1_manage/import_large_github_repo_spec.rb
@@ -88,14 +88,19 @@ module QA
let(:gh_issue_comments) do
logger.debug("= Fetching issue comments =")
github_client.issues_comments(github_repo).each_with_object(Hash.new { |h, k| h[k] = [] }) do |c, hash|
- hash[c.html_url.gsub(/\#\S+/, "")] << c.body&.gsub(gh_link_pattern, dummy_url) # use base html url as key
+ # use base html url as key
+ hash[c.html_url.gsub(/\#\S+/, "")] << c.body&.gsub(gh_link_pattern, dummy_url)
end
end
let(:gh_pr_comments) do
logger.debug("= Fetching pr comments =")
github_client.pull_requests_comments(github_repo).each_with_object(Hash.new { |h, k| h[k] = [] }) do |c, hash|
- hash[c.html_url.gsub(/\#\S+/, "")] << c.body&.gsub(gh_link_pattern, dummy_url) # use base html url as key
+ # use base html url as key
+ hash[c.html_url.gsub(/\#\S+/, "")] << c.body
+ # some suggestions can contain extra whitespaces which gitlab will remove
+ &.gsub(/suggestion\s+\r/, "suggestion\r")
+ &.gsub(gh_link_pattern, dummy_url)
end
end
diff --git a/qa/qa/specs/features/api/3_create/repository/files_spec.rb b/qa/qa/specs/features/api/3_create/repository/files_spec.rb
index 3dc783382c9..71bd03fab17 100644
--- a/qa/qa/specs/features/api/3_create/repository/files_spec.rb
+++ b/qa/qa/specs/features/api/3_create/repository/files_spec.rb
@@ -103,7 +103,7 @@ module QA
expect(response.headers[:expires]).to eq("Fri, 01 Jan 1990 00:00:00 GMT")
expect(response.headers[:content_disposition]).to include("attachment")
expect(response.headers[:content_disposition]).not_to include("inline")
- expect(response.headers[:content_type]).to include("application/octet-stream")
+ expect(response.headers[:content_type]).to include("image/svg+xml")
end
delete_project_request = Runtime::API::Request.new(@api_client, "/projects/#{sanitized_project_path}")
diff --git a/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb b/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb
index b1d59b90e9c..d299997dd3c 100644
--- a/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb
+++ b/qa/qa/specs/features/browser_ui/1_manage/project/dashboard_images_spec.rb
@@ -20,8 +20,10 @@ module QA
Flow::Login.sign_in(as: user)
Page::Dashboard::Welcome.perform do |welcome|
- expect(welcome).to have_welcome_title("Welcome to GitLab")
-
+ Support::Waiter.wait_until(sleep_interval: 2, max_duration: 60, reload_page: page,
+ retry_on_exception: true) do
+ expect(welcome).to have_welcome_title("Welcome to GitLab")
+ end
# This would be better if it were a visual validation test
expect(welcome).to have_loaded_all_images
end
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 94d75ab8d7d..e0adad832f5 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -1217,6 +1217,40 @@ RSpec.describe ProjectsController do
expect(json_response["Commits"]).to include("123456")
end
+ it 'uses gitaly pagination' do
+ expected_params = ActionController::Parameters.new(ref: '123456', per_page: 100).permit!
+
+ expect_next_instance_of(BranchesFinder, project.repository, expected_params) do |finder|
+ expect(finder).to receive(:execute).with(gitaly_pagination: true).and_call_original
+ end
+
+ expect_next_instance_of(TagsFinder, project.repository, expected_params) do |finder|
+ expect(finder).to receive(:execute).with(gitaly_pagination: true).and_call_original
+ end
+
+ get :refs, params: { namespace_id: project.namespace, id: project, ref: "123456" }
+ end
+
+ context 'when use_gitaly_pagination_for_refs is disabled' do
+ before do
+ stub_feature_flags(use_gitaly_pagination_for_refs: false)
+ end
+
+ it 'does not use gitaly pagination' do
+ expected_params = ActionController::Parameters.new(ref: '123456', per_page: 100).permit!
+
+ expect_next_instance_of(BranchesFinder, project.repository, expected_params) do |finder|
+ expect(finder).to receive(:execute).with(gitaly_pagination: false).and_call_original
+ end
+
+ expect_next_instance_of(TagsFinder, project.repository, expected_params) do |finder|
+ expect(finder).to receive(:execute).with(gitaly_pagination: false).and_call_original
+ end
+
+ get :refs, params: { namespace_id: project.namespace, id: project, ref: "123456" }
+ end
+ end
+
context 'when gitaly is unavailable' do
before do
expect_next_instance_of(TagsFinder) do |finder|
diff --git a/workhorse/go.mod b/workhorse/go.mod
index e2191c1a749..bcb529caa01 100644
--- a/workhorse/go.mod
+++ b/workhorse/go.mod
@@ -26,7 +26,7 @@ require (
github.com/sirupsen/logrus v1.9.0
github.com/smartystreets/goconvey v1.7.2
github.com/stretchr/testify v1.8.0
- gitlab.com/gitlab-org/gitaly/v15 v15.2.2
+ gitlab.com/gitlab-org/gitaly/v15 v15.3.2
gitlab.com/gitlab-org/golang-archive-zip v0.1.1
gitlab.com/gitlab-org/labkit v1.16.0
gocloud.dev v0.25.0
@@ -74,7 +74,7 @@ require (
github.com/google/wire v0.5.0 // indirect
github.com/googleapis/gax-go/v2 v2.2.0 // indirect
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect
- github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 // indirect
+ github.com/hashicorp/yamux v0.1.1 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jtolds/gls v4.20.0+incompatible // indirect
github.com/kr/text v0.2.0 // indirect
diff --git a/workhorse/go.sum b/workhorse/go.sum
index d143f62f812..71bf3dc2379 100644
--- a/workhorse/go.sum
+++ b/workhorse/go.sum
@@ -639,8 +639,8 @@ github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0m
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hashicorp/yamux v0.0.0-20210316155119-a95892c5f864/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
-github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87 h1:xixZ2bWeofWV68J+x6AzmKuVM/JWCQwkWm6GW/MUR6I=
-github.com/hashicorp/yamux v0.0.0-20211028200310-0bc27b27de87/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
+github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
+github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/hhatto/gorst v0.0.0-20181029133204-ca9f730cac5b/go.mod h1:HmaZGXHdSwQh1jnUlBGN2BeEYOHACLVGzYOXCbsLvxY=
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
@@ -1125,11 +1125,11 @@ gitlab.com/gitlab-org/gitaly v1.68.0 h1:VlcJs1+PrhW7lqJUU7Fh1q8FMJujmbbivdfde/cw
gitlab.com/gitlab-org/gitaly v1.68.0/go.mod h1:/pCsB918Zu5wFchZ9hLYin9WkJ2yQqdVNz0zlv5HbXg=
gitlab.com/gitlab-org/gitaly/v14 v14.0.0-rc1/go.mod h1:4Cz8tOAyueSZX5o6gYum1F/unupaOclxqETPcg4ODvQ=
gitlab.com/gitlab-org/gitaly/v14 v14.9.0-rc5.0.20220329111719-51da8bc17059/go.mod h1:uX1qhFKBDuPqATlpMcFL2dKDiX8D/tbUg7CYWx7OXt4=
-gitlab.com/gitlab-org/gitaly/v15 v15.2.2 h1:/hSbAhBqRrT6Epc35k83qFwwVbKottNY6wDFr+5DYQo=
-gitlab.com/gitlab-org/gitaly/v15 v15.2.2/go.mod h1:WjitFL44l9ovitGC4OvSuGwfeq0VpHUbHS6sDw13LV8=
+gitlab.com/gitlab-org/gitaly/v15 v15.3.2 h1:H8NoBZil23mZ8Y31XdPWCNp27m2nfFGV+z/pQd5Rsq4=
+gitlab.com/gitlab-org/gitaly/v15 v15.3.2/go.mod h1:DPO/d7DnhJ0TTcL6UZNJ6mcRHhdBPg+f/iBA+LEKEq0=
gitlab.com/gitlab-org/gitlab-shell v1.9.8-0.20201117050822-3f9890ef73dc/go.mod h1:5QSTbpAHY2v0iIH5uHh2KA9w7sPUqPmnLjDApI/sv1U=
gitlab.com/gitlab-org/gitlab-shell v1.9.8-0.20210720163109-50da611814d2/go.mod h1:QWDYBwuy24qGMandtCngLRPzFgnGPg6LSNoJWPKmJMc=
-gitlab.com/gitlab-org/gitlab-shell/v14 v14.8.0/go.mod h1:Z1S5MihpEmtA7GDXGsU0kUf1nzm7zr8w6bP+uXRnxaw=
+gitlab.com/gitlab-org/gitlab-shell/v14 v14.10.0/go.mod h1:ynuwa2oLLQSOs6Ss6RpLDkAQuFUNreI3NdEOJxfh1ac=
gitlab.com/gitlab-org/golang-archive-zip v0.1.1 h1:35k9giivbxwF03+8A05Cm8YoxoakU8FBCj5gysjCTCE=
gitlab.com/gitlab-org/golang-archive-zip v0.1.1/go.mod h1:ZDtqpWPGPB9qBuZnZDrKQjIdJtkN7ZAoVwhT6H2o2kE=
gitlab.com/gitlab-org/labkit v0.0.0-20190221122536-0c3fc7cdd57c/go.mod h1:rYhLgfrbEcyfinG+R3EvKu6bZSsmwQqcXzLfHWSfUKM=
@@ -1137,8 +1137,6 @@ gitlab.com/gitlab-org/labkit v0.0.0-20200908084045-45895e129029/go.mod h1:SNfxkf
gitlab.com/gitlab-org/labkit v1.0.0/go.mod h1:nohrYTSLDnZix0ebXZrbZJjymRar8HeV2roWL5/jw2U=
gitlab.com/gitlab-org/labkit v1.4.1/go.mod h1:x5JO5uvdX4t6e/TZXLXZnFL5AcKz2uLLd3uKXZcuO4k=
gitlab.com/gitlab-org/labkit v1.5.0/go.mod h1:1ZuVZpjSpCKUgjLx8P6jzkkQFxJI1thUKr6yKV3p0vY=
-gitlab.com/gitlab-org/labkit v1.14.0/go.mod h1:bcxc4ZpAC+WyACgyKl7FcvT2XXAbl8CrzN6UY+w8cMc=
-gitlab.com/gitlab-org/labkit v1.15.0/go.mod h1:bcxc4ZpAC+WyACgyKl7FcvT2XXAbl8CrzN6UY+w8cMc=
gitlab.com/gitlab-org/labkit v1.16.0 h1:Vm3NAMZ8RqAunXlvPWby3GJ2R35vsYGP6Uu0YjyMIlY=
gitlab.com/gitlab-org/labkit v1.16.0/go.mod h1:bcxc4ZpAC+WyACgyKl7FcvT2XXAbl8CrzN6UY+w8cMc=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
@@ -1477,7 +1475,6 @@ golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
@@ -1782,7 +1779,6 @@ google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
-google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.48.0 h1:rQOsyJ/8+ufEDJd/Gdsz7HG220Mh9HAhFHRGnIjda0w=
google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
diff --git a/workhorse/internal/redis/keywatcher.go b/workhorse/internal/redis/keywatcher.go
index 20e86daf5af..03f065b1ade 100644
--- a/workhorse/internal/redis/keywatcher.go
+++ b/workhorse/internal/redis/keywatcher.go
@@ -15,17 +15,27 @@ import (
"gitlab.com/gitlab-org/gitlab/workhorse/internal/log"
)
-var (
- keyWatcher = make(map[string][]chan string)
- keyWatcherMutex sync.Mutex
- shutdown = make(chan struct{})
- redisReconnectTimeout = backoff.Backoff{
- //These are the defaults
- Min: 100 * time.Millisecond,
- Max: 60 * time.Second,
- Factor: 2,
- Jitter: true,
+type KeyWatcher struct {
+ mu sync.Mutex
+ subscribers map[string][]chan string
+ shutdown chan struct{}
+ reconnectBackoff backoff.Backoff
+}
+
+func NewKeyWatcher() *KeyWatcher {
+ return &KeyWatcher{
+ subscribers: make(map[string][]chan string),
+ shutdown: make(chan struct{}),
+ reconnectBackoff: backoff.Backoff{
+ Min: 100 * time.Millisecond,
+ Max: 60 * time.Second,
+ Factor: 2,
+ Jitter: true,
+ },
}
+}
+
+var (
keyWatchers = promauto.NewGauge(
prometheus.GaugeOpts{
Name: "gitlab_workhorse_keywatcher_keywatchers",
@@ -57,15 +67,9 @@ const (
keySubChannel = "workhorse:notifications"
)
-// KeyChan holds a key and a channel
-type KeyChan struct {
- Key string
- Chan chan string
-}
-
func countAction(action string) { totalActions.WithLabelValues(action).Add(1) }
-func processInner(conn redis.Conn) error {
+func (kw *KeyWatcher) receivePubSubStream(conn redis.Conn) error {
defer conn.Close()
psc := redis.PubSubConn{Conn: conn}
if err := psc.Subscribe(keySubChannel); err != nil {
@@ -84,7 +88,7 @@ func processInner(conn redis.Conn) error {
log.WithError(fmt.Errorf("keywatcher: invalid notification: %q", dataStr)).Error()
continue
}
- notifyChanWatchers(msg[0], msg[1])
+ kw.notifySubscribers(msg[0], msg[1])
case error:
log.WithError(fmt.Errorf("keywatcher: pubsub receive: %v", v)).Error()
// Intermittent error, return nil so that it doesn't wait before reconnect
@@ -109,45 +113,42 @@ func dialPubSub(dialer redisDialerFunc) (redis.Conn, error) {
return conn, nil
}
-// Process redis subscriptions
-//
-// NOTE: There Can Only Be One!
-func Process() {
+func (kw *KeyWatcher) Process() {
log.Info("keywatcher: starting process loop")
for {
conn, err := dialPubSub(workerDialFunc)
if err != nil {
log.WithError(fmt.Errorf("keywatcher: %v", err)).Error()
- time.Sleep(redisReconnectTimeout.Duration())
+ time.Sleep(kw.reconnectBackoff.Duration())
continue
}
- redisReconnectTimeout.Reset()
+ kw.reconnectBackoff.Reset()
- if err = processInner(conn); err != nil {
- log.WithError(fmt.Errorf("keywatcher: process loop: %v", err)).Error()
+ if err = kw.receivePubSubStream(conn); err != nil {
+ log.WithError(fmt.Errorf("keywatcher: receivePubSubStream: %v", err)).Error()
}
}
}
-func Shutdown() {
+func (kw *KeyWatcher) Shutdown() {
log.Info("keywatcher: shutting down")
- keyWatcherMutex.Lock()
- defer keyWatcherMutex.Unlock()
+ kw.mu.Lock()
+ defer kw.mu.Unlock()
select {
- case <-shutdown:
+ case <-kw.shutdown:
// already closed
default:
- close(shutdown)
+ close(kw.shutdown)
}
}
-func notifyChanWatchers(key, value string) {
- keyWatcherMutex.Lock()
- defer keyWatcherMutex.Unlock()
+func (kw *KeyWatcher) notifySubscribers(key, value string) {
+ kw.mu.Lock()
+ defer kw.mu.Unlock()
- chanList, ok := keyWatcher[key]
+ chanList, ok := kw.subscribers[key]
if !ok {
countAction("drop-message")
return
@@ -158,38 +159,38 @@ func notifyChanWatchers(key, value string) {
c <- value
keyWatchers.Dec()
}
- delete(keyWatcher, key)
+ delete(kw.subscribers, key)
}
-func addKeyChan(kc *KeyChan) {
- keyWatcherMutex.Lock()
- defer keyWatcherMutex.Unlock()
+func (kw *KeyWatcher) addSubscription(key string, notify chan string) {
+ kw.mu.Lock()
+ defer kw.mu.Unlock()
- keyWatcher[kc.Key] = append(keyWatcher[kc.Key], kc.Chan)
+ kw.subscribers[key] = append(kw.subscribers[key], notify)
keyWatchers.Inc()
- if len(keyWatcher[kc.Key]) == 1 {
+ if len(kw.subscribers[key]) == 1 {
countAction("create-subscription")
}
}
-func delKeyChan(kc *KeyChan) {
- keyWatcherMutex.Lock()
- defer keyWatcherMutex.Unlock()
+func (kw *KeyWatcher) delSubscription(key string, notify chan string) {
+ kw.mu.Lock()
+ defer kw.mu.Unlock()
- chans, ok := keyWatcher[kc.Key]
+ chans, ok := kw.subscribers[key]
if !ok {
return
}
for i, c := range chans {
- if kc.Chan == c {
- keyWatcher[kc.Key] = append(chans[:i], chans[i+1:]...)
+ if notify == c {
+ kw.subscribers[key] = append(chans[:i], chans[i+1:]...)
keyWatchers.Dec()
break
}
}
- if len(keyWatcher[kc.Key]) == 0 {
- delete(keyWatcher, kc.Key)
+ if len(kw.subscribers[key]) == 0 {
+ delete(kw.subscribers, key)
countAction("delete-subscription")
}
}
@@ -209,15 +210,10 @@ const (
WatchKeyStatusNoChange
)
-// WatchKey waits for a key to be updated or expired
-func WatchKey(key, value string, timeout time.Duration) (WatchKeyStatus, error) {
- kw := &KeyChan{
- Key: key,
- Chan: make(chan string, 1),
- }
-
- addKeyChan(kw)
- defer delKeyChan(kw)
+func (kw *KeyWatcher) WatchKey(key, value string, timeout time.Duration) (WatchKeyStatus, error) {
+ notify := make(chan string, 1)
+ kw.addSubscription(key, notify)
+ defer kw.delSubscription(key, notify)
currentValue, err := GetString(key)
if errors.Is(err, redis.ErrNil) {
@@ -230,10 +226,10 @@ func WatchKey(key, value string, timeout time.Duration) (WatchKeyStatus, error)
}
select {
- case <-shutdown:
+ case <-kw.shutdown:
log.WithFields(log.Fields{"key": key}).Info("stopping watch due to shutdown")
return WatchKeyStatusNoChange, nil
- case currentValue := <-kw.Chan:
+ case currentValue := <-notify:
if currentValue == "" {
return WatchKeyStatusNoChange, fmt.Errorf("keywatcher: redis GET failed")
}
diff --git a/workhorse/internal/redis/keywatcher_test.go b/workhorse/internal/redis/keywatcher_test.go
index 29041226b14..37cd584e907 100644
--- a/workhorse/internal/redis/keywatcher_test.go
+++ b/workhorse/internal/redis/keywatcher_test.go
@@ -38,20 +38,14 @@ func createUnsubscribeMessage(key string) []interface{} {
}
}
-func countWatchers(key string) int {
- keyWatcherMutex.Lock()
- defer keyWatcherMutex.Unlock()
- return len(keyWatcher[key])
-}
-
-func deleteWatchers(key string) {
- keyWatcherMutex.Lock()
- defer keyWatcherMutex.Unlock()
- delete(keyWatcher, key)
+func (kw *KeyWatcher) countSubscribers(key string) int {
+ kw.mu.Lock()
+ defer kw.mu.Unlock()
+ return len(kw.subscribers[key])
}
// Forces a run of the `Process` loop against a mock PubSubConn.
-func processMessages(numWatchers int, value string) {
+func (kw *KeyWatcher) processMessages(numWatchers int, value string) {
psc := redigomock.NewConn()
// Setup the initial subscription message
@@ -60,11 +54,11 @@ func processMessages(numWatchers int, value string) {
psc.AddSubscriptionMessage(createSubscriptionMessage(keySubChannel, runnerKey+"="+value))
// Wait for all the `WatchKey` calls to be registered
- for countWatchers(runnerKey) != numWatchers {
+ for kw.countSubscribers(runnerKey) != numWatchers {
time.Sleep(time.Millisecond)
}
- processInner(psc)
+ kw.receivePubSubStream(psc)
}
type keyChangeTestCase struct {
@@ -81,12 +75,13 @@ func TestKeyChangesBubblesUpError(t *testing.T) {
conn, td := setupMockPool()
defer td()
+ kw := NewKeyWatcher()
+ defer kw.Shutdown()
+
conn.Command("GET", runnerKey).ExpectError(errors.New("test error"))
- _, err := WatchKey(runnerKey, "something", time.Second)
+ _, err := kw.WatchKey(runnerKey, "something", time.Second)
require.Error(t, err, "Expected error")
-
- deleteWatchers(runnerKey)
}
func TestKeyChangesInstantReturn(t *testing.T) {
@@ -135,12 +130,13 @@ func TestKeyChangesInstantReturn(t *testing.T) {
conn.Command("GET", runnerKey).Expect(tc.returnValue)
}
- val, err := WatchKey(runnerKey, tc.watchValue, tc.timeout)
+ kw := NewKeyWatcher()
+ defer kw.Shutdown()
+
+ val, err := kw.WatchKey(runnerKey, tc.watchValue, tc.timeout)
require.NoError(t, err, "Expected no error")
require.Equal(t, tc.expectedStatus, val, "Expected value")
-
- deleteWatchers(runnerKey)
})
}
}
@@ -183,18 +179,21 @@ func TestKeyChangesWhenWatching(t *testing.T) {
conn.Command("GET", runnerKey).Expect(tc.returnValue)
}
+ kw := NewKeyWatcher()
+ defer kw.Shutdown()
+
wg := &sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
- val, err := WatchKey(runnerKey, tc.watchValue, time.Second)
+ val, err := kw.WatchKey(runnerKey, tc.watchValue, time.Second)
require.NoError(t, err, "Expected no error")
require.Equal(t, tc.expectedStatus, val, "Expected value")
}()
- processMessages(1, tc.processedValue)
+ kw.processMessages(1, tc.processedValue)
wg.Wait()
})
}
@@ -238,17 +237,20 @@ func TestKeyChangesParallel(t *testing.T) {
wg := &sync.WaitGroup{}
wg.Add(runTimes)
+ kw := NewKeyWatcher()
+ defer kw.Shutdown()
+
for i := 0; i < runTimes; i++ {
go func() {
defer wg.Done()
- val, err := WatchKey(runnerKey, tc.watchValue, time.Second)
+ val, err := kw.WatchKey(runnerKey, tc.watchValue, time.Second)
require.NoError(t, err, "Expected no error")
require.Equal(t, tc.expectedStatus, val, "Expected value")
}()
}
- processMessages(runTimes, tc.processedValue)
+ kw.processMessages(runTimes, tc.processedValue)
wg.Wait()
})
}
@@ -257,7 +259,9 @@ func TestKeyChangesParallel(t *testing.T) {
func TestShutdown(t *testing.T) {
conn, td := setupMockPool()
defer td()
- defer func() { shutdown = make(chan struct{}) }()
+
+ kw := NewKeyWatcher()
+ defer kw.Shutdown()
conn.Command("GET", runnerKey).Expect("something")
@@ -265,7 +269,7 @@ func TestShutdown(t *testing.T) {
wg.Add(2)
go func() {
- val, err := WatchKey(runnerKey, "something", 10*time.Second)
+ val, err := kw.WatchKey(runnerKey, "something", 10*time.Second)
require.NoError(t, err, "Expected no error")
require.Equal(t, WatchKeyStatusNoChange, val, "Expected value not to change")
@@ -273,22 +277,22 @@ func TestShutdown(t *testing.T) {
}()
go func() {
- require.Eventually(t, func() bool { return countWatchers(runnerKey) == 1 }, 10*time.Second, time.Millisecond)
+ require.Eventually(t, func() bool { return kw.countSubscribers(runnerKey) == 1 }, 10*time.Second, time.Millisecond)
- Shutdown()
+ kw.Shutdown()
wg.Done()
}()
wg.Wait()
- require.Eventually(t, func() bool { return countWatchers(runnerKey) == 0 }, 10*time.Second, time.Millisecond)
+ require.Eventually(t, func() bool { return kw.countSubscribers(runnerKey) == 0 }, 10*time.Second, time.Millisecond)
// Adding a key after the shutdown should result in an immediate response
var val WatchKeyStatus
var err error
done := make(chan struct{})
go func() {
- val, err = WatchKey(runnerKey, "something", 10*time.Second)
+ val, err = kw.WatchKey(runnerKey, "something", 10*time.Second)
close(done)
}()
diff --git a/workhorse/internal/upstream/metrics_test.go b/workhorse/internal/upstream/metrics_test.go
index 29a9e09777c..dff849ac214 100644
--- a/workhorse/internal/upstream/metrics_test.go
+++ b/workhorse/internal/upstream/metrics_test.go
@@ -26,7 +26,7 @@ func TestInstrumentGeoProxyRoute(t *testing.T) {
handleRouteWithMatchers(u, local),
handleRouteWithMatchers(u, main),
}
- })
+ }, nil)
ts := httptest.NewServer(u)
defer ts.Close()
diff --git a/workhorse/internal/upstream/routes.go b/workhorse/internal/upstream/routes.go
index 899e5d9835e..288871d558d 100644
--- a/workhorse/internal/upstream/routes.go
+++ b/workhorse/internal/upstream/routes.go
@@ -21,7 +21,6 @@ import (
"gitlab.com/gitlab-org/gitlab/workhorse/internal/imageresizer"
proxypkg "gitlab.com/gitlab-org/gitlab/workhorse/internal/proxy"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/queueing"
- "gitlab.com/gitlab-org/gitlab/workhorse/internal/redis"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/secret"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/senddata"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/sendfile"
@@ -223,7 +222,7 @@ func configureRoutes(u *upstream) {
tempfileMultipartProxy := upload.FixedPreAuthMultipart(api, proxy, preparer)
ciAPIProxyQueue := queueing.QueueRequests("ci_api_job_requests", tempfileMultipartProxy, u.APILimit, u.APIQueueLimit, u.APIQueueTimeout)
- ciAPILongPolling := builds.RegisterHandler(ciAPIProxyQueue, redis.WatchKey, u.APICILongPollingDuration)
+ ciAPILongPolling := builds.RegisterHandler(ciAPIProxyQueue, u.watchKeyHandler, u.APICILongPollingDuration)
dependencyProxyInjector.SetUploadHandler(requestBodyUploader)
diff --git a/workhorse/internal/upstream/upstream.go b/workhorse/internal/upstream/upstream.go
index 43b470b568f..248f190e316 100644
--- a/workhorse/internal/upstream/upstream.go
+++ b/workhorse/internal/upstream/upstream.go
@@ -21,6 +21,7 @@ import (
"gitlab.com/gitlab-org/labkit/correlation"
apipkg "gitlab.com/gitlab-org/gitlab/workhorse/internal/api"
+ "gitlab.com/gitlab-org/gitlab/workhorse/internal/builds"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/config"
"gitlab.com/gitlab-org/gitlab/workhorse/internal/helper"
proxypkg "gitlab.com/gitlab-org/gitlab/workhorse/internal/proxy"
@@ -55,19 +56,21 @@ type upstream struct {
accessLogger *logrus.Logger
enableGeoProxyFeature bool
mu sync.RWMutex
+ watchKeyHandler builds.WatchKeyHandler
}
-func NewUpstream(cfg config.Config, accessLogger *logrus.Logger) http.Handler {
- return newUpstream(cfg, accessLogger, configureRoutes)
+func NewUpstream(cfg config.Config, accessLogger *logrus.Logger, watchKeyHandler builds.WatchKeyHandler) http.Handler {
+ return newUpstream(cfg, accessLogger, configureRoutes, watchKeyHandler)
}
-func newUpstream(cfg config.Config, accessLogger *logrus.Logger, routesCallback func(*upstream)) http.Handler {
+func newUpstream(cfg config.Config, accessLogger *logrus.Logger, routesCallback func(*upstream), watchKeyHandler builds.WatchKeyHandler) http.Handler {
up := upstream{
Config: cfg,
accessLogger: accessLogger,
// Kind of a feature flag. See https://gitlab.com/groups/gitlab-org/-/epics/5914#note_564974130
enableGeoProxyFeature: os.Getenv("GEO_SECONDARY_PROXY") != "0",
geoProxyBackend: &url.URL{},
+ watchKeyHandler: watchKeyHandler,
}
if up.geoProxyPollSleep == nil {
up.geoProxyPollSleep = time.Sleep
diff --git a/workhorse/internal/upstream/upstream_test.go b/workhorse/internal/upstream/upstream_test.go
index 21fa7b81fdb..7ab3e67116f 100644
--- a/workhorse/internal/upstream/upstream_test.go
+++ b/workhorse/internal/upstream/upstream_test.go
@@ -58,7 +58,7 @@ func TestRouting(t *testing.T) {
handle(u, quxbaz),
handle(u, main),
}
- })
+ }, nil)
ts := httptest.NewServer(u)
defer ts.Close()
@@ -415,7 +415,7 @@ func startWorkhorseServer(railsServerURL string, enableGeoProxyFeature bool) (*h
configureRoutes(u)
}
cfg := newUpstreamConfig(railsServerURL)
- upstreamHandler := newUpstream(*cfg, logrus.StandardLogger(), myConfigureRoutes)
+ upstreamHandler := newUpstream(*cfg, logrus.StandardLogger(), myConfigureRoutes, nil)
ws := httptest.NewServer(upstreamHandler)
waitForNextApiPoll := func() {}
diff --git a/workhorse/main.go b/workhorse/main.go
index 91008e16961..b0f9760b0d5 100644
--- a/workhorse/main.go
+++ b/workhorse/main.go
@@ -220,9 +220,10 @@ func run(boot bootConfig, cfg config.Config) error {
secret.SetPath(boot.secretPath)
+ keyWatcher := redis.NewKeyWatcher()
if cfg.Redis != nil {
redis.Configure(cfg.Redis, redis.DefaultDialFunc)
- go redis.Process()
+ go keyWatcher.Process()
}
if err := cfg.RegisterGoCloudURLOpeners(); err != nil {
@@ -237,7 +238,7 @@ func run(boot bootConfig, cfg config.Config) error {
gitaly.InitializeSidechannelRegistry(accessLogger)
- up := wrapRaven(upstream.NewUpstream(cfg, accessLogger))
+ up := wrapRaven(upstream.NewUpstream(cfg, accessLogger, keyWatcher.WatchKey))
done := make(chan os.Signal, 1)
signal.Notify(done, syscall.SIGINT, syscall.SIGTERM)
@@ -271,7 +272,7 @@ func run(boot bootConfig, cfg config.Config) error {
ctx, cancel := context.WithTimeout(context.Background(), cfg.ShutdownTimeout.Duration) // lint:allow context.Background
defer cancel()
- redis.Shutdown()
+ keyWatcher.Shutdown()
return srv.Shutdown(ctx)
}
}
diff --git a/workhorse/main_test.go b/workhorse/main_test.go
index 195b1166c75..ebb9c17999d 100644
--- a/workhorse/main_test.go
+++ b/workhorse/main_test.go
@@ -799,7 +799,7 @@ func startWorkhorseServer(authBackend string) *httptest.Server {
func startWorkhorseServerWithConfig(cfg *config.Config) *httptest.Server {
testhelper.ConfigureSecret()
- u := upstream.NewUpstream(*cfg, logrus.StandardLogger())
+ u := upstream.NewUpstream(*cfg, logrus.StandardLogger(), nil)
return httptest.NewServer(u)
}
diff --git a/yarn.lock b/yarn.lock
index 5ce2fc91ec0..7b3291b9c79 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1059,19 +1059,19 @@
stylelint-declaration-strict-value "1.8.0"
stylelint-scss "4.2.0"
-"@gitlab/svgs@3.2.0":
- version "3.2.0"
- resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.2.0.tgz#1ff40355642600e8807775f2b137c184e46380e9"
- integrity sha512-djAEmvB3AljQaVKwEoNWls8Q6oWwGvUVrmtBe3ykyPF/E50QVmiM2kXIko2BAEPzmIKhaH9YchowfYqJX3y2vg==
+"@gitlab/svgs@3.3.0":
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.3.0.tgz#99b044484fcf3d5a6431281e320e2405540ff5a9"
+ integrity sha512-S8Hqf+ms8aNrSgmci9SVoIyj/0qQnizU5uV5vUPAOwiufMDFDyI5qfcgn4EYZ6mnju3LiO+ReSL/PPTD4qNgHA==
-"@gitlab/ui@43.9.1":
- version "43.9.1"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-43.9.1.tgz#8864687ebaffe3ff71b8d6087ce55e3e52e57a79"
- integrity sha512-1Rn4ZEOyQ0flDsAbxsFSnHNFqO0I2kuJjdkXfiEI21g0pdZ3LrdNwE0WcoRZWQd+nQQ0XbvzRaqmxN53rTY21g==
+"@gitlab/ui@43.9.3":
+ version "43.9.3"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-43.9.3.tgz#2dd91b14da769a873e45ffe07b5863f6c47211ba"
+ integrity sha512-TONSf+6UJYWTVs5qnItR1uLZ/0kBE8jGN8aLOVv4CDAsORvln0ZxtcZvMTCFp76YEtzXLkMUfvm7ZngQ26tIiA==
dependencies:
"@popperjs/core" "^2.11.2"
bootstrap-vue "2.20.1"
- dompurify "^2.3.10"
+ dompurify "^2.4.0"
echarts "^5.3.2"
iframe-resizer "^4.3.2"
lodash "^4.17.20"
@@ -4834,7 +4834,7 @@ dompurify@2.3.8:
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.8.tgz#224fe9ae57d7ebd9a1ae1ac18c1c1ca3f532226f"
integrity sha512-eVhaWoVibIzqdGYjwsBWodIQIaXFSB+cKDf4cfxLMsK0xiud6SE+/WCVx/Xw/UwQsa4cS3T2eITcdtmTg2UKcw==
-dompurify@^2.3.10, dompurify@^2.4.0:
+dompurify@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.4.0.tgz#c9c88390f024c2823332615c9e20a453cf3825dd"
integrity sha512-Be9tbQMZds4a3C6xTmz68NlMfeONA//4dOavl/1rNw50E+/QO0KVpbcU0PcaW0nsQxurXls9ZocqFxk8R2mWEA==
@@ -12119,10 +12119,10 @@ webpack-dev-middleware@^5.3.1:
range-parser "^1.2.1"
schema-utils "^4.0.0"
-webpack-dev-server@4.10.0:
- version "4.10.0"
- resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.10.0.tgz#de270d0009eba050546912be90116e7fd740a9ca"
- integrity sha512-7dezwAs+k6yXVFZ+MaL8VnE+APobiO3zvpp3rBHe/HmWQ+avwh0Q3d0xxacOiBybZZ3syTZw9HXzpa3YNbAZDQ==
+webpack-dev-server@4.10.1:
+ version "4.10.1"
+ resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-4.10.1.tgz#124ac9ac261e75303d74d95ab6712b4aec3e12ed"
+ integrity sha512-FIzMq3jbBarz3ld9l7rbM7m6Rj1lOsgq/DyLGMX/fPEB1UBUPtf5iL/4eNfhx8YYJTRlzfv107UfWSWcBK5Odw==
dependencies:
"@types/bonjour" "^3.5.9"
"@types/connect-history-api-fallback" "^1.3.5"
@@ -12442,10 +12442,10 @@ yarn-check-webpack-plugin@^1.2.0:
dependencies:
chalk "^2.4.2"
-yarn-deduplicate@^5.0.2:
- version "5.0.2"
- resolved "https://registry.yarnpkg.com/yarn-deduplicate/-/yarn-deduplicate-5.0.2.tgz#b56484c94d8f1163a828bf20516607f89c078675"
- integrity sha512-pxKa+dM7DMQ4X2vYLKqGCUgtEoTtdMVk9gNoIsxsMSP0rOV51IWFcKHfRIcZjAPNgHTrxz46sKB4xr7Nte7jdw==
+yarn-deduplicate@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/yarn-deduplicate/-/yarn-deduplicate-6.0.0.tgz#91bc0b7b374efe24796606df2c6b00eabb5aab62"
+ integrity sha512-HjGVvuy10hetOuXeexXXT77V+6FfgS+NiW3FsmQD88yfF2kBqTpChvMglyKUlQ0xXEcI77VJazll5qKKBl3ssw==
dependencies:
"@yarnpkg/lockfile" "^1.1.0"
commander "^9.4.0"