Commit cbcdb6de authored by Stan Hu's avatar Stan Hu

Merge branch 'mk/decouple-geo-node-identity-from-external-url' into 'master'

Allow multiple secondary nodes behind a load balancer

See merge request gitlab-org/gitlab-ee!10755
parents 5071d015 0e6ef209
...@@ -407,6 +407,16 @@ production: &base ...@@ -407,6 +407,16 @@ production: &base
# dsn: https://<key>@sentry.io/<project> # dsn: https://<key>@sentry.io/<project>
# environment: 'production' # e.g. development, staging, production # environment: 'production' # e.g. development, staging, production
## Geo
# NOTE: These settings will only take effect if Geo is enabled
geo:
# This is an optional identifier which Geo nodes can use to identify themselves.
# For example, if external_url is the same for two secondaries, you must specify
# a unique Geo node name for those secondaries.
#
# If it is blank, it defaults to external_url.
node_name: ''
# #
# 2. GitLab CI settings # 2. GitLab CI settings
# ========================== # ==========================
......
...@@ -268,6 +268,9 @@ Settings.pages.admin['certificate'] ||= '' ...@@ -268,6 +268,9 @@ Settings.pages.admin['certificate'] ||= ''
# #
# Geo # Geo
# #
Settings['geo'] ||= Settingslogic.new({})
# For backwards compatibility, default to gitlab_url and if so, ensure it ends with "/"
Settings.geo['node_name'] = Settings.geo['node_name'].presence || Settings.gitlab['url'].chomp('/').concat('/')
# #
# External merge request diffs # External merge request diffs
......
...@@ -1385,9 +1385,10 @@ ActiveRecord::Schema.define(version: 20190426180107) do ...@@ -1385,9 +1385,10 @@ ActiveRecord::Schema.define(version: 20190426180107) do
t.integer "verification_max_capacity", default: 100, null: false t.integer "verification_max_capacity", default: 100, null: false
t.integer "minimum_reverification_interval", default: 7, null: false t.integer "minimum_reverification_interval", default: 7, null: false
t.string "internal_url" t.string "internal_url"
t.string "name", null: false
t.index ["access_key"], name: "index_geo_nodes_on_access_key", using: :btree t.index ["access_key"], name: "index_geo_nodes_on_access_key", using: :btree
t.index ["name"], name: "index_geo_nodes_on_name", unique: true, using: :btree
t.index ["primary"], name: "index_geo_nodes_on_primary", using: :btree t.index ["primary"], name: "index_geo_nodes_on_primary", using: :btree
t.index ["url"], name: "index_geo_nodes_on_url", unique: true, using: :btree
end end
create_table "geo_repositories_changed_events", force: :cascade do |t| create_table "geo_repositories_changed_events", force: :cascade do |t|
......
...@@ -19,6 +19,7 @@ Example response: ...@@ -19,6 +19,7 @@ Example response:
[ [
{ {
"id": 1, "id": 1,
"name": "us-node",
"url": "https://primary.example.com/", "url": "https://primary.example.com/",
"internal_url": "https://internal.example.com/", "internal_url": "https://internal.example.com/",
"primary": true, "primary": true,
...@@ -31,6 +32,7 @@ Example response: ...@@ -31,6 +32,7 @@ Example response:
}, },
{ {
"id": 2, "id": 2,
"name": "cn-node",
"url": "https://secondary.example.com/", "url": "https://secondary.example.com/",
"internal_url": "https://secondary.example.com/", "internal_url": "https://secondary.example.com/",
"primary": false, "primary": false,
...@@ -59,6 +61,7 @@ Example response: ...@@ -59,6 +61,7 @@ Example response:
```json ```json
{ {
"id": 1, "id": 1,
"name": "us-node",
"url": "https://primary.example.com/", "url": "https://primary.example.com/",
"internal_url": "https://primary.example.com/", "internal_url": "https://primary.example.com/",
"primary": true, "primary": true,
...@@ -85,7 +88,8 @@ PUT /geo_nodes/:id ...@@ -85,7 +88,8 @@ PUT /geo_nodes/:id
|----------------------|---------|-----------|---------------------------------------------------------------------------| |----------------------|---------|-----------|---------------------------------------------------------------------------|
| `id` | integer | yes | The ID of the Geo node. | | `id` | integer | yes | The ID of the Geo node. |
| `enabled` | boolean | no | Flag indicating if the Geo node is enabled. | | `enabled` | boolean | no | Flag indicating if the Geo node is enabled. |
| `url` | string | no | The URL to connect to the Geo node. | | `name` | string | yes | The unique identifier for the Geo node. Must match `geo_node_name` if it is set in gitlab.rb, otherwise it must match `external_url`. |
| `url` | string | yes | The user-facing URL of the Geo node. |
| `internal_url` | string | no | The URL defined on the primary node that secondary nodes should use to contact it. Returns `url` if not set.| | `internal_url` | string | no | The URL defined on the primary node that secondary nodes should use to contact it. Returns `url` if not set.|
| `files_max_capacity` | integer | no | Control the maximum concurrency of LFS/attachment backfill for this secondary node. | | `files_max_capacity` | integer | no | Control the maximum concurrency of LFS/attachment backfill for this secondary node. |
| `repos_max_capacity` | integer | no | Control the maximum concurrency of repository backfill for this secondary node. | | `repos_max_capacity` | integer | no | Control the maximum concurrency of repository backfill for this secondary node. |
...@@ -96,6 +100,7 @@ Example response: ...@@ -96,6 +100,7 @@ Example response:
```json ```json
{ {
"id": 1, "id": 1,
"name": "cn-node",
"url": "https://secondary.example.com/", "url": "https://secondary.example.com/",
"internal_url": "https://secondary.example.com/", "internal_url": "https://secondary.example.com/",
"primary": false, "primary": false,
...@@ -138,6 +143,7 @@ Example response: ...@@ -138,6 +143,7 @@ Example response:
```json ```json
{ {
"id": 1, "id": 1,
"name": "us-node",
"url": "https://primary.example.com/", "url": "https://primary.example.com/",
"internal_url": "https://primary.example.com/", "internal_url": "https://primary.example.com/",
"primary": true, "primary": true,
......
...@@ -12,7 +12,8 @@ All Geo nodes have the following settings: ...@@ -12,7 +12,8 @@ All Geo nodes have the following settings:
| Setting | Description | | Setting | Description |
| --------| ----------- | | --------| ----------- |
| Primary | This marks a Geo Node as **primary** node. There can be only one **primary** node; make sure that you first add the **primary** node and then all the others. | | Primary | This marks a Geo Node as **primary** node. There can be only one **primary** node; make sure that you first add the **primary** node and then all the others. |
| URL | The instance's full URL, in the same way it is configured in `/etc/gitlab/gitlab.rb` (Omnibus GitLab installations) or `gitlab.yml` (source based installations). | | Name | The unique identifier for the Geo node. Must match the setting `gitlab_rails[geo_node_name]` in `/etc/gitlab/gitlab.rb`. The setting defaults to `external_url` with a trailing slash. |
| URL | The instance's user-facing URL. |
The node you're reading from is indicated with a green `Current node` label, and The node you're reading from is indicated with a green `Current node` label, and
the **primary** node is given a blue `Primary` label. Remember that you can only make the **primary** node is given a blue `Primary` label. Remember that you can only make
...@@ -55,3 +56,15 @@ which is used by users. Internal URL does not need to be a private address. ...@@ -55,3 +56,15 @@ which is used by users. Internal URL does not need to be a private address.
Internal URL defaults to External URL, but you can customize it under Internal URL defaults to External URL, but you can customize it under
**Admin area > Geo Nodes**. **Admin area > Geo Nodes**.
## Multiple secondary nodes behind a load balancer
In GitLab 11.11, **secondary** nodes can use identical external URLs as long as
a unique `name` is set for each Geo node. The `gitlab.rb` setting
`gitlab_rails[geo_node_name]` must:
- Be set for each GitLab instance that runs `unicorn`, `sidekiq`, or `geo_logcursor`.
- Match a Geo node name.
The load balancer must use sticky sessions in order to avoid authentication
failures and cross site request errors.
...@@ -47,6 +47,7 @@ class Admin::Geo::NodesController < Admin::Geo::ApplicationController ...@@ -47,6 +47,7 @@ class Admin::Geo::NodesController < Admin::Geo::ApplicationController
def geo_node_params def geo_node_params
params.require(:geo_node).permit( params.require(:geo_node).permit(
:name,
:url, :url,
:internal_url, :internal_url,
:primary, :primary,
......
...@@ -14,7 +14,7 @@ module EE ...@@ -14,7 +14,7 @@ module EE
return super if signed_in? return super if signed_in?
if ::Gitlab::Geo.secondary_with_primary? if ::Gitlab::Geo.secondary_with_primary?
redirect_to oauth_geo_auth_url(host: ::Gitlab::Geo.current_node.url, state: geo_login_state.encode) redirect_to oauth_geo_auth_url(host: GeoNode.current_node_url, state: geo_login_state.encode)
else else
super super
end end
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
class GeoNode < ApplicationRecord class GeoNode < ApplicationRecord
include Presentable include Presentable
include Geo::SelectiveSync include Geo::SelectiveSync
include StripAttribute
SELECTIVE_SYNC_TYPES = %w[namespaces shards].freeze SELECTIVE_SYNC_TYPES = %w[namespaces shards].freeze
...@@ -15,7 +16,8 @@ class GeoNode < ApplicationRecord ...@@ -15,7 +16,8 @@ class GeoNode < ApplicationRecord
has_many :namespaces, through: :geo_node_namespace_links has_many :namespaces, through: :geo_node_namespace_links
has_one :status, class_name: 'GeoNodeStatus' has_one :status, class_name: 'GeoNodeStatus'
validates :url, presence: true, uniqueness: { case_sensitive: false }, addressable_url: true validates :name, presence: true, uniqueness: { case_sensitive: false }, length: { maximum: 255 }
validates :url, presence: true, addressable_url: true
validates :internal_url, addressable_url: true, allow_blank: true, allow_nil: true validates :internal_url, addressable_url: true, allow_blank: true, allow_nil: true
validates :primary, uniqueness: { message: 'node already exists' }, if: :primary validates :primary, uniqueness: { message: 'node already exists' }, if: :primary
...@@ -54,22 +56,27 @@ class GeoNode < ApplicationRecord ...@@ -54,22 +56,27 @@ class GeoNode < ApplicationRecord
mode: :per_attribute_iv, mode: :per_attribute_iv,
encode: true encode: true
strip_attributes :name
class << self class << self
# Set in gitlab.rb as external_url
def current_node_url def current_node_url
RequestStore.fetch('geo_node:current_node_url') do RequestStore.fetch('geo_node:current_node_url') do
cfg = Gitlab.config.gitlab Gitlab.config.gitlab.url
end
uri = URI.parse("#{cfg.protocol}://#{cfg.host}:#{cfg.port}#{cfg.relative_url_root}") end
uri.path += '/' unless uri.path.end_with?('/')
uri.to_s # Set in gitlab.rb as geo_node_name
def current_node_name
RequestStore.fetch('geo_node:current_node_name') do
Gitlab.config.geo.node_name
end end
end end
def current_node def current_node
return unless column_names.include?('url') return unless column_names.include?('name')
GeoNode.find_by(url: current_node_url) GeoNode.find_by(name: current_node_name)
end end
def primary_node def primary_node
...@@ -107,7 +114,7 @@ class GeoNode < ApplicationRecord ...@@ -107,7 +114,7 @@ class GeoNode < ApplicationRecord
end end
def current? def current?
self.class.current_node_url == url self.class.current_node_name == name
end end
def secondary? def secondary?
...@@ -275,7 +282,7 @@ class GeoNode < ApplicationRecord ...@@ -275,7 +282,7 @@ class GeoNode < ApplicationRecord
# Prevent locking yourself out # Prevent locking yourself out
def check_not_adding_primary_as_secondary def check_not_adding_primary_as_secondary
if url == self.class.current_node_url if name == self.class.current_node_name
errors.add(:base, 'Current node must be the primary node or you will be locking yourself out') errors.add(:base, 'Current node must be the primary node or you will be locking yourself out')
end end
end end
......
...@@ -70,7 +70,16 @@ module Geo ...@@ -70,7 +70,16 @@ module Geo
end end
def metric_labels(node) def metric_labels(node)
{ url: node.url } labels = { name: node.name }
# Installations that existed before 11.11 were using the `url` label. This
# line preserves continuity of metrics.
#
# This can be removed in 12.0+ since there will have been at least one
# release worth of data labeled with `name`.
labels[:url] = node.name
labels
end end
def prometheus_enabled? def prometheus_enabled?
......
= form_errors(geo_node) = form_errors(geo_node)
.form-row.form-group .form-row.form-group
.form-group.col-sm-6
= form.label :name, _('Name'), class: 'font-weight-bold'
= form.text_field :name, class: 'form-control'
.form-text.text-muted= _('The unique identifier for the Geo node. Must match `geo_node_name` if it is set in gitlab.rb, otherwise it must match `external_url`')
.form-group.col-sm-6 .form-group.col-sm-6
= form.label :url, s_('Geo|URL'), class: 'font-weight-bold' = form.label :url, s_('Geo|URL'), class: 'font-weight-bold'
= form.text_field :url, class: 'form-control' = form.text_field :url, class: 'form-control'
.form-text.text-muted= _('The user-facing URL of the Geo node.')
.form-group.col-sm-6.js-internal-url{ class: ('hidden' unless geo_node.primary?) } .form-group.col-sm-6.js-internal-url{ class: ('hidden' unless geo_node.primary?) }
= form.label :internal_url, s_('Geo|Internal URL'), class: 'font-weight-bold' = form.label :internal_url, s_('Geo|Internal URL'), class: 'font-weight-bold'
......
---
title: Allow multiple secondary nodes behind a load balancer
merge_request: 10755
author:
type: added
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddNameToGeoNodes < ActiveRecord::Migration[5.0]
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
def up
add_column :geo_nodes, :name, :string
# url is also unique, and its type and size is identical to the name column,
# so this is safe.
execute "UPDATE geo_nodes SET name = url;"
# url is also `null: false`, so this is safe.
change_column :geo_nodes, :name, :string, null: false
end
def down
remove_column :geo_nodes, :name
end
end
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddNameIndexToGeoNodes < ActiveRecord::Migration[5.0]
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :geo_nodes, :name, unique: true
end
def down
remove_concurrent_index :geo_nodes, :name
end
end
# frozen_string_literal: true
# See http://doc.gitlab.com/ce/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class RemoveUrlIndexFromGeoNodes < ActiveRecord::Migration[5.0]
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
disable_ddl_transaction!
def up
remove_concurrent_index :geo_nodes, :url
end
def down
add_concurrent_index :geo_nodes, :url, unique: true
end
end
...@@ -143,7 +143,8 @@ module API ...@@ -143,7 +143,8 @@ module API
end end
params do params do
optional :enabled, type: Boolean, desc: 'Flag indicating if the Geo node is enabled' optional :enabled, type: Boolean, desc: 'Flag indicating if the Geo node is enabled'
optional :url, type: String, desc: 'The URL to connect to the Geo node' optional :name, type: String, desc: 'The unique identifier for the Geo node. Must match `geo_node_name` if it is set in gitlab.rb, otherwise it must match `external_url`'
optional :url, type: String, desc: 'The user-facing URL of the Geo node'
optional :internal_url, type: String, desc: 'The URL defined on the primary node that secondary nodes should use to contact it. Defaults to url' optional :internal_url, type: String, desc: 'The URL defined on the primary node that secondary nodes should use to contact it. Defaults to url'
optional :files_max_capacity, type: Integer, desc: 'Control the maximum concurrency of LFS/attachment backfill for this secondary node' optional :files_max_capacity, type: Integer, desc: 'Control the maximum concurrency of LFS/attachment backfill for this secondary node'
optional :repos_max_capacity, type: Integer, desc: 'Control the maximum concurrency of repository backfill for this secondary node' optional :repos_max_capacity, type: Integer, desc: 'Control the maximum concurrency of repository backfill for this secondary node'
......
...@@ -456,6 +456,7 @@ module EE ...@@ -456,6 +456,7 @@ module EE
include ::API::Helpers::RelatedResourcesHelpers include ::API::Helpers::RelatedResourcesHelpers
expose :id expose :id
expose :name
expose :url expose :url
expose :internal_url expose :internal_url
expose :primary?, as: :primary expose :primary?, as: :primary
......
...@@ -6,8 +6,8 @@ module Gitlab ...@@ -6,8 +6,8 @@ module Gitlab
extend self extend self
def set_primary_geo_node def set_primary_geo_node
node = GeoNode.new(primary: true, url: GeoNode.current_node_url) node = GeoNode.new(primary: true, name: GeoNode.current_node_name, url: GeoNode.current_node_url)
$stdout.puts "Saving primary Geo node with URL #{node.url} ..." $stdout.puts "Saving primary Geo node with name #{node.name} and URL #{node.url} ..."
node.save node.save
if node.persisted? if node.persisted?
......
...@@ -217,7 +217,8 @@ namespace :geo do ...@@ -217,7 +217,8 @@ namespace :geo do
end end
puts puts
puts GeoNode.current_node_url puts "Name: #{GeoNode.current_node_name}"
puts "URL: #{GeoNode.current_node_url}"
puts '-----------------------------------------------------'.color(:yellow) puts '-----------------------------------------------------'.color(:yellow)
unless Gitlab::Database.postgresql_minimum_supported_version? unless Gitlab::Database.postgresql_minimum_supported_version?
......
...@@ -27,7 +27,7 @@ describe SessionsController, :geo do ...@@ -27,7 +27,7 @@ describe SessionsController, :geo do
redirect_params = CGI.parse(redirect_uri.query) redirect_params = CGI.parse(redirect_uri.query)
expect(response).to have_gitlab_http_status(302) expect(response).to have_gitlab_http_status(302)
expect(response).to redirect_to %r(\A#{primary_node.url}oauth/geo/auth) expect(response).to redirect_to %r(\A#{Gitlab.config.gitlab.url}/oauth/geo/auth)
expect(redirect_params['state'].first).to end_with(':') expect(redirect_params['state'].first).to end_with(':')
end end
end end
......
...@@ -82,7 +82,7 @@ describe Oauth::GeoAuthController, :geo do ...@@ -82,7 +82,7 @@ describe Oauth::GeoAuthController, :geo do
it "redirects to primary node's oauth endpoint" do it "redirects to primary node's oauth endpoint" do
get :callback, params: { state: login_state } get :callback, params: { state: login_state }
expect(response).to redirect_to('/') expect(response).to redirect_to(secondary_node.uri.path)
end end
end end
......
FactoryBot.define do FactoryBot.define do
factory :geo_node do factory :geo_node do
# Start at a number higher than the current port to avoid the GeoNode sequence(:url) do |n|
# "lock out" validation "http://node#{n}.example.com/gitlab"
sequence(:url, Gitlab.config.gitlab.port + 1) do |port| end
uri = URI.parse("http://#{Gitlab.config.gitlab.host}:#{Gitlab.config.gitlab.relative_url_root}")
uri.port = port
uri.path += '/' unless uri.path.end_with?('/')
uri.to_s sequence(:name) do |n|
"node_name_#{n}"
end end
primary false primary false
...@@ -15,14 +13,6 @@ FactoryBot.define do ...@@ -15,14 +13,6 @@ FactoryBot.define do
trait :primary do trait :primary do
primary true primary true
minimum_reverification_interval 7 minimum_reverification_interval 7
url do
uri = URI.parse("http://#{Gitlab.config.gitlab.host}:#{Gitlab.config.gitlab.relative_url_root}")
uri.port = Gitlab.config.gitlab.port
uri.path += '/' unless uri.path.end_with?('/')
uri.to_s
end
end end
end end
end end
...@@ -77,6 +77,7 @@ describe 'admin Geo Nodes', :js, :geo do ...@@ -77,6 +77,7 @@ describe 'admin Geo Nodes', :js, :geo do
it 'creates a new Geo Node' do it 'creates a new Geo Node' do
check 'This is a primary node' check 'This is a primary node'
fill_in 'geo_node_name', with: 'a node name'
fill_in 'geo_node_url', with: 'https://test.gitlab.com' fill_in 'geo_node_url', with: 'https://test.gitlab.com'
click_button 'Add Node' click_button 'Add Node'
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
"type": "object", "type": "object",
"required" : [ "required" : [
"id", "id",
"name",
"url", "url",
"primary", "primary",
"enabled", "enabled",
...@@ -14,6 +15,7 @@ ...@@ -14,6 +15,7 @@
], ],
"properties" : { "properties" : {
"id": { "type": "integer" }, "id": { "type": "integer" },
"name": { "type": ["string", "null"] },
"url": { "type": ["string", "null"] }, "url": { "type": ["string", "null"] },
"primary": { "type": "boolean" }, "primary": { "type": "boolean" },
"internal_url": { "type": ["string", "null"] }, "internal_url": { "type": ["string", "null"] },
......
...@@ -3,6 +3,7 @@ require 'spec_helper' ...@@ -3,6 +3,7 @@ require 'spec_helper'
describe Gitlab::Geo::GeoTasks do describe Gitlab::Geo::GeoTasks do
describe '.set_primary_geo_node' do describe '.set_primary_geo_node' do
before do before do
allow(GeoNode).to receive(:current_node_name).and_return('https://primary.geo.example.com')
allow(GeoNode).to receive(:current_node_url).and_return('https://primary.geo.example.com') allow(GeoNode).to receive(:current_node_url).and_return('https://primary.geo.example.com')
end end
......
require 'spec_helper' require 'spec_helper'
describe Gitlab::Geo::JwtRequestDecoder do describe Gitlab::Geo::JwtRequestDecoder do
include EE::GeoHelpers
let!(:primary_node) { FactoryBot.create(:geo_node, :primary) } let!(:primary_node) { FactoryBot.create(:geo_node, :primary) }
let(:data) { { input: 123 } } let(:data) { { input: 123 } }
let(:request) { Gitlab::Geo::TransferRequest.new(data) } let(:request) { Gitlab::Geo::TransferRequest.new(data) }
subject { described_class.new(request.headers['Authorization']) } subject { described_class.new(request.headers['Authorization']) }
before do
stub_current_geo_node(primary_node)
end
describe '#decode' do describe '#decode' do
it 'decodes correct data' do it 'decodes correct data' do
expect(subject.decode).to eq(data) expect(subject.decode).to eq(data)
......
...@@ -18,6 +18,8 @@ describe Gitlab::Geo, :geo, :request_store do ...@@ -18,6 +18,8 @@ describe Gitlab::Geo, :geo, :request_store do
describe '.current_node' do describe '.current_node' do
it 'returns a GeoNode instance' do it 'returns a GeoNode instance' do
expect(GeoNode).to receive(:current_node).and_return(primary_node)
expect(described_class.current_node).to eq(primary_node) expect(described_class.current_node).to eq(primary_node)
end end
...@@ -42,6 +44,10 @@ describe Gitlab::Geo, :geo, :request_store do ...@@ -42,6 +44,10 @@ describe Gitlab::Geo, :geo, :request_store do
describe '.primary?' do describe '.primary?' do
context 'when current node is a primary node' do context 'when current node is a primary node' do
before do
stub_current_geo_node(primary_node)
end
it 'returns true' do it 'returns true' do
expect(described_class.primary?).to be_truthy expect(described_class.primary?).to be_truthy
end end
......
...@@ -3,11 +3,15 @@ ...@@ -3,11 +3,15 @@
require 'spec_helper' require 'spec_helper'
describe Ci::JobArtifact do describe Ci::JobArtifact do
include EE::GeoHelpers
describe '#destroy' do describe '#destroy' do
set(:primary) { create(:geo_node, :primary) } set(:primary) { create(:geo_node, :primary) }
set(:secondary) { create(:geo_node) } set(:secondary) { create(:geo_node) }
it 'creates a JobArtifactDeletedEvent' do it 'creates a JobArtifactDeletedEvent' do
stub_current_geo_node(primary)
job_artifact = create(:ci_job_artifact, :archive) job_artifact = create(:ci_job_artifact, :archive)
expect do expect do
......
...@@ -23,12 +23,17 @@ describe GeoNode, :geo, type: :model do ...@@ -23,12 +23,17 @@ describe GeoNode, :geo, type: :model do
end end
context 'validations' do context 'validations' do
subject { build(:geo_node) }
it { is_expected.to validate_inclusion_of(:selective_sync_type).in_array([nil, *GeoNode::SELECTIVE_SYNC_TYPES]) } it { is_expected.to validate_inclusion_of(:selective_sync_type).in_array([nil, *GeoNode::SELECTIVE_SYNC_TYPES]) }
it { is_expected.to validate_numericality_of(:repos_max_capacity).is_greater_than_or_equal_to(0) } it { is_expected.to validate_numericality_of(:repos_max_capacity).is_greater_than_or_equal_to(0) }
it { is_expected.to validate_numericality_of(:files_max_capacity).is_greater_than_or_equal_to(0) } it { is_expected.to validate_numericality_of(:files_max_capacity).is_greater_than_or_equal_to(0) }
it { is_expected.to validate_numericality_of(:verification_max_capacity).is_greater_than_or_equal_to(0) } it { is_expected.to validate_numericality_of(:verification_max_capacity).is_greater_than_or_equal_to(0) }
it { is_expected.to validate_numericality_of(:minimum_reverification_interval).is_greater_than_or_equal_to(1) } it { is_expected.to validate_numericality_of(:minimum_reverification_interval).is_greater_than_or_equal_to(1) }
it { is_expected.to validate_presence_of(:name) }
it { is_expected.to validate_presence_of(:url) } it { is_expected.to validate_presence_of(:url) }
it { is_expected.to validate_uniqueness_of(:name).case_insensitive }
it { is_expected.to validate_length_of(:name).is_at_most(255) }
context 'when validating primary node' do context 'when validating primary node' do
it 'cannot be disabled' do it 'cannot be disabled' do
...@@ -59,6 +64,13 @@ describe GeoNode, :geo, type: :model do ...@@ -59,6 +64,13 @@ describe GeoNode, :geo, type: :model do
it { is_expected.not_to be_valid } it { is_expected.not_to be_valid }
end end
context 'when an existing GeoNode has the same url but different name' do
let!(:existing) { new_node }
let(:url) { new_node.url }
it { is_expected.to be_valid }
end
end end
context 'when validating internal_url' do context 'when validating internal_url' do
...@@ -97,7 +109,8 @@ describe GeoNode, :geo, type: :model do ...@@ -97,7 +109,8 @@ describe GeoNode, :geo, type: :model do
context 'prevent locking yourself out' do context 'prevent locking yourself out' do
it 'does not accept adding a non primary node with same details as current_node' do it 'does not accept adding a non primary node with same details as current_node' do
node = build(:geo_node, :primary, primary: false) stub_geo_setting(node_name: 'foo')
node = build(:geo_node, :primary, primary: false, name: 'foo')
expect(node).not_to be_valid expect(node).not_to be_valid
expect(node.errors.full_messages.count).to eq(1) expect(node.errors.full_messages.count).to eq(1)
...@@ -318,13 +331,13 @@ describe GeoNode, :geo, type: :model do ...@@ -318,13 +331,13 @@ describe GeoNode, :geo, type: :model do
describe '#current?' do describe '#current?' do
it 'returns true when node is the current node' do it 'returns true when node is the current node' do
node = described_class.new(url: described_class.current_node_url) node = described_class.new(name: described_class.current_node_name)
expect(node.current?).to be_truthy expect(node.current?).to be_truthy
end end
it 'returns false when node is not the current node' do it 'returns false when node is not the current node' do
node = described_class.new(url: 'http://another.node.com:8080/foo') node = described_class.new(name: 'some other node')
expect(node.current?).to be_falsy expect(node.current?).to be_falsy
end end
...@@ -658,4 +671,16 @@ describe GeoNode, :geo, type: :model do ...@@ -658,4 +671,16 @@ describe GeoNode, :geo, type: :model do
is_expected.to be_falsy is_expected.to be_falsy
end end
end end
describe '#name=' do
context 'before validation' do
it 'strips leading and trailing whitespace' do
node = build(:geo_node)
node.name = " foo\n\n "
node.valid?
expect(node.name).to eq('foo')
end
end
end
end end
require 'spec_helper' require 'spec_helper'
describe LfsObject do describe LfsObject do
include EE::GeoHelpers
describe '#destroy' do describe '#destroy' do
subject { create(:lfs_object, :with_file) } subject { create(:lfs_object, :with_file) }
...@@ -9,6 +11,8 @@ describe LfsObject do ...@@ -9,6 +11,8 @@ describe LfsObject do
set(:secondary) { create(:geo_node) } set(:secondary) { create(:geo_node) }
it 'logs an event to the Geo event log' do it 'logs an event to the Geo event log' do
stub_current_geo_node(primary)
expect { subject.destroy }.to change(Geo::LfsObjectDeletedEvent, :count).by(1) expect { subject.destroy }.to change(Geo::LfsObjectDeletedEvent, :count).by(1)
end end
end end
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require 'spec_helper' require 'spec_helper'
describe Namespace do describe Namespace do
include EE::GeoHelpers
let!(:namespace) { create(:namespace) } let!(:namespace) { create(:namespace) }
let!(:free_plan) { create(:free_plan) } let!(:free_plan) { create(:free_plan) }
let!(:bronze_plan) { create(:bronze_plan) } let!(:bronze_plan) { create(:bronze_plan) }
...@@ -148,6 +150,8 @@ describe Namespace do ...@@ -148,6 +150,8 @@ describe Namespace do
allow(gitlab_shell).to receive(:mv_namespace) allow(gitlab_shell).to receive(:mv_namespace)
.with(project_legacy.repository_storage, full_path_was, new_path) .with(project_legacy.repository_storage, full_path_was, new_path)
.and_return(true) .and_return(true)
stub_current_geo_node(primary)
end end
it 'logs the Geo::RepositoryRenamedEvent for each project inside namespace' do it 'logs the Geo::RepositoryRenamedEvent for each project inside namespace' do
......
require 'spec_helper' require 'spec_helper'
describe Upload do describe Upload do
include EE::GeoHelpers
describe '#destroy' do describe '#destroy' do
subject { create(:upload, checksum: '8710d2c16809c79fee211a9693b64038a8aae99561bc86ce98a9b46b45677fe4') } subject { create(:upload, checksum: '8710d2c16809c79fee211a9693b64038a8aae99561bc86ce98a9b46b45677fe4') }
...@@ -9,6 +11,8 @@ describe Upload do ...@@ -9,6 +11,8 @@ describe Upload do
set(:secondary) { create(:geo_node) } set(:secondary) { create(:geo_node) }
it 'logs an event to the Geo event log' do it 'logs an event to the Geo event log' do
stub_current_geo_node(primary)
expect { subject.destroy }.to change(Geo::UploadDeletedEvent, :count).by(1) expect { subject.destroy }.to change(Geo::UploadDeletedEvent, :count).by(1)
end end
end end
......
...@@ -170,7 +170,8 @@ describe API::GeoNodes, :geo, :prometheus, api: true do ...@@ -170,7 +170,8 @@ describe API::GeoNodes, :geo, :prometheus, api: true do
end end
it 'returns 200 for the primary node' do it 'returns 200 for the primary node' do
expect(GeoNodeStatus).to receive(:fast_current_node_status).and_return(secondary_status) set_current_geo_node!(primary)
create(:geo_node_status, :healthy, geo_node: primary)
post api("/geo_nodes/#{primary.id}/repair", admin) post api("/geo_nodes/#{primary.id}/repair", admin)
......
...@@ -17,6 +17,10 @@ describe API::Geo do ...@@ -17,6 +17,10 @@ describe API::Geo do
Gitlab::Geo::TransferRequest.new(transfer.request_data.merge(file_id: 100000)).headers Gitlab::Geo::TransferRequest.new(transfer.request_data.merge(file_id: 100000)).headers
end end
before do
stub_current_geo_node(primary_node)
end
shared_examples 'with terms enforced' do shared_examples 'with terms enforced' do
before do before do
enforce_terms enforce_terms
......
...@@ -12,7 +12,7 @@ describe "Git HTTP requests (Geo)", :geo do ...@@ -12,7 +12,7 @@ describe "Git HTTP requests (Geo)", :geo do
set(:secondary) { create(:geo_node) } set(:secondary) { create(:geo_node) }
# Ensure the token always comes from the real time of the request # Ensure the token always comes from the real time of the request
let!(:auth_token) { Gitlab::Geo::BaseRequest.new(scope: project.full_path).authorization } let(:auth_token) { Gitlab::Geo::BaseRequest.new(scope: project.full_path).authorization }
let!(:user) { create(:user) } let!(:user) { create(:user) }
let!(:user_without_any_access) { create(:user) } let!(:user_without_any_access) { create(:user) }
let!(:user_without_push_access) { create(:user) } let!(:user_without_push_access) { create(:user) }
...@@ -29,6 +29,9 @@ describe "Git HTTP requests (Geo)", :geo do ...@@ -29,6 +29,9 @@ describe "Git HTTP requests (Geo)", :geo do
stub_licensed_features(geo: true) stub_licensed_features(geo: true)
stub_current_geo_node(current_node) stub_current_geo_node(current_node)
# Current Geo node must be stubbed before this is instantiated
auth_token
end end
shared_examples_for 'Geo request' do shared_examples_for 'Geo request' do
......
require 'spec_helper' require 'spec_helper'
describe Geo::FileUploadService do describe Geo::FileUploadService do
include EE::GeoHelpers
set(:node) { create(:geo_node, :primary) } set(:node) { create(:geo_node, :primary) }
let(:transfer_request) { Gitlab::Geo::TransferRequest.new(request_data) } let(:transfer_request) { Gitlab::Geo::TransferRequest.new(request_data) }
let(:req_header) { transfer_request.headers['Authorization'] } let(:req_header) { transfer_request.headers['Authorization'] }
before do
stub_current_geo_node(node)
end
shared_examples 'no authorization header' do shared_examples 'no authorization header' do
it 'returns nil' do it 'returns nil' do
service = described_class.new(params, nil) service = described_class.new(params, nil)
......
...@@ -116,9 +116,10 @@ describe Geo::MetricsUpdateService, :geo, :prometheus do ...@@ -116,9 +116,10 @@ describe Geo::MetricsUpdateService, :geo, :prometheus do
expect(Gitlab::Metrics.registry.get(:geo_db_replication_lag_seconds).values.count).to eq(2) expect(Gitlab::Metrics.registry.get(:geo_db_replication_lag_seconds).values.count).to eq(2)
expect(Gitlab::Metrics.registry.get(:geo_repositories).values.count).to eq(3) expect(Gitlab::Metrics.registry.get(:geo_repositories).values.count).to eq(3)
expect(Gitlab::Metrics.registry.get(:geo_repositories).get({ url: secondary.url })).to eq(10)
expect(Gitlab::Metrics.registry.get(:geo_repositories).get({ url: another_secondary.url })).to eq(10) expect(Gitlab::Metrics.registry.get(:geo_repositories).get({ name: secondary.name, url: secondary.name })).to eq(10)
expect(Gitlab::Metrics.registry.get(:geo_repositories).get({ url: primary.url })).to eq(10) expect(Gitlab::Metrics.registry.get(:geo_repositories).get({ name: another_secondary.name, url: another_secondary.name })).to eq(10)
expect(Gitlab::Metrics.registry.get(:geo_repositories).get({ name: primary.name, url: primary.name })).to eq(10)
end end
it 'updates the GeoNodeStatus entry' do it 'updates the GeoNodeStatus entry' do
...@@ -202,7 +203,7 @@ describe Geo::MetricsUpdateService, :geo, :prometheus do ...@@ -202,7 +203,7 @@ describe Geo::MetricsUpdateService, :geo, :prometheus do
end end
def metric_value(metric_name) def metric_value(metric_name)
Gitlab::Metrics.registry.get(metric_name)&.get({ url: secondary.url }) Gitlab::Metrics.registry.get(metric_name)&.get({ name: secondary.name, url: secondary.name })
end end
end end
end end
......
...@@ -3,32 +3,32 @@ require 'spec_helper' ...@@ -3,32 +3,32 @@ require 'spec_helper'
describe Geo::NodeCreateService do describe Geo::NodeCreateService do
describe '#execute' do describe '#execute' do
it 'creates a new node with valid params' do it 'creates a new node with valid params' do
service = described_class.new(url: 'http://example.com') service = described_class.new(name: 'foo', url: 'http://example.com')
expect { service.execute }.to change(GeoNode, :count).by(1) expect { service.execute }.to change(GeoNode, :count).by(1)
end end
it 'does not create a node with invalid params' do it 'does not create a node with invalid params' do
service = described_class.new(url: 'ftp://example.com') service = described_class.new(name: 'foo', url: 'ftp://example.com')
expect { service.execute }.not_to change(GeoNode, :count) expect { service.execute }.not_to change(GeoNode, :count)
end end
it 'returns true when creation succeeds' do it 'returns true when creation succeeds' do
service = described_class.new(url: 'http://example.com') service = described_class.new(name: 'foo', url: 'http://example.com')
expect(service.execute.persisted?).to eq true expect(service.execute.persisted?).to eq true
end end
it 'returns false when creation fails' do it 'returns false when creation fails' do
service = described_class.new(url: 'ftp://example.com') service = described_class.new(name: 'foo', url: 'ftp://example.com')
expect(service.execute.persisted?).to eq false expect(service.execute.persisted?).to eq false
end end
it 'parses the namespace_ids when node have namespace restrictions' do it 'parses the namespace_ids when node have namespace restrictions' do
groups = create_list(:group, 2) groups = create_list(:group, 2)
params = { url: 'http://example.com', namespace_ids: groups.map(&:id).join(',') } params = { name: 'foo', url: 'http://example.com', namespace_ids: groups.map(&:id).join(',') }
service = described_class.new(params) service = described_class.new(params)
service.execute service.execute
......
...@@ -10,6 +10,10 @@ describe Geo::NodeStatusPostService, :geo do ...@@ -10,6 +10,10 @@ describe Geo::NodeStatusPostService, :geo do
subject { described_class.new } subject { described_class.new }
describe '#execute' do describe '#execute' do
before do
stub_current_geo_node(primary)
end
it 'parses a 401 response' do it 'parses a 401 response' do
response = double(success?: false, response = double(success?: false,
code: 401, code: 401,
...@@ -59,8 +63,6 @@ describe Geo::NodeStatusPostService, :geo do ...@@ -59,8 +63,6 @@ describe Geo::NodeStatusPostService, :geo do
end end
it 'does not include id in the payload' do it 'does not include id in the payload' do
stub_current_geo_node(primary)
expect(Gitlab::HTTP).to receive(:post) expect(Gitlab::HTTP).to receive(:post)
.with( .with(
primary.status_url, primary.status_url,
...@@ -76,8 +78,6 @@ describe Geo::NodeStatusPostService, :geo do ...@@ -76,8 +78,6 @@ describe Geo::NodeStatusPostService, :geo do
end end
it 'sends geo_node_id in the request' do it 'sends geo_node_id in the request' do
stub_current_geo_node(primary)
expect(Gitlab::HTTP).to receive(:post) expect(Gitlab::HTTP).to receive(:post)
.with( .with(
primary.status_url, primary.status_url,
......
require 'spec_helper' require 'spec_helper'
describe Geo::NodeUpdateService do describe Geo::NodeUpdateService do
include EE::GeoHelpers
set(:primary) { create(:geo_node, :primary) } set(:primary) { create(:geo_node, :primary) }
let(:geo_node) { create(:geo_node) } let(:geo_node) { create(:geo_node) }
let(:groups) { create_list(:group, 2) } let(:groups) { create_list(:group, 2) }
let(:namespace_ids) { groups.map(&:id).join(',') } let(:namespace_ids) { groups.map(&:id).join(',') }
before do
stub_current_geo_node(primary)
end
describe '#execute' do describe '#execute' do
it 'updates the node' do it 'updates the node' do
params = { url: 'http://example.com' } params = { url: 'http://example.com' }
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
require 'spec_helper' require 'spec_helper'
describe Projects::AfterRenameService do describe Projects::AfterRenameService do
include EE::GeoHelpers
describe '#execute' do describe '#execute' do
context 'when running on a primary node' do context 'when running on a primary node' do
set(:primary) { create(:geo_node, :primary) } set(:primary) { create(:geo_node, :primary) }
...@@ -12,6 +14,10 @@ describe Projects::AfterRenameService do ...@@ -12,6 +14,10 @@ describe Projects::AfterRenameService do
let!(:full_path_before_rename) { project.full_path } let!(:full_path_before_rename) { project.full_path }
let(:path_after_rename) { "#{project.path}-renamed" } let(:path_after_rename) { "#{project.path}-renamed" }
before do
stub_current_geo_node(primary)
end
it 'logs the Geo::RepositoryRenamedEvent for project backed by hashed storage' do it 'logs the Geo::RepositoryRenamedEvent for project backed by hashed storage' do
expect { service_execute }.to change(Geo::RepositoryRenamedEvent, :count) expect { service_execute }.to change(Geo::RepositoryRenamedEvent, :count)
end end
......
require 'spec_helper' require 'spec_helper'
describe Projects::CreateService, '#execute' do describe Projects::CreateService, '#execute' do
include EE::GeoHelpers
let(:user) { create :user } let(:user) { create :user }
let(:opts) do let(:opts) do
{ {
...@@ -197,6 +199,10 @@ describe Projects::CreateService, '#execute' do ...@@ -197,6 +199,10 @@ describe Projects::CreateService, '#execute' do
set(:primary) { create(:geo_node, :primary) } set(:primary) { create(:geo_node, :primary) }
set(:secondary) { create(:geo_node) } set(:secondary) { create(:geo_node) }
before do
stub_current_geo_node(primary)
end
it 'logs an event to the Geo event log' do it 'logs an event to the Geo event log' do
expect { create_project(user, opts) }.to change(Geo::RepositoryCreatedEvent, :count).by(1) expect { create_project(user, opts) }.to change(Geo::RepositoryCreatedEvent, :count).by(1)
end end
......
require 'spec_helper' require 'spec_helper'
describe Projects::DestroyService do describe Projects::DestroyService do
include EE::GeoHelpers
let!(:user) { create(:user) } let!(:user) { create(:user) }
let!(:project) { create(:project, :repository, namespace: user.namespace) } let!(:project) { create(:project, :repository, namespace: user.namespace) }
let!(:project_id) { project.id } let!(:project_id) { project.id }
...@@ -33,6 +35,10 @@ describe Projects::DestroyService do ...@@ -33,6 +35,10 @@ describe Projects::DestroyService do
set(:primary) { create(:geo_node, :primary) } set(:primary) { create(:geo_node, :primary) }
set(:secondary) { create(:geo_node) } set(:secondary) { create(:geo_node) }
before do
stub_current_geo_node(primary)
end
it 'logs an event to the Geo event log' do it 'logs an event to the Geo event log' do
# Run Sidekiq immediately to check that renamed repository will be removed # Run Sidekiq immediately to check that renamed repository will be removed
Sidekiq::Testing.inline! do Sidekiq::Testing.inline! do
......
require 'spec_helper' require 'spec_helper'
describe Projects::HashedStorage::MigrateAttachmentsService do describe Projects::HashedStorage::MigrateAttachmentsService do
include EE::GeoHelpers
let(:project) { create(:project, storage_version: 1) } let(:project) { create(:project, storage_version: 1) }
let(:legacy_storage) { Storage::LegacyProject.new(project) } let(:legacy_storage) { Storage::LegacyProject.new(project) }
let(:hashed_storage) { Storage::HashedProject.new(project) } let(:hashed_storage) { Storage::HashedProject.new(project) }
let(:old_attachments_path) { legacy_storage.disk_path } let(:old_attachments_path) { legacy_storage.disk_path }
let(:new_attachments_path) { hashed_storage.disk_path } let(:new_attachments_path) { hashed_storage.disk_path }
let(:service) { described_class.new(project, old_attachments_path) } let(:service) { described_class.new(project, old_attachments_path) }
set(:primary) { create(:geo_node, :primary) }
set(:secondary) { create(:geo_node) }
describe '#execute' do before do
set(:primary) { create(:geo_node, :primary) } stub_current_geo_node(primary)
set(:secondary) { create(:geo_node) } end
describe '#execute' do
context 'on success' do context 'on success' do
before do before do
TestEnv.clean_test_path TestEnv.clean_test_path
......
require 'spec_helper' require 'spec_helper'
describe Projects::HashedStorage::MigrateRepositoryService do describe Projects::HashedStorage::MigrateRepositoryService do
include EE::GeoHelpers
let(:project) { create(:project, :empty_repo, :wiki_repo, :legacy_storage) } let(:project) { create(:project, :empty_repo, :wiki_repo, :legacy_storage) }
let(:legacy_storage) { Storage::LegacyProject.new(project) } let(:legacy_storage) { Storage::LegacyProject.new(project) }
let(:hashed_storage) { Storage::HashedProject.new(project) } let(:hashed_storage) { Storage::HashedProject.new(project) }
...@@ -12,6 +14,7 @@ describe Projects::HashedStorage::MigrateRepositoryService do ...@@ -12,6 +14,7 @@ describe Projects::HashedStorage::MigrateRepositoryService do
before do before do
TestEnv.clean_test_path TestEnv.clean_test_path
stub_current_geo_node(primary)
end end
it 'creates a Geo::HashedStorageMigratedEvent on success' do it 'creates a Geo::HashedStorageMigratedEvent on success' do
......
require 'spec_helper' require 'spec_helper'
describe Projects::TransferService do describe Projects::TransferService do
include EE::GeoHelpers
let(:user) { create(:user) } let(:user) { create(:user) }
let(:group) { create(:group) } let(:group) { create(:group) }
let(:project) { create(:project, :repository, namespace: user.namespace) } let(:project) { create(:project, :repository, namespace: user.namespace) }
...@@ -16,6 +18,8 @@ describe Projects::TransferService do ...@@ -16,6 +18,8 @@ describe Projects::TransferService do
set(:secondary) { create(:geo_node) } set(:secondary) { create(:geo_node) }
it 'logs an event to the Geo event log' do it 'logs an event to the Geo event log' do
stub_current_geo_node(primary)
expect { subject.execute(group) }.to change(Geo::RepositoryRenamedEvent, :count).by(1) expect { subject.execute(group) }.to change(Geo::RepositoryRenamedEvent, :count).by(1)
end end
end end
......
module EE module EE
module GeoHelpers module GeoHelpers
# Actually sets the specified node to be the current one, so it works on new
# instances of GeoNode, unlike stub_current_geo_node. But this is slower.
def set_current_geo_node!(node)
node.name = GeoNode.current_node_name
node.save!(validate: false)
end
def stub_current_geo_node(node) def stub_current_geo_node(node)
allow(::Gitlab::Geo).to receive(:current_node).and_return(node) allow(::Gitlab::Geo).to receive(:current_node).and_return(node)
allow(node).to receive(:current?).and_return(true) unless node.nil? allow(node).to receive(:current?).and_return(true) unless node.nil?
......
...@@ -25,5 +25,9 @@ module EE ...@@ -25,5 +25,9 @@ module EE
allow(object).to receive_message_chain(:current_application_settings, setting) { value } allow(object).to receive_message_chain(:current_application_settings, setting) { value }
end end
end end
def stub_geo_setting(messages)
allow(::Gitlab.config.geo).to receive_messages(to_settings(messages))
end
end end
end end
...@@ -10,7 +10,8 @@ describe 'geo rake tasks', :geo do ...@@ -10,7 +10,8 @@ describe 'geo rake tasks', :geo do
describe 'set_primary_node task' do describe 'set_primary_node task' do
before do before do
stub_config_setting(protocol: 'https') stub_config_setting(url: 'https://example.com:1234/relative_part')
stub_geo_setting(node_name: 'Region 1 node')
end end
it 'creates a GeoNode' do it 'creates a GeoNode' do
...@@ -22,7 +23,9 @@ describe 'geo rake tasks', :geo do ...@@ -22,7 +23,9 @@ describe 'geo rake tasks', :geo do
node = GeoNode.first node = GeoNode.first
expect(node.name).to eq('Region 1 node')
expect(node.uri.scheme).to eq('https') expect(node.uri.scheme).to eq('https')
expect(node.url).to eq('https://example.com:1234/relative_part/')
expect(node.primary).to be_truthy expect(node.primary).to be_truthy
end end
end end
......
require 'spec_helper' require 'spec_helper'
describe RemoveUnreferencedLfsObjectsWorker do describe RemoveUnreferencedLfsObjectsWorker do
include EE::GeoHelpers
describe '#perform' do describe '#perform' do
context 'when running in a Geo primary node' do context 'when running in a Geo primary node' do
set(:primary) { create(:geo_node, :primary) } set(:primary) { create(:geo_node, :primary) }
set(:secondary) { create(:geo_node) } set(:secondary) { create(:geo_node) }
it 'logs an event to the Geo event log for every unreferenced LFS objects' do it 'logs an event to the Geo event log for every unreferenced LFS objects' do
stub_current_geo_node(primary)
unreferenced_lfs_object_1 = create(:lfs_object, :with_file) unreferenced_lfs_object_1 = create(:lfs_object, :with_file)
unreferenced_lfs_object_2 = create(:lfs_object, :with_file) unreferenced_lfs_object_2 = create(:lfs_object, :with_file)
referenced_lfs_object = create(:lfs_object) referenced_lfs_object = create(:lfs_object)
......
...@@ -11964,6 +11964,9 @@ msgstr "" ...@@ -11964,6 +11964,9 @@ msgstr ""
msgid "The time taken by each data entry gathered by that stage." msgid "The time taken by each data entry gathered by that stage."
msgstr "" msgstr ""
msgid "The unique identifier for the Geo node. Must match `geo_node_name` if it is set in gitlab.rb, otherwise it must match `external_url`"
msgstr ""
msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination." msgid "The update action will time out after %{number_of_minutes} minutes. For big repositories, use a clone/push combination."
msgstr "" msgstr ""
...@@ -11985,6 +11988,9 @@ msgstr "" ...@@ -11985,6 +11988,9 @@ msgstr ""
msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below." msgid "The user map is a mapping of the FogBugz users that participated on your projects to the way their email address and usernames will be imported into GitLab. You can change this by populating the table below."
msgstr "" msgstr ""
msgid "The user-facing URL of the Geo node."
msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6." msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "" msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment