Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
18ca66076f
commit
42f9b96246
|
|
@ -3670,7 +3670,6 @@ RSpec/MissingFeatureCategory:
|
|||
- 'spec/lib/gitlab/github_import/logger_spec.rb'
|
||||
- 'spec/lib/gitlab/github_import/markdown_text_spec.rb'
|
||||
- 'spec/lib/gitlab/github_import/milestone_finder_spec.rb'
|
||||
- 'spec/lib/gitlab/github_import/object_counter_spec.rb'
|
||||
- 'spec/lib/gitlab/github_import/parallel_importer_spec.rb'
|
||||
- 'spec/lib/gitlab/github_import/representation/diff_note_spec.rb'
|
||||
- 'spec/lib/gitlab/github_import/representation/diff_notes/suggestion_formatter_spec.rb'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,56 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddCurrentUserTodosWidgetToEpicWorkItemType < Gitlab::Database::Migration[2.1]
|
||||
disable_ddl_transaction!
|
||||
restrict_gitlab_migration gitlab_schema: :gitlab_main
|
||||
|
||||
EPIC_ENUM_VALUE = 7
|
||||
WIDGET_NAME = 'Current user todos'
|
||||
WIDGET_ENUM_VALUE = 15
|
||||
|
||||
class MigrationWorkItemType < MigrationRecord
|
||||
self.table_name = 'work_item_types'
|
||||
end
|
||||
|
||||
class MigrationWidgetDefinition < MigrationRecord
|
||||
self.table_name = 'work_item_widget_definitions'
|
||||
end
|
||||
|
||||
def up
|
||||
epic_work_item_type = MigrationWorkItemType.find_by(base_type: EPIC_ENUM_VALUE, namespace_id: nil)
|
||||
|
||||
# Epic type should exist in production applications, checking here to avoid failures
|
||||
# if inconsistent data is present.
|
||||
return say('Epic work item type does not exist, skipping widget creation') unless epic_work_item_type
|
||||
|
||||
widgets = [
|
||||
{
|
||||
work_item_type_id: epic_work_item_type.id,
|
||||
name: WIDGET_NAME,
|
||||
widget_type: WIDGET_ENUM_VALUE
|
||||
}
|
||||
]
|
||||
|
||||
MigrationWidgetDefinition.upsert_all(
|
||||
widgets,
|
||||
unique_by: :index_work_item_widget_definitions_on_default_witype_and_name
|
||||
)
|
||||
end
|
||||
|
||||
def down
|
||||
epic_work_item_type = MigrationWorkItemType.find_by(base_type: EPIC_ENUM_VALUE, namespace_id: nil)
|
||||
|
||||
return say('Epic work item type does not exist, skipping widget removal') unless epic_work_item_type
|
||||
|
||||
widget_definition = MigrationWidgetDefinition.find_by(
|
||||
work_item_type_id: epic_work_item_type.id,
|
||||
widget_type: WIDGET_ENUM_VALUE,
|
||||
name: WIDGET_NAME,
|
||||
namespace_id: nil
|
||||
)
|
||||
|
||||
return say('Widget definition not found, skipping widget removal') unless widget_definition
|
||||
|
||||
widget_definition.destroy
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
ab5b2cd527a1eb799f7f1c3d2f48c850853e498e50693dbe8148c08d52465da8
|
||||
|
|
@ -95,7 +95,14 @@ This causes all SSH requests to the newly promoted **primary** site to
|
|||
fail due to SSH host key mismatch. To prevent this, the primary SSH host
|
||||
keys must be manually replicated to the **secondary** site.
|
||||
|
||||
1. SSH into **each node on your secondary** site and login as the `root` user:
|
||||
The SSH host key path depends on the used software:
|
||||
|
||||
- If you use OpenSSH, the path is `/etc/ssh`.
|
||||
- If you use [`gitlab-sshd`](../../operations/gitlab_sshd.md), the path is `/var/opt/gitlab/gitlab-sshd`.
|
||||
|
||||
In the following steps, replace `<ssh_host_key_path>` with the one you're using:
|
||||
|
||||
1. SSH into **each Rails node on your secondary** site and log in as the `root` user:
|
||||
|
||||
```shell
|
||||
sudo -i
|
||||
|
|
@ -104,40 +111,40 @@ keys must be manually replicated to the **secondary** site.
|
|||
1. Make a backup of any existing SSH host keys:
|
||||
|
||||
```shell
|
||||
find /etc/ssh -iname 'ssh_host_*' -exec cp {} {}.backup.`date +%F` \;
|
||||
find <ssh_host_key_path> -iname 'ssh_host_*' -exec cp {} {}.backup.`date +%F` \;
|
||||
```
|
||||
|
||||
1. Copy OpenSSH host keys from the **primary** site:
|
||||
1. Copy the SSH host keys from the **primary** site:
|
||||
|
||||
If you can access one of the **nodes on your primary** site serving SSH traffic (usually, the main GitLab Rails application nodes) using the **root** user:
|
||||
|
||||
```shell
|
||||
# Run this from the secondary site, change `<primary_site_fqdn>` for the IP or FQDN of the server
|
||||
scp root@<primary_node_fqdn>:/etc/ssh/ssh_host_*_key* /etc/ssh
|
||||
scp root@<primary_node_fqdn>:<ssh_host_key_path>/ssh_host_*_key* <ssh_host_key_path>
|
||||
```
|
||||
|
||||
If you only have access through a user with `sudo` privileges:
|
||||
|
||||
```shell
|
||||
# Run this from the node on your primary site:
|
||||
sudo tar --transform 's/.*\///g' -zcvf ~/geo-host-key.tar.gz /etc/ssh/ssh_host_*_key*
|
||||
sudo tar --transform 's/.*\///g' -zcvf ~/geo-host-key.tar.gz <ssh_host_key_path>/ssh_host_*_key*
|
||||
|
||||
# Run this on each node on your secondary site:
|
||||
scp <user_with_sudo>@<primary_site_fqdn>:geo-host-key.tar.gz .
|
||||
tar zxvf ~/geo-host-key.tar.gz -C /etc/ssh
|
||||
tar zxvf ~/geo-host-key.tar.gz -C <ssh_host_key_path>
|
||||
```
|
||||
|
||||
1. On **each node on your secondary** site, ensure the file permissions are correct:
|
||||
1. On **each Rails node on your secondary** site, ensure the file permissions are correct:
|
||||
|
||||
```shell
|
||||
chown root:root /etc/ssh/ssh_host_*_key*
|
||||
chmod 0600 /etc/ssh/ssh_host_*_key
|
||||
chown root:root <ssh_host_key_path>/ssh_host_*_key*
|
||||
chmod 0600 <ssh_host_key_path>/ssh_host_*_key
|
||||
```
|
||||
|
||||
1. To verify key fingerprint matches, execute the following command on both primary and secondary nodes on each site:
|
||||
|
||||
```shell
|
||||
for file in /etc/ssh/ssh_host_*_key; do ssh-keygen -lf $file; done
|
||||
for file in <ssh_host_key_path>/ssh_host_*_key; do ssh-keygen -lf $file; done
|
||||
```
|
||||
|
||||
You should get an output similar to this one and they should be identical on both nodes:
|
||||
|
|
@ -153,24 +160,32 @@ keys must be manually replicated to the **secondary** site.
|
|||
|
||||
```shell
|
||||
# This will print the fingerprint for private keys:
|
||||
for file in /etc/ssh/ssh_host_*_key; do ssh-keygen -lf $file; done
|
||||
for file in <ssh_host_key_path>/ssh_host_*_key; do ssh-keygen -lf $file; done
|
||||
|
||||
# This will print the fingerprint for public keys:
|
||||
for file in /etc/ssh/ssh_host_*_key.pub; do ssh-keygen -lf $file; done
|
||||
for file in <ssh_host_key_path>/ssh_host_*_key.pub; do ssh-keygen -lf $file; done
|
||||
```
|
||||
|
||||
NOTE:
|
||||
The output for private keys and public keys command should generate the same fingerprint.
|
||||
|
||||
1. Restart `sshd` on **each node on your secondary** site:
|
||||
1. Restart either `sshd` for OpenSSH or the `gitlab-sshd` service on **each Rails node on your secondary** site:
|
||||
|
||||
```shell
|
||||
# Debian or Ubuntu installations
|
||||
sudo service ssh reload
|
||||
- For OpenSSH:
|
||||
|
||||
# CentOS installations
|
||||
sudo service sshd reload
|
||||
```
|
||||
```shell
|
||||
# Debian or Ubuntu installations
|
||||
sudo service ssh reload
|
||||
|
||||
# CentOS installations
|
||||
sudo service sshd reload
|
||||
```
|
||||
|
||||
- For `gitlab-sshd`:
|
||||
|
||||
```shell
|
||||
sudo gitlab-ctl restart gitlab-sshd
|
||||
```
|
||||
|
||||
1. Verify SSH is still functional.
|
||||
|
||||
|
|
|
|||
|
|
@ -46,13 +46,13 @@ To unlock a locked user:
|
|||
sudo -u git -H bundle exec rails console -e production
|
||||
```
|
||||
|
||||
1. Find the user to unlock. You can search by email or ID.
|
||||
1. Find the user to unlock. You can search by email:
|
||||
|
||||
```ruby
|
||||
user = User.find_by(email: 'admin@local.host')
|
||||
```
|
||||
|
||||
or
|
||||
Or you can search by ID:
|
||||
|
||||
```ruby
|
||||
user = User.where(id: 1).first
|
||||
|
|
@ -64,7 +64,7 @@ To unlock a locked user:
|
|||
user.unlock_access!
|
||||
```
|
||||
|
||||
1. Exit the console with <kbd>Control</kbd>+<kbd>d</kbd>
|
||||
1. Exit the console with <kbd>Control</kbd>+<kbd>d</kbd>.
|
||||
|
||||
The user should now be able to sign in.
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ module Gitlab
|
|||
requirement_legacy: 'Requirement legacy',
|
||||
test_reports: 'Test reports',
|
||||
notifications: 'Notifications',
|
||||
current_user_todos: "Current user todos",
|
||||
current_user_todos: 'Current user todos',
|
||||
award_emoji: 'Award emoji',
|
||||
linked_items: 'Linked items'
|
||||
}.freeze
|
||||
|
|
@ -124,6 +124,7 @@ module Gitlab
|
|||
:health_status,
|
||||
:status,
|
||||
:notifications,
|
||||
:current_user_todos,
|
||||
:award_emoji,
|
||||
:linked_items
|
||||
],
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ module Gitlab
|
|||
|
||||
CACHING = Gitlab::Cache::Import::Caching
|
||||
|
||||
IMPORT_CACHING_TIMEOUT = 2.weeks.to_i
|
||||
|
||||
class << self
|
||||
# Increments the project and the global counters if the given value is >= 1
|
||||
def increment(project, object_type, operation, value: 1)
|
||||
|
|
@ -50,7 +52,7 @@ module Gitlab
|
|||
.sort
|
||||
.each do |counter|
|
||||
object_type = counter.split('/').last
|
||||
result[operation][object_type] = CACHING.read_integer(counter)
|
||||
result[operation][object_type] = CACHING.read_integer(counter) || 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -84,11 +86,11 @@ module Gitlab
|
|||
|
||||
add_counter_to_list(project, operation, counter_key)
|
||||
|
||||
CACHING.increment_by(counter_key, value)
|
||||
CACHING.increment_by(counter_key, value, timeout: IMPORT_CACHING_TIMEOUT)
|
||||
end
|
||||
|
||||
def add_counter_to_list(project, operation, key)
|
||||
CACHING.set_add(counter_list_key(project, operation), key)
|
||||
CACHING.set_add(counter_list_key(project, operation), key, timeout: IMPORT_CACHING_TIMEOUT)
|
||||
end
|
||||
|
||||
def counter_list_key(project, operation)
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Gitlab::GithubImport::ObjectCounter, :clean_gitlab_redis_cache do
|
||||
RSpec.describe Gitlab::GithubImport::ObjectCounter, :clean_gitlab_redis_cache, feature_category: :importers do
|
||||
let_it_be(:project) { create(:project, :import_started, import_type: 'github', import_url: 'https://github.com/vim/vim.git') }
|
||||
|
||||
it 'validates the operation being incremented' do
|
||||
|
|
@ -38,9 +38,6 @@ RSpec.describe Gitlab::GithubImport::ObjectCounter, :clean_gitlab_redis_cache do
|
|||
expect(Gitlab::Metrics)
|
||||
.not_to receive(:counter)
|
||||
|
||||
expect(Gitlab::Metrics)
|
||||
.not_to receive(:counter)
|
||||
|
||||
described_class.increment(project, :issue, :fetched, value: 0)
|
||||
described_class.increment(project, :issue, :imported, value: nil)
|
||||
|
||||
|
|
@ -73,6 +70,27 @@ RSpec.describe Gitlab::GithubImport::ObjectCounter, :clean_gitlab_redis_cache do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when import is in progress but cache expired' do
|
||||
before do
|
||||
described_class.increment(project, :issue, :fetched, value: 10)
|
||||
described_class.increment(project, :issue, :imported, value: 8)
|
||||
allow(Gitlab::Cache::Import::Caching).to receive(:read_integer).and_return(nil)
|
||||
end
|
||||
|
||||
it 'returns 0 instead of nil so process can complete' do
|
||||
expect(described_class.summary(project)).to eq(
|
||||
{
|
||||
"fetched" => {
|
||||
"issue" => 0
|
||||
},
|
||||
"imported" => {
|
||||
"issue" => 0
|
||||
}
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there are no cached import statistics' do
|
||||
context 'when project import is in progress' do
|
||||
it 'includes an empty object counts stats in response' do
|
||||
|
|
|
|||
|
|
@ -0,0 +1,121 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
require_migration!
|
||||
|
||||
RSpec.describe AddCurrentUserTodosWidgetToEpicWorkItemType, :migration, feature_category: :team_planning do
|
||||
include MigrationHelpers::WorkItemTypesHelper
|
||||
|
||||
let(:work_item_types) { table(:work_item_types) }
|
||||
let(:work_item_widget_definitions) { table(:work_item_widget_definitions) }
|
||||
let(:base_types) do
|
||||
{
|
||||
issue: 0,
|
||||
incident: 1,
|
||||
test_case: 2,
|
||||
requirement: 3,
|
||||
task: 4,
|
||||
objective: 5,
|
||||
key_result: 6,
|
||||
epic: 7
|
||||
}
|
||||
end
|
||||
|
||||
let(:epic_widgets) do
|
||||
{
|
||||
'Assignees' => 0,
|
||||
'Description' => 1,
|
||||
'Hierarchy' => 2,
|
||||
'Labels' => 3,
|
||||
'Notes' => 5,
|
||||
'Start and due date' => 6,
|
||||
'Health status' => 7,
|
||||
'Status' => 11,
|
||||
'Notifications' => 14,
|
||||
'Award emoji' => 16
|
||||
}.freeze
|
||||
end
|
||||
|
||||
after(:all) do
|
||||
# Make sure base types are recreated after running the migration
|
||||
# because migration specs are not run in a transaction
|
||||
reset_work_item_types
|
||||
end
|
||||
|
||||
before do
|
||||
reset_db_state_prior_to_migration
|
||||
end
|
||||
|
||||
describe '#up' do
|
||||
it 'adds current user todos widget to epic work item type', :aggregate_failures do
|
||||
expect do
|
||||
migrate!
|
||||
end.to change { work_item_widget_definitions.count }.by(1)
|
||||
|
||||
epic_type = work_item_types.find_by(namespace_id: nil, base_type: described_class::EPIC_ENUM_VALUE)
|
||||
created_widget = work_item_widget_definitions.last
|
||||
|
||||
expect(created_widget).to have_attributes(
|
||||
widget_type: described_class::WIDGET_ENUM_VALUE,
|
||||
name: described_class::WIDGET_NAME,
|
||||
work_item_type_id: epic_type.id
|
||||
)
|
||||
end
|
||||
|
||||
context 'when epic type does not exist' do
|
||||
it 'skips creating the new widget definition' do
|
||||
work_item_types.where(namespace_id: nil, base_type: base_types[:epic]).delete_all
|
||||
|
||||
expect do
|
||||
migrate!
|
||||
end.to not_change(work_item_widget_definitions, :count)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#down' do
|
||||
it 'removes current user todos widget from epic work item type' do
|
||||
migrate!
|
||||
|
||||
expect { schema_migrate_down! }.to change { work_item_widget_definitions.count }.by(-1)
|
||||
end
|
||||
end
|
||||
|
||||
def reset_db_state_prior_to_migration
|
||||
# Database needs to be in a similar state as when this migration was created
|
||||
work_item_types.delete_all
|
||||
|
||||
create_work_item!('Issue', :issue, 'issue-type-issue')
|
||||
create_work_item!('Incident', :incident, 'issue-type-incident')
|
||||
create_work_item!('Test Case', :test_case, 'issue-type-test-case')
|
||||
create_work_item!('Requirement', :requirement, 'issue-type-requirements')
|
||||
create_work_item!('Task', :task, 'issue-type-task')
|
||||
create_work_item!('Objective', :objective, 'issue-type-objective')
|
||||
create_work_item!('Key Result', :key_result, 'issue-type-keyresult')
|
||||
|
||||
epic_type = create_work_item!('Epic', :epic, 'issue-type-epic')
|
||||
|
||||
widgets = epic_widgets.map do |widget_name, widget_enum_value|
|
||||
{
|
||||
work_item_type_id: epic_type.id,
|
||||
name: widget_name,
|
||||
widget_type: widget_enum_value
|
||||
}
|
||||
end
|
||||
|
||||
# Creating all widgets for the type so the state in the DB is as close as possible to the actual state
|
||||
work_item_widget_definitions.upsert_all(
|
||||
widgets,
|
||||
unique_by: :index_work_item_widget_definitions_on_default_witype_and_name
|
||||
)
|
||||
end
|
||||
|
||||
def create_work_item!(type_name, base_type, icon_name)
|
||||
work_item_types.create!(
|
||||
name: type_name,
|
||||
namespace_id: nil,
|
||||
base_type: base_types[base_type],
|
||||
icon_name: icon_name
|
||||
)
|
||||
end
|
||||
end
|
||||
Loading…
Reference in New Issue