diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2019-11-01 12:06:26 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2019-11-01 12:06:26 +0000 |
commit | deed6022efe0149d88c57ef1df736c83465643f9 (patch) | |
tree | 4cf4c7c1ce765e83547129607b6bd3881c0fad6b | |
parent | f7a13c56bf0ed7ff9591bf4cbf9e50487255c4bc (diff) | |
download | gitlab-ce-deed6022efe0149d88c57ef1df736c83465643f9.tar.gz |
Add latest changes from gitlab-org/gitlab@master
-rw-r--r-- | app/controllers/users_controller.rb | 10 | ||||
-rw-r--r-- | config/routes/user.rb | 1 | ||||
-rw-r--r-- | doc/administration/gitaly/praefect.md | 47 | ||||
-rw-r--r-- | doc/administration/integration/plantuml.md | 2 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/job.rb | 6 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/need.rb | 44 | ||||
-rw-r--r-- | lib/gitlab/ci/config/entry/needs.rb | 55 | ||||
-rw-r--r-- | lib/gitlab/ci/yaml_processor.rb | 16 | ||||
-rw-r--r-- | lib/gitlab/config/entry/configurable.rb | 29 | ||||
-rw-r--r-- | lib/gitlab/config/entry/node.rb | 4 | ||||
-rw-r--r-- | lib/gitlab/config/entry/simplifiable.rb | 11 | ||||
-rw-r--r-- | lib/gitlab/config/entry/validatable.rb | 21 | ||||
-rw-r--r-- | spec/controllers/users_controller_spec.rb | 42 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/config/entry/job_spec.rb | 17 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/config/entry/need_spec.rb | 36 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/config/entry/needs_spec.rb | 84 | ||||
-rw-r--r-- | spec/lib/gitlab/ci/yaml_processor_spec.rb | 15 |
17 files changed, 151 insertions, 289 deletions
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index c3c227b08c5..06374736dcf 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -16,7 +16,7 @@ class UsersController < ApplicationController skip_before_action :authenticate_user! prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) } - before_action :user, except: [:exists] + before_action :user, except: [:exists, :suggests] before_action :authorize_read_user_profile!, only: [:calendar, :calendar_activities, :groups, :projects, :contributed_projects, :starred_projects, :snippets] @@ -114,6 +114,14 @@ class UsersController < ApplicationController render json: { exists: !!Namespace.find_by_path_or_name(params[:username]) } end + def suggests + namespace_path = params[:username] + exists = !!Namespace.find_by_path_or_name(namespace_path) + suggestions = exists ? [Namespace.clean_path(namespace_path)] : [] + + render json: { exists: exists, suggests: suggestions } + end + private def user diff --git a/config/routes/user.rb b/config/routes/user.rb index 6e277f18e36..31af321d2b2 100644 --- a/config/routes/user.rb +++ b/config/routes/user.rb @@ -55,6 +55,7 @@ scope(constraints: { username: Gitlab::PathRegex.root_namespace_route_regex }) d get :starred, as: :starred_projects get :snippets get :exists + get :suggests get :activity get '/', to: redirect('%{username}'), as: nil end diff --git a/doc/administration/gitaly/praefect.md b/doc/administration/gitaly/praefect.md index 9038675a28f..1973ed39b89 100644 --- a/doc/administration/gitaly/praefect.md +++ b/doc/administration/gitaly/praefect.md @@ -50,10 +50,9 @@ In their own machine, configure the Gitaly server as described in the #### Praefect -Next, Praefect has to be enabled on its own node. Disable all other services, -and add each Gitaly node that will be connected to Praefect. In the example below, -the Gitaly nodes are named `praefect-git-X`. Note that one node is designated as -primary, by setting the primary to `true`: +Next, Praefect has to be enabled on its own node. + +##### Disable other services ```ruby # /etc/gitlab/gitlab.rb @@ -67,27 +66,36 @@ unicorn['enable'] = false sidekiq['enable'] = false gitlab_workhorse['enable'] = false gitaly['enable'] = false +``` + +##### Set up Praefect and its Gitaly nodes + +In the example below, the Gitaly nodes are named `praefect-git-X`. Note that one node is designated as +primary, by setting the primary to `true`: + +```ruby +# /etc/gitlab/gitlab.rb # virtual_storage_name must match the same storage name given to praefect in git_data_dirs praefect['virtual_storage_name'] = 'praefect' -praefect['auth_token'] = 'super_secret_abc' +praefect['auth_token'] = 'abc123secret' praefect['enable'] = true praefect['storage_nodes'] = [ { 'storage' => 'praefect-git-1', 'address' => 'tcp://praefect-git-1.internal', - 'token' => 'token1', + 'token' => 'xyz123secret', 'primary' => true }, { 'storage' => 'praefect-git-2', 'address' => 'tcp://praefect-git-2.internal', - 'token' => 'token2' + 'token' => 'xyz456secret', }, { 'storage' => 'praefect-git-3', 'address' => 'tcp://praefect-git-3.internal', - 'token' => 'token3' + 'token' => 'xyz789secret', } ] ``` @@ -97,7 +105,28 @@ Save the file and [reconfigure Praefect](../restart_gitlab.md#omnibus-gitlab-rec #### GitLab When Praefect is running, it should be exposed as a storage to GitLab. This -is done through setting the `git_data_dirs`. Assuming the default storage +is done through setting the `git_data_dirs`. + +##### On a fresh GitLab installation + +On a fresh Gitlab installation, set up the `default` storage to point to praefect: + +```ruby +git_data_dirs({ + "default" => { + "gitaly_address" => "tcp://praefect.internal:2305" + }, +}) +``` + +##### On an existing GitLab instance + +On an existing GitLab instance, the `default` storage is already being served by a +Gitaly node, so an additional storage can be added. `praefect` is chosen in the example +below, but it can be any name as long as it matches the `virtual_storage_name` in the +praefect attributes above. + +Assuming the default storage configuration is used, there would be two storages available to GitLab: ```ruby diff --git a/doc/administration/integration/plantuml.md b/doc/administration/integration/plantuml.md index e595c640aac..23803b82543 100644 --- a/doc/administration/integration/plantuml.md +++ b/doc/administration/integration/plantuml.md @@ -96,7 +96,7 @@ To enable the redirection, add the following line in `/etc/gitlab/gitlab.rb`: nginx['custom_gitlab_server_config'] = "location /-/plantuml/ { \n proxy_cache off; \n proxy_pass http://plantuml:8080/; \n}\n" # Built from source -nginx['custom_gitlab_server_config'] = "location /-/plantuml/ { \n proxy_cache off; \n proxy_pass http://127.0.0.1:8080/plantuml/; \n}\n" +nginx['custom_gitlab_server_config'] = "location /-/plantuml { \n rewrite ^/-/(plantuml.*) /$1 break;\n proxy_cache off; \n proxy_pass http://localhost:8080/plantuml; \n}\n" ``` To activate the changes, run the following command: diff --git a/lib/gitlab/ci/config/entry/job.rb b/lib/gitlab/ci/config/entry/job.rb index 2d5981a4255..1298e2d3462 100644 --- a/lib/gitlab/ci/config/entry/job.rb +++ b/lib/gitlab/ci/config/entry/job.rb @@ -50,6 +50,7 @@ module Gitlab validates :timeout, duration: { limit: ChronicDuration.output(Project::MAX_BUILD_TIMEOUT) } validates :dependencies, array_of_strings: true + validates :needs, array_of_strings: true validates :extends, array_of_strings_or_string: true validates :rules, array_of_hashes: true end @@ -113,11 +114,6 @@ module Gitlab description: 'List of evaluable Rules to determine job inclusion.', inherit: false - entry :needs, Entry::Needs, - description: 'Needs configuration for this job.', - metadata: { allowed_needs: %i[job] }, - inherit: false - entry :variables, Entry::Variables, description: 'Environment variables available for this job.', inherit: false diff --git a/lib/gitlab/ci/config/entry/need.rb b/lib/gitlab/ci/config/entry/need.rb deleted file mode 100644 index b6db546d8ff..00000000000 --- a/lib/gitlab/ci/config/entry/need.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Ci - class Config - module Entry - class Need < ::Gitlab::Config::Entry::Simplifiable - strategy :Job, if: -> (config) { config.is_a?(String) } - - class Job < ::Gitlab::Config::Entry::Node - include ::Gitlab::Config::Entry::Validatable - - validations do - validates :config, presence: true - validates :config, type: String - end - - def type - :job - end - - def value - { name: @config } - end - end - - class UnknownStrategy < ::Gitlab::Config::Entry::Node - def type - end - - def value - end - - def errors - ["#{location} has an unsupported type"] - end - end - end - end - end - end -end - -::Gitlab::Ci::Config::Entry::Need.prepend_if_ee('::EE::Gitlab::Ci::Config::Entry::Need') diff --git a/lib/gitlab/ci/config/entry/needs.rb b/lib/gitlab/ci/config/entry/needs.rb deleted file mode 100644 index 28452aaaa16..00000000000 --- a/lib/gitlab/ci/config/entry/needs.rb +++ /dev/null @@ -1,55 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Ci - class Config - module Entry - ## - # Entry that represents a set of needs dependencies. - # - class Needs < ::Gitlab::Config::Entry::Node - include ::Gitlab::Config::Entry::Validatable - - validations do - validates :config, presence: true - - validate do - unless config.is_a?(Hash) || config.is_a?(Array) - errors.add(:config, 'can only be a Hash or an Array') - end - end - - validate on: :composed do - extra_keys = value.keys - opt(:allowed_needs) - if extra_keys.any? - errors.add(:config, "uses invalid types: #{extra_keys.join(', ')}") - end - end - end - - def compose!(deps = nil) - super(deps) do - [@config].flatten.each_with_index do |need, index| - @entries[index] = ::Gitlab::Config::Entry::Factory.new(Entry::Need) - .value(need) - .with(key: "need", parent: self, description: "need definition.") # rubocop:disable CodeReuse/ActiveRecord - .create! - end - - @entries.each_value do |entry| - entry.compose!(deps) - end - end - end - - def value - values = @entries.values.select(&:type) - values.group_by(&:type).transform_values do |values| - values.map(&:value) - end - end - end - end - end - end -end diff --git a/lib/gitlab/ci/yaml_processor.rb b/lib/gitlab/ci/yaml_processor.rb index c2a55fa8b1b..f6a3abefcfb 100644 --- a/lib/gitlab/ci/yaml_processor.rb +++ b/lib/gitlab/ci/yaml_processor.rb @@ -40,7 +40,7 @@ module Gitlab environment: job[:environment_name], coverage_regex: job[:coverage], yaml_variables: yaml_variables(name), - needs_attributes: job.dig(:needs, :job), + needs_attributes: job[:needs]&.map { |need| { name: need } }, interruptible: job[:interruptible], rules: job[:rules], options: { @@ -59,7 +59,7 @@ module Gitlab instance: job[:instance], start_in: job[:start_in], trigger: job[:trigger], - bridge_needs: job.dig(:needs, :bridge)&.first + bridge_needs: job[:needs] }.compact }.compact end @@ -159,19 +159,17 @@ module Gitlab end def validate_job_needs!(name, job) - return unless job.dig(:needs, :job) + return unless job[:needs] stage_index = @stages.index(job[:stage]) - job.dig(:needs, :job).each do |need| - need_job_name = need[:name] + job[:needs].each do |need| + raise ValidationError, "#{name} job: undefined need: #{need}" unless @jobs[need.to_sym] - raise ValidationError, "#{name} job: undefined need: #{need_job_name}" unless @jobs[need_job_name.to_sym] - - needs_stage_index = @stages.index(@jobs[need_job_name.to_sym][:stage]) + needs_stage_index = @stages.index(@jobs[need.to_sym][:stage]) unless needs_stage_index.present? && needs_stage_index < stage_index - raise ValidationError, "#{name} job: need #{need_job_name} is not defined in prior stages" + raise ValidationError, "#{name} job: need #{need} is not defined in prior stages" end end end diff --git a/lib/gitlab/config/entry/configurable.rb b/lib/gitlab/config/entry/configurable.rb index bda84dc2cff..b7ec4b7c4f8 100644 --- a/lib/gitlab/config/entry/configurable.rb +++ b/lib/gitlab/config/entry/configurable.rb @@ -29,24 +29,22 @@ module Gitlab def compose!(deps = nil) return unless valid? - super do - self.class.nodes.each do |key, factory| - # If we override the config type validation - # we can end with different config types like String - next unless config.is_a?(Hash) + self.class.nodes.each do |key, factory| + # If we override the config type validation + # we can end with different config types like String + next unless config.is_a?(Hash) - factory - .value(config[key]) - .with(key: key, parent: self) + factory + .value(config[key]) + .with(key: key, parent: self) - entries[key] = factory.create! - end + entries[key] = factory.create! + end - yield if block_given? + yield if block_given? - entries.each_value do |entry| - entry.compose!(deps) - end + entries.each_value do |entry| + entry.compose!(deps) end end # rubocop: enable CodeReuse/ActiveRecord @@ -69,13 +67,12 @@ module Gitlab private # rubocop: disable CodeReuse/ActiveRecord - def entry(key, entry, description: nil, default: nil, inherit: nil, reserved: nil, metadata: {}) + def entry(key, entry, description: nil, default: nil, inherit: nil, reserved: nil) factory = ::Gitlab::Config::Entry::Factory.new(entry) .with(description: description) .with(default: default) .with(inherit: inherit) .with(reserved: reserved) - .metadata(metadata) (@nodes ||= {}).merge!(key.to_sym => factory) end diff --git a/lib/gitlab/config/entry/node.rb b/lib/gitlab/config/entry/node.rb index 84d3409ed91..e014f15fbd8 100644 --- a/lib/gitlab/config/entry/node.rb +++ b/lib/gitlab/config/entry/node.rb @@ -112,10 +112,6 @@ module Gitlab @aspects ||= [] end - def self.with_aspect(blk) - self.aspects.append(blk) - end - private attr_reader :entries diff --git a/lib/gitlab/config/entry/simplifiable.rb b/lib/gitlab/config/entry/simplifiable.rb index 315f1947e2c..d58aba07d15 100644 --- a/lib/gitlab/config/entry/simplifiable.rb +++ b/lib/gitlab/config/entry/simplifiable.rb @@ -4,11 +4,11 @@ module Gitlab module Config module Entry class Simplifiable < SimpleDelegator - EntryStrategy = Struct.new(:name, :klass, :condition) + EntryStrategy = Struct.new(:name, :condition) attr_reader :subject - def initialize(config, **metadata, &blk) + def initialize(config, **metadata) unless self.class.const_defined?(:UnknownStrategy) raise ArgumentError, 'UndefinedStrategy not available!' end @@ -19,13 +19,14 @@ module Gitlab entry = self.class.entry_class(strategy) - @subject = entry.new(config, metadata, &blk) + @subject = entry.new(config, metadata) + yield(@subject) if block_given? super(@subject) end def self.strategy(name, **opts) - EntryStrategy.new(name, opts.dig(:class), opts.fetch(:if)).tap do |strategy| + EntryStrategy.new(name, opts.fetch(:if)).tap do |strategy| strategies.append(strategy) end end @@ -36,7 +37,7 @@ module Gitlab def self.entry_class(strategy) if strategy.present? - strategy.klass || self.const_get(strategy.name, false) + self.const_get(strategy.name, false) else self::UnknownStrategy end diff --git a/lib/gitlab/config/entry/validatable.rb b/lib/gitlab/config/entry/validatable.rb index 45b852dc2e0..1c88c68c11c 100644 --- a/lib/gitlab/config/entry/validatable.rb +++ b/lib/gitlab/config/entry/validatable.rb @@ -7,27 +7,14 @@ module Gitlab extend ActiveSupport::Concern def self.included(node) - node.with_aspect -> do - validate(:new) + node.aspects.append -> do + @validator = self.class.validator.new(self) + @validator.validate(:new) end end - def validator - @validator ||= self.class.validator.new(self) - end - - def validate(context = nil) - validator.validate(context) - end - - def compose!(deps = nil, &blk) - super(deps, &blk) - - validate(:composed) - end - def errors - validator.messages + descendants.flat_map(&:errors) + @validator.messages + descendants.flat_map(&:errors) # rubocop:disable Gitlab/ModuleWithInstanceVariables end class_methods do diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 5566df0c216..fa057dc5b07 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -348,6 +348,48 @@ describe UsersController do end end + describe 'GET #suggests' do + context 'when user exists' do + it 'returns JSON indicating the user exists and a suggestion' do + get :suggests, params: { username: user.username } + + expected_json = { exists: true, suggests: ["#{user.username}1"] }.to_json + expect(response.body).to eq(expected_json) + end + + context 'when the casing is different' do + let(:user) { create(:user, username: 'CamelCaseUser') } + + it 'returns JSON indicating the user exists and a suggestion' do + get :suggests, params: { username: user.username.downcase } + + expected_json = { exists: true, suggests: ["#{user.username.downcase}1"] }.to_json + expect(response.body).to eq(expected_json) + end + end + end + + context 'when the user does not exist' do + it 'returns JSON indicating the user does not exist' do + get :suggests, params: { username: 'foo' } + + expected_json = { exists: false, suggests: [] }.to_json + expect(response.body).to eq(expected_json) + end + + context 'when a user changed their username' do + let(:redirect_route) { user.namespace.redirect_routes.create(path: 'old-username') } + + it 'returns JSON indicating a user by that username does not exist' do + get :suggests, params: { username: 'old-username' } + + expected_json = { exists: false, suggests: [] }.to_json + expect(response.body).to eq(expected_json) + end + end + end + end + describe '#ensure_canonical_path' do before do sign_in(user) diff --git a/spec/lib/gitlab/ci/config/entry/job_spec.rb b/spec/lib/gitlab/ci/config/entry/job_spec.rb index 9fe18caf689..d3eb5a9663f 100644 --- a/spec/lib/gitlab/ci/config/entry/job_spec.rb +++ b/spec/lib/gitlab/ci/config/entry/job_spec.rb @@ -23,7 +23,7 @@ describe Gitlab::Ci::Config::Entry::Job do let(:result) do %i[before_script script stage type after_script cache - image services only except rules needs variables artifacts + image services only except rules variables artifacts environment coverage retry] end @@ -384,6 +384,21 @@ describe Gitlab::Ci::Config::Entry::Job do end context 'when has needs' do + context 'that are not a array of strings' do + let(:config) do + { + stage: 'test', + script: 'echo', + needs: 'build-job' + } + end + + it 'returns error about invalid type' do + expect(entry).not_to be_valid + expect(entry.errors).to include 'job needs should be an array of strings' + end + end + context 'when have dependencies that are not subset of needs' do let(:config) do { diff --git a/spec/lib/gitlab/ci/config/entry/need_spec.rb b/spec/lib/gitlab/ci/config/entry/need_spec.rb deleted file mode 100644 index d119e604900..00000000000 --- a/spec/lib/gitlab/ci/config/entry/need_spec.rb +++ /dev/null @@ -1,36 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe ::Gitlab::Ci::Config::Entry::Need do - subject(:need) { described_class.new(config) } - - context 'when job is specified' do - let(:config) { 'job_name' } - - describe '#valid?' do - it { is_expected.to be_valid } - end - - describe '#value' do - it 'returns job needs configuration' do - expect(need.value).to eq(name: 'job_name') - end - end - end - - context 'when need is empty' do - let(:config) { '' } - - describe '#valid?' do - it { is_expected.not_to be_valid } - end - - describe '#errors' do - it 'is returns an error about an empty config' do - expect(need.errors) - .to contain_exactly("job config can't be blank") - end - end - end -end diff --git a/spec/lib/gitlab/ci/config/entry/needs_spec.rb b/spec/lib/gitlab/ci/config/entry/needs_spec.rb deleted file mode 100644 index f4a76b52d30..00000000000 --- a/spec/lib/gitlab/ci/config/entry/needs_spec.rb +++ /dev/null @@ -1,84 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe ::Gitlab::Ci::Config::Entry::Needs do - subject(:needs) { described_class.new(config) } - - before do - needs.metadata[:allowed_needs] = %i[job] - end - - describe 'validations' do - before do - needs.compose! - end - - context 'when entry config value is correct' do - let(:config) { ['job_name'] } - - describe '#valid?' do - it { is_expected.to be_valid } - end - end - - context 'when config value has wrong type' do - let(:config) { 123 } - - describe '#valid?' do - it { is_expected.not_to be_valid } - end - - describe '#errors' do - it 'returns error about incorrect type' do - expect(needs.errors) - .to include('needs config can only be a hash or an array') - end - end - end - - context 'when wrong needs type is used' do - let(:config) { [123] } - - describe '#valid?' do - it { is_expected.not_to be_valid } - end - - describe '#errors' do - it 'returns error about incorrect type' do - expect(needs.errors).to contain_exactly( - 'need has an unsupported type') - end - end - end - end - - describe '.compose!' do - context 'when valid job entries composed' do - let(:config) { %w[first_job_name second_job_name] } - - before do - needs.compose! - end - - describe '#value' do - it 'returns key value' do - expect(needs.value).to eq( - job: [ - { name: 'first_job_name' }, - { name: 'second_job_name' } - ] - ) - end - end - - describe '#descendants' do - it 'creates valid descendant nodes' do - expect(needs.descendants.count).to eq 2 - expect(needs.descendants) - .to all(be_an_instance_of(::Gitlab::Ci::Config::Entry::Need)) - end - end - end - end -end diff --git a/spec/lib/gitlab/ci/yaml_processor_spec.rb b/spec/lib/gitlab/ci/yaml_processor_spec.rb index c7ea6ed9ddb..c747ea670bb 100644 --- a/spec/lib/gitlab/ci/yaml_processor_spec.rb +++ b/spec/lib/gitlab/ci/yaml_processor_spec.rb @@ -1293,7 +1293,7 @@ module Gitlab end end - describe "Job Needs" do + describe "Needs" do let(:needs) { } let(:dependencies) { } @@ -1333,7 +1333,12 @@ module Gitlab stage: "test", stage_idx: 2, name: "test1", - options: { script: ["test"] }, + options: { + script: ["test"], + # This does not make sense, there is a follow-up: + # https://gitlab.com/gitlab-org/gitlab-foss/issues/65569 + bridge_needs: %w[build1 build2] + }, needs_attributes: [ { name: "build1" }, { name: "build2" } @@ -1345,6 +1350,12 @@ module Gitlab end end + context 'needs two builds defined as symbols' do + let(:needs) { [:build1, :build2] } + + it { expect { subject }.not_to raise_error } + end + context 'undefined need' do let(:needs) { ['undefined'] } |