summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-02-06 18:08:54 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-02-06 18:08:54 +0000
commit0d6fa033121a9bef708b8f2de186c4034c61d4a3 (patch)
tree851d65a09efbffa114c9a273e590d55cfb1436ab /app
parent0eb3d2f799ce4f4de87fb9fc6fd98e592323bc89 (diff)
downloadgitlab-ce-0d6fa033121a9bef708b8f2de186c4034c61d4a3.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/main.js4
-rw-r--r--app/controllers/user_callouts_controller.rb5
-rw-r--r--app/graphql/types/permission_types/project.rb3
-rw-r--r--app/helpers/markup_helper.rb2
-rw-r--r--app/helpers/user_callouts_helper.rb9
-rw-r--r--app/models/ci/bridge.rb122
-rw-r--r--app/models/concerns/prometheus_adapter.rb7
-rw-r--r--app/models/environment.rb9
-rw-r--r--app/models/project.rb1
-rw-r--r--app/models/prometheus_alert.rb81
-rw-r--r--app/models/user.rb7
-rw-r--r--app/models/user_callout.rb3
-rw-r--r--app/views/clusters/clusters/_cluster.html.haml2
-rw-r--r--app/views/layouts/_page.html.haml1
-rw-r--r--app/views/shared/_check_recovery_settings.html.haml6
15 files changed, 256 insertions, 6 deletions
diff --git a/app/assets/javascripts/main.js b/app/assets/javascripts/main.js
index 1e07469bd7a..8fb973b1c1f 100644
--- a/app/assets/javascripts/main.js
+++ b/app/assets/javascripts/main.js
@@ -36,6 +36,7 @@ import initSearchAutocomplete from './search_autocomplete';
import GlFieldErrors from './gl_field_errors';
import initUserPopovers from './user_popovers';
import initBroadcastNotifications from './broadcast_notification';
+import PersistentUserCallout from './persistent_user_callout';
import { initUserTracking } from './tracking';
import { __ } from './locale';
@@ -108,6 +109,9 @@ function deferredInitialisation() {
initUserTracking();
initBroadcastNotifications();
+ const recoverySettingsCallout = document.querySelector('.js-recovery-settings-callout');
+ PersistentUserCallout.factory(recoverySettingsCallout);
+
if (document.querySelector('.search')) initSearchAutocomplete();
addSelectOnFocusBehaviour('.js-select-on-focus');
diff --git a/app/controllers/user_callouts_controller.rb b/app/controllers/user_callouts_controller.rb
index ebf1dd8ca02..4ee75218db1 100644
--- a/app/controllers/user_callouts_controller.rb
+++ b/app/controllers/user_callouts_controller.rb
@@ -2,7 +2,10 @@
class UserCalloutsController < ApplicationController
def create
- if ensure_callout.persisted?
+ callout = ensure_callout
+
+ if callout.persisted?
+ callout.update(dismissed_at: Time.now)
respond_to do |format|
format.json { head :ok }
end
diff --git a/app/graphql/types/permission_types/project.rb b/app/graphql/types/permission_types/project.rb
index 094c72fa812..f773fce0c63 100644
--- a/app/graphql/types/permission_types/project.rb
+++ b/app/graphql/types/permission_types/project.rb
@@ -16,7 +16,8 @@ module Types
:create_deployment, :push_to_delete_protected_branch,
:admin_wiki, :admin_project, :update_pages,
:admin_remote_mirror, :create_label, :update_wiki, :destroy_wiki,
- :create_pages, :destroy_pages, :read_pages_content, :admin_operations
+ :create_pages, :destroy_pages, :read_pages_content, :admin_operations,
+ :read_merge_request
permission_field :create_snippet
diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb
index e42ea3861b8..a0228c6bd94 100644
--- a/app/helpers/markup_helper.rb
+++ b/app/helpers/markup_helper.rb
@@ -153,7 +153,7 @@ module MarkupHelper
other_markup_unsafe(file_name, text, context)
end
rescue StandardError => e
- Gitlab::ErrorTracking.track_exception(e, project_id: @project&.id, file_name: file_name, context: context)
+ Gitlab::ErrorTracking.track_exception(e, project_id: @project&.id, file_name: file_name)
simple_format(text)
end
diff --git a/app/helpers/user_callouts_helper.rb b/app/helpers/user_callouts_helper.rb
index b3eee25674b..ab691916706 100644
--- a/app/helpers/user_callouts_helper.rb
+++ b/app/helpers/user_callouts_helper.rb
@@ -22,6 +22,9 @@ module UserCalloutsHelper
def render_dashboard_gold_trial(user)
end
+ def render_account_recovery_regular_check
+ end
+
def show_suggest_popover?
!user_dismissed?(SUGGEST_POPOVER_DISMISSED)
end
@@ -32,8 +35,10 @@ module UserCalloutsHelper
private
- def user_dismissed?(feature_name)
- current_user&.callouts&.find_by(feature_name: UserCallout.feature_names[feature_name])
+ def user_dismissed?(feature_name, ignore_dismissal_earlier_than = nil)
+ return false unless current_user
+
+ current_user.dismissed_callout?(feature_name: feature_name, ignore_dismissal_earlier_than: ignore_dismissal_earlier_than)
end
end
diff --git a/app/models/ci/bridge.rb b/app/models/ci/bridge.rb
index e6d41dd2779..abd59741e89 100644
--- a/app/models/ci/bridge.rb
+++ b/app/models/ci/bridge.rb
@@ -4,19 +4,78 @@ module Ci
class Bridge < Ci::Processable
include Ci::Contextable
include Ci::PipelineDelegator
+ include Ci::Metadatable
include Importable
include AfterCommitQueue
include HasRef
include Gitlab::Utils::StrongMemoize
+ InvalidBridgeTypeError = Class.new(StandardError)
+
belongs_to :project
belongs_to :trigger_request
+ has_many :sourced_pipelines, class_name: "::Ci::Sources::Pipeline",
+ foreign_key: :source_job_id
+
validates :ref, presence: true
+ # rubocop:disable Cop/ActiveRecordSerialize
+ serialize :options
+ serialize :yaml_variables, ::Gitlab::Serializer::Ci::Variables
+ # rubocop:enable Cop/ActiveRecordSerialize
+
+ state_machine :status do
+ event :manual do
+ transition all => :manual
+ end
+
+ event :scheduled do
+ transition all => :scheduled
+ end
+ end
+
def self.retry(bridge, current_user)
raise NotImplementedError
end
+ def inherit_status_from_downstream!(pipeline)
+ case pipeline.status
+ when 'success'
+ self.success!
+ when 'failed', 'canceled', 'skipped'
+ self.drop!
+ else
+ false
+ end
+ end
+
+ def downstream_pipeline_params
+ return child_params if triggers_child_pipeline?
+ return cross_project_params if downstream_project.present?
+
+ {}
+ end
+
+ def downstream_project
+ strong_memoize(:downstream_project) do
+ if downstream_project_path
+ ::Project.find_by_full_path(downstream_project_path)
+ elsif triggers_child_pipeline?
+ project
+ end
+ end
+ end
+
+ def downstream_project_path
+ strong_memoize(:downstream_project_path) do
+ options&.dig(:trigger, :project)
+ end
+ end
+
+ def triggers_child_pipeline?
+ yaml_for_downstream.present?
+ end
+
def tags
[:bridge]
end
@@ -55,7 +114,68 @@ module Ci
end
def yaml_for_downstream
- nil
+ strong_memoize(:yaml_for_downstream) do
+ includes = options&.dig(:trigger, :include)
+ YAML.dump('include' => includes) if includes
+ end
+ end
+
+ def target_ref
+ branch = options&.dig(:trigger, :branch)
+ return unless branch
+
+ scoped_variables.to_runner_variables.yield_self do |all_variables|
+ ::ExpandVariables.expand(branch, all_variables)
+ end
+ end
+
+ def dependent?
+ strong_memoize(:dependent) do
+ options&.dig(:trigger, :strategy) == 'depend'
+ end
+ end
+
+ def downstream_variables
+ variables = scoped_variables.concat(pipeline.persisted_variables)
+
+ variables.to_runner_variables.yield_self do |all_variables|
+ yaml_variables.to_a.map do |hash|
+ { key: hash[:key], value: ::ExpandVariables.expand(hash[:value], all_variables) }
+ end
+ end
+ end
+
+ private
+
+ def cross_project_params
+ {
+ project: downstream_project,
+ source: :pipeline,
+ target_revision: {
+ ref: target_ref || downstream_project.default_branch
+ },
+ execute_params: { ignore_skip_ci: true }
+ }
+ end
+
+ def child_params
+ parent_pipeline = pipeline
+
+ {
+ project: project,
+ source: :parent_pipeline,
+ target_revision: {
+ ref: parent_pipeline.ref,
+ checkout_sha: parent_pipeline.sha,
+ before: parent_pipeline.before_sha,
+ source_sha: parent_pipeline.source_sha,
+ target_sha: parent_pipeline.target_sha
+ },
+ execute_params: {
+ ignore_skip_ci: true,
+ bridge: self
+ }
+ }
end
end
end
diff --git a/app/models/concerns/prometheus_adapter.rb b/app/models/concerns/prometheus_adapter.rb
index 3deb7edba58..2c171eecbd5 100644
--- a/app/models/concerns/prometheus_adapter.rb
+++ b/app/models/concerns/prometheus_adapter.rb
@@ -58,5 +58,12 @@ module PrometheusAdapter
def build_query_args(*args)
args.map { |arg| arg.respond_to?(:id) ? arg.id : arg }
end
+
+ def clear_prometheus_reactive_cache!(query_name, *args)
+ query_class = query_klass_for(query_name)
+ query_args = build_query_args(*args)
+
+ clear_reactive_cache!(query_class.name, *query_args)
+ end
end
end
diff --git a/app/models/environment.rb b/app/models/environment.rb
index 2d480345b5a..b29543ded32 100644
--- a/app/models/environment.rb
+++ b/app/models/environment.rb
@@ -11,6 +11,7 @@ class Environment < ApplicationRecord
has_many :deployments, -> { visible }, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent
has_many :successful_deployments, -> { success }, class_name: 'Deployment'
+ has_many :prometheus_alerts, inverse_of: :environment
has_one :last_deployment, -> { success.order('deployments.id DESC') }, class_name: 'Deployment'
has_one :last_deployable, through: :last_deployment, source: 'deployable', source_type: 'CommitStatus'
@@ -105,6 +106,14 @@ class Environment < ApplicationRecord
find_or_create_by(name: name)
end
+ def clear_prometheus_reactive_cache!(query_name)
+ cluster_prometheus_adapter&.clear_prometheus_reactive_cache!(query_name, self)
+ end
+
+ def cluster_prometheus_adapter
+ @cluster_prometheus_adapter ||= ::Gitlab::Prometheus::Adapter.new(project, deployment_platform&.cluster).cluster_prometheus_adapter
+ end
+
def predefined_variables
Gitlab::Ci::Variables::Collection.new
.append(key: 'CI_ENVIRONMENT_NAME', value: name)
diff --git a/app/models/project.rb b/app/models/project.rb
index 667618ae1bd..78c3114ce9c 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -244,6 +244,7 @@ class Project < ApplicationRecord
has_many :management_clusters, class_name: 'Clusters::Cluster', foreign_key: :management_project_id, inverse_of: :management_project
has_many :prometheus_metrics
+ has_many :prometheus_alerts, inverse_of: :project
# Container repositories need to remove data from the container registry,
# which is not managed by the DB. Hence we're still using dependent: :destroy
diff --git a/app/models/prometheus_alert.rb b/app/models/prometheus_alert.rb
new file mode 100644
index 00000000000..1014231102f
--- /dev/null
+++ b/app/models/prometheus_alert.rb
@@ -0,0 +1,81 @@
+# frozen_string_literal: true
+
+class PrometheusAlert < ApplicationRecord
+ include Sortable
+
+ OPERATORS_MAP = {
+ lt: "<",
+ eq: "==",
+ gt: ">"
+ }.freeze
+
+ belongs_to :environment, validate: true, inverse_of: :prometheus_alerts
+ belongs_to :project, validate: true, inverse_of: :prometheus_alerts
+ belongs_to :prometheus_metric, validate: true, inverse_of: :prometheus_alerts
+
+ has_many :prometheus_alert_events, inverse_of: :prometheus_alert
+ has_many :related_issues, through: :prometheus_alert_events
+
+ after_save :clear_prometheus_adapter_cache!
+ after_destroy :clear_prometheus_adapter_cache!
+
+ validates :environment, :project, :prometheus_metric, presence: true
+ validate :require_valid_environment_project!
+ validate :require_valid_metric_project!
+
+ enum operator: { lt: 0, eq: 1, gt: 2 }
+
+ delegate :title, :query, to: :prometheus_metric
+
+ scope :for_metric, -> (metric) { where(prometheus_metric: metric) }
+ scope :for_project, -> (project) { where(project_id: project) }
+ scope :for_environment, -> (environment) { where(environment_id: environment) }
+
+ def self.distinct_projects
+ sub_query = self.group(:project_id).select(1)
+ self.from(sub_query)
+ end
+
+ def self.operator_to_enum(op)
+ OPERATORS_MAP.invert.fetch(op)
+ end
+
+ def full_query
+ "#{query} #{computed_operator} #{threshold}"
+ end
+
+ def computed_operator
+ OPERATORS_MAP.fetch(operator.to_sym)
+ end
+
+ def to_param
+ {
+ "alert" => title,
+ "expr" => full_query,
+ "for" => "5m",
+ "labels" => {
+ "gitlab" => "hook",
+ "gitlab_alert_id" => prometheus_metric_id
+ }
+ }
+ end
+
+ private
+
+ def clear_prometheus_adapter_cache!
+ environment.clear_prometheus_reactive_cache!(:additional_metrics_environment)
+ end
+
+ def require_valid_environment_project!
+ return if project == environment&.project
+
+ errors.add(:environment, "invalid project")
+ end
+
+ def require_valid_metric_project!
+ return if prometheus_metric&.common?
+ return if project == prometheus_metric&.project
+
+ errors.add(:prometheus_metric, "invalid project")
+ end
+end
diff --git a/app/models/user.rb b/app/models/user.rb
index 3512e663f4a..aa7e825d516 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1644,6 +1644,13 @@ class User < ApplicationRecord
end
# End of signup_flow experiment methods
+ def dismissed_callout?(feature_name:, ignore_dismissal_earlier_than: nil)
+ callouts = self.callouts.with_feature_name(feature_name)
+ callouts = callouts.with_dismissed_after(ignore_dismissal_earlier_than) if ignore_dismissal_earlier_than
+
+ callouts.any?
+ end
+
# @deprecated
alias_method :owned_or_masters_groups, :owned_or_maintainers_groups
diff --git a/app/models/user_callout.rb b/app/models/user_callout.rb
index 027ee44c6a9..82f82356cb4 100644
--- a/app/models/user_callout.rb
+++ b/app/models/user_callout.rb
@@ -12,4 +12,7 @@ class UserCallout < ApplicationRecord
presence: true,
uniqueness: { scope: :user_id },
inclusion: { in: UserCallout.feature_names.keys }
+
+ scope :with_feature_name, -> (feature_name) { where(feature_name: UserCallout.feature_names[feature_name]) }
+ scope :with_dismissed_after, -> (dismissed_after) { where('dismissed_at > ?', dismissed_after) }
end
diff --git a/app/views/clusters/clusters/_cluster.html.haml b/app/views/clusters/clusters/_cluster.html.haml
index 04afc38a056..9b6c0c20080 100644
--- a/app/views/clusters/clusters/_cluster.html.haml
+++ b/app/views/clusters/clusters/_cluster.html.haml
@@ -4,6 +4,8 @@
.table-mobile-header{ role: "rowheader" }= s_("ClusterIntegration|Kubernetes cluster")
.table-mobile-content
= cluster.item_link(clusterable, html_options: { data: { qa_selector: 'cluster', qa_cluster_name: cluster.name } })
+ - if cluster.status_name == :creating
+ .spinner.ml-2.align-middle.has-tooltip{ title: s_("ClusterIntegration|Cluster being created") }
- unless cluster.enabled?
%span.badge.badge-danger Connection disabled
.table-section.section-25
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 443a73f5cce..2b2ffd6abeb 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -11,6 +11,7 @@
= render "layouts/nav/classification_level_banner"
= yield :flash_message
= render "shared/ping_consent"
+ = render_account_recovery_regular_check
- unless @hide_breadcrumbs
= render "layouts/nav/breadcrumbs"
.d-flex
diff --git a/app/views/shared/_check_recovery_settings.html.haml b/app/views/shared/_check_recovery_settings.html.haml
new file mode 100644
index 00000000000..e3de34a5ab9
--- /dev/null
+++ b/app/views/shared/_check_recovery_settings.html.haml
@@ -0,0 +1,6 @@
+.gl-alert.gl-alert-warning.js-recovery-settings-callout{ role: 'alert', data: { feature_id: "account_recovery_regular_check", dismiss_endpoint: user_callouts_path, defer_links: "true" } }
+ %button.js-close.gl-alert-dismiss.gl-cursor-pointer{ type: 'button', 'aria-label' => _('Dismiss') }
+ = sprite_icon('close', size: 16, css_class: 'gl-icon')
+ .gl-alert-body
+ - account_link_start = '<a class="deferred-link" href="%{url}">'.html_safe % { url: profile_account_path }
+ = _("Please ensure your account's %{account_link_start}recovery settings%{account_link_end} are up to date.").html_safe % { account_link_start: account_link_start, account_link_end: '</a>'.html_safe }