summaryrefslogtreecommitdiff
path: root/app/models
diff options
context:
space:
mode:
authorKamil Trzcinski <ayufan@ayufan.eu>2017-04-06 22:00:00 +0200
committerKamil Trzcinski <ayufan@ayufan.eu>2017-04-06 22:05:22 +0200
commit37a7b099d6eafc87209368a9fc7f608282b99e08 (patch)
treea2712e439cec8547d84bed2d57d339cb1cd5c502 /app/models
parent57c353fca7121a120142161b253004f33d815766 (diff)
parent73cb71e41c0ade92b9673a5d74c7dd78679fae91 (diff)
downloadgitlab-ce-optimise-pipelines.tar.gz
Merge remote-tracking branch 'origin/master' into optimise-pipelinesoptimise-pipelines
Diffstat (limited to 'app/models')
-rw-r--r--app/models/award_emoji.rb3
-rw-r--r--app/models/blob.rb18
-rw-r--r--app/models/ci/build.rb162
-rw-r--r--app/models/ci/trigger.rb1
-rw-r--r--app/models/ci/trigger_schedule.rb30
-rw-r--r--app/models/concerns/ghost_user.rb7
-rw-r--r--app/models/concerns/routable.rb68
-rw-r--r--app/models/container_repository.rb77
-rw-r--r--app/models/group.rb11
-rw-r--r--app/models/issue.rb15
-rw-r--r--app/models/members/group_member.rb5
-rw-r--r--app/models/namespace.rb10
-rw-r--r--app/models/project.rb58
-rw-r--r--app/models/project_services/mock_deployment_service.rb18
-rw-r--r--app/models/project_services/mock_monitoring_service.rb17
-rw-r--r--app/models/repository.rb27
-rw-r--r--app/models/service.rb4
-rw-r--r--app/models/user.rb20
18 files changed, 359 insertions, 192 deletions
diff --git a/app/models/award_emoji.rb b/app/models/award_emoji.rb
index 6937ad3bdd9..6ada6fae4eb 100644
--- a/app/models/award_emoji.rb
+++ b/app/models/award_emoji.rb
@@ -3,13 +3,14 @@ class AwardEmoji < ActiveRecord::Base
UPVOTE_NAME = "thumbsup".freeze
include Participable
+ include GhostUser
belongs_to :awardable, polymorphic: true
belongs_to :user
validates :awardable, :user, presence: true
validates :name, presence: true, inclusion: { in: Gitlab::Emoji.emojis_names }
- validates :name, uniqueness: { scope: [:user, :awardable_type, :awardable_id] }
+ validates :name, uniqueness: { scope: [:user, :awardable_type, :awardable_id] }, unless: :ghost_user?
participant :user
diff --git a/app/models/blob.rb b/app/models/blob.rb
index 95d2111a992..801d3442803 100644
--- a/app/models/blob.rb
+++ b/app/models/blob.rb
@@ -46,10 +46,22 @@ class Blob < SimpleDelegator
text? && language && language.name == 'SVG'
end
+ def pdf?
+ name && File.extname(name) == '.pdf'
+ end
+
def ipython_notebook?
text? && language&.name == 'Jupyter Notebook'
end
+ def sketch?
+ binary? && extname.downcase.delete('.') == 'sketch'
+ end
+
+ def stl?
+ extname.downcase.delete('.') == 'stl'
+ end
+
def size_within_svg_limits?
size <= MAXIMUM_SVG_SIZE
end
@@ -67,8 +79,14 @@ class Blob < SimpleDelegator
end
elsif image? || svg?
'image'
+ elsif pdf?
+ 'pdf'
elsif ipython_notebook?
'notebook'
+ elsif sketch?
+ 'sketch'
+ elsif stl?
+ 'stl'
elsif text?
'text'
else
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 9ef55fa6e18..b426c27afbb 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -165,19 +165,6 @@ module Ci
latest_builds.where('stage_idx < ?', stage_idx)
end
- def trace_html(**args)
- trace_with_state(**args)[:html] || ''
- end
-
- def trace_with_state(state: nil, last_lines: nil)
- trace_ansi = trace(last_lines: last_lines)
- if trace_ansi.present?
- Ci::Ansi2html.convert(trace_ansi, state)
- else
- {}
- end
- end
-
def timeout
project.build_timeout
end
@@ -238,136 +225,35 @@ module Ci
end
def update_coverage
- coverage = extract_coverage(trace, coverage_regex)
+ coverage = trace.extract_coverage(coverage_regex)
update_attributes(coverage: coverage) if coverage.present?
end
- def extract_coverage(text, regex)
- return unless regex
-
- matches = text.scan(Regexp.new(regex)).last
- matches = matches.last if matches.is_a?(Array)
- coverage = matches.gsub(/\d+(\.\d+)?/).first
-
- if coverage.present?
- coverage.to_f
- end
- rescue
- # if bad regex or something goes wrong we dont want to interrupt transition
- # so we just silentrly ignore error for now
- end
-
- def has_trace_file?
- File.exist?(path_to_trace) || has_old_trace_file?
+ def trace
+ Gitlab::Ci::Trace.new(self)
end
def has_trace?
- raw_trace.present?
- end
-
- def raw_trace(last_lines: nil)
- if File.exist?(trace_file_path)
- Gitlab::Ci::TraceReader.new(trace_file_path).
- read(last_lines: last_lines)
- else
- # backward compatibility
- read_attribute :trace
- end
- end
-
- ##
- # Deprecated
- #
- # This is a hotfix for CI build data integrity, see #4246
- def has_old_trace_file?
- project.ci_id && File.exist?(old_path_to_trace)
- end
-
- def trace(last_lines: nil)
- hide_secrets(raw_trace(last_lines: last_lines))
- end
-
- def trace_length
- if raw_trace
- raw_trace.bytesize
- else
- 0
- end
+ trace.exist?
end
- def trace=(trace)
- recreate_trace_dir
- trace = hide_secrets(trace)
- File.write(path_to_trace, trace)
+ def trace=(data)
+ raise NotImplementedError
end
- def recreate_trace_dir
- unless Dir.exist?(dir_to_trace)
- FileUtils.mkdir_p(dir_to_trace)
- end
+ def old_trace
+ read_attribute(:trace)
end
- private :recreate_trace_dir
-
- def append_trace(trace_part, offset)
- recreate_trace_dir
- touch if needs_touch?
-
- trace_part = hide_secrets(trace_part)
- File.truncate(path_to_trace, offset) if File.exist?(path_to_trace)
- File.open(path_to_trace, 'ab') do |f|
- f.write(trace_part)
- end
+ def erase_old_trace!
+ write_attribute(:trace, nil)
+ save
end
def needs_touch?
Time.now - updated_at > 15.minutes.to_i
end
- def trace_file_path
- if has_old_trace_file?
- old_path_to_trace
- else
- path_to_trace
- end
- end
-
- def dir_to_trace
- File.join(
- Settings.gitlab_ci.builds_path,
- created_at.utc.strftime("%Y_%m"),
- project.id.to_s
- )
- end
-
- def path_to_trace
- "#{dir_to_trace}/#{id}.log"
- end
-
- ##
- # Deprecated
- #
- # This is a hotfix for CI build data integrity, see #4246
- # Should be removed in 8.4, after CI files migration has been done.
- #
- def old_dir_to_trace
- File.join(
- Settings.gitlab_ci.builds_path,
- created_at.utc.strftime("%Y_%m"),
- project.ci_id.to_s
- )
- end
-
- ##
- # Deprecated
- #
- # This is a hotfix for CI build data integrity, see #4246
- # Should be removed in 8.4, after CI files migration has been done.
- #
- def old_path_to_trace
- "#{old_dir_to_trace}/#{id}.log"
- end
-
##
# Deprecated
#
@@ -534,6 +420,8 @@ module Ci
end
def dependencies
+ return [] if empty_dependencies?
+
depended_jobs = depends_on_builds
return depended_jobs unless options[:dependencies].present?
@@ -543,6 +431,19 @@ module Ci
end
end
+ def empty_dependencies?
+ options[:dependencies]&.empty?
+ end
+
+ def hide_secrets(trace)
+ return unless trace
+
+ trace = trace.dup
+ Ci::MaskSecret.mask!(trace, project.runners_token) if project
+ Ci::MaskSecret.mask!(trace, token)
+ trace
+ end
+
private
def update_artifacts_size
@@ -554,7 +455,7 @@ module Ci
end
def erase_trace!
- self.trace = nil
+ trace.erase!
end
def update_erased!(user = nil)
@@ -616,15 +517,6 @@ module Ci
pipeline.config_processor.build_attributes(name)
end
- def hide_secrets(trace)
- return unless trace
-
- trace = trace.dup
- Ci::MaskSecret.mask!(trace, project.runners_token) if project
- Ci::MaskSecret.mask!(trace, token)
- trace
- end
-
def update_project_statistics
return unless project
diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb
index cba1d81a861..0a89f3e0640 100644
--- a/app/models/ci/trigger.rb
+++ b/app/models/ci/trigger.rb
@@ -8,6 +8,7 @@ module Ci
belongs_to :owner, class_name: "User"
has_many :trigger_requests, dependent: :destroy
+ has_one :trigger_schedule, dependent: :destroy
validates :token, presence: true, uniqueness: true
diff --git a/app/models/ci/trigger_schedule.rb b/app/models/ci/trigger_schedule.rb
new file mode 100644
index 00000000000..10ea381ee31
--- /dev/null
+++ b/app/models/ci/trigger_schedule.rb
@@ -0,0 +1,30 @@
+module Ci
+ class TriggerSchedule < ActiveRecord::Base
+ extend Ci::Model
+ include Importable
+
+ acts_as_paranoid
+
+ belongs_to :project
+ belongs_to :trigger
+
+ delegate :ref, to: :trigger
+
+ validates :trigger, presence: { unless: :importing? }
+ validates :cron, cron: true, presence: { unless: :importing? }
+ validates :cron_timezone, cron_timezone: true, presence: { unless: :importing? }
+ validates :ref, presence: { unless: :importing? }
+
+ before_save :set_next_run_at
+
+ def set_next_run_at
+ self.next_run_at = Gitlab::Ci::CronParser.new(cron, cron_timezone).next_time_from(Time.now)
+ end
+
+ def schedule_next_run!
+ save! # with set_next_run_at
+ rescue ActiveRecord::RecordInvalid
+ update_attribute(:next_run_at, nil) # update without validation
+ end
+ end
+end
diff --git a/app/models/concerns/ghost_user.rb b/app/models/concerns/ghost_user.rb
new file mode 100644
index 00000000000..da696127a80
--- /dev/null
+++ b/app/models/concerns/ghost_user.rb
@@ -0,0 +1,7 @@
+module GhostUser
+ extend ActiveSupport::Concern
+
+ def ghost_user?
+ user && user.ghost?
+ end
+end
diff --git a/app/models/concerns/routable.rb b/app/models/concerns/routable.rb
index 529fb5ce988..aca99feee53 100644
--- a/app/models/concerns/routable.rb
+++ b/app/models/concerns/routable.rb
@@ -83,6 +83,74 @@ module Routable
AND members.source_type = r2.source_type").
where('members.user_id = ?', user_id)
end
+
+ # Builds a relation to find multiple objects that are nested under user
+ # membership. Includes the parent, as opposed to `#member_descendants`
+ # which only includes the descendants.
+ #
+ # Usage:
+ #
+ # Klass.member_self_and_descendants(1)
+ #
+ # Returns an ActiveRecord::Relation.
+ def member_self_and_descendants(user_id)
+ joins(:route).
+ joins("INNER JOIN routes r2 ON routes.path LIKE CONCAT(r2.path, '/%')
+ OR routes.path = r2.path
+ INNER JOIN members ON members.source_id = r2.source_id
+ AND members.source_type = r2.source_type").
+ where('members.user_id = ?', user_id)
+ end
+
+ # Returns all objects in a hierarchy, where any node in the hierarchy is
+ # under the user membership.
+ #
+ # Usage:
+ #
+ # Klass.member_hierarchy(1)
+ #
+ # Examples:
+ #
+ # Given the following group tree...
+ #
+ # _______group_1_______
+ # | |
+ # | |
+ # nested_group_1 nested_group_2
+ # | |
+ # | |
+ # nested_group_1_1 nested_group_2_1
+ #
+ #
+ # ... the following results are returned:
+ #
+ # * the user is a member of group 1
+ # => 'group_1',
+ # 'nested_group_1', nested_group_1_1',
+ # 'nested_group_2', 'nested_group_2_1'
+ #
+ # * the user is a member of nested_group_2
+ # => 'group1',
+ # 'nested_group_2', 'nested_group_2_1'
+ #
+ # * the user is a member of nested_group_2_1
+ # => 'group1',
+ # 'nested_group_2', 'nested_group_2_1'
+ #
+ # Returns an ActiveRecord::Relation.
+ def member_hierarchy(user_id)
+ paths = member_self_and_descendants(user_id).pluck('routes.path')
+
+ return none if paths.empty?
+
+ wheres = paths.map do |path|
+ "#{connection.quote(path)} = routes.path
+ OR
+ #{connection.quote(path)} LIKE CONCAT(routes.path, '/%')"
+ end
+
+ joins(:route).where(wheres.join(' OR '))
+ end
end
def full_name
diff --git a/app/models/container_repository.rb b/app/models/container_repository.rb
new file mode 100644
index 00000000000..9682df3a586
--- /dev/null
+++ b/app/models/container_repository.rb
@@ -0,0 +1,77 @@
+class ContainerRepository < ActiveRecord::Base
+ belongs_to :project
+
+ validates :name, length: { minimum: 0, allow_nil: false }
+ validates :name, uniqueness: { scope: :project_id }
+
+ delegate :client, to: :registry
+
+ before_destroy :delete_tags!
+
+ def registry
+ @registry ||= begin
+ token = Auth::ContainerRegistryAuthenticationService.full_access_token(path)
+
+ url = Gitlab.config.registry.api_url
+ host_port = Gitlab.config.registry.host_port
+
+ ContainerRegistry::Registry.new(url, token: token, path: host_port)
+ end
+ end
+
+ def path
+ @path ||= [project.full_path, name].select(&:present?).join('/')
+ end
+
+ def tag(tag)
+ ContainerRegistry::Tag.new(self, tag)
+ end
+
+ def manifest
+ @manifest ||= client.repository_tags(path)
+ end
+
+ def tags
+ return @tags if defined?(@tags)
+ return [] unless manifest && manifest['tags']
+
+ @tags = manifest['tags'].map do |tag|
+ ContainerRegistry::Tag.new(self, tag)
+ end
+ end
+
+ def blob(config)
+ ContainerRegistry::Blob.new(self, config)
+ end
+
+ def has_tags?
+ tags.any?
+ end
+
+ def root_repository?
+ name.empty?
+ end
+
+ def delete_tags!
+ return unless has_tags?
+
+ digests = tags.map { |tag| tag.digest }.to_set
+
+ digests.all? do |digest|
+ client.delete_repository_tag(self.path, digest)
+ end
+ end
+
+ def self.build_from_path(path)
+ self.new(project: path.repository_project,
+ name: path.repository_name)
+ end
+
+ def self.create_from_path!(path)
+ build_from_path(path).tap(&:save!)
+ end
+
+ def self.build_root_repository(project)
+ self.new(project: project, name: '')
+ end
+end
diff --git a/app/models/group.rb b/app/models/group.rb
index 60274386103..106084175ff 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -27,11 +27,14 @@ class Group < Namespace
validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
+ validates :two_factor_grace_period, presence: true, numericality: { greater_than_or_equal_to: 0 }
+
mount_uploader :avatar, AvatarUploader
has_many :uploads, as: :model, dependent: :destroy
after_create :post_create_hook
after_destroy :post_destroy_hook
+ after_save :update_two_factor_requirement
class << self
# Searches for groups matching the given query.
@@ -223,4 +226,12 @@ class Group < Namespace
type: public? ? 'O' : 'I' # Open vs Invite-only
}
end
+
+ protected
+
+ def update_two_factor_requirement
+ return unless require_two_factor_authentication_changed? || two_factor_grace_period_changed?
+
+ users.find_each(&:update_two_factor_requirement)
+ end
end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 10a5d9d2a24..f9704b0d754 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -40,6 +40,8 @@ class Issue < ActiveRecord::Base
scope :include_associations, -> { includes(:assignee, :labels, project: :namespace) }
+ after_save :expire_etag_cache
+
attr_spammable :title, spam_title: true
attr_spammable :description, spam_description: true
@@ -59,10 +61,6 @@ class Issue < ActiveRecord::Base
before_transition any => :closed do |issue|
issue.closed_at = Time.zone.now
end
-
- before_transition closed: any do |issue|
- issue.closed_at = nil
- end
end
def hook_attrs
@@ -256,4 +254,13 @@ class Issue < ActiveRecord::Base
def publicly_visible?
project.public? && !confidential?
end
+
+ def expire_etag_cache
+ key = Gitlab::Routing.url_helpers.rendered_title_namespace_project_issue_path(
+ project.namespace,
+ project,
+ self
+ )
+ Gitlab::EtagCaching::Store.new.touch(key)
+ end
end
diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb
index 446f9f8f8a7..483425cd30f 100644
--- a/app/models/members/group_member.rb
+++ b/app/models/members/group_member.rb
@@ -3,11 +3,16 @@ class GroupMember < Member
belongs_to :group, foreign_key: 'source_id'
+ delegate :update_two_factor_requirement, to: :user
+
# Make sure group member points only to group as it source
default_value_for :source_type, SOURCE_TYPE
validates :source_type, format: { with: /\ANamespace\z/ }
default_scope { where(source_type: SOURCE_TYPE) }
+ after_create :update_two_factor_requirement, unless: :invite?
+ after_destroy :update_two_factor_requirement, unless: :invite?
+
def self.access_level_roles
Gitlab::Access.options_with_owner
end
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 1d4b1f7d590..9bfa731785f 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -150,7 +150,7 @@ class Namespace < ActiveRecord::Base
end
def any_project_has_container_registry_tags?
- projects.any?(&:has_container_registry_tags?)
+ all_projects.any?(&:has_container_registry_tags?)
end
def send_update_instructions
@@ -214,6 +214,12 @@ class Namespace < ActiveRecord::Base
@old_repository_storage_paths ||= repository_storage_paths
end
+ # Includes projects from this namespace and projects from all subgroups
+ # that belongs to this namespace
+ def all_projects
+ Project.inside_path(full_path)
+ end
+
private
def repository_storage_paths
@@ -221,7 +227,7 @@ class Namespace < ActiveRecord::Base
# pending delete. Unscoping also get rids of the default order, which causes
# problems with SELECT DISTINCT.
Project.unscoped do
- projects.select('distinct(repository_storage)').to_a.map(&:repository_storage_path)
+ all_projects.select('distinct(repository_storage)').to_a.map(&:repository_storage_path)
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 112209fef30..571f9755e20 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -114,6 +114,8 @@ class Project < ActiveRecord::Base
has_one :kubernetes_service, dependent: :destroy, inverse_of: :project
has_one :prometheus_service, dependent: :destroy, inverse_of: :project
has_one :mock_ci_service, dependent: :destroy
+ has_one :mock_deployment_service, dependent: :destroy
+ has_one :mock_monitoring_service, dependent: :destroy
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
has_one :forked_from_project, through: :forked_project_link
@@ -157,6 +159,7 @@ class Project < ActiveRecord::Base
has_one :import_data, dependent: :destroy, class_name: "ProjectImportData"
has_one :project_feature, dependent: :destroy
has_one :statistics, class_name: 'ProjectStatistics', dependent: :delete
+ has_many :container_repositories, dependent: :destroy
has_many :commit_statuses, dependent: :destroy
has_many :pipelines, dependent: :destroy, class_name: 'Ci::Pipeline'
@@ -349,10 +352,15 @@ class Project < ActiveRecord::Base
end
def sort(method)
- if method == 'storage_size_desc'
+ case method.to_s
+ when 'storage_size_desc'
# storage_size is a joined column so we need to
# pass a string to avoid AR adding the table name
reorder('project_statistics.storage_size DESC, projects.id DESC')
+ when 'latest_activity_desc'
+ reorder(last_activity_at: :desc)
+ when 'latest_activity_asc'
+ reorder(last_activity_at: :asc)
else
order_by(method)
end
@@ -401,32 +409,15 @@ class Project < ActiveRecord::Base
@repository ||= Repository.new(path_with_namespace, self)
end
- def container_registry_path_with_namespace
- path_with_namespace.downcase
- end
-
- def container_registry_repository
- return unless Gitlab.config.registry.enabled
-
- @container_registry_repository ||= begin
- token = Auth::ContainerRegistryAuthenticationService.full_access_token(container_registry_path_with_namespace)
- url = Gitlab.config.registry.api_url
- host_port = Gitlab.config.registry.host_port
- registry = ContainerRegistry::Registry.new(url, token: token, path: host_port)
- registry.repository(container_registry_path_with_namespace)
- end
- end
-
- def container_registry_repository_url
+ def container_registry_url
if Gitlab.config.registry.enabled
- "#{Gitlab.config.registry.host_port}/#{container_registry_path_with_namespace}"
+ "#{Gitlab.config.registry.host_port}/#{path_with_namespace.downcase}"
end
end
def has_container_registry_tags?
- return unless container_registry_repository
-
- container_registry_repository.tags.any?
+ container_repositories.to_a.any?(&:has_tags?) ||
+ has_root_container_repository_tags?
end
def commit(ref = 'HEAD')
@@ -917,10 +908,10 @@ class Project < ActiveRecord::Base
expire_caches_before_rename(old_path_with_namespace)
if has_container_registry_tags?
- Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present"
+ Rails.logger.error "Project #{old_path_with_namespace} cannot be renamed because container registry tags are present!"
- # we currently doesn't support renaming repository if it contains tags in container registry
- raise StandardError.new('Project cannot be renamed, because tags are present in its container registry')
+ # we currently doesn't support renaming repository if it contains images in container registry
+ raise StandardError.new('Project cannot be renamed, because images are present in its container registry')
end
if gitlab_shell.mv_repository(repository_storage_path, old_path_with_namespace, new_path_with_namespace)
@@ -1110,10 +1101,6 @@ class Project < ActiveRecord::Base
self.runners_token && ActiveSupport::SecurityUtils.variable_size_secure_compare(token, self.runners_token)
end
- def build_coverage_enabled?
- build_coverage_regex.present?
- end
-
def build_timeout_in_minutes
build_timeout / 60
end
@@ -1267,7 +1254,7 @@ class Project < ActiveRecord::Base
]
if container_registry_enabled?
- variables << { key: 'CI_REGISTRY_IMAGE', value: container_registry_repository_url, public: true }
+ variables << { key: 'CI_REGISTRY_IMAGE', value: container_registry_url, public: true }
end
variables
@@ -1400,4 +1387,15 @@ class Project < ActiveRecord::Base
Project.unscoped.where(pending_delete: true).find_by_full_path(path_with_namespace)
end
+
+ ##
+ # This method is here because of support for legacy container repository
+ # which has exactly the same path like project does, but which might not be
+ # persisted in `container_repositories` table.
+ #
+ def has_root_container_repository_tags?
+ return false unless Gitlab.config.registry.enabled
+
+ ContainerRepository.build_root_repository(self).has_tags?
+ end
end
diff --git a/app/models/project_services/mock_deployment_service.rb b/app/models/project_services/mock_deployment_service.rb
new file mode 100644
index 00000000000..59a3811ce5d
--- /dev/null
+++ b/app/models/project_services/mock_deployment_service.rb
@@ -0,0 +1,18 @@
+class MockDeploymentService < DeploymentService
+ def title
+ 'Mock deployment'
+ end
+
+ def description
+ 'Mock deployment service'
+ end
+
+ def self.to_param
+ 'mock_deployment'
+ end
+
+ # No terminals support
+ def terminals(environment)
+ []
+ end
+end
diff --git a/app/models/project_services/mock_monitoring_service.rb b/app/models/project_services/mock_monitoring_service.rb
new file mode 100644
index 00000000000..dd04e04e198
--- /dev/null
+++ b/app/models/project_services/mock_monitoring_service.rb
@@ -0,0 +1,17 @@
+class MockMonitoringService < MonitoringService
+ def title
+ 'Mock monitoring'
+ end
+
+ def description
+ 'Mock monitoring service'
+ end
+
+ def self.to_param
+ 'mock_monitoring'
+ end
+
+ def metrics(environment)
+ JSON.parse(File.read(Rails.root + 'spec/fixtures/metrics.json'))
+ end
+end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 6b2383b73eb..1293cb1d486 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -6,6 +6,8 @@ class Repository
attr_accessor :path_with_namespace, :project
+ delegate :ref_name_for_sha, to: :raw_repository
+
CommitError = Class.new(StandardError)
CreateTreeError = Class.new(StandardError)
@@ -59,7 +61,7 @@ class Repository
def raw_repository
return nil unless path_with_namespace
- @raw_repository ||= Gitlab::Git::Repository.new(path_to_repo)
+ @raw_repository ||= initialize_raw_repository
end
# Return absolute path to repository
@@ -146,12 +148,7 @@ class Repository
# may cause the branch to "disappear" erroneously or have the wrong SHA.
#
# See: https://github.com/libgit2/libgit2/issues/1534 and https://gitlab.com/gitlab-org/gitlab-ce/issues/15392
- raw_repo =
- if fresh_repo
- Gitlab::Git::Repository.new(path_to_repo)
- else
- raw_repository
- end
+ raw_repo = fresh_repo ? initialize_raw_repository : raw_repository
raw_repo.find_branch(name)
end
@@ -505,9 +502,7 @@ class Repository
end
end
- def branch_names
- branches.map(&:name)
- end
+ delegate :branch_names, to: :raw_repository
cache_method :branch_names, fallback: []
delegate :tag_names, to: :raw_repository
@@ -707,14 +702,6 @@ class Repository
end
end
- def ref_name_for_sha(ref_path, sha)
- args = %W(#{Gitlab.config.git.bin_path} for-each-ref --count=1 #{ref_path} --contains #{sha})
-
- # Not found -> ["", 0]
- # Found -> ["b8d95eb4969eefacb0a58f6a28f6803f8070e7b9 commit\trefs/environments/production/77\n", 0]
- Gitlab::Popen.popen(args, path_to_repo).first.split.last
- end
-
def refs_contains_sha(ref_type, sha)
args = %W(#{Gitlab.config.git.bin_path} #{ref_type} --contains #{sha})
names = Gitlab::Popen.popen(args, path_to_repo).first
@@ -1168,4 +1155,8 @@ class Repository
def repository_storage_path
@project.repository_storage_path
end
+
+ def initialize_raw_repository
+ Gitlab::Git::Repository.new(project.repository_storage, path_with_namespace + '.git')
+ end
end
diff --git a/app/models/service.rb b/app/models/service.rb
index 54550177744..5a0ec58d193 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -238,7 +238,9 @@ class Service < ActiveRecord::Base
slack
teamcity
]
- service_names << 'mock_ci' if Rails.env.development?
+ if Rails.env.development?
+ service_names += %w[mock_ci mock_deployment mock_monitoring]
+ end
service_names.sort_by(&:downcase)
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 95a766f2ede..87eeee204f8 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -89,7 +89,8 @@ class User < ActiveRecord::Base
has_many :subscriptions, dependent: :destroy
has_many :recent_events, -> { order "id DESC" }, foreign_key: :author_id, class_name: "Event"
has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy
- has_one :abuse_report, dependent: :destroy
+ has_one :abuse_report, dependent: :destroy, foreign_key: :user_id
+ has_many :reported_abuse_reports, dependent: :destroy, foreign_key: :reporter_id, class_name: "AbuseReport"
has_many :spam_logs, dependent: :destroy
has_many :builds, dependent: :nullify, class_name: 'Ci::Build'
has_many :pipelines, dependent: :nullify, class_name: 'Ci::Pipeline'
@@ -484,6 +485,14 @@ class User < ActiveRecord::Base
Group.member_descendants(id)
end
+ def all_expanded_groups
+ Group.member_hierarchy(id)
+ end
+
+ def expanded_groups_requiring_two_factor_authentication
+ all_expanded_groups.where(require_two_factor_authentication: true)
+ end
+
def nested_groups_projects
Project.joins(:namespace).where('namespaces.parent_id IS NOT NULL').
member_descendants(id)
@@ -955,6 +964,15 @@ class User < ActiveRecord::Base
self.admin = (new_level == 'admin')
end
+ def update_two_factor_requirement
+ periods = expanded_groups_requiring_two_factor_authentication.pluck(:two_factor_grace_period)
+
+ self.require_two_factor_authentication_from_group = periods.any?
+ self.two_factor_grace_period = periods.min || User.column_defaults['two_factor_grace_period']
+
+ save
+ end
+
protected
# override, from Devise::Validatable