summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/boards/boards_util.js12
-rw-r--r--app/assets/javascripts/boards/components/board_app.vue16
-rw-r--r--app/assets/javascripts/boards/components/board_top_bar.vue44
-rw-r--r--app/assets/javascripts/boards/components/boards_selector.vue20
-rw-r--r--app/assets/javascripts/boards/constants.js11
-rw-r--r--app/assets/javascripts/boards/index.js33
-rw-r--r--app/assets/javascripts/flash.js11
-rw-r--r--app/assets/javascripts/main.js11
-rw-r--r--lib/backup/database.rb189
-rw-r--r--lib/backup/dump/postgres.rb24
-rw-r--r--lib/backup/manager.rb27
-rw-r--r--lib/tasks/gitlab/backup.rake6
-rw-r--r--locale/gitlab.pot9
-rw-r--r--spec/features/boards/new_issue_spec.rb4
-rw-r--r--spec/features/boards/reload_boards_on_browser_back_spec.rb2
-rw-r--r--spec/features/boards/sidebar_labels_in_namespaces_spec.rb2
-rw-r--r--spec/features/groups/board_spec.rb4
-rw-r--r--spec/fixtures/database.sql.gzbin0 -> 30 bytes
-rw-r--r--spec/fixtures/lib/gitlab/metrics/dashboard/schemas/custom_variable_full_syntax.json2
-rw-r--r--spec/fixtures/lib/gitlab/metrics/dashboard/schemas/custom_variable_options.json2
-rw-r--r--spec/fixtures/lib/gitlab/metrics/dashboard/schemas/dashboard.json6
-rw-r--r--spec/fixtures/lib/gitlab/metrics/dashboard/schemas/embedded_dashboard.json2
-rw-r--r--spec/fixtures/lib/gitlab/metrics/dashboard/schemas/embedded_panel_groups.json2
-rw-r--r--spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metric_label_values_variable_full_syntax.json4
-rw-r--r--spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panel_groups.json2
-rw-r--r--spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panels.json4
-rw-r--r--spec/fixtures/lib/gitlab/metrics/dashboard/schemas/templating.json2
-rw-r--r--spec/fixtures/lib/gitlab/metrics/dashboard/schemas/text_variable_full_syntax.json2
-rw-r--r--spec/fixtures/lib/gitlab/metrics/dashboard/schemas/variables.json6
-rw-r--r--spec/frontend/boards/components/board_app_spec.js2
-rw-r--r--spec/frontend/boards/components/board_top_bar_spec.js52
-rw-r--r--spec/frontend/boards/components/boards_selector_spec.js1
-rw-r--r--spec/frontend/boards/mock_data.js20
-rw-r--r--spec/frontend/flash_spec.js49
-rw-r--r--spec/lib/backup/database_spec.rb184
-rw-r--r--spec/lib/backup/dump/postgres_spec.rb36
-rw-r--r--spec/lib/backup/manager_spec.rb15
-rw-r--r--spec/support/matchers/schema_matcher.rb16
-rw-r--r--spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb15
-rw-r--r--spec/tasks/gitlab/backup_rake_spec.rb10
-rw-r--r--workhorse/go.mod12
-rw-r--r--workhorse/go.sum18
42 files changed, 578 insertions, 311 deletions
diff --git a/app/assets/javascripts/boards/boards_util.js b/app/assets/javascripts/boards/boards_util.js
index 8062460f052..cf04669ba43 100644
--- a/app/assets/javascripts/boards/boards_util.js
+++ b/app/assets/javascripts/boards/boards_util.js
@@ -1,7 +1,13 @@
import { sortBy, cloneDeep } from 'lodash';
import { TYPE_BOARD, TYPE_ITERATION, TYPE_MILESTONE, TYPE_USER } from '~/graphql_shared/constants';
import { isGid, convertToGraphQLId } from '~/graphql_shared/utils';
-import { ListType, MilestoneIDs, AssigneeFilterType, MilestoneFilterType } from './constants';
+import {
+ ListType,
+ MilestoneIDs,
+ AssigneeFilterType,
+ MilestoneFilterType,
+ boardQuery,
+} from './constants';
export function getMilestone() {
return null;
@@ -305,6 +311,10 @@ export function transformBoardConfig() {
return '';
}
+export function getBoardQuery(boardType) {
+ return boardQuery[boardType].query;
+}
+
export default {
getMilestone,
formatIssue,
diff --git a/app/assets/javascripts/boards/components/board_app.vue b/app/assets/javascripts/boards/components/board_app.vue
index 970e3509d20..d41fc1e9300 100644
--- a/app/assets/javascripts/boards/components/board_app.vue
+++ b/app/assets/javascripts/boards/components/board_app.vue
@@ -11,7 +11,12 @@ export default {
BoardSettingsSidebar,
BoardTopBar,
},
- inject: ['fullBoardId'],
+ inject: ['initialBoardId'],
+ data() {
+ return {
+ boardId: this.initialBoardId,
+ };
+ },
computed: {
...mapGetters(['isSidebarOpen']),
},
@@ -21,13 +26,18 @@ export default {
destroyed() {
window.removeEventListener('popstate', refreshCurrentPage);
},
+ methods: {
+ switchBoard(id) {
+ this.boardId = id;
+ },
+ },
};
</script>
<template>
<div class="boards-app gl-relative" :class="{ 'is-compact': isSidebarOpen }">
- <board-top-bar />
- <board-content :board-id="fullBoardId" />
+ <board-top-bar :board-id="boardId" @switchBoard="switchBoard" />
+ <board-content :board-id="boardId" />
<board-settings-sidebar />
</div>
</template>
diff --git a/app/assets/javascripts/boards/components/board_top_bar.vue b/app/assets/javascripts/boards/components/board_top_bar.vue
index 368feba9a44..2e20ed70bb0 100644
--- a/app/assets/javascripts/boards/components/board_top_bar.vue
+++ b/app/assets/javascripts/boards/components/board_top_bar.vue
@@ -2,6 +2,7 @@
import BoardAddNewColumnTrigger from '~/boards/components/board_add_new_column_trigger.vue';
import BoardsSelector from 'ee_else_ce/boards/components/boards_selector.vue';
import IssueBoardFilteredSearch from 'ee_else_ce/boards/components/issue_board_filtered_search.vue';
+import { getBoardQuery } from 'ee_else_ce/boards/boards_util';
import ConfigToggle from './config_toggle.vue';
import NewBoardButton from './new_board_button.vue';
import ToggleFocus from './toggle_focus.vue';
@@ -19,7 +20,46 @@ export default {
EpicBoardFilteredSearch: () =>
import('ee_component/boards/components/epic_filtered_search.vue'),
},
- inject: ['swimlanesFeatureAvailable', 'canAdminList', 'isSignedIn', 'isIssueBoard'],
+ inject: [
+ 'swimlanesFeatureAvailable',
+ 'canAdminList',
+ 'isSignedIn',
+ 'isIssueBoard',
+ 'fullPath',
+ 'boardType',
+ 'isEpicBoard',
+ 'isApolloBoard',
+ ],
+ props: {
+ boardId: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ board: {},
+ };
+ },
+ apollo: {
+ board: {
+ query() {
+ return getBoardQuery(this.boardType, this.isEpicBoard);
+ },
+ variables() {
+ return {
+ fullPath: this.fullPath,
+ boardId: this.boardId,
+ };
+ },
+ skip() {
+ return !this.isApolloBoard;
+ },
+ update(data) {
+ return data.workspace.board;
+ },
+ },
+ },
};
</script>
@@ -31,7 +71,7 @@ export default {
<div
class="gl-display-flex gl-flex-direction-column gl-md-flex-direction-row gl-flex-grow-1 gl-lg-mb-0 gl-mb-3 gl-w-full"
>
- <boards-selector />
+ <boards-selector :board-apollo="board" @switchBoard="$emit('switchBoard', $event)" />
<new-board-button />
<issue-board-filtered-search v-if="isIssueBoard" />
<epic-board-filtered-search v-else />
diff --git a/app/assets/javascripts/boards/components/boards_selector.vue b/app/assets/javascripts/boards/components/boards_selector.vue
index d26aeb69dd5..33a82ab3827 100644
--- a/app/assets/javascripts/boards/components/boards_selector.vue
+++ b/app/assets/javascripts/boards/components/boards_selector.vue
@@ -51,6 +51,7 @@ export default {
'weights',
'boardType',
'isGroupBoard',
+ 'isApolloBoard',
],
props: {
throttleDuration: {
@@ -58,6 +59,11 @@ export default {
default: 200,
required: false,
},
+ boardApollo: {
+ type: Object,
+ required: false,
+ default: () => ({}),
+ },
},
data() {
return {
@@ -77,6 +83,9 @@ export default {
computed: {
...mapState(['board', 'isBoardLoading']),
+ boardToUse() {
+ return this.isApolloBoard ? this.boardApollo : this.board;
+ },
parentType() {
return this.boardType;
},
@@ -116,7 +125,7 @@ export default {
this.scrollFadeInitialized = false;
this.$nextTick(this.setScrollFade);
},
- board(newBoard) {
+ boardToUse(newBoard) {
document.title = newBoard.name;
},
},
@@ -210,9 +219,14 @@ export default {
boardType: this.boardType,
});
},
+ fullBoardId(boardId) {
+ return fullBoardId(boardId);
+ },
async switchBoard(boardId, e) {
if (isMetaKey(e)) {
window.open(`${this.boardBaseUrl}/${boardId}`, '_blank');
+ } else if (this.isApolloBoard) {
+ this.$emit('switchBoard', this.fullBoardId(boardId));
} else {
this.unsetActiveId();
this.fetchCurrentBoard(boardId);
@@ -235,7 +249,7 @@ export default {
toggle-class="dropdown-menu-toggle"
menu-class="flex-column dropdown-extended-height"
:loading="isBoardLoading"
- :text="board.name"
+ :text="boardToUse.name"
@show="loadBoards"
>
<p class="gl-dropdown-header-top" @mousedown.prevent>
@@ -333,7 +347,7 @@ export default {
:can-admin-board="canAdminBoard"
:scoped-issue-board-feature-enabled="scopedIssueBoardFeatureEnabled"
:weights="weights"
- :current-board="board"
+ :current-board="boardToUse"
:current-page="currentPage"
@cancel="cancel"
/>
diff --git a/app/assets/javascripts/boards/constants.js b/app/assets/javascripts/boards/constants.js
index 91b7f5004ad..7a5ef01606f 100644
--- a/app/assets/javascripts/boards/constants.js
+++ b/app/assets/javascripts/boards/constants.js
@@ -7,6 +7,8 @@ import updateBoardListMutation from './graphql/board_list_update.mutation.graphq
import issueSetSubscriptionMutation from './graphql/issue_set_subscription.mutation.graphql';
import issueSetTitleMutation from './graphql/issue_set_title.mutation.graphql';
+import groupBoardQuery from './graphql/group_board.query.graphql';
+import projectBoardQuery from './graphql/project_board.query.graphql';
/* eslint-disable-next-line @gitlab/require-i18n-strings */
export const AssigneeIdParamValues = ['Any', 'None'];
@@ -59,6 +61,15 @@ export const INCIDENT = 'INCIDENT';
export const flashAnimationDuration = 2000;
+export const boardQuery = {
+ [BoardType.group]: {
+ query: groupBoardQuery,
+ },
+ [BoardType.project]: {
+ query: projectBoardQuery,
+ },
+};
+
export const listsQuery = {
[issuableTypes.issue]: {
query: boardListsQuery,
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index 968832a092d..a44684ab12d 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -24,6 +24,7 @@ const apolloProvider = new VueApollo({
function mountBoardApp(el) {
const { boardId, groupId, fullPath, rootPath } = el.dataset;
+ const isApolloBoard = window.gon?.features?.apolloBoards;
const rawFilterParams = queryToObject(window.location.search, { gatherArrays: true });
@@ -33,20 +34,22 @@ function mountBoardApp(el) {
const boardType = el.dataset.parent;
- store.dispatch('fetchBoard', {
- fullPath,
- fullBoardId: fullBoardId(boardId),
- boardType,
- });
+ if (!isApolloBoard) {
+ store.dispatch('fetchBoard', {
+ fullPath,
+ fullBoardId: fullBoardId(boardId),
+ boardType,
+ });
- store.dispatch('setInitialBoardData', {
- boardId,
- fullBoardId: fullBoardId(boardId),
- fullPath,
- boardType,
- disabled: parseBoolean(el.dataset.disabled) || true,
- issuableType: issuableTypes.issue,
- });
+ store.dispatch('setInitialBoardData', {
+ boardId,
+ fullBoardId: fullBoardId(boardId),
+ fullPath,
+ boardType,
+ disabled: parseBoolean(el.dataset.disabled) || true,
+ issuableType: issuableTypes.issue,
+ });
+ }
// eslint-disable-next-line no-new
new Vue({
@@ -55,8 +58,8 @@ function mountBoardApp(el) {
store,
apolloProvider,
provide: {
- isApolloBoard: window.gon?.features?.apolloBoards,
- fullBoardId: fullBoardId(boardId),
+ isApolloBoard,
+ initialBoardId: fullBoardId(boardId),
disabled: parseBoolean(el.dataset.disabled),
groupId: Number(groupId),
rootPath,
diff --git a/app/assets/javascripts/flash.js b/app/assets/javascripts/flash.js
index cebf73ef8e5..20fb2b1aa94 100644
--- a/app/assets/javascripts/flash.js
+++ b/app/assets/javascripts/flash.js
@@ -18,10 +18,6 @@ const VARIANT_TIP = 'tip';
const FLASH_CLOSED_EVENT = 'flashClosed';
-const getCloseEl = (flashEl) => {
- return flashEl.querySelector('.js-close-icon');
-};
-
const hideFlash = (flashEl, fadeTransition = true) => {
if (fadeTransition) {
Object.assign(flashEl.style, {
@@ -48,12 +44,6 @@ const hideFlash = (flashEl, fadeTransition = true) => {
if (!fadeTransition) flashEl.dispatchEvent(new Event('transitionend'));
};
-const addDismissFlashClickListener = (flashEl, fadeTransition) => {
- // There are some flash elements which do not have a closeEl.
- // https://gitlab.com/gitlab-org/gitlab/blob/763426ef344488972eb63ea5be8744e0f8459e6b/ee/app/views/layouts/header/_read_only_banner.html.haml
- getCloseEl(flashEl)?.addEventListener('click', () => hideFlash(flashEl, fadeTransition));
-};
-
/**
* Render an alert at the top of the page, or, optionally an
* arbitrary existing container. This alert is always dismissible.
@@ -183,7 +173,6 @@ const createAlert = function createAlert({
export {
hideFlash,
- addDismissFlashClickListener,
FLASH_TYPES,
FLASH_CLOSED_EVENT,
createAlert,
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index fd5c4abe729..4c715c4993f 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -16,7 +16,6 @@ import * as tooltips from '~/tooltips';
import { initPrefetchLinks } from '~/lib/utils/navigation_utility';
import { logHelloDeferred } from 'jh_else_ce/lib/logger/hello_deferred';
import initAlertHandler from './alert_handler';
-import { addDismissFlashClickListener } from './flash';
import initTodoToggle from './header';
import initLayoutNav from './layout_nav';
import { handleLocationHash, addSelectOnFocusBehaviour } from './lib/utils/common_utils';
@@ -253,16 +252,6 @@ $('form.filter-form').on('submit', function filterFormSubmitCallback(event) {
visitUrl(action);
});
-const flashContainer = document.querySelector('.flash-container');
-
-if (flashContainer && flashContainer.children.length) {
- flashContainer
- .querySelectorAll('.flash-alert, .flash-notice, .flash-success')
- .forEach((flashEl) => {
- addDismissFlashClickListener(flashEl);
- });
-}
-
// initialize field errors
$('.gl-show-field-errors').each((i, form) => new GlFieldErrors(form));
diff --git a/lib/backup/database.rb b/lib/backup/database.rb
index cf19b4fa8ff..61dd6033eb0 100644
--- a/lib/backup/database.rb
+++ b/lib/backup/database.rb
@@ -6,7 +6,7 @@ module Backup
class Database < Task
extend ::Gitlab::Utils::Override
include Backup::Helper
- attr_reader :force, :config
+ attr_reader :force
IGNORED_ERRORS = [
# Ignore warnings
@@ -18,98 +18,108 @@ module Backup
].freeze
IGNORED_ERRORS_REGEXP = Regexp.union(IGNORED_ERRORS).freeze
- def initialize(database_name, progress, force:)
+ def initialize(progress, force:)
super(progress)
- @database_name = database_name
- @config = base_model.connection_db_config.configuration_hash
@force = force
end
override :dump
- def dump(db_file_name, backup_id)
- FileUtils.mkdir_p(File.dirname(db_file_name))
- FileUtils.rm_f(db_file_name)
- compress_rd, compress_wr = IO.pipe
- compress_pid = spawn(gzip_cmd, in: compress_rd, out: [db_file_name, 'w', 0600])
- compress_rd.close
-
- dump_pid =
- case config[:adapter]
- when "postgresql" then
- progress.print "Dumping PostgreSQL database #{database} ... "
- pg_env
- pgsql_args = ["--clean"] # Pass '--clean' to include 'DROP TABLE' statements in the DB dump.
- pgsql_args << '--if-exists'
-
- if Gitlab.config.backup.pg_schema
- pgsql_args << '-n'
- pgsql_args << Gitlab.config.backup.pg_schema
+ def dump(destination_dir, backup_id)
+ snapshot_ids = base_models_for_backup.each_with_object({}) do |(database_name, base_model), snapshot_ids|
+ base_model.connection.begin_transaction(isolation: :repeatable_read)
- Gitlab::Database::EXTRA_SCHEMAS.each do |schema|
- pgsql_args << '-n'
- pgsql_args << schema.to_s
- end
- end
+ snapshot_ids[database_name] =
+ base_model.connection.execute("SELECT pg_export_snapshot() as snapshot_id;").first['snapshot_id']
+ end
+
+ FileUtils.mkdir_p(destination_dir)
+
+ snapshot_ids.each do |database_name, snapshot_id|
+ base_model = base_models_for_backup[database_name]
+
+ config = base_model.connection_db_config.configuration_hash
+
+ db_file_name = file_name(destination_dir, database_name)
+ FileUtils.rm_f(db_file_name)
+
+ pg_database = config[:database]
- Process.spawn('pg_dump', *pgsql_args, database, out: compress_wr)
+ progress.print "Dumping PostgreSQL database #{pg_database} ... "
+ pg_env(config)
+ pgsql_args = ["--clean"] # Pass '--clean' to include 'DROP TABLE' statements in the DB dump.
+ pgsql_args << '--if-exists'
+ pgsql_args << "--snapshot=#{snapshot_ids[database_name]}"
+
+ if Gitlab.config.backup.pg_schema
+ pgsql_args << '-n'
+ pgsql_args << Gitlab.config.backup.pg_schema
+
+ Gitlab::Database::EXTRA_SCHEMAS.each do |schema|
+ pgsql_args << '-n'
+ pgsql_args << schema.to_s
+ end
end
- compress_wr.close
- success = [compress_pid, dump_pid].all? do |pid|
- Process.waitpid(pid)
- $?.success?
- end
+ success = Backup::Dump::Postgres.new.dump(pg_database, db_file_name, pgsql_args)
+
+ base_model.connection.rollback_transaction
- report_success(success)
- progress.flush
+ raise DatabaseBackupError.new(config, db_file_name) unless success
- raise DatabaseBackupError.new(config, db_file_name) unless success
+ report_success(success)
+ progress.flush
+ end
end
override :restore
- def restore(db_file_name)
- unless File.exist?(db_file_name)
- raise(Backup::Error, "Source database file does not exist #{db_file_name}") if main_database?
+ def restore(destination_dir)
+ base_models_for_backup.each do |database_name, base_model|
+ config = base_model.connection_db_config.configuration_hash
- progress.puts "Source backup for the database #{@database_name} doesn't exist. Skipping the task"
- return
- end
+ db_file_name = file_name(destination_dir, database_name)
+ database = config[:database]
- unless force
- progress.puts 'Removing all tables. Press `Ctrl-C` within 5 seconds to abort'.color(:yellow)
- sleep(5)
- end
+ unless File.exist?(db_file_name)
+ raise(Backup::Error, "Source database file does not exist #{db_file_name}") if main_database?(database_name)
- # Drop all tables Load the schema to ensure we don't have any newer tables
- # hanging out from a failed upgrade
- puts_time 'Cleaning the database ... '.color(:blue)
- Rake::Task['gitlab:db:drop_tables'].invoke
- puts_time 'done'.color(:green)
-
- decompress_rd, decompress_wr = IO.pipe
- decompress_pid = spawn(*%w(gzip -cd), out: decompress_wr, in: db_file_name)
- decompress_wr.close
-
- status, @errors =
- case config[:adapter]
- when "postgresql" then
- progress.print "Restoring PostgreSQL database #{database} ... "
- pg_env
- execute_and_track_errors(pg_restore_cmd, decompress_rd)
+ progress.puts "Source backup for the database #{database_name} doesn't exist. Skipping the task"
+ return false
end
- decompress_rd.close
- Process.waitpid(decompress_pid)
- success = $?.success? && status.success?
+ unless force
+ progress.puts 'Removing all tables. Press `Ctrl-C` within 5 seconds to abort'.color(:yellow)
+ sleep(5)
+ end
- if @errors.present?
- progress.print "------ BEGIN ERRORS -----\n".color(:yellow)
- progress.print @errors.join.color(:yellow)
- progress.print "------ END ERRORS -------\n".color(:yellow)
- end
+ # Drop all tables Load the schema to ensure we don't have any newer tables
+ # hanging out from a failed upgrade
+ drop_tables(database_name)
+
+ decompress_rd, decompress_wr = IO.pipe
+ decompress_pid = spawn(*%w(gzip -cd), out: decompress_wr, in: db_file_name)
+ decompress_wr.close
+
+ status, @errors =
+ case config[:adapter]
+ when "postgresql" then
+ progress.print "Restoring PostgreSQL database #{database} ... "
+ pg_env(config)
+ execute_and_track_errors(pg_restore_cmd(database), decompress_rd)
+ end
+ decompress_rd.close
+
+ Process.waitpid(decompress_pid)
+ success = $?.success? && status.success?
- report_success(success)
- raise Backup::Error, 'Restore failed' unless success
+ if @errors.present?
+ progress.print "------ BEGIN ERRORS -----\n".color(:yellow)
+ progress.print @errors.join.color(:yellow)
+ progress.print "------ END ERRORS -------\n".color(:yellow)
+ end
+
+ report_success(success)
+ raise Backup::Error, 'Restore failed' unless success
+ end
end
override :pre_restore_warning
@@ -144,16 +154,22 @@ module Backup
protected
- def database
- @config[:database]
+ def base_models_for_backup
+ @base_models_for_backup ||= Gitlab::Database.database_base_models_with_gitlab_shared
end
- def base_model
- Gitlab::Database.database_base_models[@database_name]
+ def main_database?(database_name)
+ database_name.to_sym == :main
end
- def main_database?
- @database_name == :main
+ def file_name(base_dir, database_name)
+ prefix = if database_name.to_sym != :main
+ "#{database_name}_"
+ else
+ ''
+ end
+
+ File.join(base_dir, "#{prefix}database.sql.gz")
end
def ignore_error?(line)
@@ -189,7 +205,7 @@ module Backup
end
end
- def pg_env
+ def pg_env(config)
args = {
username: 'PGUSER',
host: 'PGHOST',
@@ -223,7 +239,20 @@ module Backup
private
- def pg_restore_cmd
+ def drop_tables(database_name)
+ if Rake::Task.task_defined? "gitlab:db:drop_tables:#{database_name}"
+ puts_time 'Cleaning the database ... '.color(:blue)
+ Rake::Task["gitlab:db:drop_tables:#{database_name}"].invoke
+ puts_time 'done'.color(:green)
+ elsif Gitlab::Database.database_base_models.one?
+ # In single database, we do not have rake tasks per database
+ puts_time 'Cleaning the database ... '.color(:blue)
+ Rake::Task["gitlab:db:drop_tables"].invoke
+ puts_time 'done'.color(:green)
+ end
+ end
+
+ def pg_restore_cmd(database)
['psql', database]
end
end
diff --git a/lib/backup/dump/postgres.rb b/lib/backup/dump/postgres.rb
new file mode 100644
index 00000000000..1a5128b5a6b
--- /dev/null
+++ b/lib/backup/dump/postgres.rb
@@ -0,0 +1,24 @@
+# frozen_string_literal: true
+module Backup
+ module Dump
+ class Postgres
+ include Backup::Helper
+
+ FILE_PERMISSION = 0o600
+
+ def dump(database_name, output_file, pgsql_args)
+ compress_rd, compress_wr = IO.pipe
+ compress_pid = spawn(gzip_cmd, in: compress_rd, out: [output_file, 'w', FILE_PERMISSION])
+ compress_rd.close
+
+ dump_pid = Process.spawn('pg_dump', *pgsql_args, database_name, out: compress_wr)
+ compress_wr.close
+
+ [compress_pid, dump_pid].all? do |pid|
+ Process.waitpid(pid)
+ $?.success?
+ end
+ end
+ end
+ end
+end
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index f8424f6250e..a7dddcf8619 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -22,7 +22,6 @@ module Backup
:destination_optional, # `true` if the destination might not exist on a successful backup.
:cleanup_path, # Path to remove after a successful backup. Uses `destination_path` when not specified.
:task,
- :task_group,
keyword_init: true
) do
def enabled?
@@ -121,20 +120,11 @@ module Backup
def build_definitions # rubocop:disable Metrics/AbcSize
{
- 'main_db' => TaskDefinition.new(
- human_name: _('main_database'),
- destination_path: 'db/database.sql.gz',
+ 'db' => TaskDefinition.new(
+ human_name: _('database'),
+ destination_path: 'db',
cleanup_path: 'db',
- task: build_db_task(:main),
- task_group: 'db'
- ),
- 'ci_db' => TaskDefinition.new(
- human_name: _('ci_database'),
- destination_path: 'db/ci_database.sql.gz',
- cleanup_path: 'db',
- task: build_db_task(:ci),
- enabled: Gitlab::Database.has_config?(:ci),
- task_group: 'db'
+ task: build_db_task
),
'repositories' => TaskDefinition.new(
human_name: _('repositories'),
@@ -186,11 +176,10 @@ module Backup
}.freeze
end
- def build_db_task(database_name)
- return unless Gitlab::Database.has_config?(database_name) # It will be disabled for a single db setup
-
+ def build_db_task
force = Gitlab::Utils.to_boolean(ENV['force'], default: false)
- Database.new(database_name, progress, force: force)
+
+ Database.new(progress, force: force)
end
def build_repositories_task
@@ -483,7 +472,7 @@ module Backup
end
def skipped?(item)
- skipped.include?(item) || skipped.include?(definitions[item]&.task_group)
+ skipped.include?(item)
end
def skipped
diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake
index 6647a10898f..787df37a8f8 100644
--- a/lib/tasks/gitlab/backup.rake
+++ b/lib/tasks/gitlab/backup.rake
@@ -44,15 +44,13 @@ namespace :gitlab do
namespace :db do
task create: :gitlab_environment do
lock do
- Backup::Manager.new(progress).run_create_task('main_db')
- Backup::Manager.new(progress).run_create_task('ci_db')
+ Backup::Manager.new(progress).run_create_task('db')
end
end
task restore: :gitlab_environment do
lock do
- Backup::Manager.new(progress).run_restore_task('main_db')
- Backup::Manager.new(progress).run_restore_task('ci_db')
+ Backup::Manager.new(progress).run_restore_task('db')
end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 786733b32ee..5a60905422d 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -49682,9 +49682,6 @@ msgstr ""
msgid "ciReport|is loading, errors when loading results"
msgstr ""
-msgid "ci_database"
-msgstr ""
-
msgid "closed"
msgstr ""
@@ -49768,6 +49765,9 @@ msgstr ""
msgid "data"
msgstr ""
+msgid "database"
+msgstr ""
+
msgid "date must not be after 9999-12-31"
msgstr ""
@@ -50208,9 +50208,6 @@ msgstr ""
msgid "locked by %{path_lock_user_name} %{created_at}"
msgstr ""
-msgid "main_database"
-msgstr ""
-
msgid "manual"
msgstr ""
diff --git a/spec/features/boards/new_issue_spec.rb b/spec/features/boards/new_issue_spec.rb
index 1b0695e4e60..d597c57ac1c 100644
--- a/spec/features/boards/new_issue_spec.rb
+++ b/spec/features/boards/new_issue_spec.rb
@@ -14,6 +14,10 @@ RSpec.describe 'Issue Boards new issue', :js, feature_category: :team_planning d
let(:board_list_header) { first('[data-testid="board-list-header"]') }
let(:project_select_dropdown) { find('[data-testid="project-select-dropdown"]') }
+ before do
+ stub_feature_flags(apollo_boards: false)
+ end
+
context 'authorized user' do
before do
project.add_maintainer(user)
diff --git a/spec/features/boards/reload_boards_on_browser_back_spec.rb b/spec/features/boards/reload_boards_on_browser_back_spec.rb
index 0ca680c5ed5..036daee7655 100644
--- a/spec/features/boards/reload_boards_on_browser_back_spec.rb
+++ b/spec/features/boards/reload_boards_on_browser_back_spec.rb
@@ -9,6 +9,8 @@ RSpec.describe 'Ensure Boards do not show stale data on browser back', :js, feat
context 'authorized user' do
before do
+ stub_feature_flags(apollo_boards: false)
+
project.add_maintainer(user)
sign_in(user)
diff --git a/spec/features/boards/sidebar_labels_in_namespaces_spec.rb b/spec/features/boards/sidebar_labels_in_namespaces_spec.rb
index c3bb58df797..39485fe21a9 100644
--- a/spec/features/boards/sidebar_labels_in_namespaces_spec.rb
+++ b/spec/features/boards/sidebar_labels_in_namespaces_spec.rb
@@ -15,6 +15,8 @@ RSpec.describe 'Issue boards sidebar labels select', :js, feature_category: :tea
let_it_be(:board_list) { create(:backlog_list, board: group_board) }
before do
+ stub_feature_flags(apollo_boards: false)
+
load_board group_board_path(group, group_board)
end
diff --git a/spec/features/groups/board_spec.rb b/spec/features/groups/board_spec.rb
index 11ec38f637b..c451a97bed5 100644
--- a/spec/features/groups/board_spec.rb
+++ b/spec/features/groups/board_spec.rb
@@ -14,6 +14,8 @@ RSpec.describe 'Group Boards', feature_category: :team_planning do
let_it_be(:project) { create(:project_empty_repo, group: group) }
before do
+ stub_feature_flags(apollo_boards: false)
+
group.add_maintainer(user)
sign_in(user)
@@ -60,6 +62,8 @@ RSpec.describe 'Group Boards', feature_category: :team_planning do
let_it_be(:issue2) { create(:issue, title: 'issue2', project: project2) }
before do
+ stub_feature_flags(apollo_boards: false)
+
project1.add_guest(user)
project2.add_reporter(user)
diff --git a/spec/fixtures/database.sql.gz b/spec/fixtures/database.sql.gz
new file mode 100644
index 00000000000..a98aa7c53f2
--- /dev/null
+++ b/spec/fixtures/database.sql.gz
Binary files differ
diff --git a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/custom_variable_full_syntax.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/custom_variable_full_syntax.json
index c40befcf8ce..0d9c217afd1 100644
--- a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/custom_variable_full_syntax.json
+++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/custom_variable_full_syntax.json
@@ -7,7 +7,7 @@
"properties": {
"type": { "enum": ["custom"] },
"label": { "type": "string" },
- "options": { "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/custom_variable_options.json" }
+ "options": { "$ref": "custom_variable_options.json" }
},
"additionalProperties": false
}
diff --git a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/custom_variable_options.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/custom_variable_options.json
index de72b947eed..bb78294e43e 100644
--- a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/custom_variable_options.json
+++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/custom_variable_options.json
@@ -5,7 +5,7 @@
"properties": {
"values": {
"type": "array",
- "items": { "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/custom_variable_values.json" }
+ "items": { "$ref": "custom_variable_values.json" }
}
},
"additionalProperties": false
diff --git a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/dashboard.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/dashboard.json
index 40453c61a65..f38f74ae13f 100644
--- a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/dashboard.json
+++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/dashboard.json
@@ -11,10 +11,10 @@
"priority": { "type": "number" },
"panel_groups": {
"type": "array",
- "items": { "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panel_groups.json" }
+ "items": { "$ref": "panel_groups.json" }
},
- "templating": { "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/templating.json" },
- "links": { "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/links.json" }
+ "templating": { "$ref": "templating.json" },
+ "links": { "$ref": "links.json" }
},
"additionalProperties": false
}
diff --git a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/embedded_dashboard.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/embedded_dashboard.json
index b47b81fc103..a5228bc0888 100644
--- a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/embedded_dashboard.json
+++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/embedded_dashboard.json
@@ -6,7 +6,7 @@
"panel_groups": {
"type": "array",
"items": {
- "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/embedded_panel_groups.json"
+ "$ref": "embedded_panel_groups.json"
}
}
},
diff --git a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/embedded_panel_groups.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/embedded_panel_groups.json
index 063016c22fd..b1c34ba1b86 100644
--- a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/embedded_panel_groups.json
+++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/embedded_panel_groups.json
@@ -5,7 +5,7 @@
"properties": {
"panels": {
"type": "array",
- "items": { "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panels.json" }
+ "items": { "$ref": "panels.json" }
}
},
"additionalProperties": false
diff --git a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metric_label_values_variable_full_syntax.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metric_label_values_variable_full_syntax.json
index 145cc476d64..742708e60bd 100644
--- a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metric_label_values_variable_full_syntax.json
+++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metric_label_values_variable_full_syntax.json
@@ -15,8 +15,8 @@
"type": "string"
},
"options": {
- "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metric_label_values_variable_options.json"
+ "$ref": "metric_label_values_variable_options.json"
}
},
"additionalProperties": false
-} \ No newline at end of file
+}
diff --git a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panel_groups.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panel_groups.json
index 392aa0e4480..a5a4428f2f3 100644
--- a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panel_groups.json
+++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panel_groups.json
@@ -9,7 +9,7 @@
"group": { "type": "string" },
"panels": {
"type": "array",
- "items": { "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panels.json" }
+ "items": { "$ref": "panels.json" }
},
"has_custom_metrics": { "type": "boolean" }
},
diff --git a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panels.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panels.json
index 3224e7cfe3f..78369a7a055 100644
--- a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panels.json
+++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/panels.json
@@ -11,11 +11,11 @@
"id": { "type": "string" },
"type": { "type": "string" },
"y_label": { "type": "string" },
- "y_axis": { "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/axis.json" },
+ "y_axis": { "$ref": "axis.json" },
"max_value": { "type": "number" },
"metrics": {
"type": "array",
- "items": { "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metrics.json" }
+ "items": { "$ref": "metrics.json" }
}
},
"additionalProperties": false
diff --git a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/templating.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/templating.json
index 439f7b6b044..c339edec128 100644
--- a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/templating.json
+++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/templating.json
@@ -3,7 +3,7 @@
"type": "object",
"required": ["variables"],
"properties": {
- "variables": { "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/variables.json" }
+ "variables": { "$ref": "variables.json" }
},
"additionalProperties": false
}
diff --git a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/text_variable_full_syntax.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/text_variable_full_syntax.json
index c4382326854..37ff4fdba5f 100644
--- a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/text_variable_full_syntax.json
+++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/text_variable_full_syntax.json
@@ -7,7 +7,7 @@
"properties": {
"type": { "enum": ["text"] },
"label": { "type": "string" },
- "options": { "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/text_variable_options.json" }
+ "options": { "$ref": "text_variable_options.json" }
},
"additionalProperties": false
}
diff --git a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/variables.json b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/variables.json
index 1cf5ae2eaa4..73841d5bd82 100644
--- a/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/variables.json
+++ b/spec/fixtures/lib/gitlab/metrics/dashboard/schemas/variables.json
@@ -4,14 +4,14 @@
"patternProperties": {
"^[a-zA-Z0-9_]*$": {
"anyOf": [
- { "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/text_variable_full_syntax.json" },
+ { "$ref": "text_variable_full_syntax.json" },
{ "type": "string" },
{
"type": "array",
"items": { "type": "string" }
},
- { "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/custom_variable_full_syntax.json" },
- { "$ref": "spec/fixtures/lib/gitlab/metrics/dashboard/schemas/metric_label_values_variable_full_syntax.json" }
+ { "$ref": "custom_variable_full_syntax.json" },
+ { "$ref": "metric_label_values_variable_full_syntax.json" }
]
}
},
diff --git a/spec/frontend/boards/components/board_app_spec.js b/spec/frontend/boards/components/board_app_spec.js
index 872a67a71fb..12318fb5d16 100644
--- a/spec/frontend/boards/components/board_app_spec.js
+++ b/spec/frontend/boards/components/board_app_spec.js
@@ -27,7 +27,7 @@ describe('BoardApp', () => {
wrapper = shallowMount(BoardApp, {
store,
provide: {
- fullBoardId: 'gid://gitlab/Board/1',
+ initialBoardId: 'gid://gitlab/Board/1',
},
});
};
diff --git a/spec/frontend/boards/components/board_top_bar_spec.js b/spec/frontend/boards/components/board_top_bar_spec.js
index af492145eb0..8258d9fe7f4 100644
--- a/spec/frontend/boards/components/board_top_bar_spec.js
+++ b/spec/frontend/boards/components/board_top_bar_spec.js
@@ -1,6 +1,8 @@
import { shallowMount } from '@vue/test-utils';
-import Vue from 'vue';
+import Vue, { nextTick } from 'vue';
+import VueApollo from 'vue-apollo';
import Vuex from 'vuex';
+import createMockApollo from 'helpers/mock_apollo_helper';
import BoardTopBar from '~/boards/components/board_top_bar.vue';
import BoardAddNewColumnTrigger from '~/boards/components/board_add_new_column_trigger.vue';
@@ -9,11 +11,18 @@ import ConfigToggle from '~/boards/components/config_toggle.vue';
import IssueBoardFilteredSearch from '~/boards/components/issue_board_filtered_search.vue';
import NewBoardButton from '~/boards/components/new_board_button.vue';
import ToggleFocus from '~/boards/components/toggle_focus.vue';
+import { BoardType } from '~/boards/constants';
+
+import groupBoardQuery from '~/boards/graphql/group_board.query.graphql';
+import projectBoardQuery from '~/boards/graphql/project_board.query.graphql';
+import { mockProjectBoardResponse, mockGroupBoardResponse } from '../mock_data';
+
+Vue.use(VueApollo);
+Vue.use(Vuex);
describe('BoardTopBar', () => {
let wrapper;
-
- Vue.use(Vuex);
+ let mockApollo;
const createStore = () => {
return new Vuex.Store({
@@ -21,10 +30,22 @@ describe('BoardTopBar', () => {
});
};
+ const projectBoardQueryHandlerSuccess = jest.fn().mockResolvedValue(mockProjectBoardResponse);
+ const groupBoardQueryHandlerSuccess = jest.fn().mockResolvedValue(mockGroupBoardResponse);
+
const createComponent = ({ provide = {} } = {}) => {
const store = createStore();
+ mockApollo = createMockApollo([
+ [projectBoardQuery, projectBoardQueryHandlerSuccess],
+ [groupBoardQuery, groupBoardQueryHandlerSuccess],
+ ]);
+
wrapper = shallowMount(BoardTopBar, {
store,
+ apolloProvider: mockApollo,
+ props: {
+ boardId: 'gid://gitlab/Board/1',
+ },
provide: {
swimlanesFeatureAvailable: false,
canAdminList: false,
@@ -33,7 +54,9 @@ describe('BoardTopBar', () => {
boardType: 'group',
releasesFetchPath: '/releases',
isIssueBoard: true,
+ isEpicBoard: false,
isGroupBoard: true,
+ isApolloBoard: false,
...provide,
},
stubs: { IssueBoardFilteredSearch },
@@ -42,6 +65,7 @@ describe('BoardTopBar', () => {
afterEach(() => {
wrapper.destroy();
+ mockApollo = null;
});
describe('base template', () => {
@@ -83,4 +107,26 @@ describe('BoardTopBar', () => {
expect(wrapper.findComponent(BoardAddNewColumnTrigger).exists()).toBe(true);
});
});
+
+ describe('Apollo boards', () => {
+ it.each`
+ boardType | queryHandler | notCalledHandler
+ ${BoardType.group} | ${groupBoardQueryHandlerSuccess} | ${projectBoardQueryHandlerSuccess}
+ ${BoardType.project} | ${projectBoardQueryHandlerSuccess} | ${groupBoardQueryHandlerSuccess}
+ `('fetches $boardType boards', async ({ boardType, queryHandler, notCalledHandler }) => {
+ createComponent({
+ provide: {
+ boardType,
+ isProjectBoard: boardType === BoardType.project,
+ isGroupBoard: boardType === BoardType.group,
+ isApolloBoard: true,
+ },
+ });
+
+ await nextTick();
+
+ expect(queryHandler).toHaveBeenCalled();
+ expect(notCalledHandler).not.toHaveBeenCalled();
+ });
+ });
});
diff --git a/spec/frontend/boards/components/boards_selector_spec.js b/spec/frontend/boards/components/boards_selector_spec.js
index 7b61ca5e6fd..dfd8d2351a6 100644
--- a/spec/frontend/boards/components/boards_selector_spec.js
+++ b/spec/frontend/boards/components/boards_selector_spec.js
@@ -108,6 +108,7 @@ describe('BoardsSelector', () => {
boardType: isGroupBoard ? 'group' : 'project',
isGroupBoard,
isProjectBoard,
+ isApolloBoard: false,
},
});
};
diff --git a/spec/frontend/boards/mock_data.js b/spec/frontend/boards/mock_data.js
index df41eb05eae..0ab8a89bcca 100644
--- a/spec/frontend/boards/mock_data.js
+++ b/spec/frontend/boards/mock_data.js
@@ -50,6 +50,26 @@ export const mockBoard = {
weight: 2,
};
+export const mockProjectBoardResponse = {
+ data: {
+ workspace: {
+ id: 'gid://gitlab/Project/114',
+ board: mockBoard,
+ __typename: 'Project',
+ },
+ },
+};
+
+export const mockGroupBoardResponse = {
+ data: {
+ workspace: {
+ id: 'gid://gitlab/Group/114',
+ board: mockBoard,
+ __typename: 'Group',
+ },
+ },
+};
+
export const mockBoardConfig = {
milestoneId: 'gid://gitlab/Milestone/114',
milestoneTitle: '14.9',
diff --git a/spec/frontend/flash_spec.js b/spec/frontend/flash_spec.js
index 2f0a52a9884..334117e0e3c 100644
--- a/spec/frontend/flash_spec.js
+++ b/spec/frontend/flash_spec.js
@@ -1,12 +1,6 @@
import * as Sentry from '@sentry/browser';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
-import {
- hideFlash,
- addDismissFlashClickListener,
- FLASH_CLOSED_EVENT,
- createAlert,
- VARIANT_WARNING,
-} from '~/flash';
+import { hideFlash, FLASH_CLOSED_EVENT, createAlert, VARIANT_WARNING } from '~/flash';
jest.mock('@sentry/browser');
@@ -338,45 +332,4 @@ describe('Flash', () => {
});
});
});
-
- describe('addDismissFlashClickListener', () => {
- let el;
-
- describe('with close icon', () => {
- beforeEach(() => {
- el = document.createElement('div');
- el.innerHTML = `
- <div class="flash-container">
- <div class="flash">
- <div class="close-icon js-close-icon"></div>
- </div>
- </div>
- `;
- });
-
- it('removes global flash on click', () => {
- addDismissFlashClickListener(el, false);
-
- el.querySelector('.js-close-icon').click();
-
- expect(document.querySelector('.flash')).toBeNull();
- });
- });
-
- describe('without close icon', () => {
- beforeEach(() => {
- el = document.createElement('div');
- el.innerHTML = `
- <div class="flash-container">
- <div class="flash">
- </div>
- </div>
- `;
- });
-
- it('does not throw', () => {
- expect(() => addDismissFlashClickListener(el, false)).not.toThrow();
- });
- });
- });
});
diff --git a/spec/lib/backup/database_spec.rb b/spec/lib/backup/database_spec.rb
index ed5b34b7f8c..bb7f8c63ee5 100644
--- a/spec/lib/backup/database_spec.rb
+++ b/spec/lib/backup/database_spec.rb
@@ -2,11 +2,14 @@
require 'spec_helper'
-RSpec.describe Backup::Database do
+RSpec.describe Backup::Database, feature_category: :backup_restore do
let(:progress) { StringIO.new }
let(:output) { progress.string }
+ let(:one_db_configured?) { Gitlab::Database.database_base_models.one? }
+ let(:database_models_for_backup) { Gitlab::Database.database_base_models_with_gitlab_shared }
before(:all) do
+ Rake::Task.define_task(:environment)
Rake.application.rake_require 'active_record/railties/databases'
Rake.application.rake_require 'tasks/gitlab/backup'
Rake.application.rake_require 'tasks/gitlab/shell'
@@ -14,14 +17,106 @@ RSpec.describe Backup::Database do
Rake.application.rake_require 'tasks/cache'
end
+ describe '#dump', :delete do
+ let(:backup_id) { 'some_id' }
+ let(:force) { true }
+
+ subject { described_class.new(progress, force: force) }
+
+ before do
+ database_models_for_backup.each do |database_name, base_model|
+ base_model.connection.rollback_transaction unless base_model.connection.open_transactions.zero?
+ end
+ end
+
+ it 'creates gzipped database dumps' do
+ Dir.mktmpdir do |dir|
+ subject.dump(dir, backup_id)
+
+ database_models_for_backup.each_key do |database_name|
+ filename = database_name == 'main' ? 'database.sql.gz' : "#{database_name}_database.sql.gz"
+ expect(File.exist?(File.join(dir, filename))).to eq(true)
+ end
+ end
+ end
+
+ it 'uses snapshots' do
+ Dir.mktmpdir do |dir|
+ base_model = Gitlab::Database.database_base_models['main']
+ expect(base_model.connection).to receive(:begin_transaction).with(
+ isolation: :repeatable_read
+ ).and_call_original
+ expect(base_model.connection).to receive(:execute).with(
+ "SELECT pg_export_snapshot() as snapshot_id;"
+ ).and_call_original
+ expect(base_model.connection).to receive(:rollback_transaction).and_call_original
+
+ subject.dump(dir, backup_id)
+ end
+ end
+
+ describe 'pg_dump arguments' do
+ let(:snapshot_id) { 'fake_id' }
+ let(:pg_args) do
+ [
+ '--clean',
+ '--if-exists',
+ "--snapshot=#{snapshot_id}"
+ ]
+ end
+
+ let(:dumper) { double }
+ let(:destination_dir) { 'tmp' }
+
+ before do
+ allow(Backup::Dump::Postgres).to receive(:new).and_return(dumper)
+ allow(dumper).to receive(:dump).with(any_args).and_return(true)
+
+ database_models_for_backup.each do |database_name, base_model|
+ allow(base_model.connection).to receive(:execute).with(
+ "SELECT pg_export_snapshot() as snapshot_id;"
+ ).and_return(['snapshot_id' => snapshot_id])
+ end
+ end
+
+ it 'calls Backup::Dump::Postgres with correct pg_dump arguments' do
+ expect(dumper).to receive(:dump).with(anything, anything, pg_args)
+
+ subject.dump(destination_dir, backup_id)
+ end
+
+ context 'when a PostgreSQL schema is used' do
+ let(:schema) { 'gitlab' }
+ let(:additional_args) do
+ pg_args + ['-n', schema] + Gitlab::Database::EXTRA_SCHEMAS.flat_map do |schema|
+ ['-n', schema.to_s]
+ end
+ end
+
+ before do
+ allow(Gitlab.config.backup).to receive(:pg_schema).and_return(schema)
+ end
+
+ it 'calls Backup::Dump::Postgres with correct pg_dump arguments' do
+ expect(dumper).to receive(:dump).with(anything, anything, additional_args)
+
+ subject.dump(destination_dir, backup_id)
+ end
+ end
+ end
+ end
+
describe '#restore' do
let(:cmd) { %W[#{Gem.ruby} -e $stdout.puts(1)] }
- let(:data) { Rails.root.join("spec/fixtures/pages_empty.tar.gz").to_s }
+ let(:backup_dir) { Rails.root.join("spec/fixtures/") }
let(:force) { true }
+ let(:rake_task) { instance_double(Rake::Task, invoke: true) }
- subject { described_class.new(Gitlab::Database::MAIN_DATABASE_NAME.to_sym, progress, force: force) }
+ subject { described_class.new(progress, force: force) }
before do
+ allow(Rake::Task).to receive(:[]).with(any_args).and_return(rake_task)
+
allow(subject).to receive(:pg_restore_cmd).and_return(cmd)
end
@@ -30,9 +125,14 @@ RSpec.describe Backup::Database do
it 'warns the user and waits' do
expect(subject).to receive(:sleep)
- expect(Rake::Task['gitlab:db:drop_tables']).to receive(:invoke)
- subject.restore(data)
+ if one_db_configured?
+ expect(Rake::Task['gitlab:db:drop_tables']).to receive(:invoke)
+ else
+ expect(Rake::Task['gitlab:db:drop_tables:main']).to receive(:invoke)
+ end
+
+ subject.restore(backup_dir)
expect(output).to include('Removing all tables. Press `Ctrl-C` within 5 seconds to abort')
end
@@ -43,12 +143,14 @@ RSpec.describe Backup::Database do
end
context 'with an empty .gz file' do
- let(:data) { Rails.root.join("spec/fixtures/pages_empty.tar.gz").to_s }
-
it 'returns successfully' do
- expect(Rake::Task['gitlab:db:drop_tables']).to receive(:invoke)
+ if one_db_configured?
+ expect(Rake::Task['gitlab:db:drop_tables']).to receive(:invoke)
+ else
+ expect(Rake::Task['gitlab:db:drop_tables:main']).to receive(:invoke)
+ end
- subject.restore(data)
+ subject.restore(backup_dir)
expect(output).to include("Restoring PostgreSQL database")
expect(output).to include("[DONE]")
@@ -57,12 +159,18 @@ RSpec.describe Backup::Database do
end
context 'with a corrupted .gz file' do
- let(:data) { Rails.root.join("spec/fixtures/big-image.png").to_s }
+ before do
+ allow(subject).to receive(:file_name).and_return("#{backup_dir}big-image.png")
+ end
it 'raises a backup error' do
- expect(Rake::Task['gitlab:db:drop_tables']).to receive(:invoke)
+ if one_db_configured?
+ expect(Rake::Task['gitlab:db:drop_tables']).to receive(:invoke)
+ else
+ expect(Rake::Task['gitlab:db:drop_tables:main']).to receive(:invoke)
+ end
- expect { subject.restore(data) }.to raise_error(Backup::Error)
+ expect { subject.restore(backup_dir) }.to raise_error(Backup::Error)
end
end
@@ -72,9 +180,13 @@ RSpec.describe Backup::Database do
let(:cmd) { %W[#{Gem.ruby} -e $stderr.write("#{noise}#{visible_error}")] }
it 'filters out noise from errors and has a post restore warning' do
- expect(Rake::Task['gitlab:db:drop_tables']).to receive(:invoke)
+ if one_db_configured?
+ expect(Rake::Task['gitlab:db:drop_tables']).to receive(:invoke)
+ else
+ expect(Rake::Task['gitlab:db:drop_tables:main']).to receive(:invoke)
+ end
- subject.restore(data)
+ subject.restore(backup_dir)
expect(output).to include("ERRORS")
expect(output).not_to include(noise)
@@ -95,9 +207,13 @@ RSpec.describe Backup::Database do
end
it 'overrides default config values' do
- expect(Rake::Task['gitlab:db:drop_tables']).to receive(:invoke)
+ if one_db_configured?
+ expect(Rake::Task['gitlab:db:drop_tables']).to receive(:invoke)
+ else
+ expect(Rake::Task['gitlab:db:drop_tables:main']).to receive(:invoke)
+ end
- subject.restore(data)
+ subject.restore(backup_dir)
expect(output).to include(%("PGHOST"=>"test.example.com"))
expect(output).to include(%("PGPASSWORD"=>"donotchange"))
@@ -107,22 +223,30 @@ RSpec.describe Backup::Database do
end
context 'when the source file is missing' do
- let(:main_database) { described_class.new(Gitlab::Database::MAIN_DATABASE_NAME.to_sym, progress, force: force) }
- let(:ci_database) { described_class.new(Gitlab::Database::CI_DATABASE_NAME.to_sym, progress, force: force) }
- let(:missing_file) { Rails.root.join("spec/fixtures/missing_file.tar.gz").to_s }
-
- it 'main database raises an error about missing source file' do
- expect(Rake::Task['gitlab:db:drop_tables']).not_to receive(:invoke)
-
- expect do
- main_database.restore(missing_file)
- end.to raise_error(Backup::Error, /Source database file does not exist/)
+ context 'for main database' do
+ before do
+ allow(File).to receive(:exist?).and_call_original
+ allow(File).to receive(:exist?).with("#{backup_dir}database.sql.gz").and_return(false)
+ allow(File).to receive(:exist?).with("#{backup_dir}ci_database.sql.gz").and_return(false)
+ end
+
+ it 'raises an error about missing source file' do
+ if one_db_configured?
+ expect(Rake::Task['gitlab:db:drop_tables']).not_to receive(:invoke)
+ else
+ expect(Rake::Task['gitlab:db:drop_tables:main']).not_to receive(:invoke)
+ end
+
+ expect do
+ subject.restore('db')
+ end.to raise_error(Backup::Error, /Source database file does not exist/)
+ end
end
- it 'ci database tolerates missing source file' do
- expect(Rake::Task['gitlab:db:drop_tables']).not_to receive(:invoke)
- skip_if_multiple_databases_not_setup
- expect { ci_database.restore(missing_file) }.not_to raise_error
+ context 'for ci database' do
+ it 'ci database tolerates missing source file' do
+ expect { subject.restore(backup_dir) }.not_to raise_error
+ end
end
end
end
diff --git a/spec/lib/backup/dump/postgres_spec.rb b/spec/lib/backup/dump/postgres_spec.rb
new file mode 100644
index 00000000000..f6a68ab6db9
--- /dev/null
+++ b/spec/lib/backup/dump/postgres_spec.rb
@@ -0,0 +1,36 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Backup::Dump::Postgres, feature_category: :backup_restore do
+ describe '#dump' do
+ let(:pg_database) { 'gitlabhq_test' }
+ let(:destination_dir) { Dir.mktmpdir }
+ let(:db_file_name) { File.join(destination_dir, 'output.gz') }
+
+ let(:pipes) { IO.pipe }
+ let(:gzip_pid) { spawn('gzip -c -1', in: pipes[0], out: [db_file_name, 'w', 0o600]) }
+ let(:pg_dump_pid) { Process.spawn('pg_dump', *args, pg_database, out: pipes[1]) }
+ let(:args) { ['--help'] }
+
+ subject { described_class.new }
+
+ before do
+ allow(IO).to receive(:pipe).and_return(pipes)
+ end
+
+ after do
+ FileUtils.remove_entry destination_dir
+ end
+
+ it 'creates gzipped dump using supplied arguments' do
+ expect(subject).to receive(:spawn).with('gzip -c -1', in: pipes.first,
+ out: [db_file_name, 'w', 0o600]).and_return(gzip_pid)
+ expect(Process).to receive(:spawn).with('pg_dump', *args, pg_database, out: pipes[1]).and_return(pg_dump_pid)
+
+ subject.dump(pg_database, db_file_name, args)
+
+ expect(File.exist?(db_file_name)).to eq(true)
+ end
+ end
+end
diff --git a/spec/lib/backup/manager_spec.rb b/spec/lib/backup/manager_spec.rb
index 992dbec73c2..02889c1535d 100644
--- a/spec/lib/backup/manager_spec.rb
+++ b/spec/lib/backup/manager_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Backup::Manager do
+RSpec.describe Backup::Manager, feature_category: :backup_restore do
include StubENV
let(:progress) { StringIO.new }
@@ -30,8 +30,7 @@ RSpec.describe Backup::Manager do
task: task,
enabled: enabled,
destination_path: 'my_task.tar.gz',
- human_name: 'my task',
- task_group: 'group1'
+ human_name: 'my task'
)
}
end
@@ -63,16 +62,6 @@ RSpec.describe Backup::Manager do
subject.run_create_task('my_task')
end
end
-
- describe 'task group skipped' do
- it 'informs the user' do
- stub_env('SKIP', 'group1')
-
- expect(Gitlab::BackupLogger).to receive(:info).with(message: 'Dumping my task ... [SKIPPED]')
-
- subject.run_create_task('my_task')
- end
- end
end
describe '#run_restore_task' do
diff --git a/spec/support/matchers/schema_matcher.rb b/spec/support/matchers/schema_matcher.rb
index d2f32b60464..d5a07f200dd 100644
--- a/spec/support/matchers/schema_matcher.rb
+++ b/spec/support/matchers/schema_matcher.rb
@@ -16,20 +16,8 @@ module SchemaPath
end
def self.validator(schema_path)
- unless @schema_cache.key?(schema_path)
- @schema_cache[schema_path] = JSONSchemer.schema(schema_path, ref_resolver: SchemaPath.file_ref_resolver)
- end
-
- @schema_cache[schema_path]
- end
-
- def self.file_ref_resolver
- proc do |uri|
- file = Rails.root.join(uri.path)
- raise StandardError, "Ref file #{uri.path} must be json" unless uri.path.ends_with?('.json')
- raise StandardError, "File #{file.to_path} doesn't exists" unless file.exist?
-
- Gitlab::Json.parse(File.read(file))
+ @schema_cache.fetch(schema_path) do
+ @schema_cache[schema_path] = JSONSchemer.schema(schema_path)
end
end
end
diff --git a/spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb b/spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb
index 4df12f7849b..bdb01b12607 100644
--- a/spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb
+++ b/spec/support/shared_examples/services/metrics/dashboard_shared_examples.rb
@@ -12,27 +12,20 @@ RSpec.shared_examples 'misconfigured dashboard service response' do |status_code
end
RSpec.shared_examples 'valid dashboard service response for schema' do
- file_ref_resolver = proc do |uri|
- file = Rails.root.join(uri.path)
- raise StandardError, "Ref file #{uri.path} must be json" unless uri.path.ends_with?('.json')
- raise StandardError, "File #{file.to_path} doesn't exists" unless file.exist?
-
- Gitlab::Json.parse(File.read(file))
- end
-
it 'returns a json representation of the dashboard' do
result = service_call
expect(result.keys).to contain_exactly(:dashboard, :status)
expect(result[:status]).to eq(:success)
- validator = JSONSchemer.schema(dashboard_schema, ref_resolver: file_ref_resolver)
+ schema_path = Rails.root.join('spec/fixtures', dashboard_schema)
+ validator = JSONSchemer.schema(schema_path)
expect(validator.valid?(result[:dashboard].with_indifferent_access)).to be true
end
end
RSpec.shared_examples 'valid dashboard service response' do
- let(:dashboard_schema) { Gitlab::Json.parse(fixture_file('lib/gitlab/metrics/dashboard/schemas/dashboard.json')) }
+ let(:dashboard_schema) { 'lib/gitlab/metrics/dashboard/schemas/dashboard.json' }
it_behaves_like 'valid dashboard service response for schema'
end
@@ -76,7 +69,7 @@ RSpec.shared_examples 'dashboard_version contains SHA256 hash of dashboard file
end
RSpec.shared_examples 'valid embedded dashboard service response' do
- let(:dashboard_schema) { Gitlab::Json.parse(fixture_file('lib/gitlab/metrics/dashboard/schemas/embedded_dashboard.json')) }
+ let(:dashboard_schema) { 'lib/gitlab/metrics/dashboard/schemas/embedded_dashboard.json' }
it_behaves_like 'valid dashboard service response for schema'
end
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index 972851cba8c..4aa6edf4789 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -7,9 +7,7 @@ RSpec.describe 'gitlab:app namespace rake task', :delete, feature_category: :bac
let(:backup_restore_pid_path) { "#{Rails.application.root}/tmp/backup_restore.pid" }
let(:backup_tasks) { %w[db repo uploads builds artifacts pages lfs terraform_state registry packages] }
let(:backup_types) do
- %w[main_db repositories uploads builds artifacts pages lfs terraform_state registry packages].tap do |array|
- array.insert(1, 'ci_db') if Gitlab::Database.has_config?(:ci)
- end
+ %w[db repositories uploads builds artifacts pages lfs terraform_state registry packages]
end
def tars_glob
@@ -94,7 +92,7 @@ RSpec.describe 'gitlab:app namespace rake task', :delete, feature_category: :bac
let(:pid_file) { instance_double(File, write: 12345) }
where(:tasks_name, :rake_task) do
- %w[main_db ci_db] | 'gitlab:backup:db:restore'
+ 'db' | 'gitlab:backup:db:restore'
'repositories' | 'gitlab:backup:repo:restore'
'builds' | 'gitlab:backup:builds:restore'
'uploads' | 'gitlab:backup:uploads:restore'
@@ -260,9 +258,7 @@ RSpec.describe 'gitlab:app namespace rake task', :delete, feature_category: :bac
end
it 'logs the progress to log file' do
- ci_database_status = Gitlab::Database.has_config?(:ci) ? "[SKIPPED]" : "[DISABLED]"
- expect(Gitlab::BackupLogger).to receive(:info).with(message: "Dumping main_database ... [SKIPPED]")
- expect(Gitlab::BackupLogger).to receive(:info).with(message: "Dumping ci_database ... #{ci_database_status}")
+ expect(Gitlab::BackupLogger).to receive(:info).with(message: "Dumping database ... [SKIPPED]")
expect(Gitlab::BackupLogger).to receive(:info).with(message: "Dumping repositories ... ")
expect(Gitlab::BackupLogger).to receive(:info).with(message: "Dumping repositories ... done")
expect(Gitlab::BackupLogger).to receive(:info).with(message: "Dumping uploads ... ")
diff --git a/workhorse/go.mod b/workhorse/go.mod
index 03728719cac..70439ee2bae 100644
--- a/workhorse/go.mod
+++ b/workhorse/go.mod
@@ -32,10 +32,10 @@ require (
gocloud.dev v0.28.0
golang.org/x/image v0.0.0-20220722155232-062f8c9fd539
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616
- golang.org/x/net v0.4.0
+ golang.org/x/net v0.5.0
golang.org/x/oauth2 v0.2.0
golang.org/x/tools v0.2.0
- google.golang.org/grpc v1.51.0
+ google.golang.org/grpc v1.52.0
google.golang.org/protobuf v1.28.1
honnef.co/go/tools v0.3.3
)
@@ -44,7 +44,7 @@ require (
cloud.google.com/go v0.107.0 // indirect
cloud.google.com/go/compute v1.13.0 // indirect
cloud.google.com/go/compute/metadata v0.2.2 // indirect
- cloud.google.com/go/iam v0.7.0 // indirect
+ cloud.google.com/go/iam v0.8.0 // indirect
cloud.google.com/go/monitoring v1.9.0 // indirect
cloud.google.com/go/profiler v0.1.0 // indirect
cloud.google.com/go/storage v1.28.0 // indirect
@@ -111,13 +111,13 @@ require (
golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e // indirect
golang.org/x/mod v0.6.0 // indirect
golang.org/x/sync v0.1.0 // indirect
- golang.org/x/sys v0.3.0 // indirect
- golang.org/x/text v0.5.0 // indirect
+ golang.org/x/sys v0.4.0 // indirect
+ golang.org/x/text v0.6.0 // indirect
golang.org/x/time v0.2.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/api v0.103.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
- google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3 // indirect
+ google.golang.org/genproto v0.0.0-20230117162540-28d6b9783ac4 // indirect
gopkg.in/DataDog/dd-trace-go.v1 v1.32.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
diff --git a/workhorse/go.sum b/workhorse/go.sum
index 0eafc108726..dbb22f7a90a 100644
--- a/workhorse/go.sum
+++ b/workhorse/go.sum
@@ -203,8 +203,9 @@ cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSu
cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc=
cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc=
-cloud.google.com/go/iam v0.7.0 h1:k4MuwOsS7zGJJ+QfZ5vBK8SgHBAvYN/23BWsiihJ1vs=
cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg=
+cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk=
+cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE=
cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc=
cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A=
cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM=
@@ -2118,8 +2119,9 @@ golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfS
golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
-golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
+golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
+golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
golang.org/x/oauth2 v0.0.0-20170912212905-13449ad91cb2/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -2320,8 +2322,9 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220919091848-fb04ddd9f9c8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
+golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -2341,8 +2344,9 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
+golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/time v0.0.0-20170424234030-8be79e1e0910/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -2646,8 +2650,9 @@ google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+
google.golang.org/genproto v0.0.0-20221109142239-94d6d90a7d66/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
-google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3 h1:BCcW+lhENGqZ2R2MsM9oty220E8vY9E4QC1Tq05hN1E=
google.golang.org/genproto v0.0.0-20221201204527-e3fa12d562f3/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg=
+google.golang.org/genproto v0.0.0-20230117162540-28d6b9783ac4 h1:yF0uHwqqYt2tIL2F4hxRWA1ZFX43SEunWAK8MnQiclk=
+google.golang.org/genproto v0.0.0-20230117162540-28d6b9783ac4/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.2.1-0.20170921194603-d4b75ebd4f9f/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
@@ -2693,8 +2698,9 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu
google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
-google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
+google.golang.org/grpc v1.52.0 h1:kd48UiU7EHsV4rnLyOJRuP/Il/UHE7gdDAQ+SZI7nZk=
+google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5vorUY=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=